Merge remote-tracking branch 'origin/main'

This commit is contained in:
2026-02-04 17:24:34 +08:00
9 changed files with 207 additions and 104 deletions

View File

@@ -8,8 +8,8 @@ import lombok.Getter;
* 固定配置文件元数据响应中需要填充的字典标签字段映射关系
*
* 映射关系:
* - disciplineTypeDictClass/disciplineDictValue → DISCIPLINE_TYPE
* - fileTypeDictClass/fileTypeDictValue → ALL_FILE_TYPE
* - disciplineTypeDictClass/disciplineDictValue/disciplineDictName → DISCIPLINE_TYPE
* - fileTypeDictClass/fileTypeDictValue/fileTypeDictName → ALL_FILE_TYPE
*/
@Getter
@AllArgsConstructor
@@ -20,14 +20,14 @@ public enum FileDictTagEnum {
* 字段: disciplineTypeDictClass, disciplineDictValue
* dictClass: DISCIPLINE_TYPE
*/
DISCIPLINE_TYPE("DISCIPLINE_TYPE", "disciplineTypeDictClass", "disciplineDictValue"),
DISCIPLINE_TYPE("DISCIPLINE_TYPE", "disciplineTypeDictClass", "disciplineDictValue", "disciplineDictName"),
/**
* 文件类型标签
* 字段: fileTypeDictClass, fileTypeDictValue
* dictClass: ALL_FILE_TYPE
*/
FILE_TYPE("ALL_FILE_TYPE", "fileTypeDictClass", "fileTypeDictValue");
FILE_TYPE("ALL_FILE_TYPE", "fileTypeDictClass", "fileTypeDictValue", "fileTypeDictName");
/**
* 字典分类(对应数据库中的 dictClass
@@ -43,6 +43,11 @@ public enum FileDictTagEnum {
* dictValue 字段名(完整字段名,如 disciplineDictValue
*/
private final String dictValueFieldName;
/**
* dictName 字段名(完整字段名,如 disciplineDictName
*/
private final String dictNameFieldName;
/**
* 根据 dictClass 获取对应的枚举

View File

@@ -56,4 +56,26 @@ public class BaseResp {
@Schema(description= "文件业务类型1模型文件 2仿真报告、3计算文件、4曲线文件、5云图文件6网格文件7计算过程文件")
@TableField("fileType")
private Integer fileType;
// ----------------------------------------------------------------
// 很重要,用于设置标签
@Schema(description = "文件类型字典类")
private String fileTypeDictClass;
@Schema(description = "文件类型字典值")
private String fileTypeDictValue;
@Schema(description = "文件类型字典名称")
private String fileTypeDictName;
@Schema(description = "学科类型字典类")
private String disciplineTypeDictClass;
@Schema(description = "学科类型字典值")
private String disciplineDictValue;
@Schema(description = "学科类型字典名称")
private String disciplineDictName;
// ----------------------------------------------------------------
}

View File

@@ -63,21 +63,6 @@ public class FileMetadataInfoResp extends BaseResp implements Serializable {
@Schema(description= "文件业务类型1模型文件 2仿真报告、3计算文件、4曲线文件、5云图文件6网格文件7计算过程文件")
private Integer fileType;
// ----------------------------------------------------------------
// 很重要,用于设置标签
@Schema(description = "文件类型字典类")
private String fileTypeDictClass;
@Schema(description = "文件类型字典值")
private String fileTypeDictValue;
@Schema(description = "学科类型字典类")
private String disciplineTypeDictClass;
@Schema(description = "学科类型字典值")
private String disciplineDictValue;
// ----------------------------------------------------------------
@Schema(description = "数据类型1-文件2-文件夹")
private Integer dataType;

View File

@@ -22,10 +22,16 @@ public class FileDictTagsAggDTO implements Serializable {
@Schema(description = "文件类型字典值(逗号分隔)")
private String fileTypeDictValue;
@Schema(description = "文件类型字典名称(逗号分隔)")
private String fileTypeDictName;
@Schema(description = "学科类型字典类")
private String disciplineTypeDictClass;
@Schema(description = "学科类型字典值(逗号分隔)")
private String disciplineDictValue;
@Schema(description = "学科类型字典名称(逗号分隔)")
private String disciplineDictName;
}

View File

@@ -1,9 +1,7 @@
package com.sdm.data.service;
import com.sdm.data.model.dto.FileDictTagsAggDTO;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/**
* 文件字典标签聚合查询服务
@@ -12,10 +10,14 @@ import java.util.Map;
public interface IFileDictTagQueryService {
/**
* 按文件ID批量查询并聚合字典标签
* @param fileIds 文件ID列表
* @return fileId -> 聚合结果
* 泛型方法:为包含标签字段的响应对象列表批量填充文件字典标签
* 要求 T 至少包含如下字段的 getter/setter
* - getId()
* - setFileTypeDictClass(String)
* - setFileTypeDictValue(String)
* - setDisciplineTypeDictClass(String)
* - setDisciplineDictValue(String)
*/
Map<Long, FileDictTagsAggDTO> queryByFileIds(List<Long> fileIds);
<T> void fillFileTagsForRespList(List<T> respList, Function<T, Long> idGetter);
}

