Quellcode durchsuchen

plan: dimension knowledge base implementation plan (17 tasks, 4 phases)

liaoxg vor 4 Monaten
Ursprung
Commit
f4abf39a0c
1 geänderte Dateien mit 472 neuen und 0 gelöschten Zeilen
  1. 472 0
      docs/superpowers/plans/2026-06-20-dimension-knowledge-base-plan.md

+ 472 - 0
docs/superpowers/plans/2026-06-20-dimension-knowledge-base-plan.md

@@ -0,0 +1,472 @@
+# 维度配置 + 知识库管理平台 — 实施计划
+
+**日期:** 2026-06-21
+**状态:** 准备就绪
+**基准:** `docs/superpowers/specs/2026-06-20-dimension-knowledge-base-design.md`
+**范围:** cfc-backend(5实体 + 3 Service + 5 Controller)+ cfc-web(api + store + 4组件 + 2页面)+ Dify 集成
+
+---
+
+## 总览
+
+17 个任务,分 4 阶段执行。后端先于前端,模块独立,可并行。
+
+---
+
+## Phase 1: 后端基础设施(Task 1–7)
+
+### Task 1: 创建 5 张数据库表
+
+**文件:** `cfc-backend/src/main/resources/`
+
+**操作:**
+- 在 `schema.sql` 或 `DatabaseInitializer` 中添加 5 张表的 CREATE TABLE 语句(见设计文档 2.1–2.5)
+- 表名: `product_dimension_config`, `dan_knowledge_base`, `dan_knowledge_base_dimension`, `dan_knowledge_tag`, `dan_knowledge_base_tag`
+- 自增主键、默认值、唯一约束、外键级联删除
+
+**验证:**
+```sql
+DESC product_dimension_config;
+SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='cfc' AND TABLE_NAME IN ('product_dimension_config','dan_knowledge_base','dan_knowledge_base_dimension','dan_knowledge_tag','dan_knowledge_base_tag');
+```
+
+---
+
+### Task 2: 创建 Entity + Mapper
+
+**文件:** 每个 Entity 一个文件,Mapper 接口同理
+
+| Entity | 文件 | 说明 |
+|--------|------|------|
+| `ProductDimensionConfig` | `entity/ProductDimensionConfig.java` | `@TableName("product_dimension_config")` |
+| `DanKnowledgeBase` | `entity/DanKnowledgeBase.java` | `@TableName("dan_knowledge_base")` |
+| `DanKnowledgeBaseDimension` | `entity/DanKnowledgeBaseDimension.java` | `@TableName("dan_knowledge_base_dimension")` |
+| `DanKnowledgeTag` | `entity/DanKnowledgeTag.java` | `@TableName("dan_knowledge_tag")` |
+| `DanKnowledgeBaseTag` | `entity/DanKnowledgeBaseTag.java` | `@TableName("dan_knowledge_base_tag")` |
+
+**模式参考:** `ZodiacConfig.java`(`@Data`, `@TableId(type = IdType.AUTO)`, implements `Serializable`)
+**Mapper 参考:** `ZodiacConfigMapper.java`(`extends BaseMapper<ZodiacConfig>`)
+
+**API 路径:**
+- Entity: `cfc-backend/src/main/java/com/etotem/cfc/entity/`
+- Mapper: `cfc-backend/src/main/java/com/etotem/cfc/mapper/`
+
+**验证:** `mvn clean compile` 通过
+
+---
+
+### Task 3: DimensionConfig CRUD Controller + Service
+
+**文件:**
+- `service/ProductDimensionConfigService.java`
+- `controller/admin/ProductDimensionConfigController.java`
+
+**Service 方法:**
+- `list(String dimensionType, Integer enabled)` — 列表(含筛选)
+- `get(Long id)` — 详情
+- `save(ProductDimensionConfig config)` — 新增/更新(id 有值则 update,否则 insert)
+- `delete(Long id)` — 删除
+- `tree()` — 树形结构(递归组装 parent/children)
+
+**Controller 端点:**
+| 端点 | Service 方法 |
+|------|-------------|
+| `POST /api/admin/dimension-config/list` | `list()` |
+| `POST /api/admin/dimension-config/get` | `get()` |
+| `POST /api/admin/dimension-config/save` | `save()` |
+| `POST /api/admin/dimension-config/delete` | `delete()` |
+| `POST /api/admin/dimension-config/tree` | `tree()` |
+
+**验证:** `mvn clean compile` → 启动 → curl 测试端点
+
+**模式参考:** `ZodiacConfigController.java`(`@Resource` Service, `@PostMapping`, 返回 `Result`)
+
+---
+
+### Task 4: KnowledgeBase CRUD Controller + Service
+
+**文件:**
+- `service/DanKnowledgeBaseService.java`
+- `controller/admin/DanKnowledgeBaseController.java`
+
+**Service 方法:**
+- `list(String keyword, Integer page, Integer size)` — 分页(支持 keyword 标题搜索)
+- `get(Long id)` — 详情(含关联的 dimensionIds、tagIds)
+- `save(DanKnowledgeBase knowledge)` — 新增/更新
+- `delete(Long id)` — 物理删除(级联删除关联表记录)
+
+**Controller 端点:**
+| 端点 | Service 方法 |
+|------|-------------|
+| `POST /api/admin/knowledge-base/list` | `list()` |
+| `POST /api/admin/knowledge-base/get` | `get()` |
+| `POST /api/admin/knowledge-base/save` | `save()` |
+| `POST /api/admin/knowledge-base/delete` | `delete()` |
+
+**验证:** `mvn clean compile` 通过
+
+> 注:筛选(dimensionCode/tagId)依赖关联表查询,在 Task 6 完善
+
+---
+
+### Task 5: KnowledgeTag CRUD Controller + Service
+
+**文件:**
+- `service/DanKnowledgeTagService.java`
+- `controller/admin/DanKnowledgeTagController.java`
+
+**Service 方法:**
+- `list()` — 全量列表(无分页,标签数量少)
+- `save(DanKnowledgeTag tag)` — 新增/更新(tag_name 有 UNIQUE 约束)
+- `delete(Long id)` — 删除(级联删除关联记录)
+
+**Controller 端点:**
+| 端点 | Service 方法 |
+|------|-------------|
+| `POST /api/admin/knowledge-tag/list` | `list()` |
+| `POST /api/admin/knowledge-tag/save` | `save()` |
+| `POST /api/admin/knowledge-tag/delete` | `delete()` |
+
+**验证:** `mvn clean compile` 通过
+
+---
+
+### Task 6: 关联管理(KnowledgeBase ↔ Dimension ↔ Tag)
+
+**文件:** 在已有的 `DanKnowledgeBaseService`、`DanKnowledgeBaseController` 中增强
+
+**新增 Service 方法:**
+- `bindDimensions(Long knowledgeId, List<Long> dimensionIds)` — 先删后加(先删 dan_knowledge_base_dimension 中该 knowledge 的所有记录,再批量插入)
+- `bindTags(Long knowledgeId, List<Long> tagIds)` — 同上
+- `listByDimension(String dimensionCode, int page, int size)` — 按维度代码筛选分页
+- `listByTag(Long tagId, int page, int size)` — 按标签筛选分页
+- `listWithFilters(String keyword, Long dimensionId, Long tagId, int page, int size)` — 综合筛选
+
+**新增/增强 Controller 端点:**
+| 端点 | 说明 |
+|------|------|
+| `POST /api/admin/knowledge-base/bind-dimensions` | 绑定维度 |
+| `POST /api/admin/knowledge-base/bind-tags` | 绑定标签 |
+
+**增强 `save()`:** 保存知识点的同时保存关联的 dimensionIds/tagIds(避免两次调用)
+
+**验证:** 手动测试绑定/解绑逻辑
+
+---
+
+### Task 7: DifySyncService(占位符)
+
+**文件:**
+- `service/DifySyncService.java`
+
+**Service 方法:**
+- `syncToDify(Long knowledgeId, List<String> dimensionCodes)` — `@Async` 异步推送
+- `removeFromDify(Long knowledgeId, String difyDatasetId)` — `@Async` 异步删除
+
+**实现:**
+- 读取 `dify.base-url`、`dify.api-key` 配置
+- Dify API 调用先用 `log.warn("Dify sync not configured: {}", url)` 占位
+- 在 `DanKnowledgeBaseService.save()` 和 `delete()` 末尾调用 `difySyncService.syncToDify()` / `removeFromDify()`(try-catch,失败只 log 不抛异常)
+
+**配置:**
+```yaml
+# application.yml
+dify:
+  base-url: http://dify.bianwoyou.cn
+  api-key:
+  timeout: 10000
+```
+
+**验证:** 启动后执行知识库 CRUD,观察日志无异常
+
+---
+
+## Phase 2: 前端核心组件(Task 8–13)
+
+### Phase 2 模式参考
+
+- **API 文件:** `cfc-web/src/api/xxx.js`(已有模式: `export const func = (data) => request({ url, method: 'post', data })`)
+- **Vuex store:** `cfc-web/src/store/index.js`(目前是 flat 结构,无 modules 目录。为保持一致性,新增 `dimension.js` 作为独立模块,在 `store/index.js` 中 `import dimension from './modules/dimension'` 注册 `modules: { dimension }`)
+- **组件:** Element UI 组件库(`el-table`, `el-dialog`, `el-select`, `el-tree`, `el-tag`)
+- **页面:** `cfc-web/src/views/admin/` 下的 Vue 2 组件(Options API)
+
+---
+
+### Task 8: api/dimension.js API 封装
+
+**文件:** `cfc-web/src/api/dimension.js`
+
+**函数列表:**
+```javascript
+// 维度配置
+export const getDimensionList = (params) => request(...)
+export const getDimensionDetail = (id) => request(...)
+export const saveDimension = (data) => request(...)
+export const deleteDimension = (id) => request(...)
+export const getDimensionTree = () => request(...)
+
+// 知识库
+export const getKnowledgeList = (params) => request(...)
+export const getKnowledgeDetail = (id) => request(...)
+export const saveKnowledge = (data) => request(...)
+export const deleteKnowledge = (id) => request(...)
+export const bindKnowledgeDimensions = (data) => request(...)
+export const bindKnowledgeTags = (data) => request(...)
+
+// 标签
+export const getTagList = () => request(...)
+export const saveTag = (data) => request(...)
+export const deleteTag = (id) => request(...)
+```
+
+**验证:** 无运行时验证,作为模块静态检查
+
+---
+
+### Task 9: store/modules/dimension.js Vuex 模块
+
+**文件:** `cfc-web/src/store/modules/dimension.js`
+
+**State:**
+- `dimensions: []` — 维度列表(flat)
+- `dimensionTree: []` — 树形维度
+- `knowledgeList: { items, total }` — 知识点分页
+- `tags: []` — 标签列表
+- `loading: false`
+
+**Actions:** 对应 Task 8 的 API 调用
+**Mutations:** 更新 state
+
+**注册:** 在 `store/index.js` 中 `import dimension from './modules/dimension'`,`modules: { dimension }`
+
+**验证:** 将 `dimension.js` import 到 store 后无 Babel 错误
+
+---
+
+### Task 10: DimensionSelector 组件
+
+**文件:** `cfc-web/src/components/DimensionSelector/index.vue`
+
+**Props:**
+- `value` — v-model 绑定值(多选时是 Array,单选时是 String/Number)
+- `multiple` — 是否多选(默认 true)
+- `dimensionType` — 筛选维度类型(可选,传则只展示该类型的维度)
+- `showTree` — 是否树形展示(默认 false,flat)
+- `placeholder` — 默认 "请选择维度"
+
+**实现:** 基于 `el-select` + `el-option`,options 来自 Vuex `dimensions` (flat) 或 `dimensionTree` (树形转 flat)
+
+**事件:** `@input` 同步 v-model
+
+**验证:** 在任意页面临时引用测试
+
+---
+
+### Task 11: TagSelector 组件
+
+**文件:** `cfc-web/src/components/TagSelector/index.vue`
+
+**Props:**
+- `value` — v-model 绑定(Array of tagIds)
+- `placeholder` — 默认 "请选择标签"
+
+**实现:**
+- `el-select` 多选
+- 支持输入新标签(`filterable`, `allow-create`),触发 `@new-tag` 事件调用 `saveTag` API
+- options 来自 Vuex `tags`
+
+**事件:** `@input`, `@new-tag`
+
+**验证:** 在任意页面临时引用测试
+
+---
+
+### Task 12: KnowledgeTable 组件
+
+**文件:** `cfc-web/src/components/KnowledgeTable/index.vue`
+
+**Props:**
+- `dimensionCode` — 筛选维度(可选)
+- `tagId` — 筛选标签(可选)
+- `keyword` — 搜索关键词(可选,双向绑定)
+- `page` / `size` — 分页参数
+
+**实现:**
+- `el-table` 带 `:data`、`v-loading`
+- 列: 标题、内容(截断)、关联维度(tag 形式)、状态(switch)、排序、操作按钮
+- `el-pagination` 底部
+- 行内操作: 编辑 / 删除 / 绑定维度 / 绑定标签(emit 事件,由父页面处理弹窗)
+- 内容列过长时 `show-overflow-tooltip`
+
+**事件:**
+- `@edit(row)` / `@delete(row)` / `@bind-dimensions(row)` / `@bind-tags(row)`
+- `@page-change(page, size)` — 分页切换
+
+**验证:** 在任意页面临时引用测试
+
+---
+
+### Task 13: KnowledgeDialog 组件
+
+**文件:** `cfc-web/src/components/KnowledgeDialog/index.vue`
+
+**Props:**
+- `visible` — 显示/隐藏
+- `knowledge` — 编辑时传入数据(新增传 null)
+
+**实现:**
+- `el-dialog` 包装
+- 表单: 标题(`el-input`)、内容(`el-input type="textarea"`)、维度多选(`DimensionSelector`)、标签多选(`TagSelector`)、排序(`el-input-number`)、状态(`el-switch`)
+- 表单验证(标题必填、内容必填)
+- @open 时如果 `knowledge` 有值则 populate 表单
+- 提交时调用 `saveKnowledge` API
+
+**事件:**
+- `@close` — 关闭对话框
+- `@saved` — 保存成功后通知父页面刷新列表
+
+**验证:** 在任意页面临时引用测试
+
+---
+
+## Phase 3: 管理页面(Task 14–15)
+
+### Task 14: 维度配置管理页
+
+**文件:** `cfc-web/src/views/admin/dimension/index.vue`
+
+**布局:**
+- 左上方搜索栏: 维度类型筛选 + 新增按钮
+- 主区域: `el-tree` 树形展示(`node-key="id"`, `default-expand-all`)
+- 点击树节点 → 右侧展示详情/编辑弹窗
+
+**功能:**
+- 新增维度: 弹窗表单输入所有字段
+- 编辑维度: 点击树节点行内编辑按钮 → 弹窗预填
+- 删除维度: 行内删除,二次确认
+- `dify_dataset_id` 字段为可选项,文本框输入
+
+**路由:** `/admin/dimension`(在 `router/index.js` 的 admin 路由下添加)
+
+**验证:** `npm run serve` → 浏览器访问页面,测试 CRUD
+
+---
+
+### Task 15: 知识库管理页
+
+**文件:** `cfc-web/src/views/admin/knowledge/index.vue`
+
+**布局:**
+```
+┌──────────────────────────────────────┐
+│      搜索框 [keyword]    [新增按钮]   │
+├──────────┬───────────────────────────┤
+│ 筛选面板  │    KnowledgeTable 组件    │
+│ - 维度树  │                           │
+│ - 标签列表 │                           │
+│          │                           │
+└──────────┴───────────────────────────┘
+```
+
+**左侧筛选面板:**
+- 维度树(`el-tree`)— 点击筛选该维度的知识点
+- 标签列表(`el-tag` + `el-select`)— 点击筛选该标签的知识点
+- 支持组合筛选(dimensionCode + tagId + keyword)
+
+**实现:**
+- 引用 `KnowledgeTable` 组件展示主区域
+- 点击筛选 → 更新 `dimensionCode`/`tagId`/`keyword` → 传入 `KnowledgeTable`
+- 新增知识点 → 打开 `KnowledgeDialog`
+- 删除/绑定操作 → 分别调用对应 API,成功后刷新
+
+**路由:** `/admin/knowledge`(在 `router/index.js` 的 admin 路由下添加)
+
+**验证:** `npm run serve` → 浏览器访问页面,测试完整流
+
+---
+
+## Phase 4: Dify 集成(Task 16–17)
+
+### Task 16: 补充 Dify API 实现
+
+**前置条件:** 获取 Dify 最新的 Document Create API 格式
+
+**操作:**
+- 确认 Dify 文档创建 API endpoint
+- 在 `DifySyncService` 中补全实际 HTTP 调用(使用 RestTemplate)
+- 配置 `dify.api-key` 从环境变量读取
+
+**Dify REST API 调用:**
+```java
+// 参考实现
+HttpHeaders headers = new HttpHeaders();
+headers.setBearerAuth(difyApiKey);
+headers.setContentType(MediaType.APPLICATION_JSON);
+
+String url = difyBaseUrl + "/v1/datasets/" + datasetId + "/documents/";
+// POST with text content
+```
+
+**验证:** 用 curl 手动测试 Dify API 连通性
+
+---
+
+### Task 17: 全流程联调
+
+**操作:**
+1. 后端启动 → 验证所有 API 端点
+2. 前端启动 → 验证维度管理页面 CRUD
+3. 前端启动 → 验证知识库管理页面完整流程
+4. 验证 Dify 同步(如果有可用 Dify 环境)
+5. `mvn clean compile` 最终编译确认
+
+**验证清单:**
+- [ ] `POST /api/admin/dimension-config/list` 返回正确
+- [ ] `POST /api/admin/dimension-config/tree` 返回树形结构
+- [ ] `POST /api/admin/knowledge-base/save` 含 dimensionIds 时同步保存关联
+- [ ] `POST /api/admin/knowledge-base/list` 支持维度/标签筛选
+- [ ] 前端维度管理页: 新增/编辑/删除/树形展示
+- [ ] 前端知识库管理页: 筛选/分页/新增/编辑/删除/绑定维度/绑定标签
+- [ ] 新增维度后前端正常展示(无需改代码)
+
+---
+
+## 依赖图
+
+```
+Task 1 (DB)
+  ↓
+Task 2 (Entity+Mapper)
+  ├─→ Task 3 (DimensionConfig CRUD)
+  ├─→ Task 4 (KnowledgeBase CRUD)
+  └─→ Task 5 (KnowledgeTag CRUD)
+        ↓
+Task 6 (关联管理) ← 依赖 Task 3,4,5
+Task 7 (DifySyncService) ← 依赖 Task 4
+  ↓
+Task 8 (api/dimension.js) ← 依赖 Task 3,4,5
+Task 9 (store/modules/dimension.js) ← 依赖 Task 8
+  ↓
+Task 10 (DimensionSelector) ← 依赖 Task 9
+Task 11 (TagSelector) ← 依赖 Task 9
+Task 12 (KnowledgeTable) ← 依赖 Task 8
+Task 13 (KnowledgeDialog) ← 依赖 Task 10,11,8
+  ↓
+Task 14 (dimension/index.vue) ← 依赖 Task 10,8,9
+Task 15 (knowledge/index.vue) ← 依赖 Task 12,13,8,9
+  ↓
+Task 16 (Dify API) ← 依赖 Task 7
+Task 17 (联调)
+```
+
+---
+
+## 各阶段验证
+
+| 阶段 | 验证命令 | 预期 |
+|------|---------|------|
+| Phase 1 | `cd cfc-backend && mvn clean compile` | BUILD SUCCESS |
+| Phase 1 | `mvn spring-boot:run` + curl 端点 | 200 OK |
+| Phase 2 | `cd cfc-web && npm run serve` | 编译无错 |
+| Phase 3 | 浏览器访问页面 | 页面正常渲染 |
+| Phase 4 | curl Dify API | 连通性确认 |