# 知识库管理系统 - 架构设计 ## 1. 技术栈 | 层 | 技术 | 版本 | |---|---|---| | 后端框架 | Spring Boot | 3.2.x | | ORM | MyBatis Plus | 3.5.x | | 数据库 | MySQL | 8.0+ | | 认证 | JWT (jjwt) | 0.12.x | | 抓取 | Jsoup | 1.17.x | | Diff | google-diff-match-patch | 1.2 | | HTTP 客户端 | Spring RestClient(内置) | - | | 工具库 | Hutool / Lombok | 最新稳定 | | 前端框架 | Vue | 3.4.x | | 构建工具 | Vite | 5.x | | UI 库 | Element Plus | 最新稳定 | | 状态管理 | Pinia | 最新稳定 | | 路由 | Vue Router | 4.x | | 前端 Diff 展示 | diff-match-patch(自渲染)或 vue-diff | - | ## 2. 项目目录结构 ``` knowledge-extractor/ ├── backend/ 后端 Spring Boot 工程 │ ├── pom.xml │ ├── src/main/java/com/knowledge/extractor/ │ │ ├── KnowledgeExtractorApplication.java │ │ ├── config/ │ │ │ ├── WebMvcConfig.java (CORS) │ │ │ ├── SpaController.java (SPA fallback) │ │ │ ├── SecurityConfig.java (JWT 过滤器链) │ │ │ ├── MybatisPlusConfig.java │ │ │ └── RestClientConfig.java │ │ ├── common/ │ │ │ ├── R.java (统一返回) │ │ │ ├── PageResult.java │ │ │ ├── BizException.java │ │ │ ├── GlobalExceptionHandler.java │ │ │ ├── JwtUtil.java │ │ │ └── AesUtil.java │ │ ├── module/ │ │ │ ├── auth/ 登录、JWT │ │ │ │ ├── AuthController.java │ │ │ │ ├── AuthService.java │ │ │ │ ├── JwtFilter.java │ │ │ │ └── entity/SysUser.java │ │ │ ├── tag/ 标签体系(按 tag-system 技能) │ │ │ │ ├── TagController.java │ │ │ │ ├── TagService.java │ │ │ │ ├── impl/TagServiceImpl.java │ │ │ │ ├── entity/{TagGroup,Tag,TagAssignment}.java │ │ │ │ ├── mapper/{TagGroupMapper,TagMapper,TagAssignmentMapper}.java │ │ │ │ ├── dto/{TagExportDTO,TagImportDTO,TagImportResultDTO}.java │ │ │ │ └── vo/TagBriefVO.java │ │ │ ├── article/ 文章录入与抓取 │ │ │ │ ├── ArticleController.java │ │ │ │ ├── ArticleService.java │ │ │ │ ├── LinkFetcher.java (Jsoup + readability) │ │ │ │ ├── entity/Article.java │ │ │ │ └── mapper/ArticleMapper.java │ │ │ ├── llm/ LLM 配置与客户端 │ │ │ │ ├── LlmConfigController.java │ │ │ │ ├── LlmConfigService.java │ │ │ │ ├── OpenAiCompatibleClient.java │ │ │ │ ├── entity/LlmConfig.java │ │ │ │ └── mapper/LlmConfigMapper.java │ │ │ └── knowledge/ 知识聚合 + 版本管理 │ │ │ ├── KnowledgeController.java │ │ │ ├── KnowledgeService.java │ │ │ ├── KnowledgeAggregateService.java (调用 LLM、聚合、diff) │ │ │ ├── DiffService.java │ │ │ ├── entity/{Knowledge,KnowledgeVersion}.java │ │ │ └── mapper/{KnowledgeMapper,KnowledgeVersionMapper}.java │ │ └── mapper/ MyBatis mapper xml(如需) │ └── src/main/resources/ │ ├── application.yml │ ├── mapper/ MyBatis xml │ └── static/ 前端构建产物(vite 直出) ├── frontend/ 前端 Vue 工程 │ ├── package.json │ ├── vite.config.js │ ├── index.html │ └── src/ │ ├── main.js │ ├── App.vue │ ├── router/index.js │ ├── stores/ Pinia stores │ │ ├── auth.js │ │ ├── tag.js │ │ └── ... │ ├── api/ axios 封装 │ │ ├── request.js │ │ ├── auth.js │ │ ├── tag.js │ │ ├── article.js │ │ ├── knowledge.js │ │ └── llm.js │ ├── layouts/ │ │ └── MainLayout.vue (顶部导航 + 侧边栏) │ ├── components/ │ │ ├── tag/ (来自 tag-system 技能) │ │ │ ├── TreeNodeItem.vue │ │ │ └── TagSelector.vue │ │ └── knowledge/ │ │ ├── VersionTimeline.vue │ │ └── DiffViewer.vue │ └── views/ │ ├── LoginView.vue │ ├── SetupView.vue (首次启动引导) │ ├── TagsView.vue (标签管理) │ ├── ArticlesView.vue (文章录入) │ ├── KnowledgeListView.vue (知识列表) │ ├── KnowledgeDetailView.vue (知识详情 + 时间轴) │ └── SettingsView.vue (LLM 配置 + 改密) ├── docs/ 本设计文档目录 ├── run.bat Windows 一键构建脚本 ├── run.sh Linux/macOS 一键构建脚本 └── .gitattributes ``` ## 3. 数据库表设计(共 8 张) ### 3.1 标签体系(tag-system 技能标准) ```sql -- 标签组(分类维度) CREATE TABLE tag_group ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, description VARCHAR(500), sort_order INT DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_name (name) ); -- 标签节点(自引用树,每组有一个同名根节点 parent_id=NULL) CREATE TABLE tag ( id BIGINT PRIMARY KEY AUTO_INCREMENT, group_id BIGINT NOT NULL, parent_id BIGINT, name VARCHAR(100) NOT NULL, sort_order INT DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_group (group_id), KEY idx_parent (parent_id) ); -- 通用打标关联 CREATE TABLE tag_assignment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, entity_type VARCHAR(50) NOT NULL, -- article / knowledge entity_id BIGINT NOT NULL, tag_id BIGINT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_entity_tag (entity_type, entity_id, tag_id), KEY idx_entity (entity_type, entity_id) ); ``` ### 3.2 系统与配置 ```sql -- 单用户 CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password_hash VARCHAR(100) NOT NULL, salt VARCHAR(50) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- LLM 配置(单行记录,id 固定为 1) CREATE TABLE llm_config ( id BIGINT PRIMARY KEY, base_url VARCHAR(255) NOT NULL, -- 如 https://api.openai.com/v1 api_key VARCHAR(500) NOT NULL, -- AES 加密后内容 model VARCHAR(100) NOT NULL, -- 如 gpt-4o-mini temperature DECIMAL(3,2) DEFAULT 0.30, max_tokens INT DEFAULT 4000, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); ``` ### 3.3 文章 ```sql CREATE TABLE article ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255) NOT NULL, source_type VARCHAR(20) NOT NULL, -- md / txt / link source_url VARCHAR(500), -- link 类型记录原 URL content MEDIUMTEXT, -- 原始正文(用于展示) raw_text MEDIUMTEXT, -- 抽干后的纯文本(喂给 LLM) llm_topic VARCHAR(100), -- LLM 归纳的主题名 status VARCHAR(20) DEFAULT 'pending', -- pending/classified/aggregated error_msg VARCHAR(500), -- LLM 调用失败信息 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_status (status), KEY idx_topic (llm_topic) ); ``` ### 3.4 知识与版本 ```sql -- 聚合知识(每个 LLM 自由生成的主题对应一条记录) CREATE TABLE knowledge ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, -- 主题名(如"大模型部署方法") current_content MEDIUMTEXT NOT NULL, -- 最新版全文(前端展示) article_count INT DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_title (title) ); -- 版本快照(每次变更一条) CREATE TABLE knowledge_version ( id BIGINT PRIMARY KEY AUTO_INCREMENT, knowledge_id BIGINT NOT NULL, version_no INT NOT NULL, -- 第几版(1=初版) article_id BIGINT NOT NULL, -- 触发本次变更的文章 change_type VARCHAR(20) NOT NULL, -- create / append / revise change_desc VARCHAR(500) NOT NULL, -- 变更描述(LLM 生成) content_snapshot MEDIUMTEXT NOT NULL, -- 本版完整 MD 快照 diff_added INT DEFAULT 0, -- 新增行数 diff_removed INT DEFAULT 0, -- 删除行数 diff_patch TEXT, -- diff-match-patch patch 文本(可选) created_at DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_knowledge (knowledge_id), KEY idx_version (knowledge_id, version_no) ); ``` ## 4. API 设计 ### 4.1 认证 | 方法 | 路径 | 说明 | |---|---|---| | POST | `/api/auth/setup` | 首次启动设置管理员账号 | | GET | `/api/auth/setup-status` | 查询是否已初始化 | | POST | `/api/auth/login` | 登录,返回 JWT | | POST | `/api/auth/change-password` | 修改密码 | | GET | `/api/auth/me` | 获取当前用户 | ### 4.2 标签(按 tag-system 技能规范) | 方法 | 路径 | 说明 | |---|---|---| | GET | `/api/tags/groups` | 列出所有标签组 | | POST | `/api/tags/groups` | 创建标签组 | | POST | `/api/tags/groups/{id}/edit` | 更新 | | POST | `/api/tags/groups/{id}/delete` | 删除 | | GET | `/api/tags/groups/{groupId}/tags` | 获取组内扁平标签列表 | | POST | `/api/tags/groups/{groupId}/tags` | 创建标签节点 | | POST | `/api/tags/{id}/edit` | 重命名 | | POST | `/api/tags/{id}/delete` | 删除(递归) | | POST | `/api/tags/{id}/move` | 移动(含防循环) | | GET | `/api/tags/export` | 导出 | | POST | `/api/tags/import` | 导入 | | POST | `/api/tags/assignments` | 设置实体标签(全量替换) | | GET | `/api/tags/assignments?entityType=&entityId=` | 获取实体标签 | | GET | `/api/tags/all-with-tags` | 全部标签(选择器用) | ### 4.3 文章 | 方法 | 路径 | 说明 | |---|---|---| | GET | `/api/articles?page=&size=&keyword=` | 分页列表 | | GET | `/api/articles/{id}` | 文章详情 | | POST | `/api/articles` | 录入文章(md/txt/link) | | POST | `/api/articles/{id}/fetch-link` | 重新抓取链接 | | POST | `/api/articles/{id}/classify` | 触发 LLM 重新归类 | | POST | `/api/articles/{id}/aggregate` | 触发聚合(手动) | | DELETE | `/api/articles/{id}` | 删除 | ### 4.4 知识 | 方法 | 路径 | 说明 | |---|---|---| | GET | `/api/knowledge?page=&size=&keyword=` | 知识列表 | | GET | `/api/knowledge/{id}` | 知识详情(含 current_content) | | GET | `/api/knowledge/{id}/versions` | 时间轴(版本列表) | | GET | `/api/knowledge/{id}/versions/{versionNo}` | 单版本详情(含 snapshot + diff) | | GET | `/api/knowledge/{id}/versions/{versionNo}/article` | 跳转关联原文章 | | DELETE | `/api/knowledge/{id}` | 删除知识(级联删版本) | ### 4.5 LLM 配置 | 方法 | 路径 | 说明 | |---|---|---| | GET | `/api/llm/config` | 获取配置(api_key 返回掩码) | | POST | `/api/llm/config` | 保存配置 | | POST | `/api/llm/config/test` | 测试连通性 | ## 5. 业务流程 ### 5.1 文章录入与归类流程 ``` 1. 用户提交文章(md/txt/link) ├── md/txt:直接保存 content + raw_text └── link:Jsoup 抓取 + readability 提取正文;失败提示用户手动贴入 2. 文章入库后状态 = pending └── 自动(或用户手动点)触发 classify 3. classify 流程: a. 加载所有标签扁平列表(含路径,如"计算机/人工智能/LLM") b. 调用 LLM 接口,prompt 让其输出 JSON: { "candidateTagIds": [12, 34], // 匹配的已有标签 ID "newTags": [ // 需要新建的标签 { "parentPath": "计算机/人工智能", "name": "RAG" } ], "topic": "大模型部署方法" // 文章主题 } c. 新建缺失标签,写入 tag_assignment d. 更新 article.llm_topic = topic, status = classified 4. 自动触发 aggregate(或用户手动) a. 查询现有所有 knowledge 列表(仅 id + title) b. 调用 LLM,让其判断本文应归入哪个 knowledge(按 topic + 现有标题语义匹配) c. 若归入已有: - 调用 LLM 生成补充段落 + 变更描述 - 新建 knowledge_version(type=append, version_no=last+1) - 更新 knowledge.current_content d. 若不归入任何已有: - 调用 LLM 生成初版知识全文 - 新建 knowledge + knowledge_version(type=create, version_no=1) e. 计算 diff(前一版 vs 当前版),存入 diff_added/diff_removed/diff_patch ``` ### 5.2 知识查看流程 ``` 1. 进入知识列表 → 选择一条知识 → 进入详情页 2. 详情页三栏布局: ├── 左:版本时间轴(点击节点) ├── 中:当前 knowledge.current_content 渲染 └── 右:选中版本的变更详情面板 ├── 变更类型徽章(create/append/revise) ├── 变更描述 ├── diff 行数(+x / -y) ├── diff 可视化(红绿块) └── "查看关联原文章"按钮 → 跳转 /articles/{article_id} 3. 切换时间轴节点 → 右侧面板与中间内容联动: - 点击历史版本 → 中间展示该版本 content_snapshot ``` ## 6. 初始化与引导 ``` 首次启动 → 检查 sys_user 表 ├── 空 → 前端访问任意页面跳转 /setup │ → 用户设置 username/password → 同时配置 LLM │ → 写入 sys_user + llm_config → 跳转 /login └── 非空 → 正常登录流程 ``` ## 7. 安全设计 - JWT:HS256,密钥从配置读取,过期时间 7 天 - api_key:AES-128 加密入库,密钥从 application.yml 读取 - 密码:BCrypt 加盐 - 所有 /api/** 除 /api/auth/setup、/api/auth/login、/api/auth/setup-status 外需 JWT