日期: 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 阶段执行。后端先于前端,模块独立,可并行。
文件: 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验证:
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');
文件: 每个 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 路径:
cfc-backend/src/main/java/com/etotem/cfc/entity/cfc-backend/src/main/java/com/etotem/cfc/mapper/验证: mvn clean compile 通过
文件:
service/ProductDimensionConfigService.javacontroller/admin/ProductDimensionConfigController.javaService 方法:
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)
文件:
service/DanKnowledgeBaseService.javacontroller/admin/DanKnowledgeBaseController.javaService 方法:
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 完善
文件:
service/DanKnowledgeTagService.javacontroller/admin/DanKnowledgeTagController.javaService 方法:
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 通过
文件: 在已有的 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(避免两次调用)
验证: 手动测试绑定/解绑逻辑
文件:
service/DifySyncService.javaService 方法:
syncToDify(Long knowledgeId, List<String> dimensionCodes) — @Async 异步推送removeFromDify(Long knowledgeId, String difyDatasetId) — @Async 异步删除实现:
dify.base-url、dify.api-key 配置log.warn("Dify sync not configured: {}", url) 占位DanKnowledgeBaseService.save() 和 delete() 末尾调用 difySyncService.syncToDify() / removeFromDify()(try-catch,失败只 log 不抛异常)配置:
# application.yml
dify:
base-url: http://dify.bianwoyou.cn
api-key:
timeout: 10000
验证: 启动后执行知识库 CRUD,观察日志无异常
cfc-web/src/api/xxx.js(已有模式: export const func = (data) => request({ url, method: 'post', data }))cfc-web/src/store/index.js(目前是 flat 结构,无 modules 目录。为保持一致性,新增 dimension.js 作为独立模块,在 store/index.js 中 import dimension from './modules/dimension' 注册 modules: { dimension })el-table, el-dialog, el-select, el-tree, el-tag)cfc-web/src/views/admin/ 下的 Vue 2 组件(Options API)文件: cfc-web/src/api/dimension.js
函数列表:
// 维度配置
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(...)
验证: 无运行时验证,作为模块静态检查
文件: cfc-web/src/store/modules/dimension.js
State:
dimensions: [] — 维度列表(flat)dimensionTree: [] — 树形维度knowledgeList: { items, total } — 知识点分页tags: [] — 标签列表loading: falseActions: 对应 Task 8 的 API 调用 Mutations: 更新 state
注册: 在 store/index.js 中 import dimension from './modules/dimension',modules: { dimension }
验证: 将 dimension.js import 到 store 后无 Babel 错误
文件: 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
验证: 在任意页面临时引用测试
文件: cfc-web/src/components/TagSelector/index.vue
Props:
value — v-model 绑定(Array of tagIds)placeholder — 默认 "请选择标签"实现:
el-select 多选filterable, allow-create),触发 @new-tag 事件调用 saveTag APItags事件: @input, @new-tag
验证: 在任意页面临时引用测试
文件: cfc-web/src/components/KnowledgeTable/index.vue
Props:
dimensionCode — 筛选维度(可选)tagId — 筛选标签(可选)keyword — 搜索关键词(可选,双向绑定)page / size — 分页参数实现:
el-table 带 :data、v-loadingel-pagination 底部show-overflow-tooltip事件:
@edit(row) / @delete(row) / @bind-dimensions(row) / @bind-tags(row)@page-change(page, size) — 分页切换验证: 在任意页面临时引用测试
文件: 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)knowledge 有值则 populate 表单saveKnowledge API事件:
@close — 关闭对话框@saved — 保存成功后通知父页面刷新列表验证: 在任意页面临时引用测试
文件: 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
文件: cfc-web/src/views/admin/knowledge/index.vue
布局:
┌──────────────────────────────────────┐
│ 搜索框 [keyword] [新增按钮] │
├──────────┬───────────────────────────┤
│ 筛选面板 │ KnowledgeTable 组件 │
│ - 维度树 │ │
│ - 标签列表 │ │
│ │ │
└──────────┴───────────────────────────┘
左侧筛选面板:
el-tree)— 点击筛选该维度的知识点el-tag + el-select)— 点击筛选该标签的知识点实现:
KnowledgeTable 组件展示主区域dimensionCode/tagId/keyword → 传入 KnowledgeTableKnowledgeDialog路由: /admin/knowledge(在 router/index.js 的 admin 路由下添加)
验证: npm run serve → 浏览器访问页面,测试完整流
前置条件: 获取 Dify 最新的 Document Create API 格式
操作:
DifySyncService 中补全实际 HTTP 调用(使用 RestTemplate)dify.api-key 从环境变量读取Dify REST API 调用:
// 参考实现
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 连通性
操作:
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 | 连通性确认 |