# AI 会话管理页面实现计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 开发管理员使用的 AI 会话管理页面,支持查看所有用户会话、聊天记录(类IM风格)和核心统计图表。 **Architecture:** 后端新增管理端 Controller + Mapper XML 自定义分页 SQL(JOIN sys_user),前端单页面集成统计卡片 + 趋势图 + 左右分栏会话管理。 **Tech Stack:** Spring Boot 3 + MyBatis-Plus(自定义XML分页) + Sa-Token | Vue 3 + Naive UI + ECharts + Markdown渲染 --- ## Task 1: 后端 DTO/VO 类 **Files:** - Create: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/dto/AiSessionPageQuery.java` - Create: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/vo/AiSessionVO.java` - Create: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/vo/AiSessionStatisticsVO.java` - Create: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/vo/DailyTrendItem.java` - [ ] **Step 1: 创建 AiSessionPageQuery** ```java package com.mdframe.forge.plugin.ai.session.dto; import com.mdframe.forge.starter.core.domain.PageQuery; import lombok.Data; import lombok.EqualsAndHashCode; @Data @EqualsAndHashCode(callSuper = true) public class AiSessionPageQuery extends PageQuery { private String keyword; private String startTime; private String endTime; private String status; } ``` - [ ] **Step 2: 创建 AiSessionVO** ```java package com.mdframe.forge.plugin.ai.session.vo; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.time.LocalDateTime; @Data public class AiSessionVO { private String id; private Long userId; private String nickName; private String avatar; private String sessionName; private String agentCode; private String status; private Integer messageCount; private Long tokenUsage; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime updateTime; } ``` - [ ] **Step 3: 创建 DailyTrendItem** ```java package com.mdframe.forge.plugin.ai.session.vo; import lombok.Data; @Data public class DailyTrendItem { private String date; private Long sessionCount; private Long messageCount; } ``` - [ ] **Step 4: 创建 AiSessionStatisticsVO** ```java package com.mdframe.forge.plugin.ai.session.vo; import lombok.Data; import java.util.List; @Data public class AiSessionStatisticsVO { private Long totalSessions; private Long totalMessages; private Long todaySessions; private Long totalTokenUsage; private List dailyTrend; } ``` - [ ] **Step 5: Commit** ```bash git add forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/dto/ git add forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/vo/ git commit -m "[ai-session] 新增会话管理DTO/VO类" ``` --- ## Task 2: Mapper XML + Mapper 接口扩展 **Files:** - Modify: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/mapper/AiChatSessionMapper.java` - Create: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/resources/mapper/AiChatSessionMapper.xml` - [ ] **Step 1: 扩展 AiChatSessionMapper 接口** 在现有接口中添加 3 个自定义方法: ```java package com.mdframe.forge.plugin.ai.session.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.mdframe.forge.plugin.ai.session.domain.AiChatSession; import com.mdframe.forge.plugin.ai.session.dto.AiSessionPageQuery; import com.mdframe.forge.plugin.ai.session.vo.AiSessionStatisticsVO; import com.mdframe.forge.plugin.ai.session.vo.AiSessionVO; import com.mdframe.forge.plugin.ai.session.vo.DailyTrendItem; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; @Mapper public interface AiChatSessionMapper extends BaseMapper { Page selectSessionPage(Page page, @Param("query") AiSessionPageQuery query); AiSessionStatisticsVO selectStatistics(); List selectDailyTrend(); } ``` - [ ] **Step 2: 创建 Mapper XML** 文件路径:`forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/resources/mapper/AiChatSessionMapper.xml` ```xml ``` - [ ] **Step 3: Commit** ```bash git add forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/mapper/AiChatSessionMapper.java git add forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/resources/mapper/AiChatSessionMapper.xml git commit -m "[ai-session] 新增Mapper自定义分页SQL和统计SQL" ``` --- ## Task 3: Service 层扩展 **Files:** - Modify: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/service/AiChatSessionService.java` - [ ] **Step 1: 在 AiChatSessionService 中新增管理端方法** 在现有类中追加以下方法(保留现有方法不变): ```java import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.mdframe.forge.plugin.ai.session.dto.AiSessionPageQuery; import com.mdframe.forge.plugin.ai.session.vo.AiSessionStatisticsVO; import com.mdframe.forge.plugin.ai.session.vo.AiSessionVO; import com.mdframe.forge.plugin.ai.session.vo.DailyTrendItem; // 新增以下方法到 AiChatSessionService 类中 public Page adminPage(AiSessionPageQuery query) { return baseMapper.selectSessionPage(query.toPage(), query); } public AiSessionStatisticsVO getStatistics() { AiSessionStatisticsVO stats = baseMapper.selectStatistics(); if (stats == null) { stats = new AiSessionStatisticsVO(); stats.setTotalSessions(0L); stats.setTotalMessages(0L); stats.setTodaySessions(0L); stats.setTotalTokenUsage(0L); } List trend = baseMapper.selectDailyTrend(); stats.setDailyTrend(trend); return stats; } ``` - [ ] **Step 2: Commit** ```bash git add forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/session/service/AiChatSessionService.java git commit -m "[ai-session] Service新增管理端分页和统计方法" ``` --- ## Task 4: 管理端 Controller **Files:** - Create: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/admin/controller/AiSessionAdminController.java` - [ ] **Step 1: 创建 AiSessionAdminController** ```java package com.mdframe.forge.plugin.ai.admin.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.mdframe.forge.plugin.ai.chat.domain.AiChatRecord; import com.mdframe.forge.plugin.ai.chat.service.AiChatRecordService; import com.mdframe.forge.plugin.ai.session.dto.AiSessionPageQuery; import com.mdframe.forge.plugin.ai.session.service.AiChatSessionService; import com.mdframe.forge.plugin.ai.session.vo.AiSessionStatisticsVO; import com.mdframe.forge.plugin.ai.session.vo.AiSessionVO; import com.mdframe.forge.starter.core.domain.RespInfo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import java.util.List; @Slf4j @RestController @RequestMapping("/ai/admin/session") @RequiredArgsConstructor public class AiSessionAdminController { private final AiChatSessionService sessionService; private final AiChatRecordService recordService; @GetMapping("/page") public RespInfo> page(AiSessionPageQuery query) { return RespInfo.success(sessionService.adminPage(query)); } @GetMapping("/{sessionId}/messages") public RespInfo> messages(@PathVariable String sessionId) { return RespInfo.success(recordService.listBySession(sessionId)); } @DeleteMapping("/{sessionId}") public RespInfo delete(@PathVariable String sessionId) { sessionService.deleteSession(sessionId); return RespInfo.success(); } @GetMapping("/statistics") public RespInfo statistics() { return RespInfo.success(sessionService.getStatistics()); } } ``` - [ ] **Step 2: Commit** ```bash git add forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/java/com/mdframe/forge/plugin/ai/admin/controller/AiSessionAdminController.java git commit -m "[ai-session] 新增管理端Controller" ``` --- ## Task 5: 菜单权限 SQL **Files:** - Create: `forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/resources/sql/ai_session_admin.sql` - [ ] **Step 1: 创建菜单权限 SQL** ```sql -- ============================================= -- AI 会话管理菜单及权限 -- ============================================= -- 会话管理菜单(挂在 AI管理 目录下) INSERT INTO sys_resource (tenant_id, resource_name, parent_id, resource_type, sort, path, component, is_external, is_public, menu_status, visible, icon, keep_alive, always_show, remark, create_time, update_time) SELECT 0, '会话管理', id, 2, 3, '/ai/session', '/ai/session', 0, 0, 1, 1, 'mdi:chat-processing', 0, 0, 'AI会话管理', NOW(), NOW() FROM sys_resource WHERE tenant_id = 0 AND path = '/ai' AND resource_type = 1 LIMIT 1; SET @ai_session_menu_id = LAST_INSERT_ID(); -- 会话管理按钮权限 INSERT INTO sys_resource (tenant_id, resource_name, parent_id, resource_type, sort, perms, is_external, is_public, menu_status, visible, remark, create_time, update_time) VALUES (0, '会话查看', @ai_session_menu_id, 3, 1, 'ai:session:query', 0, 0, 1, 1, '查看AI会话', NOW(), NOW()), (0, '会话删除', @ai_session_menu_id, 3, 2, 'ai:session:delete', 0, 0, 1, 1, '删除AI会话', NOW(), NOW()); -- 给超级管理员角色分配权限 INSERT INTO sys_role_resource (role_id, resource_id) SELECT 1, id FROM sys_resource WHERE tenant_id = 0 AND id >= @ai_session_menu_id; ``` - [ ] **Step 2: Commit** ```bash git add forge/forge-framework/forge-plugin-parent/forge-plugin-ai/src/main/resources/sql/ai_session_admin.sql git commit -m "[ai-session] 新增会话管理菜单权限SQL" ``` --- ## Task 6: 前端 API 接口 **Files:** - Modify: `forge-admin-ui/src/api/ai.js` - [ ] **Step 1: 在 ai.js 末尾追加会话管理 API** ```js // ========== 会话管理 ========== export function sessionPage(params) { return request.get('/ai/admin/session/page', { params }) } export function sessionMessages(sessionId) { return request.get(`/ai/admin/session/${sessionId}/messages`) } export function sessionDelete(sessionId) { return request.delete(`/ai/admin/session/${sessionId}`) } export function sessionStatistics() { return request.get('/ai/admin/session/statistics') } ``` - [ ] **Step 2: Commit** ```bash git add forge-admin-ui/src/api/ai.js git commit -m "[ai-session] 前端新增会话管理API" ``` --- ## Task 7: 前端会话管理页面 **Files:** - Create: `forge-admin-ui/src/views/ai/session.vue` 这是核心前端页面,包含统计卡片、趋势图、左右分栏会话管理。 - [ ] **Step 1: 创建 session.vue 完整页面** ```vue ``` - [ ] **Step 2: 检查 marked 和 highlight.js 是否已安装** Run: `cd forge-admin-ui && cat package.json | grep -E '"marked"|"highlight.js"'` 如果没有安装,运行: ```bash cd forge-admin-ui && pnpm add marked highlight.js ``` - [ ] **Step 3: Commit** ```bash git add forge-admin-ui/src/views/ai/session.vue git add forge-admin-ui/package.json forge-admin-ui/pnpm-lock.yaml git commit -m "[ai-session] 新增AI会话管理页面" ``` --- ## Task 8: 路由注册与最终验证 **Files:** - 检查路由是否自动注册(基于菜单配置) - 运行前端 lint - [ ] **Step 1: 确认路由自动注册机制** 项目的菜单路由通常由后端 `sys_resource` 的 `path` 和 `component` 字段驱动。Task 5 的 SQL 中已配置 `path=/ai/session`,`component=/ai/session`,对应 `views/ai/session.vue`。 确认方式:启动后端后,在菜单管理中能看到"AI管理 > 会话管理"菜单,前端能自动加载路由。 - [ ] **Step 2: 执行 SQL 插入菜单权限** 连接 MySQL 数据库,执行 Task 5 中创建的 `ai_session_admin.sql`。 - [ ] **Step 3: 运行前端 lint** ```bash cd forge-admin-ui && pnpm lint:fix -- src/views/ai/session.vue src/api/ai.js ``` 修复所有 lint 错误后重新 lint 确认通过。 - [ ] **Step 4: 最终 Commit** ```bash git add -A git commit -m "[ai-session] AI会话管理功能完成" ``` --- ## 自查清单 | Spec 要求 | 对应 Task | |-----------|-----------| | 会话列表查询(分页、搜索、时间范围、状态) | Task 2 (Mapper XML) + Task 4 (Controller) + Task 7 (前端列表) | | 会话下的消息记录-类IM聊天记录 | Task 4 (messages API) + Task 7 (聊天气泡) | | 核心统计图表 | Task 2 (统计SQL) + Task 4 (statistics API) + Task 7 (卡片+趋势图) | | 关联sys_user显示用户昵称等 | Task 2 (LEFT JOIN sys_user) + Task 7 (nickName/avatar渲染) | | 前端页面美观易操作 | Task 7 (完整UI:统计卡片+趋势图+左右分栏+聊天气泡+Markdown渲染) | | 菜单权限 | Task 5 (SQL) |