feat(knowledge): 添加知识条目文件上传和下载功能

- 新增文件上传接口支持批量上传知识条目文件
- 实现文件存储路径管理和安全验证机制
- 添加文件下载功能支持知识条目文件导出
- 扩展知识内容类型枚举增加FILE类型
- 扩展知识来源类型枚举增加FILE_UPLOAD类型
- 新增上传请求DTO定义文件验证和元数据配置
- 实现文件上传目录管理和文件名安全处理
- 添加文件扩展名识别和内容类型转换逻辑
This commit is contained in:
2026-01-29 11:17:15 +08:00
parent 3c4b66b451
commit d0b5473068
10 changed files with 511 additions and 87 deletions

View File

@@ -19,12 +19,14 @@ import useFetchData from "@/hooks/useFetchData";
import {
deleteKnowledgeItemByIdUsingDelete,
deleteKnowledgeSetByIdUsingDelete,
downloadKnowledgeItemFileUsingGet,
exportKnowledgeItemsUsingGet,
queryKnowledgeItemsUsingGet,
queryKnowledgeSetByIdUsingGet,
} from "../knowledge-management.api";
import {
knowledgeContentTypeOptions,
knowledgeSourceTypeOptions,
knowledgeStatusMap,
mapKnowledgeItem,
KnowledgeItemView,
@@ -33,6 +35,7 @@ import {
KnowledgeItem,
KnowledgeSet,
KnowledgeContentType,
KnowledgeSourceType,
KnowledgeStatusType,
} from "../knowledge-management.model";
import CreateKnowledgeSet from "../components/CreateKnowledgeSet";
@@ -108,6 +111,12 @@ const KnowledgeSetDetail = () => {
};
const isReadableItem = (record: KnowledgeItemView) => {
if (
record.contentType === KnowledgeContentType.FILE ||
record.sourceType === KnowledgeSourceType.FILE_UPLOAD
) {
return false;
}
return (
record.contentType === KnowledgeContentType.TEXT ||
record.contentType === KnowledgeContentType.MARKDOWN
@@ -115,6 +124,13 @@ const KnowledgeSetDetail = () => {
};
const handleReadItem = async (record: KnowledgeItemView) => {
if (
record.contentType === KnowledgeContentType.FILE ||
record.sourceType === KnowledgeSourceType.FILE_UPLOAD
) {
message.info("该条目为文件,请使用下载查看");
return;
}
setReadItemId(record.id);
setReadTitle(record.title || "知识条目");
@@ -156,6 +172,16 @@ const KnowledgeSetDetail = () => {
}
};
const handleDownloadItem = async (record: KnowledgeItemView) => {
if (!id) return;
try {
await downloadKnowledgeItemFileUsingGet(id, record.id, record.sourceFileId);
} catch (error) {
console.error("下载知识条目文件失败", error);
message.error("下载失败,请稍后重试");
}
};
const statusMeta = knowledgeSet?.status
? knowledgeStatusMap[knowledgeSet.status]
: undefined;
@@ -218,6 +244,10 @@ const KnowledgeSetDetail = () => {
key: "sourceType",
width: 140,
ellipsis: true,
render: (sourceType: string) =>
knowledgeSourceTypeOptions.find((opt) => opt.value === sourceType)?.label ||
sourceType ||
"-",
},
{
title: "更新时间",
@@ -232,16 +262,28 @@ const KnowledgeSetDetail = () => {
width: 200,
render: (_: unknown, record: KnowledgeItemView) => (
<div className="flex items-center gap-2">
<Tooltip title="阅读">
<Button
type="text"
icon={<EyeOutlined />}
onClick={() => handleReadItem(record)}
disabled={!isReadableItem(record)}
loading={readItemId === record.id}
aria-label="阅读"
/>
</Tooltip>
{isReadableItem(record) && (
<Tooltip title="阅读">
<Button
type="text"
icon={<EyeOutlined />}
onClick={() => handleReadItem(record)}
loading={readItemId === record.id}
aria-label="阅读"
/>
</Tooltip>
)}
{(record.contentType === KnowledgeContentType.FILE ||
record.sourceType === KnowledgeSourceType.FILE_UPLOAD) && (
<Tooltip title="下载文件">
<Button
type="text"
icon={<DownloadOutlined />}
onClick={() => handleDownloadItem(record)}
aria-label="下载文件"
/>
</Tooltip>
)}
<Button
type="text"
icon={<EditOutlined />}