# 维度配置 + 知识库管理平台 — 实施计划 **日期:** 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`) **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 dimensionIds)` — 先删后加(先删 dan_knowledge_base_dimension 中该 knowledge 的所有记录,再批量插入) - `bindTags(Long knowledgeId, List 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 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 | 连通性确认 |