Files
wecom_it_smart_desk/docs/02-技术文档/技术方案-REQ-集成-002-管理后台v1.2-IA重构整体.md
T

451 lines
16 KiB
Markdown
Raw Normal View History

# 技术方案 — 管理后台 v1.2 IA 重构整体
> **版本**: v1.2 | **日期**: 2026-07-28
> **作者**: 宋献 · 主理人齐活林(Qi) · 高见远(Gao)
> **关联 PRD**: `docs/01-产品文档/08-集成生态/PRD-REQ-集成-002-管理后台-v1.2.md`
> **关联原型**: `docs/01-产品文档/08-集成生态/原型-REQ-集成-000-管理后台-v1.0.html`
> **任务说明书**: `docs/07-项目管理/任务说明书/任务说明书-REQ-集成-002-IA重构整体.md`
> **状态**: ✅ 已实施(生产环境上线 2026-07-28
---
## 1. 目标
把管理后台从 v1.0 的「4 组按优先级」导航重构为 v1.1 的「5 组按用户场景」结构,覆盖完整 27 项功能域(v1.0 漏列 12 项),并通过单一真源 menu.config.ts 消除「Sidebar 与 router 双重维护脱节」的历史包袱。
本次重构由 6 个阶段组成:
| 阶段 | 内容 | 优先级 | 状态 |
|------|------|--------|------|
| P0 | 三 bug 修复(router 重复 / sidebar 漏权限矩阵 / locked-menu-item 可点) | P0 | ✅ 已上线 |
| P1 | 单一真源 menu.config.ts 重构(5 组 27 项 + 子标签 + 角色过滤) | P1 | ✅ 已上线 |
| P1-b | Dashboard 嵌入会话监控 widget(双视图:Dashboard 概览 + /monitor 详情) | P1 | ✅ 已上线 |
| P2-a | `/quick-rules-audit` 顶级路由补全 | P2 | ✅ 已上线 |
| P2-c | `/quick-rules/template` 路由 child 补全(复用 QuickReplies.vue | P2 | ✅ 已上线 |
| v1.2 | 分配模式 Tab 收编到坐席管理 | P2 | 🟡 待实施 |
---
## 2. 整体架构
### 2.1 5 组导航结构(v1.1 替代 v1.0 4 组)
```
🟦 运营中心(7 项 · 实时作战 + 实时监控)
运营总览 / 会话监控 / 坐席管理 / 角色管理 / 权限矩阵 / OTP 管理 / 功能开关
🟪 数据与监控(4 项 · 离线分析 + AI 指标)
会话审计 / 坐席绩效 / 满意度评价 / 自动化指标
🟥 安全审计(5 项 · 合规追溯 + 系统安全)
配置变更历史 / 运行期日志 / 安全审计日志 / 终端安全 / 快速回复审计日志
🟩 系统与集成(4 项 · 底层能力配置)
系统集成 / 欢迎与引导 / 自动化场景 / 规则版本
🟪 知识与 AI(7 项 · 配置规则 + 知识沉淀 + 智能建议)
快速回复规则 / 排查流程图 / 代答排除 / 知识迭代 / RAGFlow / 拓扑预览 / 知识库建议
```
### 2.2 单一真源架构
```
menu.config.ts(单一真源)
├─ groups[] // 5 个分组(顺序、图标、颜色)
├─ items[] // 27 项业务菜单
├─ quickRulesSubTabs[] // 快速回复规则页 5 子 tab
├─ lockedItems[] // 3 个 P2 折叠区占位
└─ rolesFilter // 按角色过滤的字段
↓ 派生
Sidebar.vue // v-for 渲染,零硬编码
router/index.ts // 从 menuConfig 派生路由(meta.menuKey 反查)
```
**收益**:新增菜单仅改 menu.ts,自动出现在 Sidebar + router + 角色过滤三处。
---
## 3. P0 三 bug 修复
### 3.1 router 重复注册
**症状**`router/index.ts` L87 + L201 重复注册 `path: 'knowledge'`,后注册 Placeholder 覆盖 Knowledge.vue,导致真页面成死代码。
**修复**`router/index.ts`):
```diff
{
path: 'knowledge',
name: 'Knowledge',
component: () => import('@/views/Knowledge.vue'),
},
+ {
+ path: 'knowledge-mgmt', // 占位路由改名避免冲突
+ name: 'KnowledgeMgmt',
+ component: () => import('@/views/Placeholder.vue'),
+ meta: { title: '知识库管理', priority: 'P2', locked: true },
+ }
```
### 3.2 Sidebar 漏权限矩阵
**症状**`/permissions-matrix` 路由已注册但 Sidebar 完全没显示(菜单与路由双重维护脱节典型案例)。
**修复**P1 阶段由 menu.config.ts 自动修复,菜单项已在 menu.ts 声明)。
### 3.3 locked-menu-item 可点击
**症状**`.locked-menu-item { pointer-events: auto !important }` 让"开发中"灰化菜单仍可点击,误导用户。
**修复**`Sidebar.vue``global.css`):
```css
.locked-menu-item {
pointer-events: none;
cursor: not-allowed;
}
```
---
## 4. P1 单一真源重构
### 4.1 menu.config.ts 数据结构(232 行)
```ts
export const menuConfig = {
groups: [
{ key: 'operation', label: '运营中心', icon: 'Operation', color: '#3b82f6', order: 1 },
{ key: 'monitor', label: '数据与监控', icon: 'Monitor', color: '#8b5cf6', order: 2 },
{ key: 'security', label: '安全审计', icon: 'Security', color: '#ef4444', order: 3 },
{ key: 'system', label: '系统与集成', icon: 'System', color: '#10b981', order: 4 },
{ key: 'ai', label: '知识与 AI', icon: 'AI', color: '#ec4899', order: 5 },
],
items: [
// 27 项业务菜单,每项包含 path/title/icon/groupKey/roles/badge
],
quickRulesSubTabs: [
{ key: 'greeting', label: '打招呼' },
{ key: 'contacts', label: '联系人' },
{ key: 'routing', label: '路由关键词' },
{ key: 'targets', label: '路由目标' },
{ key: 'template', label: '模板', badge: 'v5 收编' },
],
lockedItems: [
{ key: 'themes', label: '主题模板', stage: '阶段二' },
{ key: 'dashboard-board', label: '数据看板', stage: '阶段四' },
{ key: 'knowledge-mgmt', label: '知识库管理', stage: '阶段四' },
],
rolesFilter: ['admin', 'super_admin'],
}
```
### 4.2 Sidebar.vue 重构(282 → 222 行)
```vue
<template>
<el-menu :default-active="activeRoute">
<template v-for="group in menuConfig.groups" :key="group.key">
<el-sub-menu :index="group.key">
<template #title>
<el-icon><component :is="group.icon" /></el-icon>
<span>{{ group.label }}</span>
</template>
<template v-for="item in itemsOfGroup(group.key)" :key="item.path">
<el-menu-item :index="item.path" :disabled="isLocked(item)">
<el-icon><component :is="item.icon" /></el-icon>
<span>{{ item.title }}</span>
<el-badge v-if="item.badge" :value="item.badge" class="menu-badge" />
</el-menu-item>
</template>
</el-sub-menu>
</template>
<!-- 折叠区占位 -->
<el-sub-menu v-for="locked in lockedItems" :key="locked.key" class="locked-menu-item">
...
</el-sub-menu>
</el-menu>
</template>
```
### 4.3 图标替换诚实记录
| 原计划 | 实际使用 | 原因 |
|--------|---------|------|
| `Shield`(终端安全) | `Warning` | 与原 sidebar 一致 |
| `Bulb`(知识库建议) | `Sunny` | element-plus/icons-vue@2.3.0 不存在 Bulb |
---
## 5. P1-b Dashboard 嵌入会话监控 widget
### 5.1 双视图设计(Datadog/Grafana 同款)
```
Dashboard 概览(/admin/dashboard
├─ 第一行 4 张核心 KPI(在线坐席/今日会话/平均响应/AI 命中率)
├─ 第二行 3 张副卡(进行中/等待中/异常告警)
├─ 🆕 实时会话 Top 5 widget
├─ 待处理事项
└─ 系统健康
会话监控详情(/admin/monitor
└─ 完整会话表格 + 详情
```
### 5.2 Dashboard.vue 单文件实现
```vue
<template>
<!-- 原有 KPI + 待处理 + 系统健康 -->
<!-- 🆕 实时会话监控 widget -->
<div class="monitor-widget">
<div class="widget-header">
<h3>实时会话监控</h3>
<el-button text @click="$router.push('/admin/monitor')">
查看全部
</el-button>
</div>
<div class="monitor-stats">
<StatCard label="进行中" :value="monitorStats.in_progress" color="green" />
<StatCard label="等待中" :value="monitorStats.queued" color="yellow" />
<StatCard label="今日已结" :value="monitorStats.resolved_today" color="blue" />
<StatCard label="异常告警" :value="monitorStats.alerts" color="red" />
</div>
<el-table :data="monitorSessions.slice(0, 6)" stripe>
<el-table-column prop="session_id" label="会话 ID" />
<el-table-column prop="employee_name" label="员工" />
<el-table-column prop="agent_name" label="坐席" />
<el-table-column label="持续时长">
<template #default="{ row }">{{ formatDuration(row.started_at) }}</template>
</el-table-column>
<el-table-column label="状态">
<template #default="{ row }">
<el-tag :type="getSessionStatusTag(row.status)">
{{ getSessionStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup lang="ts">
import { getMonitorSessions } from '@/api/admin'
import type { MonitorSession, MonitorStats } from '@/types'
const monitorStats = reactive<MonitorStats>({
in_progress: 0, queued: 0, resolved_today: 0, alerts: 0,
})
const monitorSessions = ref<MonitorSession[]>([])
onMounted(async () => {
try {
const res = await getMonitorSessions()
Object.assign(monitorStats, res.stats)
monitorSessions.value = res.items
} catch (err) {
// Demo 兜底(与 Monitor.vue 一致)
monitorStats.in_progress = 5
monitorStats.queued = 0
monitorStats.resolved_today = 42
monitorStats.alerts = 1
monitorSessions.value = [...]
}
})
// 复用 formatDuration / getSessionStatusTag / getSessionStatusText
</script>
```
### 5.3 API 设计
```ts
// api/admin.ts
export async function getMonitorSessions(params?: { limit?: number }) {
return request<MonitorSessionsData>({
url: '/admin/monitor/sessions',
method: 'get',
params,
})
}
export interface MonitorStats {
in_progress: number
queued: number
resolved_today: number
alerts: number
}
export interface MonitorSession {
session_id: string
employee_name: string
agent_name: string
status: 'pending' | 'active' | 'resolved' | 'abandoned'
started_at: string
}
export interface MonitorSessionsData {
stats: MonitorStats
items: MonitorSession[]
}
```
---
## 6. P2-a `/quick-rules-audit` 顶级路由补全
### 6.1 修复内容(`router/index.ts`
```ts
{
// 快速回复审计日志(v1.1 从快速回复规则组移至安全审计组)
path: 'quick-rules-audit',
name: 'QuickRulesAuditLog',
component: () => import('@/views/quick-rules/audit.vue'),
meta: { title: '快速回复审计日志', requiresAuth: true },
},
```
**复用现有 `views/quick-rules/audit.vue`**name 与嵌套 `QuickRulesAudit` 区分避免冲突。
---
## 7. P2-c `/quick-rules/template` 路由 child 补全
### 7.1 修复内容(`router/index.ts` L219-225
```ts
{
// 快速回复模板(v5 收编:菜单已配于 menu.ts L197;复用旧 QuickReplies.vue 组件)
path: 'quick-rules/template',
name: 'QuickRulesTemplate',
component: () => import('@/views/QuickReplies.vue'),
meta: { title: '快速回复模板', requiresAuth: true },
},
```
**复用现有 `views/QuickReplies.vue`**(独立页面组件,用全局 `quickReplyStore` Pinia 单例,无父级 route props 依赖,安全作 route component)。
---
## 8. v1.2 分配模式 Tab 收编(待实施)
### 8.1 路由改造
```diff
- {
- path: 'assignment-mode',
- name: 'AssignmentMode',
- component: () => import('@/views/AssignmentMode.vue'),
- meta: { title: '消息分配模式', priority: 'P1' },
- },
```
### 8.2 Agents.vue 双 Tab 改造
```vue
<el-tabs v-model="activeTab" class="agents-tabs">
<el-tab-pane label="坐席列表" name="agents-list">
<!-- toolbar + AgentTable + 三个 dialog -->
</el-tab-pane>
<el-tab-pane label="分配策略" name="agents-assignment">
<div class="page-desc">
当前坐席 {{ agentStore.agents.length }} ...
</div>
<div class="mode-card" v-for="mode in modes" :key="mode.id">
<!-- 复用 AssignmentMode.vue mode-card -->
</div>
</el-tab-pane>
</el-tabs>
```
### 8.3 旧页面保留
`views/AssignmentMode.vue` 保留但停用(不删除),方便阶段二/三分配策略膨胀时回滚为独立页。
### 8.4 后端零改动
仍走 `system_configs.assignment_mode` + `GET/PUT /api/admin/assignment-mode`
---
## 9. 关键技术决策
| # | 决策 | 理由 |
|---|------|------|
| 1 | **单一真源 menu.config.ts** | 消除「Sidebar 与 router 双重维护脱节」;新增菜单仅改 1 文件 |
| 2 | **5 组按用户场景分组**v1.1) | v1.0 按优先级分组不符合实际运营场景;安全审计类功能分散 |
| 3 | **快速回复审计移出知识与 AI 组** | 审计类功能属安全合规范畴,不应混杂在业务规则配置组 |
| 4 | **终端安全移入安全审计组** | 终端安全是合规管控(联软/火绒),与底层引擎集成性质不同 |
| 5 | **快速回复模板作为 tab 收编** | 与快速回复规则同属「快速回复」业务的不同维度 |
| 6 | **Dashboard widget + /monitor 双视图** | 用户进 Dashboard 即见实时状态,无需跳转(Datadog/Grafana 同款) |
| 7 | **P2 路由补全复用既有组件** | audit.vue 与 QuickReplies.vue 已有完整实现,避免重复 |
| 8 | **v1.2 分配模式 Tab 收编可逆** | 旧 AssignmentMode.vue 保留文件 + 回滚步骤文档化 |
---
## 10. 风险与回滚
| 风险 | 概率 | 影响 | 缓解 |
|------|------|------|------|
| menu.config.ts 数据错误导致菜单错位 | 低 | 用户困惑 | 双重核对(sidebar 渲染 + 路由可达) |
| 子标签 / 角色过滤边界 | 低 | 普通管理员看到超管菜单 | menuConfig.rolesFilter 字段校验 |
| Tab 切换时数据二次请求 | 低 | 网络浪费 | watch(activeTab) 懒加载或一次性加载 |
| 旧页面成僵尸代码 | 低 | 维护混淆 | 注释中写明"vX.Y 起停用,仅供回滚" |
| 阶段二/三分配模式复杂化需拆回 | 中 | 需重构 | 已记录回滚步骤,30 分钟内可逆 |
**回滚预案**
- 服务器 `/tmp/dist-template-deploy.zip` + `/tmp/dist-widget.zip`(最近 2 个 dist 包,保留 7 天)
- 各版本 dist md5 在 memory 记录中可查(af00cf1c→a0c93a6c→6e8933dd→6f261495
---
## 11. 验证清单(端到端三层验证)
### 11.1 代码层
- [x] 工程师 IS_PASS: YES(单文件最小变更)
- [x] 主理人 Grep 核对:行号 / 函数名 / 引用一致
- [x] QA 工程师六维回归 PASS(路由 entry / 组件独立 / name 唯一 / menu 对齐 / 无回归 / build 成功)
### 11.2 Build 层
- [x] `npm run build` 成功(9.57s, 2409 modules transformed, 51 chunks
- [x] dist 总大小 3.32 MB / 89 文件
- [x] 主 chunk index-Dnr-1RNY.js (1173 KB)
### 11.3 部署层
- [x] 主机 dist md5 ↔ 服务器挂载目录 md5 一致
- [x] `curl http://localhost/itadmin/` HTTP 200
- [x] 关键路由抽查:dashboard / monitor / quick-rules / quick-rules-audit / quick-rules/template / permissions-matrix 全部 200
- [x] nginx bind mount 源路径:`/opt/wecom-it-desk/frontend-admin/dist/`**带 dist 后缀**
### 11.4 浏览器层
- [ ] 用户登录后 1-click 视觉确认(agent-browser 自动化 / 人工)
- 关键页面:Dashboard 会话监控 widget / 5 组导航 / 快速回复模板 tab
---
## 12. 变更日志
| 版本 | 日期 | 变更 | 变更人 | 影响范围 |
|------|------|------|--------|---------|
| v1.0 | 2026-07-28 12:00 | 初版(仅覆盖分配模式 Tab 收编) | 宋献 | §8 |
| v1.1 | 2026-07-28 13:00 | 扩展为 IA 重构整体(P0+P1+P2 | 宋献 | §1-§7, §9-§11 |
| v1.2 | 2026-07-28 16:30 | 三件套对齐,标记「分配模式 Tab 收编」为待实施 | 宋献 | §8 标记 🟡 待实施 |
---
## 13. 关联文档
- **PRD v1.2**`docs/01-产品文档/08-集成生态/PRD-REQ-集成-002-管理后台-v1.2.md`
- **任务说明书 v1.2**`docs/07-项目管理/任务说明书/任务说明书-REQ-集成-002-IA重构整体.md`
- **原型 HTML**`docs/01-产品文档/08-集成生态/原型-REQ-集成-000-管理后台-v1.0.html`v1.1 已同步更新)
- **整改记录**`docs/04-运维文档/部署运维/00-文档规范化整改记录.md`(新增整改 #6 - IA 重构三件套补齐)
- **memory 日志**`D:\资料\03-项目开发\wecom_it_smart_desk\.workbuddy\memory\2026-07-28.md`IA 重构全过程记录)