From a7d0ad87c9734f2f2c39eca6bdbbee5b99715590 Mon Sep 17 00:00:00 2001 From: wanghanlin <1533525126@qq.com> Date: Fri, 4 Sep 2026 09:26:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(doc):=20=E6=96=87=E6=A1=A3=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=9B=AE=E5=BD=95=E6=A0=91=E9=BB=98=E8=AE=A4=E6=94=B6?= =?UTF-8?q?=E8=B5=B7=E5=B9=B6=E7=A7=BB=E9=99=A4=E6=A0=87=E7=AD=BE=E7=AD=9B?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 目录树去除 expand-all,初始仅显示分类、子目录折叠,点击展开 - 移除工具栏「全部标签」下拉及其数据源链路:filterTag/tagOptions、 api 的 listDocuments tag 参数与 getTagList、store 的 tags/loadTags、 路由守卫预加载 - 后端删除 /document/list 的 tag 参数过滤与 /tag/list 聚合接口 (getTagList 实现一并移除) - 文档自带 tags 属性、行内/详情展示、向量 metadata 均保留不变 --- frontend/src/api/document.ts | 10 +---- frontend/src/router/index.ts | 1 - frontend/src/stores/document.ts | 14 ++---- frontend/src/views/DocList.vue | 6 +-- .../controller/DocumentController.java | 29 +------------ .../supportbot/service/DocumentService.java | 43 +------------------ 6 files changed, 9 insertions(+), 94 deletions(-) diff --git a/frontend/src/api/document.ts b/frontend/src/api/document.ts index 2b7549d..a35ff73 100644 --- a/frontend/src/api/document.ts +++ b/frontend/src/api/document.ts @@ -12,13 +12,12 @@ function authHeaders(): Record { // ==================== 文档 CRUD ==================== /** 文档列表(分页 + 过滤 + 搜索) */ -export function listDocuments(page = 1, size = 10, categoryId?: string, status?: string, keyword?: string, tag?: string, folderId?: string, sortField?: string, sortOrder?: string): Promise { +export function listDocuments(page = 1, size = 10, categoryId?: string, status?: string, keyword?: string, folderId?: string, sortField?: string, sortOrder?: string): Promise { let path = `/document/list?page=${page}&size=${size}` if (categoryId) path += `&categoryId=${categoryId}` if (folderId != null) path += `&folderId=${folderId}` if (status) path += `&status=${status}` if (keyword) path += `&keyword=${encodeURIComponent(keyword)}` - if (tag) path += `&tag=${encodeURIComponent(tag)}` if (sortField) path += `&sortField=${sortField}` if (sortOrder) path += `&sortOrder=${sortOrder}` return request.get(path).then(r => r.data) @@ -128,10 +127,3 @@ export function searchDocuments(query: string, topK?: number, similarityThreshol export function getStats(): Promise { return request.get('/document/stats').then(r => r.data) } - -// ==================== 标签 ==================== - -/** 获取标签列表 */ -export function getTagList(): Promise { - return request.get('/tag/list').then(r => r.data) -} diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 11d1be7..06e7d94 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -62,7 +62,6 @@ router.beforeEach(async (to, _from, next) => { await Promise.all([ categoryStore.loadCategories(), documentStore.loadStats(), - documentStore.loadTags(), ]) } catch { /* 预加载失败不阻塞导航 */ } break diff --git a/frontend/src/stores/document.ts b/frontend/src/stores/document.ts index b1f4fb5..dc79c55 100644 --- a/frontend/src/stores/document.ts +++ b/frontend/src/stores/document.ts @@ -1,13 +1,12 @@ /** - * 文档状态管理 —— 替代旧 store.js 中 stats / tags 部分 + * 文档状态管理 —— 替代旧 store.js 中 stats 部分 */ import { defineStore } from 'pinia' import { ref } from 'vue' -import { getStats, getTagList } from '@/api/document' +import { getStats } from '@/api/document' export const useDocumentStore = defineStore('document', () => { const stats = ref(null) - const tags = ref([]) async function loadStats(): Promise { try { @@ -16,12 +15,5 @@ export const useDocumentStore = defineStore('document', () => { } catch (e) { console.error('加载统计失败', e) } } - async function loadTags(): Promise { - try { - const res = await getTagList() - if (res.success) tags.value = res.data || [] - } catch (e) { console.error('加载标签列表失败', e) } - } - - return { stats, tags, loadStats, loadTags } + return { stats, loadStats } }) diff --git a/frontend/src/views/DocList.vue b/frontend/src/views/DocList.vue index 09e2192..ea35a2b 100644 --- a/frontend/src/views/DocList.vue +++ b/frontend/src/views/DocList.vue @@ -15,7 +15,6 @@ :activable="true" :active-multiple="false" hover - expand-all empty="暂无分类与目录" v-model:actived="activedKeys" @active="onTreeActive" @@ -44,7 +43,6 @@ - 刷新 ⏳ 有文档处理中... 📁 目录筛选 @@ -172,7 +170,6 @@ const loading = ref(false) const filterCategory = ref('') const filterStatus = ref('') const keyword = ref('') -const filterTag = ref('') const selectedIds = ref(new Set()) const hasProcessing = ref(false) @@ -247,7 +244,6 @@ const columns = [ // ==================== 选项 ==================== const categoryOptions = computed(() => [{ label: '全部分类', value: '' }, ...categoryStore.categories.map(c => ({ label: c.name, value: String(c.id) }))]) const statusOptions = [{ label: '全部状态', value: '' }, { label: '已完成', value: 'READY' }, { label: '处理中', value: 'PROCESSING' }, { label: '失败', value: 'FAILED' }] -const tagOptions = computed(() => [{ label: '全部标签', value: '' }, ...(documentStore.tags || []).map((t: any) => ({ label: `${t.tag} (${t.count})`, value: t.tag }))]) // 移动弹窗目录树:确保有「未分类」节点,保留移出到未分类的能力 const moveTreeData = computed(() => { const hasUnclassified = treeData.value.some(n => n.value === 'cat:0') @@ -519,7 +515,7 @@ async function loadData(p = page.value) { page.value = p loading.value = true try { - const json = await listDocuments(p, pageSize.value, filterCategory.value || undefined, filterStatus.value || undefined, keyword.value.trim() || undefined, filterTag.value || undefined, filterFolder.value || undefined, sortInfo.value?.sortBy || undefined, sortInfo.value ? (sortInfo.value.descending ? 'desc' : 'asc') : undefined) + const json = await listDocuments(p, pageSize.value, filterCategory.value || undefined, filterStatus.value || undefined, keyword.value.trim() || undefined, filterFolder.value || undefined, sortInfo.value?.sortBy || undefined, sortInfo.value ? (sortInfo.value.descending ? 'desc' : 'asc') : undefined) if (json.success) { documents.value = (json.data || []).map((d: any) => ({ ...d, _enabled: d.enabled !== false })) total.value = json.total || 0 diff --git a/src/main/java/com/wok/supportbot/controller/DocumentController.java b/src/main/java/com/wok/supportbot/controller/DocumentController.java index 54265e2..8e61063 100644 --- a/src/main/java/com/wok/supportbot/controller/DocumentController.java +++ b/src/main/java/com/wok/supportbot/controller/DocumentController.java @@ -329,7 +329,7 @@ public class DocumentController { } /** - * 查询文档列表(分页 + 过滤 + 关键词搜索 + 标签筛选) + * 查询文档列表(分页 + 过滤 + 关键词搜索) * * @param page 页码(默认1) * @param size 每页大小(默认10) @@ -337,7 +337,6 @@ public class DocumentController { * @param folderId 目录ID过滤(可选,0 表示只看根目录) * @param status 状态过滤(PROCESSING/READY/FAILED,可选) * @param keyword 关键词搜索(模糊匹配标题和文件名,可选) - * @param tag 标签筛选(精确匹配,可选) * @return 分页文档列表 */ @GetMapping("/document/list") @@ -349,11 +348,10 @@ public class DocumentController { @RequestParam(required = false) Long folderId, @RequestParam(required = false) String status, @RequestParam(required = false) String keyword, - @RequestParam(required = false) String tag, @RequestParam(required = false) String sortField, @RequestParam(required = false) String sortOrder) { try { - Map result = documentService.listDocuments(page, size, categoryId, folderId, status, keyword, tag, sortField, sortOrder); + Map result = documentService.listDocuments(page, size, categoryId, folderId, status, keyword, sortField, sortOrder); Map data = new HashMap<>(); data.put("success", true); data.put("data", result.get("records")); @@ -727,29 +725,6 @@ public class DocumentController { return categoryFilter.parse(rawCategoryIds); } - // ==================== 标签管理 ==================== - - /** - * P1-2.3: 获取标签列表(从所有文档的 tags 聚合去重,含使用次数) - */ - @GetMapping("/tag/list") - @PreAuthorize("hasAnyRole('admin','kb_operator')") - public ResponseEntity> getTagList() { - try { - List> tags = documentService.getTagList(); - return ResponseEntity.ok(Map.of( - "success", true, - "data", tags, - "total", tags.size() - )); - } catch (Exception e) { - return ResponseEntity.status(500).body(Map.of( - "success", false, - "message", "获取标签列表失败:" + e.getMessage() - )); - } - } - // ==================== 分块管理 ==================== /** diff --git a/src/main/java/com/wok/supportbot/service/DocumentService.java b/src/main/java/com/wok/supportbot/service/DocumentService.java index 6049c04..5d54498 100644 --- a/src/main/java/com/wok/supportbot/service/DocumentService.java +++ b/src/main/java/com/wok/supportbot/service/DocumentService.java @@ -457,10 +457,10 @@ public class DocumentService { // ==================== 文档管理 ==================== /** - * 分页查询文档列表(手动分页,支持关键词搜索 + 标签筛选 + 目录筛选) + * 分页查询文档列表(手动分页,支持关键词搜索 + 目录筛选) */ public Map listDocuments(int page, int size, Long categoryId, Long folderId, String status, - String keyword, String tag, String sortField, String sortOrder) { + String keyword, String sortField, String sortOrder) { // 参数安全校验 if (page < 1) page = 1; if (size < 1 || size > 100) size = 10; @@ -482,16 +482,6 @@ public class DocumentService { if (kw != null) { countWrapper.and(w -> w.like("title", kw).or().like("source_name", kw)); } - // P1-2.3: 标签筛选(JSONB 包含查询,使用 ObjectMapper 构建合法 JSON) - if (tag != null && !tag.trim().isEmpty()) { - try { - ObjectMapper om = new ObjectMapper(); - String jsonArray = om.writeValueAsString(List.of(tag.trim())); - countWrapper.apply("tags->'tags' @> {0}::jsonb", jsonArray); - } catch (Exception e) { - log.warn("构建标签筛选 JSON 失败: tag={}", tag, e); - } - } // 先查询总数(不加 ORDER BY) Long total = documentMapper.selectCount(countWrapper); @@ -510,15 +500,6 @@ public class DocumentService { if (kw != null) { listWrapper.and(w -> w.like("title", kw).or().like("source_name", kw)); } - if (tag != null && !tag.trim().isEmpty()) { - try { - ObjectMapper om = new ObjectMapper(); - String jsonArray = om.writeValueAsString(List.of(tag.trim())); - listWrapper.apply("tags->'tags' @> {0}::jsonb", jsonArray); - } catch (Exception e) { - log.warn("构建标签筛选 JSON 失败: tag={}", tag, e); - } - } // 排序字段白名单(前端 colKey -> 数据库列名),防 SQL 注入 Map sortColumns = Map.of( "title", "title", @@ -867,26 +848,6 @@ public class DocumentService { } } - // ==================== 标签管理 ==================== - - /** - * P1-2.3: 获取标签列表(从所有文档的 tags JSONB 聚合去重,含使用次数) - * - * @return [{tag: "标签名", count: 使用次数}] - */ - public List> getTagList() { - String sql = "SELECT tag_value AS tag, COUNT(*) AS count " + - "FROM knowledge_document, jsonb_array_elements(tags->'tags') AS tag_value " + - "WHERE is_delete = false AND tags IS NOT NULL AND tags->'tags' IS NOT NULL " + - "GROUP BY tag_value ORDER BY count DESC, tag_value ASC"; - try { - return jdbcTemplate.queryForList(sql); - } catch (Exception e) { - log.warn("获取标签列表失败", e); - return List.of(); - } - } - // ==================== 分块管理 ==================== /**