2026-06-20-cfc-web-refactor.md 15 KB

cfc-web 管理后台重构:基于 ProductDimensionConfig + KnowledgeBase 的动态渲染

日期: 2026-06-20 状态: 草稿 影响范围: cfc-web (Vue 2 Admin Panel) + cfc-backend (部分 API 增强) 任务数: 13 个 (Task 1-13, Task 4 推迟) 总工时预估: 5-10 天

1. 概述

1.1 问题

cfc-web 管理后台中大量硬编码了产品维度结构(五维、三维、行动维度等),导致:

  • 新增/修改维度需要改多处前端代码
  • KnowledgeBase CRUD 与维度配置的关联不清晰
  • 后端 ProductDimensionConfigDanKnowledgeBase 表已有数据,但前端未充分使用

1.2 目标

将管理后台重构为基于 ProductDimensionConfig + KnowledgeBase 动态渲染的模式:

ProductDimensionConfig (维度定义)
        ↓ 动态加载
KnowledgeBase (知识点内容) ──→ 统一管理 CRUD
        ↓
渲染组件 (动态表单/表格/树)

1.3 成功标准

  • 新增维度只需在 ProductDimensionConfig 表插入一行 + 对应 KnowledgeBase 数据
  • 无需修改前端代码即可展示新维度
  • 所有 KnowledgeBase CRUD 路径一致
  • 现有功能完全保留,UI 无退化

2. 先行发现

已有后端实体和 API:

资源 位置 状态
ProductDimensionConfig entity cfc-backend/.../entity/ProductDimensionConfig.java ✅ 存在
DanKnowledgeBase entity cfc-backend/.../entity/DanKnowledgeBase.java ✅ 存在
DanKnowledgeBaseController cfc-backend/.../controller/admin/DanKnowledgeBaseController.java ✅ 存在
AdminController (含维度相关接口) cfc-backend/.../controller/admin/AdminController.java ✅ 存在
KnowledgeBase 管理页面 cfc-web/src/views/admin/knowledge/ ✅ 存在
维度相关配置 cfc-web/src/views/admin/dimension/ 硬编码严重

3. 实施计划

任务依赖关系

Task 1 ─→ Task 3 ─→ Task 5 ─→ Task 7 ─→ Task 10
Task 2 ─→ Task 6 ─→ Task 8 ─→ Task 9 ─→ Task 10
                                            ↓
Task 11 (独立) ←──────────────────── Task 10
Task 12 (依赖 Task 5)
Task 13 (依赖 Task 5)

3.1 阶段 1:后端 API 统一 (1-3 天)

Task 1: 梳理 ProductDimensionConfig API 端点和数据结构 依赖: 无

涉及文件:

  • cfc-backend/.../controller/admin/AdminController.java — 检查现有维度接口
  • cfc-backend/.../entity/ProductDimensionConfig.java — 确认字段
  • cfc-backend/.../service/ProductDimensionConfigService.java — 确认业务逻辑

