2026-04-19-ai-chat-session-management-design.md 12 KB

AI 会话管理页面设计

1. 背景与目标

现状

  • 后端已有 ai_chat_sessionai_chat_record 两张表,以及面向当前用户的会话 API(/ai/session/list/ai/session/{id}/messagesDELETE /ai/session/{id}
  • 前端 views/ai/ 下仅有 provider.vuemodel.vue无会话管理页面
  • 现有 API 仅支持查询当前登录用户的会话,管理后台需要查看所有用户的会话数据

目标

  • 开发管理员使用的 AI 会话管理页面,支持查看所有用户的会话和聊天记录
  • 展示核心统计指标(会话总数、消息总数、今日会话、Token 消耗、30天趋势)
  • 关联 sys_user 表显示用户昵称、头像等核心数据
  • 前端页面美观、易操作、用户友好

2. 页面布局

采用单页面集成方案,一个页面包含统计区 + 左右分栏会话管理:

┌──────────────────────────────────────────────────┐
│  统计卡片区(4个 NStatistic)                       │
│  [会话总数] [消息总数] [今日会话] [Token消耗]         │
├──────────────────────────────────────────────────┤
│  趋势图(echarts 双Y轴:近30天每日会话数+消息数)      │
├────────────────┬─────────────────────────────────┤
│  会话列表(左)   │  聊天记录(右)                      │
│  🔍 搜索/筛选   │                                  │
│  ┌──────────┐ │  用户头像 昵称  14:30              │
│  │ 张三 会话  │ │  ┌───────────────────┐           │
│  │ 3条消息   │ │  │ 用户消息(蓝色气泡)  │           │
│  │ 10分钟前  │ │  └───────────────────┘           │
│  ├──────────┤ │                                  │
│  │ 李四 会话  │ │         AI助手  14:31            │
│  │ 8条消息   │ │           ┌───────────────┐     │
│  │ 1小时前   │ │           │ AI回复(灰色气泡)│     │
│  └──────────┘ │           └───────────────┘     │
│               │                                  │
└────────────────┴─────────────────────────────────┘

2.1 统计卡片区

  • 4 个 NStatistic 卡片横排:会话总数、消息总数、今日会话数、Token 消耗总量
  • 每个卡片带图标和数字,数字使用大字号

2.2 趋势图

  • echarts 双Y轴折线图
  • X轴:近30天日期
  • 左Y轴:每日会话数(折线)
  • 右Y轴:每日消息数(折线)
  • 使用项目已有的 echarts-theme.js 主题适配

2.3 会话列表(左侧)

  • 顶部:搜索框(用户昵称/会话标题)+ 时间范围选择器 + 状态下拉
  • 列表项展示:用户头像 + 用户昵称 + 会话标题 + 消息条数 + 最后活跃时间
  • 支持分页加载(滚动加载或分页器)
  • 选中会话高亮
  • 每项右侧有删除按钮

2.4 聊天记录(右侧)

  • 类 IM 气泡样式:
    • 用户消息:靠右,蓝色气泡,显示用户头像+昵称+时间
    • AI 回复:靠左,灰色气泡,显示 AI 图标+时间
  • 支持消息内容中 Markdown 渲染(代码块等)
  • 消息区域自动滚动到底部
  • 无选中会话时显示空状态提示

3. 后端 API 设计

新增管理端 Controller,路径前缀 /ai/admin/session,不修改现有 AiChatController。

3.1 API 列表

方法 路径 说明
GET /ai/admin/session/page 分页查询所有会话
GET /ai/admin/session/{sessionId}/messages 查看会话消息记录
DELETE /ai/admin/session/{sessionId} 删除会话(软删除)
GET /ai/admin/session/statistics 统计数据

3.2 分页查询会话 GET /ai/admin/session/page

请求参数(Query): | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | | keyword | String | 否 | 搜索关键词(匹配用户昵称或会话标题) | | startTime | String | 否 | 开始时间(yyyy-MM-dd) | | endTime | String | 否 | 结束时间(yyyy-MM-dd) | | status | String | 否 | 状态(0正常,1已删除) |

响应数据

{
  "code": 200,
  "data": {
    "records": [
      {
        "id": "session-uuid",
        "userId": 1,
        "nickName": "张三",
        "avatar": "file-id-or-url",
        "sessionName": "帮我写一个用户管理模块",
        "agentCode": "code-assistant",
        "status": "0",
        "messageCount": 8,
        "tokenUsage": 1234,
        "createTime": "2026-04-19 14:30:00",
        "updateTime": "2026-04-19 14:35:00"
      }
    ],
    "total": 100,
    "pageNum": 1,
    "pageSize": 10
  }
}

SQL 实现:在 Mapper XML 中写自定义分页 SQL:

SELECT s.*, u.real_name AS nickName, u.avatar,
       (SELECT COUNT(*) FROM ai_chat_record r WHERE r.session_id = s.id) AS messageCount,
       (SELECT COALESCE(SUM(r.token_usage), 0) FROM ai_chat_record r WHERE r.session_id = s.id) AS tokenUsage
FROM ai_chat_session s
LEFT JOIN sys_user u ON s.user_id = u.id
WHERE s.tenant_id = #{tenantId}
  AND (#{keyword} IS NULL OR u.real_name LIKE CONCAT('%',#{keyword},'%') OR s.session_name LIKE CONCAT('%',#{keyword},'%'))
  AND (#{startTime} IS NULL OR s.create_time >= #{startTime})
  AND (#{endTime} IS NULL OR s.create_time <= #{endTime})
  AND (#{status} IS NULL OR s.status = #{status})
ORDER BY s.update_time DESC

3.3 查看会话消息 GET /ai/admin/session/{sessionId}/messages

复用现有 AiChatRecordService.listBySession(sessionId) 逻辑,但在管理端 Controller 中暴露。

响应数据

{
  "code": 200,
  "data": [
    {
      "id": 123,
      "sessionId": "session-uuid",
      "role": "user",
      "content": "帮我写一个用户管理模块",
      "tokenUsage": 50,
      "createTime": "2026-04-19 14:30:00"
    },
    {
      "id": 124,
      "sessionId": "session-uuid",
      "role": "assistant",
      "content": "好的,我来帮你写...",
      "tokenUsage": 200,
      "createTime": "2026-04-19 14:30:05"
    }
  ]
}

3.4 删除会话 DELETE /ai/admin/session/{sessionId}

复用现有 AiChatSessionService.deleteSession(sessionId),软删除(status=1)。

3.5 统计数据 GET /ai/admin/session/statistics

响应数据

{
  "code": 200,
  "data": {
    "totalSessions": 1234,
    "totalMessages": 5678,
    "todaySessions": 15,
    "totalTokenUsage": 98765,
    "dailyTrend": [
      { "date": "2026-03-20", "sessionCount": 10, "messageCount": 45 },
      { "date": "2026-03-21", "sessionCount": 12, "messageCount": 50 }
    ]
  }
}

SQL 实现

-- 汇总统计
SELECT COUNT(*) AS totalSessions FROM ai_chat_session WHERE tenant_id = #{tenantId} AND status = '0';
SELECT COUNT(*) AS totalMessages FROM ai_chat_record WHERE tenant_id = #{tenantId};
SELECT COUNT(*) AS todaySessions FROM ai_chat_session WHERE tenant_id = #{tenantId} AND status = '0' AND DATE(create_time) = CURDATE();
SELECT COALESCE(SUM(token_usage), 0) AS totalTokenUsage FROM ai_chat_record WHERE tenant_id = #{tenantId};

-- 30天趋势
SELECT DATE(s.create_time) AS date,
       COUNT(DISTINCT s.id) AS sessionCount,
       (SELECT COUNT(*) FROM ai_chat_record r WHERE r.tenant_id = #{tenantId} AND DATE(r.create_time) = DATE(s.create_time)) AS messageCount
FROM ai_chat_session s
WHERE s.tenant_id = #{tenantId} AND s.create_time >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
GROUP BY DATE(s.create_time)
ORDER BY date;

4. 数据模型变更

4.1 新增 DTO

AiSessionPageQuery(分页查询条件): | 字段 | 类型 | 说明 | |------|------|------| | pageNum | Integer | 页码 | | pageSize | Integer | 每页条数 | | keyword | String | 搜索关键词 | | startTime | String | 开始时间 | | endTime | String | 结束时间 | | status | String | 状态 |

AiSessionVO(会话列表返回对象): | 字段 | 类型 | 说明 | |------|------|------| | id | String | 会话ID | | userId | Long | 用户ID | | nickName | String | 用户昵称(来自 sys_user.real_name) | | avatar | String | 用户头像(来自 sys_user.avatar) | | sessionName | String | 会话标题 | | agentCode | String | Agent编码 | | status | String | 状态 | | messageCount | Integer | 消息条数 | | tokenUsage | Integer | Token消耗 | | createTime | LocalDateTime | 创建时间 | | updateTime | LocalDateTime | 更新时间 |

AiSessionStatisticsVO(统计数据返回对象): | 字段 | 类型 | 说明 | |------|------|------| | totalSessions | Long | 会话总数 | | totalMessages | Long | 消息总数 | | todaySessions | Long | 今日会话数 | | totalTokenUsage | Long | Token消耗总量 | | dailyTrend | List | 30天趋势 |

DailyTrendItem: | 字段 | 类型 | 说明 | |------|------|------| | date | String | 日期 | | sessionCount | Long | 会话数 | | messageCount | Long | 消息数 |

4.2 无数据库表结构变更

使用现有的 ai_chat_sessionai_chat_record 表,无需新增或修改表。

5. 后端文件清单

forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/
├── admin/
│   └── controller/
│       └── AiSessionAdminController.java     # 管理端会话Controller
├── session/
│   ├── dto/
│   │   ├── AiSessionPageQuery.java           # 分页查询条件
│   │   └── AiSessionVO.java                  # 会话列表VO
│   ├── vo/
│   │   ├── AiSessionStatisticsVO.java        # 统计VO
│   │   └── DailyTrendItem.java               # 趋势项
│   ├── mapper/
│   │   └── AiChatSessionMapper.java          # 新增自定义方法
│   └── service/
│       └── AiChatSessionService.java         # 新增管理端方法
└── resources/mapper/
    └── AiChatSessionMapper.xml               # 新增自定义SQL

6. 前端文件清单

forge-admin-ui/src/
├── api/ai.js                                 # 新增会话管理API
└── views/ai/
    └── session.vue                           # AI会话管理页面

7. 前端技术要点

  • 使用 Naive UI 组件:NStatistic、NCard、NInput、NDatePicker、NSelect、NList、NEmpty、NSpin
  • echarts 折线图:复用 @/utils/echarts-theme.js 主题
  • 聊天气泡:自定义 CSS 实现,用户消息蓝色靠右,AI回复灰色靠左
  • 消息内容 Markdown 渲染
  • 用户头像使用 AuthImage 组件(鉴权图片需带 token)
  • 响应式布局:统计卡片横排自适应,左右分栏固定比例(3:7 或 2:3)

8. 风险与注意事项

  1. 租户隔离:所有查询必须加 tenant_id 条件,项目 TenantLineInnerInterceptor 会自动处理
  2. 性能:分页查询中 messageCount 子查询在大数据量下可能较慢,后续可考虑冗余字段或定时统计
  3. Token 消耗:目前 ai_chat_record.token_usage 字段未被实际写入,统计值可能为 0,需确认后续是否补全
  4. 权限:管理端接口不加 @ApiPermissionIgnore,走正常权限体系,在 sys_resource 中配置菜单权限,仅管理员可访问

9. 测试策略

  • 后端:手动测试分页查询(带/不带筛选条件)、统计接口、删除会话
  • 前端:验证会话列表加载、搜索筛选、聊天记录展示、统计图表渲染、删除操作
  • 边界:无会话数据的空状态、无选中会话的右侧提示、超长消息内容截断