From 742b77c10ca0e3dfcea7c1002fe820a2297e2fee Mon Sep 17 00:00:00 2001 From: gulongcheng <474084054@qq.com> Date: Tue, 23 Dec 2025 16:30:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataStorageAnalysisController.java | 7 +- .../service/impl/DataAnalysisServiceImpl.java | 125 +++++++++++------- .../service/impl/DataStorageAnalysisImpl.java | 70 ++++++---- .../impl/DimensionTemplateServiceImpl.java | 16 ++- 4 files changed, 136 insertions(+), 82 deletions(-) diff --git a/data/src/main/java/com/sdm/data/controller/DataStorageAnalysisController.java b/data/src/main/java/com/sdm/data/controller/DataStorageAnalysisController.java index 24d0b06b..f0bb1f43 100644 --- a/data/src/main/java/com/sdm/data/controller/DataStorageAnalysisController.java +++ b/data/src/main/java/com/sdm/data/controller/DataStorageAnalysisController.java @@ -1,6 +1,7 @@ package com.sdm.data.controller; import com.alibaba.fastjson2.JSONObject; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.sdm.common.common.SdmResponse; import com.sdm.common.entity.resp.PageDataResp; @@ -34,7 +35,7 @@ public class DataStorageAnalysisController { /** * 根据nodeId(项目nodeId)获取指定查询类型(queryNodeType)文件空间占用(近几个月、增量查询) * 查询项目存储空间占用:项目Uuid+项目queryNodeType - * 查询学科存储空间占用:项目Uuid+学科queryNodeType + * 查询学科存储空间占用:学科字典名:MVH+学科queryNodeType * * @param queryNodeType 需要统计的节点类型 * @param uuids 节点uuid @@ -44,7 +45,7 @@ public class DataStorageAnalysisController { @Operation(summary = "根据nodeId(项目nodeId)获取指定查询类型(queryNodeType)文件空间占用") public SdmResponse getNodeSizeByNodeType( @Parameter(description = "查询节点类型:project,discipline") @RequestParam(value = "queryNodeType", required = false) String queryNodeType, - @Parameter(description = "节点id:项目Uuid列表") @RequestParam(value = "uuids", required = false) List uuids, + @Parameter(description = "节点id:项目Uuid列表或者学科字典名") @RequestParam(value = "uuids", required = false) List uuids, @Parameter(description = "查询时间间隔(月)") @RequestParam(value = "intervalMonths", required = false) Integer intervalMonths, @Parameter(description = "增量查询指定的月:2025-06") @RequestParam(value = "targetYm", required = false) String targetYm ) { @@ -56,7 +57,7 @@ public class DataStorageAnalysisController { for (String uuid : uuids) { try { SdmResponse> sdmResponse = dataStorageAnalysis.getNodeSizeByNodeType(queryNodeType, uuid, intervalMonths, targetYm); - if (sdmResponse.getData() != null) { + if (CollectionUtils.isNotEmpty(sdmResponse.getData())) { result.add(sdmResponse.getData()); } } catch (Exception e) { diff --git a/data/src/main/java/com/sdm/data/service/impl/DataAnalysisServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/DataAnalysisServiceImpl.java index c9c9969b..7ee64057 100644 --- a/data/src/main/java/com/sdm/data/service/impl/DataAnalysisServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/DataAnalysisServiceImpl.java @@ -29,8 +29,8 @@ import java.io.ByteArrayInputStream; import java.io.InputStreamReader; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.stream.Collectors; @Service @Slf4j @@ -46,76 +46,103 @@ public class DataAnalysisServiceImpl implements IDataAnalysisService { @Override public SdmResponse>> getSimulationTaskFile(GetSimulationTaskFileReq getSimulationTaskFileReq) { + // 1. 构造查询条件 QueryBigFileReq queryBigFileReq = new QueryBigFileReq(); BeanUtils.copyProperties(getSimulationTaskFileReq, queryBigFileReq); queryBigFileReq.setFileBizType(List.of(getSimulationTaskFileReq.getFileBizType())); - if(ObjectUtils.isNotEmpty(getSimulationTaskFileReq.getUuid())){ + // 获取特定 UUID 对应的目录 ID + if (ObjectUtils.isNotEmpty(getSimulationTaskFileReq.getUuid())) { FileMetadataInfo fileMetadataInfo = fileMetadataInfoService.lambdaQuery() .eq(FileMetadataInfo::getRelatedResourceUuid, getSimulationTaskFileReq.getUuid()) .eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId()) .one(); - if(ObjectUtils.isNotEmpty(fileMetadataInfo)) { + if (ObjectUtils.isNotEmpty(fileMetadataInfo)) { queryBigFileReq.setDirId(fileMetadataInfo.getId()); } } - + // 2. 调用大文件查询服务 SdmResponse>> searchResult = dataStorageAnalysis.listBigFile(queryBigFileReq); PageDataResp> pageDataResp = searchResult.getData(); - List data = pageDataResp.getData(); - if(CollectionUtils.isEmpty(data)) { - List emptyData = new ArrayList<>(); - PageInfo emptyPageInfo =new PageInfo<>(); - emptyPageInfo.setTotal(0); - emptyPageInfo.setPageNum(0); - emptyPageInfo.setPageSize(0); - return PageUtils.getJsonObjectSdmResponse(emptyData,emptyPageInfo); - } - List fileIdList = data.stream().map(FileStorage::getFileId).toList(); + List fileStorages = pageDataResp.getData(); - List simulationTaskFileMetadataInfos = fileMetadataInfoService - .lambdaQuery() + // 如果数据为空,提前返回 + if (CollectionUtils.isEmpty(fileStorages)) { + return PageUtils.getJsonObjectSdmResponse(new ArrayList<>(), new PageInfo<>()); + } + + // 3. 获取当前页文件的详细元数据 + List fileIdList = fileStorages.stream().map(FileStorage::getFileId).toList(); + List currentFiles = fileMetadataInfoService.lambdaQuery() .eq(FileMetadataInfo::getTenantId, ThreadLocalContext.getTenantId()) .in(FileMetadataInfo::getId, fileIdList) .list(); - PageInfo pageInfo =new PageInfo<>(); + // 批量分层获取所有相关的父目录 + // key 是 ID,value 是对应的元数据实体。用于在内存中快速查找。 + Map parentCacheMap = new HashMap<>(); + // 当前需要去数据库查的父级 ID 集合 + Set nextFetchIds = currentFiles.stream() + .map(FileMetadataInfo::getParentId) + .filter(pid -> pid != null && pid != 0) + .collect(Collectors.toSet()); + + int safetyDepth = 0; // 防死循环计数器 + // 只要还有没查过的父 ID,且深度在合理范围内(10层),就继续批量查 + while (CollectionUtils.isNotEmpty(nextFetchIds) && safetyDepth < 10) { + // 一次性查出当前这一层所有的父节点信息 + List parents = fileMetadataInfoService.listByIds(nextFetchIds); + if (CollectionUtils.isEmpty(parents)) break; + + nextFetchIds = new HashSet<>(); // 重置,准备收集下一层 ID + for (FileMetadataInfo p : parents) { + parentCacheMap.put(p.getId(), p); + // 如果这个父节点还有上级,且我们之前没查过这个上级,就加进下一次查询列表 + if (p.getParentId() != null && p.getParentId() != 0 && !parentCacheMap.containsKey(p.getParentId())) { + nextFetchIds.add(p.getParentId()); + } + } + safetyDepth++; + } + + // 内存组装数据:将 FileMetadata 转换为 Response,并回溯层级信息 + List finalResultList = currentFiles.stream().map(file -> { + SimulationTaskResultCurveResp resp = new SimulationTaskResultCurveResp(); + BeanUtils.copyProperties(file, resp); + resp.setFormatFileSize(FileSizeUtils.formatFileSize(BigDecimal.valueOf(file.getFileSize()))); + + // 从 parentCacheMap 中回溯,设置项目、阶段、专业信息 + Long pid = file.getParentId(); + int limit = 0; + // 这里的循环完全在内存中进行,速度极快且不产生日志 + while (pid != null && parentCacheMap.containsKey(pid) && limit < 15) { + FileMetadataInfo folder = parentCacheMap.get(pid); + String ownType = folder.getRelatedResourceUuidOwnType(); + + if (NodeTypeEnum.PROJECT.getValue().equals(ownType)) { + resp.setProjectName(folder.getOriginalName()); + resp.setProjectId(folder.getRelatedResourceUuid()); + } else if (NodeTypeEnum.PHASE.getValue().equals(ownType)) { + resp.setPhaseName(folder.getOriginalName()); + resp.setPhaseId(folder.getRelatedResourceUuid()); + } else if (NodeTypeEnum.DISCIPLINE.getValue().equals(ownType)) { + resp.setDisciplineName(folder.getOriginalName()); + resp.setDisciplineId(folder.getRelatedResourceUuid()); + } + pid = folder.getParentId(); + limit++; + } + return resp; + }).toList(); + + // 6. 构造分页信息并返回 + PageInfo pageInfo = new PageInfo<>(); pageInfo.setTotal(pageDataResp.getTotal()); pageInfo.setPageNum(pageDataResp.getCurrentPage()); pageInfo.setPageSize(pageDataResp.getPageSize()); - - List taskResultCurveResps = simulationTaskFileMetadataInfos.stream().map(simulationTaskFile -> { - SimulationTaskResultCurveResp taskResultCurveResp = new SimulationTaskResultCurveResp(); - BeanUtils.copyProperties(simulationTaskFile, taskResultCurveResp); - taskResultCurveResp.setFormatFileSize(FileSizeUtils.formatFileSize(BigDecimal.valueOf(simulationTaskFile.getFileSize()))); - - Long parentId = simulationTaskFile.getParentId(); - while (ObjectUtils.isNotEmpty(parentId)){ - FileMetadataInfo parDir = fileMetadataInfoService.lambdaQuery() - .eq(FileMetadataInfo::getId, parentId).one(); - if(ObjectUtils.isNotEmpty(parDir)){ - if(NodeTypeEnum.PROJECT.getValue().equals(parDir.getRelatedResourceUuidOwnType())){ - taskResultCurveResp.setProjectName(parDir.getOriginalName()); - taskResultCurveResp.setProjectId(parDir.getRelatedResourceUuid()); - } - - if(NodeTypeEnum.PHASE.getValue().equals(parDir.getRelatedResourceUuidOwnType())){ - taskResultCurveResp.setPhaseName(parDir.getOriginalName()); - taskResultCurveResp.setPhaseId(parDir.getRelatedResourceUuid()); - } - - if(NodeTypeEnum.DISCIPLINE.getValue().equals(parDir.getRelatedResourceUuidOwnType())){ - taskResultCurveResp.setDisciplineName(parDir.getOriginalName()); - taskResultCurveResp.setDisciplineId(parDir.getRelatedResourceUuid()); - } - parentId = parDir.getParentId(); - } - } - return taskResultCurveResp; - }).toList(); - return PageUtils.getJsonObjectSdmResponse(taskResultCurveResps,pageInfo); + return PageUtils.getJsonObjectSdmResponse(finalResultList, pageInfo); } @Override diff --git a/data/src/main/java/com/sdm/data/service/impl/DataStorageAnalysisImpl.java b/data/src/main/java/com/sdm/data/service/impl/DataStorageAnalysisImpl.java index 106d7a41..6eea7178 100644 --- a/data/src/main/java/com/sdm/data/service/impl/DataStorageAnalysisImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/DataStorageAnalysisImpl.java @@ -6,12 +6,11 @@ import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.sdm.common.common.SdmResponse; import com.sdm.common.common.ThreadLocalContext; +import com.sdm.common.entity.enums.NodeTypeEnum; import com.sdm.common.entity.req.system.UserListReq; import com.sdm.common.entity.req.system.UserQueryReq; -import com.sdm.common.entity.resp.AllNodeByProjectIdAndTypeResp; import com.sdm.common.entity.resp.PageDataResp; import com.sdm.common.entity.resp.system.CIDUserResp; -import com.sdm.common.feign.impl.project.SimulationNodeFeignClientImpl; import com.sdm.common.feign.impl.system.SysUserFeignClientImpl; import com.sdm.common.utils.FileSizeUtils; import com.sdm.common.utils.PageUtils; @@ -29,6 +28,7 @@ import com.sdm.data.model.resp.FileStorageQuotaResp; import com.sdm.data.service.*; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; +import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; @@ -52,9 +52,6 @@ public class DataStorageAnalysisImpl implements DataStorageAnalysis { @Autowired IFileStorageService fileStorageService; - @Autowired - SimulationNodeFeignClientImpl simuluationNodeFeignClient; - @Autowired SysUserFeignClientImpl sysUserFeignClient; @@ -66,26 +63,48 @@ public class DataStorageAnalysisImpl implements DataStorageAnalysis { IFileStorageQuotaService fileStorageQuotaService; public SdmResponse> getNodeSizeByNodeType(String queryNodeType, String uuid, Integer intervalMonths, String targetYm) { - SdmResponse> response = simuluationNodeFeignClient.getAllNodeByProjectIdAndType(List.of(uuid), queryNodeType); - Long tenantId = ThreadLocalContext.getTenantId(); - if (!response.isSuccess()) { - log.error("获取节点信息失败"); - return SdmResponse.success(); + List nodeList = null; + String actualNodeName = uuid; // 存储实际的节点名称 + + if(NodeTypeEnum.PROJECT.getValue().equals(queryNodeType)){ + // uuid是项目的uuid + nodeList = fileMetadataInfoService.lambdaQuery() + .eq(FileMetadataInfo::getRelatedResourceUuidOwnType, queryNodeType) + .eq(FileMetadataInfo::getRelatedResourceUuid, uuid) + .list(); + actualNodeName = nodeList.get(0).getOriginalName(); // 对于项目,节点名称是项目的名字 + }else if(NodeTypeEnum.DISCIPLINE.getValue().equals(queryNodeType)){ + // uuid就是字典中配置的学科名:MHC,不再是uuid了 + nodeList = fileMetadataInfoService.lambdaQuery() + .eq(FileMetadataInfo::getRelatedResourceUuidOwnType, queryNodeType) + .eq(FileMetadataInfo::getOriginalName, uuid) + .list(); + actualNodeName = uuid; // 对于学科,节点名称就是传入的uuid值 + }else { + log.error("不支持的节点类型: {}", queryNodeType); + return SdmResponse.success(new ArrayList<>()); } - List nodeLists = response.getData(); + Long tenantId = ThreadLocalContext.getTenantId(); + if (ObjectUtils.isEmpty(nodeList)) { + log.error("获取节点信息失败,节点类型: {}, 标识符: {}", queryNodeType, uuid); + return getEmptyNodeSize(actualNodeName); + } + + // List nodeLists = response.getData(); - // uuid1-学科1 uuid2-学科1 转换成 valueToKeysMap: 学科1->[uuid1,uuid2] 学科2->[uuid3,uuid4] - Map> nodeNameToUuidListMap = nodeLists.stream() - .collect(Collectors.groupingBy( - AllNodeByProjectIdAndTypeResp::getNodeName, - Collectors.mapping(AllNodeByProjectIdAndTypeResp::getUuid, Collectors.toList()) + //将 nodeList的 relatedResourceUuid1-学科1 relatedResourceUuid2-学科1 转换成 valueToKeysMap: 学科1->[uuid1,uuid2] 学科2->[uuid3,uuid4] + Map> nodeNameToUuidListMap = nodeList.stream() + .collect(Collectors.groupingBy(FileMetadataInfo::getOriginalName, + Collectors.mapping(FileMetadataInfo::getRelatedResourceUuid, Collectors.toList()) )); + + List result = new ArrayList<>(); //nodeIds获取所有节点id: [nodeid1,nodeid2,nodeid3,nodeid4] - List uuids = nodeLists.stream().map(AllNodeByProjectIdAndTypeResp::getUuid).collect(Collectors.toList()); + List uuids = nodeList.stream().map(FileMetadataInfo::getRelatedResourceUuid).collect(Collectors.toList()); if (ObjectUtils.isNotEmpty(uuids)) { // uuidToDirIdMap :获取uuid->fileID映射 Map uuidToDirIdMap = fileMetadataInfoService.lambdaQuery().select(FileMetadataInfo::getId, FileMetadataInfo::getRelatedResourceUuid) @@ -93,8 +112,8 @@ public class DataStorageAnalysisImpl implements DataStorageAnalysis { .eq(FileMetadataInfo::getTenantId, tenantId) .list().stream().collect(Collectors.toMap(FileMetadataInfo::getRelatedResourceUuid, FileMetadataInfo::getId)); if (CollectionUtils.isEmpty(uuidToDirIdMap)) { - log.error("获取节点信息失败"); - return SdmResponse.success(); + log.error("获取节点ID映射失败,节点类型: {}, 标识符: {}", queryNodeType, uuid); + return getEmptyNodeSize(actualNodeName); } // fileMetadIds: uuid对应的fileid结合 @@ -113,10 +132,7 @@ public class DataStorageAnalysisImpl implements DataStorageAnalysis { if (ObjectUtils.isEmpty(nodeSizeDTOS)) { // 空间为空 也要返回nodeName - JSONObject jsonObject = new JSONObject(); - jsonObject.put("nodeName", nodeLists.get(0).getNodeName()); - jsonObject.put("totalSize", 0); - return SdmResponse.success(Arrays.asList(jsonObject)); + return getEmptyNodeSize(actualNodeName); } @@ -141,6 +157,14 @@ public class DataStorageAnalysisImpl implements DataStorageAnalysis { return SdmResponse.success(result); } + @NotNull + private static SdmResponse> getEmptyNodeSize(String actualNodeName) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("nodeName", actualNodeName); + jsonObject.put("totalSize", 0); + return SdmResponse.success(Arrays.asList(jsonObject)); + } + @Override public SdmResponse getDirectorySizeByUserId(List userIds, Integer intervalMonths, String targetYm) { Long tenantId = ThreadLocalContext.getTenantId(); diff --git a/data/src/main/java/com/sdm/data/service/impl/DimensionTemplateServiceImpl.java b/data/src/main/java/com/sdm/data/service/impl/DimensionTemplateServiceImpl.java index b7ca9916..0edf4e61 100644 --- a/data/src/main/java/com/sdm/data/service/impl/DimensionTemplateServiceImpl.java +++ b/data/src/main/java/com/sdm/data/service/impl/DimensionTemplateServiceImpl.java @@ -257,15 +257,17 @@ public class DimensionTemplateServiceImpl extends ServiceImpl nodeDirInfos = fileMetadataInfoService.lambdaQuery() + .in(FileMetadataInfo::getRelatedResourceUuid, uuids) + .eq(FileMetadataInfo::getTenantId, tenantId) + .orderByDesc(FileMetadataInfo::getCreateTime).list(); + resultDir.addAll(nodeDirInfos); } - List nodeDirInfos = fileMetadataInfoService.lambdaQuery() - .in(FileMetadataInfo::getRelatedResourceUuid, uuids) - .eq(FileMetadataInfo::getTenantId, tenantId) - .orderByDesc(FileMetadataInfo::getCreateTime).list(); - resultDir.addAll(nodeDirInfos); + if(CollectionUtils.isEmpty(resultDir)){ + return SdmResponse.success(Collections.emptyList()); + } // 对nodeDirInfos按照originalName进行合并,并处理bucketName生成totalName List mergedResult = mergeNodeDirInfos(resultDir);