feat(data-management): 添加数据集相似度推荐功能

- 在DatasetApplicationService中实现getSimilarDatasets方法,支持基于标签匹配的相似数据集推荐
- 新增normalizeSimilarLimit、normalizeTagNames、countSharedTags等辅助方法用于相似度计算
- 在DatasetRepository接口及其实现类中添加findSimilarByTags方法,支持数据库层面的标签匹配查询
- 在DatasetController中暴露/similar REST API端点,支持按需获取相似数据集
- 在前端Overview组件中展示相似数据集表格,包含名称、标签、类型、文件数和更新时间等信息
- 在DatasetDetail页面集成相似数据集获取逻辑,限制默认返回数量为4条
- 移除KnowledgeItem中的冗余title字段,统一使用其他标识信息
- 优化知识管理相关组件中的标题显示逻辑,移除硬编码标题值
This commit is contained in:
2026-01-30 11:43:44 +08:00
parent c51cd2b6e4
commit c221666e67
12 changed files with 481 additions and 98 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { Breadcrumb, App, Tabs, Table, Tag } from "antd";
import {
ReloadOutlined,
@@ -19,6 +19,7 @@ import {
queryDatasetByIdUsingGet,
queryDatasetsUsingGet,
queryDatasetTagsUsingGet,
querySimilarDatasetsUsingGet,
updateDatasetByIdUsingPut,
} from "../dataset.api";
import DataQuality from "./components/DataQuality";
@@ -26,8 +27,10 @@ import DataLineageFlow from "./components/DataLineageFlow";
import Overview from "./components/Overview";
import { Activity, Clock, File, FileType } from "lucide-react";
import EditDataset from "../Create/EditDataset";
import ImportConfiguration from "./components/ImportConfiguration";
import ImportConfiguration from "./components/ImportConfiguration";
const SIMILAR_DATASET_LIMIT = 4;
export default function DatasetDetail() {
const { id } = useParams(); // 获取动态路由参数
const navigate = useNavigate();
@@ -39,9 +42,61 @@ export default function DatasetDetail() {
const [parentDataset, setParentDataset] = useState<Dataset | null>(null);
const [childDatasets, setChildDatasets] = useState<Dataset[]>([]);
const [childDatasetsLoading, setChildDatasetsLoading] = useState(false);
const [similarDatasets, setSimilarDatasets] = useState<Dataset[]>([]);
const [similarDatasetsLoading, setSimilarDatasetsLoading] = useState(false);
const [similarTagNames, setSimilarTagNames] = useState<string[]>([]);
const similarRequestRef = useRef(0);
const filesOperation = useFilesOperation(dataset);
const [showUploadDialog, setShowUploadDialog] = useState(false);
const normalizeTagNames = (
tags?: Array<string | { name?: string | null } | null>
) => {
if (!tags || tags.length === 0) {
return [];
}
const names = tags
.map((tag) => (typeof tag === "string" ? tag : tag?.name))
.filter((name): name is string => !!name && name.trim().length > 0)
.map((name) => name.trim());
return Array.from(new Set(names));
};
const fetchSimilarDatasets = async (currentDataset: Dataset) => {
const requestId = similarRequestRef.current + 1;
similarRequestRef.current = requestId;
if (!currentDataset?.id) {
setSimilarDatasets([]);
setSimilarTagNames([]);
setSimilarDatasetsLoading(false);
return;
}
const tagNames = normalizeTagNames(
currentDataset.tags as Array<string | { name?: string }>
);
setSimilarTagNames(tagNames);
setSimilarDatasets([]);
if (tagNames.length === 0) {
setSimilarDatasetsLoading(false);
return;
}
setSimilarDatasetsLoading(true);
try {
const { data } = await querySimilarDatasetsUsingGet(currentDataset.id, {
limit: SIMILAR_DATASET_LIMIT,
});
if (similarRequestRef.current !== requestId) {
return;
}
const list = Array.isArray(data) ? data : [];
setSimilarDatasets(list.map((item) => mapDataset(item)));
} catch (error) {
console.error("Failed to fetch similar datasets:", error);
} finally {
if (similarRequestRef.current === requestId) {
setSimilarDatasetsLoading(false);
}
}
};
const navigateItems = useMemo(() => {
const items = [
{
@@ -110,6 +165,7 @@ export default function DatasetDetail() {
const { data } = await queryDatasetByIdUsingGet(id);
const mapped = mapDataset(data);
setDataset(mapped);
fetchSimilarDatasets(mapped);
if (data?.parentDatasetId) {
const { data: parentData } = await queryDatasetByIdUsingGet(
data.parentDatasetId
@@ -351,6 +407,9 @@ export default function DatasetDetail() {
filesOperation={filesOperation}
fetchDataset={fetchDataset}
onUpload={() => setShowUploadDialog(true)}
similarDatasets={similarDatasets}
similarDatasetsLoading={similarDatasetsLoading}
similarTags={similarTagNames}
/>
)}
{activeTab === "children" && (