You've already forked DataMate
feat(knowledge-base): 添加知识库统计功能
- 后端服务新增 KnowledgeBaseStatisticsResp 和 RagFileStatistics 数据传输对象 - 在 KnowledgeBaseService 中实现 getStatistics 方法提供统计信息查询 - 为 RagFileRepository 添加 getStatistics 接口及其实现 - 通过 MyBatis Mapper 实现数据库层面的统计查询功能 - 在 KnowledgeBaseController 中暴露 /statistics 接口供前端调用 - 前端页面集成统计卡片组件展示知识库、文件数量及总大小信息 - 实现前后端数据同步机制确保统计数据实时更新
This commit is contained in:
@@ -140,6 +140,18 @@ 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) {
|
||||
KnowledgeBase knowledgeBase = Optional.ofNullable(knowledgeBaseRepository.getById(request.getKnowledgeBaseId()))
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
|
||||
@@ -24,4 +25,6 @@ public interface RagFileRepository extends IRepository<RagFile> {
|
||||
IPage<RagFile> page(IPage<RagFile> page, RagFileReq request);
|
||||
|
||||
IPage<RagFile> searchPage(IPage<RagFile> page, KnowledgeBaseFileSearchReq request);
|
||||
|
||||
RagFileStatistics getStatistics();
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ 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;
|
||||
|
||||
@@ -60,6 +61,11 @@ public class RagFileRepositoryImpl extends CrudRepository<RagFileMapper, RagFile
|
||||
.page(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RagFileStatistics getStatistics() {
|
||||
return baseMapper.getStatistics();
|
||||
}
|
||||
|
||||
private String normalizeRelativePath(String relativePath) {
|
||||
if (!StringUtils.hasText(relativePath)) {
|
||||
return "";
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.datamate.rag.indexer.infrastructure.persistence.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.datamate.rag.indexer.domain.model.RagFile;
|
||||
import com.datamate.rag.indexer.interfaces.dto.RagFileStatistics;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
/**
|
||||
* RAG文件映射器接口
|
||||
@@ -13,4 +15,9 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
*/
|
||||
@Mapper
|
||||
public interface RagFileMapper extends BaseMapper<RagFile> {
|
||||
@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();
|
||||
}
|
||||
|
||||
@@ -80,6 +80,16 @@ public class KnowledgeBaseController {
|
||||
return knowledgeBaseService.list(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取知识库统计信息
|
||||
*
|
||||
* @return 知识库统计信息
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
public KnowledgeBaseStatisticsResp statistics() {
|
||||
return knowledgeBaseService.getStatistics();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加文件到知识库
|
||||
*
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,23 +1,48 @@
|
||||
import { useState } from "react";
|
||||
import { Card, Button, Table, Tooltip, message } from "antd";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { Card, Button, Table, Tooltip, message, Statistic } 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 } from "../knowledge-base.model";
|
||||
import { KnowledgeBaseItem, KnowledgeBaseStatistics } 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<KnowledgeBaseItem | null>(null);
|
||||
const [statisticsData, setStatisticsData] = useState<StatisticsItem[]>(
|
||||
DEFAULT_STATISTICS
|
||||
);
|
||||
const {
|
||||
loading,
|
||||
tableData,
|
||||
@@ -32,11 +57,43 @@ 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("知识库删除成功");
|
||||
fetchData();
|
||||
await refreshAll();
|
||||
} catch {
|
||||
message.error("知识库删除失败");
|
||||
}
|
||||
@@ -140,7 +197,7 @@ export default function KnowledgeBasePage() {
|
||||
isEdit={isEdit}
|
||||
data={currentKB}
|
||||
onUpdate={() => {
|
||||
fetchData();
|
||||
refreshAll();
|
||||
}}
|
||||
onClose={() => {
|
||||
setIsEdit(false);
|
||||
@@ -150,6 +207,20 @@ export default function KnowledgeBasePage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<Card>
|
||||
<div className="grid grid-cols-3">
|
||||
{statisticsData.map((item) => (
|
||||
<Statistic
|
||||
title={item.title}
|
||||
key={item.title}
|
||||
value={`${item.value}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<SearchControls
|
||||
searchTerm={searchParams.keyword}
|
||||
onSearchChange={handleKeywordChange}
|
||||
@@ -160,7 +231,7 @@ export default function KnowledgeBasePage() {
|
||||
viewMode={viewMode}
|
||||
onViewModeChange={setViewMode}
|
||||
showViewToggle
|
||||
onReload={fetchData}
|
||||
onReload={refreshAll}
|
||||
/>
|
||||
{viewMode === "card" ? (
|
||||
<CardView
|
||||
|
||||
@@ -38,6 +38,11 @@ export function queryKnowledgeBaseFilesSearchUsingGet(params: RequestParams) {
|
||||
return get("/api/knowledge-base/files/search", params);
|
||||
}
|
||||
|
||||
// 获取知识库统计
|
||||
export function getKnowledgeBaseStatisticsUsingGet() {
|
||||
return get("/api/knowledge-base/statistics");
|
||||
}
|
||||
|
||||
// 添加文件到知识库
|
||||
export function addKnowledgeBaseFilesUsingPost(baseId: string, data: RequestPayload) {
|
||||
return post(`/api/knowledge-base/${baseId}/files`, data);
|
||||
|
||||
@@ -25,6 +25,12 @@ export interface KnowledgeBaseItem {
|
||||
chat: never;
|
||||
}
|
||||
|
||||
export interface KnowledgeBaseStatistics {
|
||||
totalKnowledgeBases: number;
|
||||
totalFiles: number;
|
||||
totalSize: number;
|
||||
}
|
||||
|
||||
export interface KBFile {
|
||||
id: string;
|
||||
fileName: string;
|
||||
|
||||
Reference in New Issue
Block a user