具体操作:

  1. 检查 AdminController 中已有接口:listProductDimensionConfigs, getProductDimensionConfig, saveProductDimensionConfig, deleteProductDimensionConfig
  2. 确保 API 路径统一为 /api/admin/dimension-config/**
  3. 验证返回 JSON 结构包含:id, dimensionCode, dimensionName, dimensionType, parentId, sort, enabled, remark
  4. 验证完成后,写下确认的 API 接口清单

Task 2: 统一 KnowledgeBase API 依赖: 无

涉及文件:

  • cfc-backend/.../controller/admin/DanKnowledgeBaseController.java — 检查/补充接口
  • cfc-backend/.../entity/DanKnowledgeBase.java — 确认字段

具体操作:

  1. 检查 DanKnowledgeBaseController 现有接口是否覆盖分页查询 + 按 dimensionCode 筛选
  2. 确认所有接口统一前缀 /api/admin/knowledge-base/
  3. 若缺少接口则补充:
    • POST /api/admin/knowledge-base/list → 分页 + 按 dimensionCode 筛选
    • POST /api/admin/knowledge-base/create → 新建
    • POST /api/admin/knowledge-base/update → 更新
    • POST /api/admin/knowledge-base/delete → 删除 (逻辑删除)
    • POST /api/admin/knowledge-base/get → 详情

Task 3: 添加维度树接口 依赖: Task 1 完成

  • POST /api/admin/dimension-config/tree → 返回层级树结构
  • 用于前端树形选择器
  • 如果后台已有类似接口则跳过

3.2 阶段 2: 前端核心组件 (2-3 天)

Task 5: 创建 Vuex Store 模块

cfc-web/src/store/modules/dimension.js:

import { getDimensionConfigs, getKnowledgeBaseList } from '@/api/dimension'

export default {
  namespaced: true,
  state: {
    dimensions: [],        // ProductDimensionConfig 列表
    dimensionTree: [],     // 维度树
    knowledgeList: [],     // KnowledgeBase 列表
    knowledgeTotal: 0,
    loading: false
  },
  mutations: {
    SET_DIMENSIONS(state, list) {
      state.dimensions = list
    },
    SET_DIMENSION_TREE(state, tree) {
      state.dimensionTree = tree
    },
    SET_KNOWLEDGE_LIST(state, { list, total }) {
      state.knowledgeList = list
      state.knowledgeTotal = total
    }
  },
  actions: {
    async fetchDimensions({ commit }) {
      const res = await getDimensionConfigs()
      commit('SET_DIMENSIONS', res.data || [])
    },
    async fetchKnowledge({ commit }, params) {
      const res = await getKnowledgeBaseList(params)
      commit('SET_KNOWLEDGE_LIST', {
        list: res.data?.records || [],
        total: res.data?.total || 0
      })
    }
  }
}

Task 6: 通用 API 封装

cfc-web/src/api/dimension.js:

import request from '@/utils/request'

export function getDimensionConfigs(data) {
  return request({
    url: '/api/admin/dimension-config/list',
    method: 'post',
    data
  })
}

export function getDimensionTree() {
  return request({
    url: '/api/admin/dimension-config/tree',
    method: 'post'
  })
}

export function saveDimensionConfig(data) {
  return request({
    url: '/api/admin/dimension-config/save',
    method: 'post',
    data
  })
}

export function deleteDimensionConfig(id) {
  return request({
    url: '/api/admin/dimension-config/delete',
    method: 'post',
    data: { id }
  })
}

export function getKnowledgeBaseList(data) {
  return request({
    url: '/api/admin/knowledge-base/list',
    method: 'post',
    data
  })
}

export function saveKnowledgeBase(data) {
  return request({
    url: '/api/admin/knowledge-base/save',
    method: 'post',
    data
  })
}

export function deleteKnowledgeBase(id) {
  return request({
    url: '/api/admin/knowledge-base/delete',
    method: 'post',
    data: { id }
  })
}

Task 7: 动态维度选择器组件

cfc-web/src/components/DimensionSelector/index.vue:

<template>
  <el-select v-model="selectedValue" :multiple="multiple" clearable filterable placeholder="选择维度">
    <el-option
      v-for="dim in dimensionOptions"
      :key="dim.dimensionCode"
      :label="dim.dimensionName"
      :value="dim.dimensionCode"
    />
  </el-select>
</template>

<script>
import { mapState } from 'vuex'

export default {
  props: {
    value: { type: [String, Array], default: '' },
    multiple: { type: Boolean, default: false },
    dimensionType: { type: String, default: '' }
  },
  computed: {
    ...mapState('dimension', ['dimensions']),
    dimensionOptions() {
      let list = this.dimensions
      if (this.dimensionType) {
        list = list.filter(d => d.dimensionType === this.dimensionType)
      }
      return list
    },
    selectedValue: {
      get() { return this.value },
      set(val) { this.$emit('input', val) }
    }
  }
}
</script>

Task 8: KnowledgeBase 动态表格组件

cfc-web/src/components/KnowledgeTable/index.vue — 通用表格展示,根据传入的 dimensionCode 动态加载对应数据:

<template>
  <el-table :data="list" v-loading="loading" stripe>
    <el-table-column prop="id" label="ID" width="80" />
    <el-table-column prop="title" label="标题" min-width="200" show-overflow-tooltip />
    <el-table-column prop="content" label="内容" min-width="300" show-overflow-tooltip />
    <el-table-column prop="sort" label="排序" width="80" align="center" />
    <el-table-column label="状态" width="100" align="center">
      <template slot-scope="{ row }">
        <el-tag :type="row.status === 1 ? 'success' : 'info'">
          {{ row.status === 1 ? '启用' : '禁用' }}
        </el-tag>
      </template>
    </el-table-column>
    <el-table-column label="操作" width="200" fixed="right">
      <template slot-scope="{ row }">
        <el-button size="mini" type="text" @click="handleEdit(row)">编辑</el-button>
        <el-button size="mini" type="text" style="color: #f56c6c" @click="handleDelete(row)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

Task 9: 通用 KnowledgeBase 编辑对话框

cfc-web/src/components/KnowledgeDialog/index.vue — 新增/编辑共用的对话框:

<template>
  <el-dialog :title="isEdit ? '编辑知识点' : '新增知识点'" :visible.sync="visible" width="700px">
    <el-form ref="form" :model="form" :rules="rules" label-width="120px">
      <el-form-item label="维度" prop="dimensionCode">
        <dimension-selector v-model="form.dimensionCode" />
      </el-form-item>
      <el-form-item label="标题" prop="title">
        <el-input v-model="form.title" placeholder="请输入标题" maxlength="200" />
      </el-form-item>
      <el-form-item label="内容" prop="content">
        <el-input type="textarea" v-model="form.content" :rows="6" placeholder="请输入内容" />
      </el-form-item>
      <el-form-item label="排序" prop="sort">
        <el-input-number v-model="form.sort" :min="0" :max="9999" />
      </el-form-item>
      <el-form-item label="状态">
        <el-switch v-model="form.status" :active-value="1" :inactive-value="0" />
      </el-form-item>
    </el-form>
    <span slot="footer">
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="handleSubmit" :loading="submitting">保存</el-button>
    </span>
  </el-dialog>
</template>

3.3 阶段 3: 现有视图迁移 (2-4 天)

Task 10: 重构 KnowledgeBase 管理页面 依赖: Task 2, 5, 6, 7, 8, 9

涉及文件:

  • cfc-web/src/views/admin/knowledge/index.vue — 主页面
  • cfc-web/src/views/admin/knowledge/*.vue — 相关子页面

具体操作:

  1. 使用 DimensionSelector 组件替换硬编码的维度下拉框
  2. 使用 KnowledgeTable 组件替换现有表格
  3. 使用 KnowledgeDialog 组件替换新增/编辑弹窗
  4. 清理不再需要的本地状态和方法
  5. 移除硬编码的维度字段

Task 11: 重构维度配置管理页面 依赖: Task 1, 3

涉及文件:

  • cfc-web/src/views/admin/dimension/index.vue — 维度配置页面

具体操作:

  1. ProductDimensionConfig API 动态加载维度列表
  2. 树形展示维度层级(使用 el-tree + dimension tree API)
  3. 支持拖拽排序(el-tree draggable + API 保存排序)
  4. CRUD 操作直接对接后端 API

Task 12: 替换硬编码维度引用 依赖: Task 5 完成

通过以下 grep 模式搜索所有硬编码引用:

# 搜索硬编码的五维名称
grep -rn "五维·\|五維·" cfc-web/src/
grep -rn "五维\.\|五維\." cfc-web/src/

# 搜索硬编码的三维名称
grep -rn "三维·\|三維·" cfc-web/src/

# 搜索硬编码的行动维度
grep -rn "行动维度\|行動維度" cfc-web/src/

# 搜索其他可能的硬编码维度名
grep -rn "'五维'\|\"五维\"\|'三维'\|\"三维\"\|'行动'\|\"行动\"" cfc-web/src/

涉及文件(预计):

  • cfc-web/src/views/admin/dimension/index.vue — 维度管理
  • cfc-web/src/views/admin/knowledge/*.vue — 知识点管理
  • cfc-web/src/views/admin/task/*.vue — 任务管理
  • cfc-web/src/views/admin/energy/*.vue — 能量管理
  • cfc-web/src/views/admin/assessment/*.vue — 测评管理

具体操作:

  1. 对每个匹配文件,将硬编码字符串替换为 dimensions[dimensionCode].dimensionName 的动态读取
  2. 使用 dimensionCode 作为唯一标识,dimensionName 作为展示名
  3. 确保替换后显示内容与原样一致

Task 13: 添加缓存机制 依赖: Task 5 完成

具体操作:

  1. Dimension config 数据缓存到 localStorage,key: cfc_dimensions_${version}
  2. 后端返回 version 字段,前端对比版本号决定是否刷新
  3. 管理后台发布新维度后更新版本号
  4. 缓存有效期 24 小时,过期自动刷新

4. 阶段 4 (可选): 批量导入支持

⚠️ 推迟至下一迭代 — 当前不实施

Task 4: 添加批量接口 POST /api/admin/knowledge-base/batch-save

  • 当前阶段不需要批量导入功能
  • 基础 CRUD 已满足日常运营需求

5. 测试策略

5.1 后端测试 (每个 API Task 后执行)

测试类型 内容 命令
编译验证 API 编译通过 mvn clean compile -pl cfc-backend
单元测试 Controller + Service 层 mvn test -pl cfc-backend -Dtest=*ControllerTest,*ServiceTest
API 测试 手动 curl 验证 CRUD curl -X POST http://localhost:8080/api/admin/...

5.2 前端测试 (每个组件 Task 后执行)

测试类型 内容 命令
编译验证 无语法错误 cd cfc-web && npm run build -- --no-progress
Lint 代码风格 cd cfc-web && npm run lint
手动测试 浏览器打开确认 UI 正常 打开 localhost:8082 对应页面

5.3 回归测试 (全部 Task 完成后)

  1. 遍历所有管理后台页面,确认无 404/白屏
  2. 验证 CRUD 操作正常
  3. grep 确认无残留硬编码(使用 Task 12 中列出的 grep 模式)
  4. 检查浏览器 Console 无报错

6. 验收清单

# 检查项 验证方式
1 维度配置 CRUD 正常 手动测试增删改查
2 KnowledgeBase CRUD 正常 手动测试增删改查
3 维度树展示正确 检查层级关系
4 新增维度后前端自动展示 配置 → 刷新页面 → 出现
5 无硬编码维度名称 grep 检查 五维三维 等字符串
6 所有页面正常渲染 遍历所有 admin 页面,Console 无报错
7 旧功能无退化 回归测试各管理页面

7. 分支策略

feature/refactor-dimension-dynamic
├── task/01-backend-api-review     # Task 1
├── task/02-knowledge-api          # Task 2
├── task/03-dimension-tree-api     # Task 3
├── task/05-vuex-store             # Task 5
├── task/06-api-package            # Task 6
├── task/07-dimension-selector     # Task 7
├── task/08-knowledge-table        # Task 8
├── task/09-knowledge-dialog       # Task 9
├── task/10-migrate-knowledge      # Task 10
├── task/11-migrate-dimension      # Task 11
├── task/12-replace-hardcode       # Task 12 (可拆分子分支)
└── task/13-cache                  # Task 13
  • 每个 Task 在 feature/refactor-dimension-dynamic 上开独立分支
  • 合并前需 Code Review
  • 合并后验证 CI 通过

8. 回滚方案

  • 每个 Task 独立分支,合并前 Code Review
  • 若某 Task 导致问题,revert 该分支后重新合并
  • 后端 API 变更需确保向前兼容(旧接口保留至下个版本)
  • 若阶段 3 迁移导致页面故障,可回退到旧版组件(新旧组件共存)