View File

@@ -21,10 +21,7 @@ import com.sdm.data.model.entity.FileStorage;
import com.sdm.common.entity.req.data.GetSimulationTaskFileReq;
import com.sdm.data.model.req.QueryBigFileReq;
import com.sdm.common.entity.resp.data.SimulationTaskResultCurveResp;
import com.sdm.data.service.DataStorageAnalysis;
import com.sdm.data.service.IDataAnalysisService;
import com.sdm.data.service.IFileMetadataInfoService;
import com.sdm.data.service.IMinioService;
import com.sdm.data.service.*;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -60,6 +57,9 @@ public class DataAnalysisServiceImpl implements IDataAnalysisService {
@Autowired
private ExportOperate exportOperate;
@Autowired
IFileDictTagQueryService fileDictTagQueryService;
@Override
public SdmResponse<PageDataResp<List<SimulationTaskResultCurveResp>>> getSimulationTaskFile(GetSimulationTaskFileReq getSimulationTaskFileReq) {
// 1. 构造查询条件
@@ -102,6 +102,8 @@ public class DataAnalysisServiceImpl implements IDataAnalysisService {
FileMetadataHierarchyHelper::setFileHierarchy
);
fileDictTagQueryService.fillFileTagsForRespList(finalResultList, SimulationTaskResultCurveResp::getId);
// 4. 构造分页信息并返回
PageInfo<FileMetadataInfo> pageInfo = new PageInfo<>();
pageInfo.setTotal(pageDataResp.getTotal());

View File

@@ -31,6 +31,7 @@ import com.sdm.data.model.req.QueryBigFileReq;
import com.sdm.data.model.resp.FileStorageQuotaResp;
import com.sdm.data.model.resp.ListBigFileResp;
import com.sdm.data.service.*;
import com.sdm.data.service.IFileDictTagQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
@@ -77,6 +78,9 @@ public class DataStorageAnalysisImpl implements DataStorageAnalysis {
@Autowired
IFileTagRelService fileTagRelService;
@Autowired
IFileDictTagQueryService fileDictTagQueryService;
/**
* 根据节点类型获取存储空间占用(支持批量查询)
*/
@@ -442,6 +446,9 @@ public class DataStorageAnalysisImpl implements DataStorageAnalysis {
FileMetadataHierarchyHelper::setFileHierarchy
);
// 2.1 批量填充文件字典标签(文件类型、学科类型)
fileDictTagQueryService.fillFileTagsForRespList(finalResultList, ListBigFileResp::getId);
// 3. 构造分页信息并返回
PageInfo<FileMetadataInfo> pageInfo = new PageInfo<>();
pageInfo.setTotal(pageDataResp.getTotal());
@@ -451,6 +458,7 @@ public class DataStorageAnalysisImpl implements DataStorageAnalysis {
return PageUtils.getJsonObjectSdmResponse(finalResultList, pageInfo);
}
@Override
public SdmResponse<PageDataResp<List<FileStorage>>> listBigFile(QueryBigFileReq queryBigFileReq) {
PageInfo<FileStorage> fileStorages = getFileStorages(queryBigFileReq);

View File

@@ -14,6 +14,7 @@ import com.sdm.data.service.IFileTagRelService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Service;
import java.util.*;
@@ -31,8 +32,7 @@ public class FileDictTagQueryServiceImpl implements IFileDictTagQueryService {
private final IFileTagRelService fileTagRelService;
private final ISysConfigFeignClient sysConfigFeignClient;
@Override
public Map<Long, FileDictTagsAggDTO> queryByFileIds(List<Long> fileIds) {
private Map<Long, FileDictTagsAggDTO> queryByFileIds(List<Long> fileIds) {
if (CollectionUtils.isEmpty(fileIds)) {
return Collections.emptyMap();
}
@@ -81,8 +81,8 @@ public class FileDictTagQueryServiceImpl implements IFileDictTagQueryService {
(v1, v2) -> v1
));
// 3) fileId -> (dictClass -> Set<dictValue>)
Map<Long, Map<String, LinkedHashSet<String>>> agg = new HashMap<>();
// 3) fileId -> (dictClass -> 聚合数据:Set<dictValue> + Set<dictName>)
Map<Long, Map<String, DictAggData>> agg = new HashMap<>();
for (FileTagRel rel : fileTagRels) {
if (rel == null || rel.getFileId() == null || rel.getTagId() == null) {
continue;
@@ -94,9 +94,13 @@ public class FileDictTagQueryServiceImpl implements IFileDictTagQueryService {
if (!FileDictTagEnum.isConfigured(dict.dictClass)) {
continue;
}
agg.computeIfAbsent(rel.getFileId(), k -> new HashMap<>())
.computeIfAbsent(dict.dictClass, k -> new LinkedHashSet<>())
.add(dict.dictValue);
DictAggData data = agg
.computeIfAbsent(rel.getFileId(), k -> new HashMap<>())
.computeIfAbsent(dict.dictClass, k -> new DictAggData());
data.getDictValues().add(dict.dictValue);
if (dict.dictName != null) {
data.getDictNames().add(dict.dictName);
}
}
if (agg.isEmpty()) {
@@ -105,9 +109,9 @@ public class FileDictTagQueryServiceImpl implements IFileDictTagQueryService {
// 4) 转为 DTO目前只输出枚举配置的两类字段
Map<Long, FileDictTagsAggDTO> result = new HashMap<>();
for (Map.Entry<Long, Map<String, LinkedHashSet<String>>> e : agg.entrySet()) {
for (Map.Entry<Long, Map<String, DictAggData>> e : agg.entrySet()) {
Long fileId = e.getKey();
Map<String, LinkedHashSet<String>> classMap = e.getValue();
Map<String, DictAggData> classMap = e.getValue();
if (classMap == null || classMap.isEmpty()) {
continue;
}
@@ -115,9 +119,16 @@ public class FileDictTagQueryServiceImpl implements IFileDictTagQueryService {
FileDictTagsAggDTO dto = new FileDictTagsAggDTO();
dto.setFileId(fileId);
for (Map.Entry<String, LinkedHashSet<String>> classEntry : classMap.entrySet()) {
for (Map.Entry<String, DictAggData> classEntry : classMap.entrySet()) {
String dictClass = classEntry.getKey();
String dictValues = String.join(",", classEntry.getValue());
DictAggData data = classEntry.getValue();
if (data == null || data.getDictValues().isEmpty()) {
continue;
}
String dictValues = String.join(",", data.getDictValues());
String dictNames = data.getDictNames().isEmpty()
? null
: String.join(",", data.getDictNames());
FileDictTagEnum tagEnum = FileDictTagEnum.getByDictClass(dictClass);
if (tagEnum == null) {
continue;
@@ -125,9 +136,11 @@ public class FileDictTagQueryServiceImpl implements IFileDictTagQueryService {
if (tagEnum == FileDictTagEnum.FILE_TYPE) {
dto.setFileTypeDictClass(tagEnum.getDictClass());
dto.setFileTypeDictValue(dictValues);
dto.setFileTypeDictName(dictNames);
} else if (tagEnum == FileDictTagEnum.DISCIPLINE_TYPE) {
dto.setDisciplineTypeDictClass(tagEnum.getDictClass());
dto.setDisciplineDictValue(dictValues);
dto.setDisciplineDictName(dictNames);
}
}
result.put(fileId, dto);
@@ -135,5 +148,117 @@ public class FileDictTagQueryServiceImpl implements IFileDictTagQueryService {
return result;
}
}
@Override
public <T> void fillFileTagsForRespList(List<T> respList, Function<T, Long> idGetter) {
if (CollectionUtils.isEmpty(respList)) {
return;
}
// 收集文件ID
List<Long> fileIds = respList.stream()
.map(idGetter)
.filter(Objects::nonNull)
.distinct()
.toList();
if (CollectionUtils.isEmpty(fileIds)) {
return;
}
// 查询聚合标签
Map<Long, FileDictTagsAggDTO> fileIdToTagsMap = queryByFileIds(fileIds);
if (MapUtils.isEmpty(fileIdToTagsMap)) {
return;
}
// 通过反射,将标签字段填充到 Resp 对象中(基于 FileDictTagEnum 的配置)
for (T resp : respList) {
Long fileId = idGetter.apply(resp);
if (fileId == null) {
continue;
}
FileDictTagsAggDTO tags = fileIdToTagsMap.get(fileId);
if (tags == null) {
continue;
}
for (FileDictTagEnum tagEnum : FileDictTagEnum.values()) {
String dictValues = getDictValuesByEnum(tags, tagEnum);
String dictNames = getDictNamesByEnum(tags, tagEnum);
if (dictValues == null && dictNames == null) {
continue;
}
String dictClass = tagEnum.getDictClass();
String dictClassFieldName = tagEnum.getDictClassFieldName();
String dictValueFieldName = tagEnum.getDictValueFieldName();
String dictNameFieldName = tagEnum.getDictNameFieldName();
try {
// setXxxDictClass
String classSetterName = "set" + Character.toUpperCase(dictClassFieldName.charAt(0)) + dictClassFieldName.substring(1);
resp.getClass().getMethod(classSetterName, String.class)
.invoke(resp, dictClass);
// setXxxDictValue
if (dictValues != null) {
String valueSetterName = "set" + Character.toUpperCase(dictValueFieldName.charAt(0)) + dictValueFieldName.substring(1);
resp.getClass().getMethod(valueSetterName, String.class)
.invoke(resp, dictValues);
}
// setXxxDictName如果存在
if (dictNames != null) {
String nameSetterName = "set" + Character.toUpperCase(dictNameFieldName.charAt(0)) + dictNameFieldName.substring(1);
resp.getClass().getMethod(nameSetterName, String.class)
.invoke(resp, dictNames);
}
} catch (Exception e) {
log.warn("填充文件标签信息失败, fileId: {}, respClass: {}, dictClass: {}, error: {}",
fileId, resp.getClass().getName(), dictClass, e.getMessage());
}
}
}
}
/**
* 根据枚举类型,从聚合结果中取出对应的字典值
*/
private String getDictValuesByEnum(FileDictTagsAggDTO tags, FileDictTagEnum tagEnum) {
if (tags == null || tagEnum == null) {
return null;
}
if (tagEnum == FileDictTagEnum.FILE_TYPE) {
return tags.getFileTypeDictValue();
} else if (tagEnum == FileDictTagEnum.DISCIPLINE_TYPE) {
return tags.getDisciplineDictValue();
}
return null;
}
/**
* 根据枚举类型,从聚合结果中取出对应的字典名称
*/
private String getDictNamesByEnum(FileDictTagsAggDTO tags, FileDictTagEnum tagEnum) {
if (tags == null || tagEnum == null) {
return null;
}
if (tagEnum == FileDictTagEnum.FILE_TYPE) {
return tags.getFileTypeDictName();
} else if (tagEnum == FileDictTagEnum.DISCIPLINE_TYPE) {
return tags.getDisciplineDictName();
}
return null;
}
/**
* 内部聚合数据结构:同时持有 dictValue 和 dictName 的去重集合
*/
private static class DictAggData {
private final LinkedHashSet<String> dictValues = new LinkedHashSet<>();
private final LinkedHashSet<String> dictNames = new LinkedHashSet<>();
public LinkedHashSet<String> getDictValues() {
return dictValues;
}
public LinkedHashSet<String> getDictNames() {
return dictNames;
}
}
}

View File

@@ -806,6 +806,11 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
dto.setKnowledgeBaseName(extractRelativePath(fileMetadataInfoService.lambdaQuery().eq(FileMetadataInfo::getId, dto.getParentId()).one()));
return dto;
}).collect(Collectors.toList());
// 批量填充文件标签信息(仅对文件类型生效)
List<FileMetadataInfoResp> fileDtos = dtoList.stream()
.filter(dto -> dto != null && dto.getDataType() != null && DataTypeEnum.FILE.getValue() == dto.getDataType())
.toList();
fileDictTagQueryService.fillFileTagsForRespList(fileDtos, FileMetadataInfoResp::getId);
PageDataResp<List<FileStorage>> pageDataResp = searchResult.getData();
PageInfo page = new PageInfo();
page.setPageNum(pageDataResp.getCurrentPage());
@@ -892,7 +897,11 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
return dto;
}).collect(Collectors.toList());
fillFileTagsToDtos(dtoList);
// 批量填充文件标签信息(仅对文件类型生效)
List<FileMetadataInfoResp> fileDtos = dtoList.stream()
.filter(dto -> dto != null && dto.getDataType() != null && DataTypeEnum.FILE.getValue() == dto.getDataType())
.toList();
fileDictTagQueryService.fillFileTagsForRespList(fileDtos, FileMetadataInfoResp::getId);
PageInfo<FileMetadataInfoResp> page1 = new PageInfo<>(dtoList);
page1.setTotal(total);
return PageUtils.getJsonObjectSdmResponse(dtoList, page1);
@@ -2117,67 +2126,6 @@ public class MinioFileIDataFileServiceImpl implements IDataFileService {
/**
* 批量填充文件标签信息到 dtoList只处理 dataType=FILE 的 dto
*/
private void fillFileTagsToDtos(List<FileMetadataInfoResp> dtoList) {
if (CollectionUtils.isEmpty(dtoList)) {
return;
}
List<Long> fileIdsForTagQuery = dtoList.stream()
.filter(dto -> dto != null && dto.getId() != null && DataTypeEnum.FILE.getValue() == dto.getDataType())
.map(FileMetadataInfoResp::getId)
.distinct()
.toList();
Map<Long, FileDictTagsAggDTO> fileIdToTagsMap = fillFileTags(fileIdsForTagQuery);
if (MapUtils.isEmpty(fileIdToTagsMap)) {
return;
}
for (FileMetadataInfoResp dto : dtoList) {
if (dto == null || dto.getId() == null || DataTypeEnum.FILE.getValue() != dto.getDataType()) {
continue;
}
FileDictTagsAggDTO tags = fileIdToTagsMap.get(dto.getId());
if (tags == null) {
continue;
}
dto.setFileTypeDictClass(tags.getFileTypeDictClass());
dto.setFileTypeDictValue(tags.getFileTypeDictValue());
dto.setDisciplineTypeDictClass(tags.getDisciplineTypeDictClass());
dto.setDisciplineDictValue(tags.getDisciplineDictValue());
}
}
/**
* 批量填充文件标签信息到 FileMetadataInfoResp
* 从 file_tag_rel 和 simulation_data_dictionary 查询标签,按 dictClass 分组
* 格式:
* disciplineTypeDictClass: DISCIPLINE_TYPE,
* disciplineDictValue: '流体,机器人,动画'
*
* @param fileIds 文件ID列表
* @return fileId -> 标签聚合结果
*/
private Map<Long, FileDictTagsAggDTO> fillFileTags(List<Long> fileIds) {
if (CollectionUtils.isEmpty(fileIds)) {
return Collections.emptyMap();
}
List<Long> idList = fileIds.stream()
.filter(Objects::nonNull)
.distinct()
.toList();
if (CollectionUtils.isEmpty(idList)) {
return Collections.emptyMap();
}
Map<Long, FileDictTagsAggDTO> map = fileDictTagQueryService.queryByFileIds(idList);
return MapUtils.isEmpty(map) ? Collections.emptyMap() : map;
}
private void bindSimulationPool(UploadFilesReq req, FileMetadataInfo fileInfo) {
if (CollectionUtils.isEmpty(req.getSimulationPoolInfoList())) {
return;