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 b9f7f26..4ffbbc2 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 @@ -35,7 +35,9 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; /** @@ -47,6 +49,8 @@ import java.util.Optional; @Service @RequiredArgsConstructor public class KnowledgeBaseService { + private static final String RELATIVE_PATH_KEY = "relativePath"; + private static final String PATH_SEPARATOR = "/"; private final KnowledgeBaseRepository knowledgeBaseRepository; private final RagFileRepository ragFileRepository; private final ApplicationEventPublisher eventPublisher; @@ -146,6 +150,12 @@ public class KnowledgeBaseService { ragFile.setKnowledgeBaseId(knowledgeBase.getId()); ragFile.setFileId(fileInfo.id()); ragFile.setFileName(fileInfo.fileName()); + String relativePath = normalizeRelativePath(fileInfo.relativePath()); + if (StringUtils.hasText(relativePath)) { + Map metadata = new HashMap<>(); + metadata.put(RELATIVE_PATH_KEY, relativePath); + ragFile.setMetadata(metadata); + } ragFile.setStatus(FileStatus.UNPROCESSED); return ragFile; }).toList(); @@ -153,6 +163,17 @@ public class KnowledgeBaseService { eventPublisher.publishEvent(new DataInsertedEvent(knowledgeBase, request)); } + private String normalizeRelativePath(String relativePath) { + if (!StringUtils.hasText(relativePath)) { + return ""; + } + String normalized = relativePath.replace("\\", PATH_SEPARATOR).trim(); + while (normalized.startsWith(PATH_SEPARATOR)) { + normalized = normalized.substring(1); + } + return normalized; + } + public PagedResponse listFiles(String knowledgeBaseId, RagFileReq request) { IPage page = new Page<>(request.getPage(), request.getSize()); request.setKnowledgeBaseId(knowledgeBaseId); @@ -222,4 +243,4 @@ public class KnowledgeBaseService { }); return searchResults; } -} \ No newline at end of file +} diff --git a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/AddFilesReq.java b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/AddFilesReq.java index 65a642f..30f340d 100644 --- a/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/AddFilesReq.java +++ b/backend/services/rag-indexer-service/src/main/java/com/datamate/rag/indexer/interfaces/dto/AddFilesReq.java @@ -21,6 +21,6 @@ public class AddFilesReq { private String delimiter; private List files; - public record FileInfo(String id, String fileName) { + public record FileInfo(String id, String fileName, String relativePath) { } } diff --git a/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx b/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx index ff46c5f..4e34da1 100644 --- a/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx +++ b/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx @@ -57,14 +57,19 @@ type KBFileRow = KBFile & { fileCount?: number; }; -const normalizePath = (value?: string) => (value ?? "").replace(/\\/g, "/"); +const PATH_SEPARATOR = "/"; +const RELATIVE_PATH_KEY = "relativePath"; +const normalizePath = (value?: string) => + (value ?? "").replace(/\\/g, PATH_SEPARATOR); const normalizePrefix = (value?: string) => { const trimmed = normalizePath(value).replace(/^\/+/, "").trim(); if (!trimmed) { return ""; } - return trimmed.endsWith("/") ? trimmed : `${trimmed}/`; + return trimmed.endsWith(PATH_SEPARATOR) + ? trimmed + : `${trimmed}${PATH_SEPARATOR}`; }; const splitRelativePath = (fullPath: string, prefix: string) => { @@ -72,7 +77,17 @@ const splitRelativePath = (fullPath: string, prefix: string) => { return []; } const remainder = fullPath.slice(prefix.length); - return remainder.split("/").filter(Boolean); + return remainder.split(PATH_SEPARATOR).filter(Boolean); +}; + +const resolveFileRelativePath = (file: KBFile) => { + const metadata = file?.metadata as Record | undefined; + const metadataPath = + metadata && typeof metadata[RELATIVE_PATH_KEY] === "string" + ? String(metadata[RELATIVE_PATH_KEY]) + : ""; + const rawPath = metadataPath || file.fileName || file.name || ""; + return normalizePath(rawPath).replace(/^\/+/, ""); }; const KnowledgeBaseDetailPage: React.FC = () => { @@ -196,9 +211,11 @@ const KnowledgeBaseDetailPage: React.FC = () => { return; } const trimmed = currentPrefix.replace(/\/$/, ""); - const parts = trimmed.split("/").filter(Boolean); + const parts = trimmed.split(PATH_SEPARATOR).filter(Boolean); parts.pop(); - const parentPrefix = parts.length ? `${parts.join("/")}/` : ""; + const parentPrefix = parts.length + ? `${parts.join(PATH_SEPARATOR)}${PATH_SEPARATOR}` + : ""; setFilePrefix(parentPrefix); }; @@ -210,7 +227,7 @@ const KnowledgeBaseDetailPage: React.FC = () => { const directoryPrefix = normalizePrefix(`${currentPrefix}${directoryName}`); const targetIds = allFiles .filter((file) => { - const fullPath = normalizePath(file.fileName || file.name); + const fullPath = resolveFileRelativePath(file); return fullPath.startsWith(directoryPrefix); }) .map((file) => file.id); @@ -245,7 +262,7 @@ const KnowledgeBaseDetailPage: React.FC = () => { const fileItems: KBFileRow[] = []; allFiles.forEach((file) => { - const fullPath = normalizePath(file.fileName || file.name); + const fullPath = resolveFileRelativePath(file); if (!fullPath) { return; } @@ -278,10 +295,11 @@ const KnowledgeBaseDetailPage: React.FC = () => { return; } + const displayName = file.fileName || leafName; fileItems.push({ ...file, - name: leafName, - displayName: leafName, + name: displayName, + displayName, fullPath, }); }); diff --git a/frontend/src/pages/KnowledgeBase/components/AddDataDialog.tsx b/frontend/src/pages/KnowledgeBase/components/AddDataDialog.tsx index 7f635e6..a738ac0 100644 --- a/frontend/src/pages/KnowledgeBase/components/AddDataDialog.tsx +++ b/frontend/src/pages/KnowledgeBase/components/AddDataDialog.tsx @@ -26,19 +26,20 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) { const [selectedFilesMap, setSelectedFilesMap] = useState({}); + const PATH_SEPARATOR = "/"; const normalizePath = (value?: string) => - (value ?? "").replace(/\\/g, "/"); + (value ?? "").replace(/\\/g, PATH_SEPARATOR); - const resolveRelativeFileName = (file: DatasetFile) => { + const resolveRelativePath = (file: DatasetFile) => { const normalizedName = normalizePath(file.fileName); - if (normalizedName.includes("/")) { + if (normalizedName.includes(PATH_SEPARATOR)) { return normalizedName.replace(/^\/+/, ""); } const rawPath = normalizePath(file.path || file.filePath); const datasetId = String(file.datasetId || ""); if (rawPath && datasetId) { - const marker = `/${datasetId}/`; + const marker = `${PATH_SEPARATOR}${datasetId}${PATH_SEPARATOR}`; const index = rawPath.lastIndexOf(marker); if (index >= 0) { const relative = rawPath @@ -50,7 +51,7 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) { } } - const fallbackName = rawPath.split("/").pop(); + const fallbackName = rawPath.split(PATH_SEPARATOR).pop(); return fallbackName || file.fileName; }; @@ -158,7 +159,8 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) { const requestData = { files: Object.values(selectedFilesMap).map((file) => ({ id: String(file.id), - fileName: resolveRelativeFileName(file as DatasetFile), + fileName: (file as DatasetFile).fileName, + relativePath: resolveRelativePath(file as DatasetFile), })), processType: newKB.processType, chunkSize: Number(newKB.chunkSize), // 确保是数字类型