diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/KnowledgeItemApplicationService.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/KnowledgeItemApplicationService.java index 030416a..e2f60c4 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/KnowledgeItemApplicationService.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/application/KnowledgeItemApplicationService.java @@ -25,6 +25,9 @@ import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeItemRequest; import com.datamate.datamanagement.interfaces.dto.ImportKnowledgeItemsRequest; import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery; import com.datamate.datamanagement.interfaces.dto.KnowledgeItemResponse; +import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchQuery; +import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse; +import com.datamate.datamanagement.interfaces.dto.KnowledgeManagementStatisticsResponse; import com.datamate.datamanagement.interfaces.dto.ReplaceKnowledgeItemFileRequest; import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeItemRequest; import com.datamate.datamanagement.interfaces.dto.UploadKnowledgeItemsRequest; @@ -196,6 +199,39 @@ public class KnowledgeItemApplicationService { return PagedResponse.of(responses, page.getCurrent(), page.getTotal(), page.getPages()); } + @Transactional(readOnly = true) + public KnowledgeManagementStatisticsResponse getKnowledgeManagementStatistics() { + KnowledgeManagementStatisticsResponse response = new KnowledgeManagementStatisticsResponse(); + response.setTotalKnowledgeSets(knowledgeSetRepository.count()); + + long totalFiles = knowledgeItemRepository.countBySourceTypes(List.of( + KnowledgeSourceType.DATASET_FILE, + KnowledgeSourceType.FILE_UPLOAD + )); + response.setTotalFiles(totalFiles); + + long datasetFileSize = safeLong(knowledgeItemRepository.sumDatasetFileSize()); + long uploadFileSize = calculateUploadFileTotalSize(); + response.setTotalSize(datasetFileSize + uploadFileSize); + + return response; + } + + @Transactional(readOnly = true) + public PagedResponse searchKnowledgeItems(KnowledgeItemSearchQuery query) { + BusinessAssert.notNull(query, CommonErrorCode.PARAM_ERROR); + String keyword = StringUtils.trimToEmpty(query.getKeyword()); + BusinessAssert.isTrue(StringUtils.isNotBlank(keyword), CommonErrorCode.PARAM_ERROR); + + IPage page = new Page<>(query.getPage(), query.getSize()); + IPage result = knowledgeItemRepository.searchFileItems(page, keyword); + List responses = result.getRecords() + .stream() + .map(this::normalizeSearchResponse) + .toList(); + return PagedResponse.of(responses, result.getCurrent(), result.getTotal(), result.getPages()); + } + public List importKnowledgeItems(String setId, ImportKnowledgeItemsRequest request) { KnowledgeSet knowledgeSet = requireKnowledgeSet(setId); BusinessAssert.isTrue(!isReadOnlyStatus(knowledgeSet.getStatus()), @@ -447,6 +483,58 @@ public class KnowledgeItemApplicationService { return target; } + private KnowledgeItemSearchResponse normalizeSearchResponse(KnowledgeItemSearchResponse item) { + BusinessAssert.notNull(item, CommonErrorCode.PARAM_ERROR); + if (item.getSourceType() == KnowledgeSourceType.FILE_UPLOAD) { + item.setFileSize(resolveUploadFileSize(item.getContent())); + if (StringUtils.isBlank(item.getFileName())) { + item.setFileName(item.getSourceFileId()); + } + } + if (item.getSourceType() == KnowledgeSourceType.DATASET_FILE) { + if (item.getFileSize() == null) { + item.setFileSize(0L); + } + if (StringUtils.isBlank(item.getFileName())) { + item.setFileName(item.getSourceFileId()); + } + } + item.setContent(null); + return item; + } + + private long calculateUploadFileTotalSize() { + List items = knowledgeItemRepository.findFileUploadItems(); + if (CollectionUtils.isEmpty(items)) { + return 0L; + } + long total = 0L; + for (KnowledgeItem item : items) { + total += resolveUploadFileSize(item.getContent()); + } + return total; + } + + private long resolveUploadFileSize(String relativePath) { + if (StringUtils.isBlank(relativePath)) { + return 0L; + } + try { + Path filePath = resolveKnowledgeItemStoragePath(relativePath); + if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) { + return 0L; + } + return Files.size(filePath); + } catch (Exception e) { + log.warn("resolve knowledge item file size error, path: {}", relativePath, e); + return 0L; + } + } + + private long safeLong(Long value) { + return value == null ? 0L : value; + } + private String buildRelativeFilePath(String setId, String storedName) { String relativePath = Paths.get(KNOWLEDGE_ITEM_UPLOAD_DIR, setId, storedName).toString(); return relativePath.replace(File.separatorChar, '/'); diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/KnowledgeItemMapper.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/KnowledgeItemMapper.java index c354ebd..6b48c98 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/KnowledgeItemMapper.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/mapper/KnowledgeItemMapper.java @@ -1,9 +1,49 @@ package com.datamate.datamanagement.infrastructure.persistence.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem; +import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; @Mapper public interface KnowledgeItemMapper extends BaseMapper { + @Select(""" + SELECT + ki.id AS id, + ki.set_id AS setId, + ks.name AS setName, + ki.content_type AS contentType, + ki.source_type AS sourceType, + ki.source_dataset_id AS sourceDatasetId, + ki.source_file_id AS sourceFileId, + CASE + WHEN ki.source_type = 'DATASET_FILE' THEN df.file_name + ELSE ki.source_file_id + END AS fileName, + df.file_size AS fileSize, + CASE + WHEN ki.source_type = 'FILE_UPLOAD' THEN ki.content + ELSE NULL + END AS content, + ki.created_at AS createdAt, + ki.updated_at AS updatedAt + FROM t_dm_knowledge_items ki + LEFT JOIN t_dm_knowledge_sets ks ON ki.set_id = ks.id + LEFT JOIN t_dm_dataset_files df ON ki.source_file_id = df.id AND ki.source_type = 'DATASET_FILE' + WHERE (ki.source_type = 'FILE_UPLOAD' AND ki.source_file_id LIKE CONCAT('%', #{keyword}, '%')) + OR (ki.source_type = 'DATASET_FILE' AND df.file_name LIKE CONCAT('%', #{keyword}, '%')) + ORDER BY ki.created_at DESC + """) + IPage searchFileItems(IPage page, @Param("keyword") String keyword); + + @Select(""" + SELECT COALESCE(SUM(df.file_size), 0) + FROM t_dm_knowledge_items ki + LEFT JOIN t_dm_dataset_files df ON ki.source_file_id = df.id + WHERE ki.source_type = 'DATASET_FILE' + """) + Long sumDatasetFileSize(); } diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/KnowledgeItemRepository.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/KnowledgeItemRepository.java index be8295a..fc8188d 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/KnowledgeItemRepository.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/KnowledgeItemRepository.java @@ -2,8 +2,11 @@ package com.datamate.datamanagement.infrastructure.persistence.repository; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.repository.IRepository; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.datamate.datamanagement.common.enums.KnowledgeSourceType; import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem; import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery; +import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse; import java.util.List; /** @@ -15,4 +18,12 @@ public interface KnowledgeItemRepository extends IRepository { long countBySetId(String setId); List findAllBySetId(String setId); + + long countBySourceTypes(List sourceTypes); + + List findFileUploadItems(); + + IPage searchFileItems(IPage page, String keyword); + + Long sumDatasetFileSize(); } diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/KnowledgeItemRepositoryImpl.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/KnowledgeItemRepositoryImpl.java index 5ceeddb..7ee07a7 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/KnowledgeItemRepositoryImpl.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/infrastructure/persistence/repository/impl/KnowledgeItemRepositoryImpl.java @@ -3,10 +3,12 @@ package com.datamate.datamanagement.infrastructure.persistence.repository.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.repository.CrudRepository; +import com.datamate.datamanagement.common.enums.KnowledgeSourceType; import com.datamate.datamanagement.domain.model.knowledge.KnowledgeItem; import com.datamate.datamanagement.infrastructure.persistence.mapper.KnowledgeItemMapper; import com.datamate.datamanagement.infrastructure.persistence.repository.KnowledgeItemRepository; import com.datamate.datamanagement.interfaces.dto.KnowledgeItemPagingQuery; +import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Repository; @@ -52,4 +54,27 @@ public class KnowledgeItemRepositoryImpl extends CrudRepository sourceTypes) { + return knowledgeItemMapper.selectCount(new LambdaQueryWrapper() + .in(KnowledgeItem::getSourceType, sourceTypes)); + } + + @Override + public List findFileUploadItems() { + return knowledgeItemMapper.selectList(new LambdaQueryWrapper() + .eq(KnowledgeItem::getSourceType, KnowledgeSourceType.FILE_UPLOAD) + .select(KnowledgeItem::getId, KnowledgeItem::getContent, KnowledgeItem::getSourceFileId)); + } + + @Override + public IPage searchFileItems(IPage page, String keyword) { + return knowledgeItemMapper.searchFileItems(page, keyword); + } + + @Override + public Long sumDatasetFileSize() { + return knowledgeItemMapper.sumDatasetFileSize(); + } } diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeItemSearchQuery.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeItemSearchQuery.java new file mode 100644 index 0000000..870f49e --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeItemSearchQuery.java @@ -0,0 +1,17 @@ +package com.datamate.datamanagement.interfaces.dto; + +import com.datamate.common.interfaces.PagingQuery; +import lombok.Getter; +import lombok.Setter; + +/** + * 知识条目文件搜索请求 + */ +@Getter +@Setter +public class KnowledgeItemSearchQuery extends PagingQuery { + /** + * 文件名关键词 + */ + private String keyword; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeItemSearchResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeItemSearchResponse.java new file mode 100644 index 0000000..b9308bd --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeItemSearchResponse.java @@ -0,0 +1,31 @@ +package com.datamate.datamanagement.interfaces.dto; + +import com.datamate.datamanagement.common.enums.KnowledgeContentType; +import com.datamate.datamanagement.common.enums.KnowledgeSourceType; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +/** + * 知识条目文件搜索响应 + */ +@Getter +@Setter +public class KnowledgeItemSearchResponse { + private String id; + private String setId; + private String setName; + private KnowledgeContentType contentType; + private KnowledgeSourceType sourceType; + private String sourceDatasetId; + private String sourceFileId; + private String fileName; + private Long fileSize; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + + @JsonIgnore + private String content; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeManagementStatisticsResponse.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeManagementStatisticsResponse.java new file mode 100644 index 0000000..7abddc1 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/dto/KnowledgeManagementStatisticsResponse.java @@ -0,0 +1,15 @@ +package com.datamate.datamanagement.interfaces.dto; + +import lombok.Getter; +import lombok.Setter; + +/** + * 知识管理统计响应 + */ +@Getter +@Setter +public class KnowledgeManagementStatisticsResponse { + private Long totalKnowledgeSets = 0L; + private Long totalFiles = 0L; + private Long totalSize = 0L; +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/KnowledgeItemSearchController.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/KnowledgeItemSearchController.java new file mode 100644 index 0000000..7657284 --- /dev/null +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/KnowledgeItemSearchController.java @@ -0,0 +1,27 @@ +package com.datamate.datamanagement.interfaces.rest; + +import com.datamate.common.interfaces.PagedResponse; +import com.datamate.datamanagement.application.KnowledgeItemApplicationService; +import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchQuery; +import com.datamate.datamanagement.interfaces.dto.KnowledgeItemSearchResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 知识条目搜索控制器 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/data-management/knowledge-items") +public class KnowledgeItemSearchController { + private final KnowledgeItemApplicationService knowledgeItemApplicationService; + + @GetMapping("/search") + public PagedResponse search(KnowledgeItemSearchQuery query) { + return knowledgeItemApplicationService.searchKnowledgeItems(query); + } +} diff --git a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/KnowledgeSetController.java b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/KnowledgeSetController.java index 1f55960..57efa19 100644 --- a/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/KnowledgeSetController.java +++ b/backend/services/data-management-service/src/main/java/com/datamate/datamanagement/interfaces/rest/KnowledgeSetController.java @@ -1,10 +1,12 @@ package com.datamate.datamanagement.interfaces.rest; import com.datamate.common.interfaces.PagedResponse; +import com.datamate.datamanagement.application.KnowledgeItemApplicationService; import com.datamate.datamanagement.application.KnowledgeSetApplicationService; import com.datamate.datamanagement.domain.model.knowledge.KnowledgeSet; import com.datamate.datamanagement.interfaces.converter.KnowledgeConverter; import com.datamate.datamanagement.interfaces.dto.CreateKnowledgeSetRequest; +import com.datamate.datamanagement.interfaces.dto.KnowledgeManagementStatisticsResponse; import com.datamate.datamanagement.interfaces.dto.KnowledgeSetPagingQuery; import com.datamate.datamanagement.interfaces.dto.KnowledgeSetResponse; import com.datamate.datamanagement.interfaces.dto.UpdateKnowledgeSetRequest; @@ -22,6 +24,7 @@ import org.springframework.web.bind.annotation.*; @RequestMapping("/data-management/knowledge-sets") public class KnowledgeSetController { private final KnowledgeSetApplicationService knowledgeSetApplicationService; + private final KnowledgeItemApplicationService knowledgeItemApplicationService; @GetMapping public PagedResponse getKnowledgeSets(KnowledgeSetPagingQuery query) { @@ -51,4 +54,9 @@ public class KnowledgeSetController { public void deleteKnowledgeSet(@PathVariable("setId") String setId) { knowledgeSetApplicationService.deleteKnowledgeSet(setId); } + + @GetMapping("/statistics") + public KnowledgeManagementStatisticsResponse getKnowledgeManagementStatistics() { + return knowledgeItemApplicationService.getKnowledgeManagementStatistics(); + } } diff --git a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/application/KnowledgeBaseService.java b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/application/KnowledgeBaseService.java index c739774..2372e61 100644 --- a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/application/KnowledgeBaseService.java +++ b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/application/KnowledgeBaseService.java @@ -140,17 +140,6 @@ public class KnowledgeBaseService { return PagedResponse.of(respList, page.getCurrent(), page.getTotal(), page.getPages()); } - public KnowledgeBaseStatisticsResp getStatistics() { - KnowledgeBaseStatisticsResp resp = new KnowledgeBaseStatisticsResp(); - resp.setTotalKnowledgeBases(knowledgeBaseRepository.count()); - - RagFileStatistics fileStatistics = ragFileRepository.getStatistics(); - if (fileStatistics != null) { - resp.setTotalFiles(fileStatistics.getTotalFiles() != null ? fileStatistics.getTotalFiles() : 0L); - resp.setTotalSize(fileStatistics.getTotalSize() != null ? fileStatistics.getTotalSize() : 0L); - } - return resp; - } @Transactional(rollbackFor = Exception.class) public void addFiles(AddFilesReq request) { diff --git a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/domain/repository/RagFileRepository.java b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/domain/repository/RagFileRepository.java index 63f3e7a..61b86dd 100644 --- a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/domain/repository/RagFileRepository.java +++ b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/domain/repository/RagFileRepository.java @@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.extension.repository.IRepository; import com.datamate.rag.indexer.domain.model.RagFile; import com.datamate.rag.indexer.interfaces.dto.KnowledgeBaseFileSearchReq; import com.datamate.rag.indexer.interfaces.dto.RagFileReq; -import com.datamate.rag.indexer.interfaces.dto.RagFileStatistics; import java.util.List; @@ -25,6 +24,4 @@ public interface RagFileRepository extends IRepository { IPage page(IPage page, RagFileReq request); IPage searchPage(IPage page, KnowledgeBaseFileSearchReq request); - - RagFileStatistics getStatistics(); } diff --git a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/infrastructure/persistence/impl/RagFileRepositoryImpl.java b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/infrastructure/persistence/impl/RagFileRepositoryImpl.java index 77d9aea..14b4dd8 100644 --- a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/infrastructure/persistence/impl/RagFileRepositoryImpl.java +++ b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/infrastructure/persistence/impl/RagFileRepositoryImpl.java @@ -8,7 +8,6 @@ import com.datamate.rag.indexer.domain.repository.RagFileRepository; import com.datamate.rag.indexer.infrastructure.persistence.mapper.RagFileMapper; import com.datamate.rag.indexer.interfaces.dto.KnowledgeBaseFileSearchReq; import com.datamate.rag.indexer.interfaces.dto.RagFileReq; -import com.datamate.rag.indexer.interfaces.dto.RagFileStatistics; import org.springframework.stereotype.Repository; import org.springframework.util.StringUtils; @@ -61,11 +60,6 @@ public class RagFileRepositoryImpl extends CrudRepository { - @Select("SELECT COUNT(*) AS totalFiles, " + - "COALESCE(SUM(df.file_size), 0) AS totalSize " + - "FROM t_rag_file rf " + - "LEFT JOIN t_dm_dataset_files df ON rf.file_id = df.id") - RagFileStatistics getStatistics(); } diff --git a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/KnowledgeBaseController.java b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/KnowledgeBaseController.java index 8929927..4109069 100644 --- a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/KnowledgeBaseController.java +++ b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/KnowledgeBaseController.java @@ -80,15 +80,6 @@ public class KnowledgeBaseController { return knowledgeBaseService.list(request); } - /** - * 获取知识库统计信息 - * - * @return 知识库统计信息 - */ - @GetMapping("/statistics") - public KnowledgeBaseStatisticsResp statistics() { - return knowledgeBaseService.getStatistics(); - } /** * 添加文件到知识库 diff --git a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/KnowledgeBaseStatisticsResp.java b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/KnowledgeBaseStatisticsResp.java deleted file mode 100644 index 3aaffab..0000000 --- a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/KnowledgeBaseStatisticsResp.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.datamate.rag.indexer.interfaces.dto; - -import lombok.Getter; -import lombok.Setter; - -/** - * 知识库统计响应 - */ -@Getter -@Setter -public class KnowledgeBaseStatisticsResp { - private Long totalKnowledgeBases = 0L; - private Long totalFiles = 0L; - private Long totalSize = 0L; -} diff --git a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/RagFileStatistics.java b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/RagFileStatistics.java deleted file mode 100644 index 0cc06d9..0000000 --- a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/RagFileStatistics.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.datamate.rag.indexer.interfaces.dto; - -import lombok.Getter; -import lombok.Setter; - -/** - * 知识库文件统计 - */ -@Getter -@Setter -public class RagFileStatistics { - private Long totalFiles = 0L; - private Long totalSize = 0L; -} diff --git a/frontend/src/pages/KnowledgeBase/Home/KnowledgeBasePage.tsx b/frontend/src/pages/KnowledgeBase/Home/KnowledgeBasePage.tsx index 254aa40..55ddb53 100644 --- a/frontend/src/pages/KnowledgeBase/Home/KnowledgeBasePage.tsx +++ b/frontend/src/pages/KnowledgeBase/Home/KnowledgeBasePage.tsx @@ -1,48 +1,23 @@ -import { useCallback, useEffect, useState } from "react"; -import { Card, Button, Table, Tooltip, message, Statistic } from "antd"; +import { useState } from "react"; +import { Card, Button, Table, Tooltip, message } from "antd"; import { DeleteOutlined, EditOutlined } from "@ant-design/icons"; import { SearchControls } from "@/components/SearchControls"; import { useNavigate } from "react-router"; import CardView from "@/components/CardView"; import { deleteKnowledgeBaseByIdUsingDelete, - getKnowledgeBaseStatisticsUsingGet, queryKnowledgeBasesUsingPost, } from "../knowledge-base.api"; import useFetchData from "@/hooks/useFetchData"; -import { KnowledgeBaseItem, KnowledgeBaseStatistics } from "../knowledge-base.model"; +import { KnowledgeBaseItem } from "../knowledge-base.model"; import CreateKnowledgeBase from "../components/CreateKnowledgeBase"; import { mapKnowledgeBase } from "../knowledge-base.const"; -import { formatBytes } from "@/utils/unit"; - -type StatisticsItem = { - title: string; - value: number | string; -}; - -const DEFAULT_STATISTICS: StatisticsItem[] = [ - { - title: "知识库总数", - value: 0, - }, - { - title: "文件总数", - value: 0, - }, - { - title: "总大小", - value: "0 B", - }, -]; export default function KnowledgeBasePage() { const navigate = useNavigate(); const [viewMode, setViewMode] = useState<"card" | "list">("card"); const [isEdit, setIsEdit] = useState(false); const [currentKB, setCurrentKB] = useState(null); - const [statisticsData, setStatisticsData] = useState( - DEFAULT_STATISTICS - ); const { loading, tableData, @@ -57,43 +32,11 @@ export default function KnowledgeBasePage() { (kb) => mapKnowledgeBase(kb, false) // 在首页不显示索引模型和文本理解模型字段 ); - const fetchStatistics = useCallback(async () => { - try { - const { data } = await getKnowledgeBaseStatisticsUsingGet(); - const stats = data as KnowledgeBaseStatistics | undefined; - setStatisticsData([ - { - title: "知识库总数", - value: stats?.totalKnowledgeBases ?? 0, - }, - { - title: "文件总数", - value: stats?.totalFiles ?? 0, - }, - { - title: "总大小", - value: formatBytes(stats?.totalSize ?? 0), - }, - ]); - } catch { - message.error("统计数据加载失败"); - setStatisticsData(DEFAULT_STATISTICS); - } - }, []); - - const refreshAll = useCallback(async () => { - await Promise.all([fetchData(), fetchStatistics()]); - }, [fetchData, fetchStatistics]); - - useEffect(() => { - fetchStatistics(); - }, [fetchStatistics]); - const handleDeleteKB = async (kb: KnowledgeBaseItem) => { try { await deleteKnowledgeBaseByIdUsingDelete(kb.id); message.success("知识库删除成功"); - await refreshAll(); + fetchData(); } catch { message.error("知识库删除失败"); } @@ -197,7 +140,7 @@ export default function KnowledgeBasePage() { isEdit={isEdit} data={currentKB} onUpdate={() => { - refreshAll(); + fetchData(); }} onClose={() => { setIsEdit(false); @@ -207,20 +150,6 @@ export default function KnowledgeBasePage() { -
- -
- {statisticsData.map((item) => ( - - ))} -
-
-
- {viewMode === "card" ? ( (null); const [tags, setTags] = useState([]); + const [statisticsData, setStatisticsData] = useState( + DEFAULT_STATISTICS + ); useEffect(() => { const fetchTags = async () => { @@ -75,10 +100,45 @@ export default function KnowledgeManagementPage() { 0 ); + const fetchStatistics = useCallback(async () => { + try { + const { data } = await getKnowledgeManagementStatisticsUsingGet(); + const stats = data as KnowledgeManagementStatistics | undefined; + setStatisticsData([ + { + title: "知识集总数", + value: stats?.totalKnowledgeSets ?? 0, + }, + { + title: "文件总数", + value: stats?.totalFiles ?? 0, + }, + { + title: "总大小", + value: formatBytes(stats?.totalSize ?? 0), + }, + ]); + } catch { + message.error("统计数据加载失败"); + setStatisticsData(DEFAULT_STATISTICS); + } + }, [message]); + + const refreshAll = useCallback( + async (extraParams: Record = {}) => { + await Promise.all([fetchData(extraParams), fetchStatistics()]); + }, + [fetchData, fetchStatistics] + ); + + useEffect(() => { + fetchStatistics(); + }, [fetchStatistics]); + const handleDeleteSet = async (setId: string) => { await deleteKnowledgeSetByIdUsingDelete(setId); message.success("知识集删除成功"); - fetchData({ pageOffset: 0 }); + await refreshAll({ pageOffset: 0 }); }; const operations = [ @@ -191,6 +251,9 @@ export default function KnowledgeManagementPage() {

知识管理

+ deleteDatasetTagUsingDelete({ ids })} @@ -201,7 +264,7 @@ export default function KnowledgeManagementPage() { isEdit={isEdit} data={currentSet} onUpdate={() => { - fetchData(); + refreshAll(); }} onClose={() => { setIsEdit(false); @@ -211,6 +274,20 @@ export default function KnowledgeManagementPage() {
+
+ +
+ {statisticsData.map((item) => ( + + ))} +
+
+
+ {viewMode === "card" ? ( diff --git a/frontend/src/pages/KnowledgeManagement/Search/KnowledgeManagementSearch.tsx b/frontend/src/pages/KnowledgeManagement/Search/KnowledgeManagementSearch.tsx new file mode 100644 index 0000000..61344d1 --- /dev/null +++ b/frontend/src/pages/KnowledgeManagement/Search/KnowledgeManagementSearch.tsx @@ -0,0 +1,212 @@ +import { useCallback, useMemo, useState } from "react"; +import { App, Breadcrumb, Button, Input, Table } from "antd"; +import { useNavigate } from "react-router"; +import { + KnowledgeContentType, + KnowledgeItemSearchResult, + KnowledgeSourceType, +} from "../knowledge-management.model"; +import { + knowledgeContentTypeOptions, + knowledgeSourceTypeOptions, +} from "../knowledge-management.const"; +import { searchKnowledgeItemsUsingGet } from "../knowledge-management.api"; +import { formatBytes, formatDateTime } from "@/utils/unit"; + +const buildOptionMap = (options: { label: string; value: string }[]) => + options.reduce>((acc, option) => { + acc[option.value] = option.label; + return acc; + }, {}); + +export default function KnowledgeManagementSearch() { + const navigate = useNavigate(); + const { message } = App.useApp(); + const [searchTerm, setSearchTerm] = useState(""); + const [activeKeyword, setActiveKeyword] = useState(""); + const [loading, setLoading] = useState(false); + const [searched, setSearched] = useState(false); + const [results, setResults] = useState([]); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 10, + total: 0, + }); + + const contentTypeMap = useMemo( + () => buildOptionMap(knowledgeContentTypeOptions), + [] + ); + const sourceTypeMap = useMemo( + () => buildOptionMap(knowledgeSourceTypeOptions), + [] + ); + + const fetchResults = useCallback( + async (keyword: string, page?: number, pageSize?: number) => { + const resolvedPage = page ?? pagination.current; + const resolvedPageSize = pageSize ?? pagination.pageSize; + if (!keyword) { + setResults([]); + setPagination((prev) => ({ ...prev, total: 0, current: resolvedPage })); + setSearched(false); + return; + } + setLoading(true); + try { + const { data } = await searchKnowledgeItemsUsingGet({ + keyword, + page: Math.max(resolvedPage - 1, 0), + size: resolvedPageSize, + }); + const content = Array.isArray(data?.content) ? data.content : []; + setResults(content); + setPagination({ + current: resolvedPage, + pageSize: resolvedPageSize, + total: data?.totalElements ?? 0, + }); + setSearched(true); + } catch (error) { + console.error("Failed to search knowledge items:", error); + message.error("检索失败,请稍后重试"); + } finally { + setLoading(false); + } + }, + [message, pagination] + ); + + const handleSearch = (value?: string) => { + const keyword = (value ?? searchTerm).trim(); + if (!keyword) { + message.warning("请输入文件名"); + return; + } + setActiveKeyword(keyword); + fetchResults(keyword, 1, pagination.pageSize); + }; + + const columns = useMemo( + () => [ + { + title: "知识集", + dataIndex: "setName", + key: "setName", + width: 220, + ellipsis: true, + render: (text: string) => text || "-", + }, + { + title: "文件名", + dataIndex: "fileName", + key: "fileName", + width: 220, + ellipsis: true, + render: (_: string, record: KnowledgeItemSearchResult) => + record.fileName || record.sourceFileId || "-", + }, + { + title: "来源类型", + dataIndex: "sourceType", + key: "sourceType", + width: 140, + render: (value: KnowledgeSourceType) => + sourceTypeMap[value] || value || "-", + }, + { + title: "内容类型", + dataIndex: "contentType", + key: "contentType", + width: 120, + render: (value: KnowledgeContentType) => + contentTypeMap[value] || value || "-", + }, + { + title: "大小", + dataIndex: "fileSize", + key: "fileSize", + width: 120, + render: (value?: number) => formatBytes(value ?? 0), + }, + { + title: "更新时间", + dataIndex: "updatedAt", + key: "updatedAt", + width: 180, + ellipsis: true, + render: (value: string) => formatDateTime(value) || "-", + }, + { + title: "操作", + key: "action", + width: 120, + align: "right" as const, + render: (_: unknown, record: KnowledgeItemSearchResult) => ( + + ), + }, + ], + [contentTypeMap, navigate, sourceTypeMap] + ); + + return ( +
+ + + navigate("/data/knowledge-management")}>知识管理 + + 全库搜索 + +
+

知识管理全库检索

+
+
+ setSearchTerm(event.target.value)} + onSearch={handleSearch} + placeholder="输入文件名,回车或点击搜索" + enterButton="搜索" + loading={loading} + /> +
+ `共 ${total} 条`, + onChange: (page, pageSize) => { + const nextKeyword = activeKeyword.trim(); + if (!nextKeyword) { + message.warning("请输入文件名"); + return; + } + fetchResults(nextKeyword, page, pageSize || pagination.pageSize); + }, + }} + locale={{ + emptyText: searched ? "暂无匹配文件" : "请输入文件名开始检索", + }} + onRow={(record) => ({ + onClick: () => { + navigate(`/data/knowledge-management/detail/${record.setId}`); + }, + })} + /> + + ); +} diff --git a/frontend/src/pages/KnowledgeManagement/knowledge-management.api.ts b/frontend/src/pages/KnowledgeManagement/knowledge-management.api.ts index 110f5ee..70b29a7 100644 --- a/frontend/src/pages/KnowledgeManagement/knowledge-management.api.ts +++ b/frontend/src/pages/KnowledgeManagement/knowledge-management.api.ts @@ -5,6 +5,11 @@ export function queryKnowledgeSetsUsingGet(params?: Record) { return get("/api/data-management/knowledge-sets", params); } +// 知识管理统计 +export function getKnowledgeManagementStatisticsUsingGet() { + return get("/api/data-management/knowledge-sets/statistics"); +} + // 创建知识集 export function createKnowledgeSetUsingPost(data: Record) { return post("/api/data-management/knowledge-sets", data); @@ -30,6 +35,11 @@ export function queryKnowledgeItemsUsingGet(setId: string, params?: Record) { + return get("/api/data-management/knowledge-items/search", params); +} + // 创建知识条目 export function createKnowledgeItemUsingPost(setId: string, data: Record) { return post(`/api/data-management/knowledge-sets/${setId}/items`, data); diff --git a/frontend/src/pages/KnowledgeManagement/knowledge-management.model.ts b/frontend/src/pages/KnowledgeManagement/knowledge-management.model.ts index b490f05..dd9ef56 100644 --- a/frontend/src/pages/KnowledgeManagement/knowledge-management.model.ts +++ b/frontend/src/pages/KnowledgeManagement/knowledge-management.model.ts @@ -67,3 +67,23 @@ export interface KnowledgeItem { createdBy?: string; updatedBy?: string; } + +export interface KnowledgeManagementStatistics { + totalKnowledgeSets: number; + totalFiles: number; + totalSize: number; +} + +export interface KnowledgeItemSearchResult { + id: string; + setId: string; + setName: string; + contentType: KnowledgeContentType; + sourceType: KnowledgeSourceType; + sourceDatasetId?: string; + sourceFileId?: string; + fileName?: string; + fileSize?: number; + createdAt?: string; + updatedAt?: string; +} diff --git a/frontend/src/routes/routes.ts b/frontend/src/routes/routes.ts index 221192d..2b67b4a 100644 --- a/frontend/src/routes/routes.ts +++ b/frontend/src/routes/routes.ts @@ -9,6 +9,7 @@ import DatasetCreate from "@/pages/DataManagement/Create/CreateDataset"; import DatasetDetail from "@/pages/DataManagement/Detail/DatasetDetail"; import KnowledgeManagementPage from "@/pages/KnowledgeManagement/Home/KnowledgeManagementPage"; import KnowledgeSetDetail from "@/pages/KnowledgeManagement/Detail/KnowledgeSetDetail"; +import KnowledgeManagementSearch from "@/pages/KnowledgeManagement/Search/KnowledgeManagementSearch"; import DataCleansing from "@/pages/DataCleansing/Home/DataCleansing"; import CleansingTaskCreate from "@/pages/DataCleansing/Create/CreateTask"; @@ -116,6 +117,10 @@ const router = createBrowserRouter([ index: true, Component: KnowledgeManagementPage, }, + { + path: "search", + Component: KnowledgeManagementSearch, + }, { path: "detail/:id", Component: KnowledgeSetDetail,