diff --git a/AI助手后端接口文档.md b/AI助手后端接口文档.md deleted file mode 100644 index 3a22e69..0000000 --- a/AI助手后端接口文档.md +++ /dev/null @@ -1,396 +0,0 @@ -# AI助手后端接口文档 - -## 重要提示 - 分类API更新需求 - -前端已修复分类数量显示问题,需要后端配合更新以下API: - -### 论坛分类API (`/api/categories`) -返回的分类数据需要包含 `postCount` 字段: -```json -{ - "code": 200, - "data": { - "list": [ - { - "id": 1, - "name": "学术交流", - "description": "学术讨论与交流", - "postCount": 15, // ← 新增:该分类下的帖子数量 - "status": 1, - "createdAt": "2024-12-21T10:00:00Z" - } - ] - } -} -``` - -### 资源分类API (`/api/categories`) -当用于资源模块时,需要包含 `resourceCount` 字段: -```json -{ - "code": 200, - "data": { - "list": [ - { - "id": 1, - "name": "学术资料", - "description": "学术相关资源", - "resourceCount": 8, // ← 新增:该分类下的资源数量 - "status": 1, - "createdAt": "2024-12-21T10:00:00Z" - } - ] - } -} -``` - ---- - -## 概述 - -AI助手功能为UniLife平台提供智能问答服务,支持多轮对话、会话管理、历史记录等功能。 - -## 基础信息 - -- **基础URL**: `/api/ai` -- **认证方式**: JWT Token (Header: `Authorization: Bearer `) -- **响应格式**: JSON - -## 通用响应格式 - -```json -{ - "code": 200, - "message": "success", - "data": { ... }, - "timestamp": "2024-12-21T10:30:00Z" -} -``` - -## 数据模型 - -### ChatMessage 聊天消息 - -```json -{ - "id": "string", - "sessionId": "string", - "role": "user|assistant|system", - "content": "string", - "timestamp": "2024-12-21T10:30:00Z", - "userId": "number" -} -``` - -### ChatSession 聊天会话 - -```json -{ - "id": "string", - "userId": "number", - "title": "string", - "createdAt": "2024-12-21T10:30:00Z", - "updatedAt": "2024-12-21T10:30:00Z", - "lastMessageTime": "2024-12-21T10:30:00Z", - "messageCount": "number" -} -``` - -## API接口 - -### 1. 发送消息 (核心接口) - -**POST** `/ai/chat` - -向AI发送消息并获取回复。 - -**请求体:** -```json -{ - "message": "如何学习Java编程?", - "sessionId": "session_123", // 可选,不传则创建新会话 - "conversationHistory": [ // 可选,用于上下文 - { - "role": "user", - "content": "之前的问题", - "timestamp": "2024-12-21T10:25:00Z" - } - ] -} -``` - -**响应:** -```json -{ - "code": 200, - "message": "success", - "data": { - "messageId": "msg_456", - "content": "学习Java编程可以从以下几个方面开始:\n\n1. **基础语法**\n - 变量和数据类型\n - 控制流程\n - 面向对象\n\n2. **实践项目**\n - 简单的控制台程序\n - Web应用开发\n\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n System.out.println(\"Hello, World!\");\n }\n}\n```\n\n希望这个建议对你有帮助!", - "sessionId": "session_123", - "timestamp": "2024-12-21T10:30:00Z" - } -} -``` - -### 2. 获取聊天会话列表 - -**GET** `/ai/sessions` - -获取用户的所有聊天会话。 - -**查询参数:** -- `page`: number (默认: 1) -- `size`: number (默认: 20) - -**响应:** -```json -{ - "code": 200, - "data": { - "sessions": [ - { - "id": "session_123", - "userId": 1, - "title": "Java学习咨询", - "createdAt": "2024-12-21T10:00:00Z", - "updatedAt": "2024-12-21T10:30:00Z", - "lastMessageTime": "2024-12-21T10:30:00Z", - "messageCount": 5 - } - ], - "total": 10 - } -} -``` - -### 3. 获取会话消息历史 - -**GET** `/ai/sessions/{sessionId}/messages` - -获取指定会话的消息历史。 - -**路径参数:** -- `sessionId`: string - 会话ID - -**查询参数:** -- `page`: number (默认: 1) -- `size`: number (默认: 50) - -**响应:** -```json -{ - "code": 200, - "data": { - "messages": [ - { - "id": "msg_001", - "sessionId": "session_123", - "role": "user", - "content": "如何学习Java编程?", - "timestamp": "2024-12-21T10:25:00Z", - "userId": 1 - }, - { - "id": "msg_002", - "sessionId": "session_123", - "role": "assistant", - "content": "学习Java编程可以从基础语法开始...", - "timestamp": "2024-12-21T10:30:00Z", - "userId": null - } - ], - "total": 5, - "sessionInfo": { - "id": "session_123", - "title": "Java学习咨询", - "createdAt": "2024-12-21T10:00:00Z" - } - } -} -``` - -### 4. 创建新的聊天会话 - -**POST** `/ai/sessions` - -创建一个新的聊天会话。 - -**请求体:** -```json -{ - "title": "新的聊天" // 可选,不传则自动生成 -} -``` - -**响应:** -```json -{ - "code": 200, - "data": { - "sessionId": "session_456", - "title": "新的聊天" - } -} -``` - -### 5. 更新会话标题 - -**PUT** `/ai/sessions/{sessionId}` - -更新指定会话的标题。 - -**路径参数:** -- `sessionId`: string - -**请求体:** -```json -{ - "title": "Java学习讨论" -} -``` - -**响应:** -```json -{ - "code": 200, - "message": "会话标题更新成功" -} -``` - -### 6. 删除聊天会话 - -**DELETE** `/ai/sessions/{sessionId}` - -删除指定的聊天会话及其所有消息。 - -**路径参数:** -- `sessionId`: string - -**响应:** -```json -{ - "code": 200, - "message": "会话删除成功" -} -``` - -### 7. 清空会话消息 - -**DELETE** `/ai/sessions/{sessionId}/messages` - -清空指定会话的所有消息,但保留会话。 - -**路径参数:** -- `sessionId`: string - -**响应:** -```json -{ - "code": 200, - "message": "会话消息已清空" -} -``` - -## 错误码说明 - -| 错误码 | 说明 | -|--------|------| -| 400 | 请求参数错误 | -| 401 | 未授权访问 | -| 403 | 无权限访问 | -| 404 | 会话或消息不存在 | -| 429 | 请求频率过高 | -| 500 | AI服务异常 | -| 503 | AI服务暂时不可用 | - -## 实现建议 - -### 1. 数据库设计 - -**ai_sessions表:** -```sql -CREATE TABLE ai_sessions ( - id VARCHAR(64) PRIMARY KEY, - user_id BIGINT NOT NULL, - title VARCHAR(200) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - last_message_time TIMESTAMP, - message_count INT DEFAULT 0, - INDEX idx_user_id (user_id), - INDEX idx_updated_at (updated_at) -); -``` - -**ai_messages表:** -```sql -CREATE TABLE ai_messages ( - id VARCHAR(64) PRIMARY KEY, - session_id VARCHAR(64) NOT NULL, - user_id BIGINT, - role ENUM('user', 'assistant', 'system') NOT NULL, - content TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - INDEX idx_session_id (session_id), - INDEX idx_created_at (created_at), - FOREIGN KEY (session_id) REFERENCES ai_sessions(id) ON DELETE CASCADE -); -``` - -### 2. AI服务集成 - -建议集成以下AI服务之一: -- **OpenAI GPT API** - 功能强大,支持多种模型 -- **百度文心一言** - 中文优化,国内访问稳定 -- **阿里通义千问** - 企业级服务,安全可靠 -- **智谱GLM** - 性价比高,中文效果好 - -### 3. 关键功能 - -1. **上下文管理**: 维护对话历史,提供连贯的多轮对话 -2. **流式输出**: 支持Server-Sent Events实现打字机效果 -3. **内容安全**: 过滤敏感内容,确保回复安全合规 -4. **速率限制**: 防止滥用,控制每用户请求频率 -5. **缓存机制**: 相似问题缓存回复,提高响应速度 - -### 4. 安全考虑 - -- 所有接口需要JWT认证 -- 验证用户对会话的权限 -- 过滤和审核用户输入 -- 监控和记录异常使用行为 -- 设置合理的内容长度限制 - -### 5. 性能优化 - -- 使用连接池管理AI服务连接 -- 实现请求排队和负载均衡 -- 添加Redis缓存热门问答 -- 定期清理过期会话数据 -- 监控API调用频率和成本 - -## 前端配合说明 - -前端已实现: -- ✅ 现代化聊天界面设计 -- ✅ Markdown内容渲染 -- ✅ 打字机效果动画 -- ✅ 会话历史管理 -- ✅ 响应式设计 -- ✅ 示例问题快速发送 -- ✅ 键盘快捷键支持 - -需要后端配合: -- 🔄 实现上述所有API接口 -- 🔄 选择并集成AI服务 -- 🔄 建立数据库表结构 -- 🔄 添加安全和性能优化 - -## 测试建议 - -1. **单元测试**: 测试各API接口的基本功能 -2. **集成测试**: 测试AI服务的集成和响应 -3. **压力测试**: 测试并发请求的处理能力 -4. **安全测试**: 验证权限控制和内容安全 -5. **用户体验测试**: 测试实际使用场景和响应时间 \ No newline at end of file diff --git a/UniLife接口文档.md b/UniLife接口文档.md index b88b8c9..b2c115f 100644 --- a/UniLife接口文档.md +++ b/UniLife接口文档.md @@ -2,6 +2,16 @@ ## 更新日志 +### v1.3.0 (2025-01-27) +- **新增AI辅助学习模块**: 实现了完整的AI聊天助手功能 +- **AI会话管理**: 支持创建、查询、更新、删除聊天会话 +- **AI消息系统**: 支持发送消息、获取回复、查看历史记录 +- **上下文感知**: AI可以根据会话历史提供连贯的对话体验 +- **Markdown支持**: AI回复支持丰富的格式化内容 +- **会话标题编辑**: 支持双击编辑或点击编辑按钮修改会话标题 +- **数据库设计**: 新增ai_chat_sessions和ai_chat_messages表 +- **多会话支持**: 用户可以同时进行多个独立的AI对话会话 + ### v1.2.0 (2025-01-27) - **修复资源点赞功能**: 实现了完整的资源点赞表 (`resource_likes`),防止重复点赞 - **优化响应数据结构**: 统一时间格式为ISO 8601格式 (`yyyy-MM-ddTHH:mm:ss`) @@ -31,7 +41,8 @@ - [4.3 分类管理](#43-分类管理) - [5. 学习资源共享模块](#5-学习资源共享模块) - [6. 课程表与日程管理模块](#6-课程表与日程管理模块) -- [7. 待实现模块](#7-待实现模块) +- [7. AI辅助学习模块](#7-AI辅助学习模块) +- [8. 待实现模块](#8-待实现模块) ## 1. 基础信息 @@ -1646,10 +1657,295 @@ CREATE TABLE `schedules` ( - 第12节课: 19:20-20:10 - 第13节课: 20:10-21:00 -## 7. 待实现模块 +## 7. AI辅助学习模块 + +### 7.1 核心接口 + +#### 7.1.1 发送消息给AI(流式响应) +- **URL**: `/ai/chat` +- **方法**: POST +- **描述**: 向AI发送消息并获取流式回复 +- **认证**: 需要JWT Token +- **响应类型**: `text/event-stream`(Server-Sent Events) + +请求参数: +```json +{ + "message": "如何学好Python编程?", + "sessionId": "session_1234567890", + "conversationHistory": [ + { + "id": "msg_001", + "role": "user", + "content": "你好", + "timestamp": "2025-01-27T10:00:00" + } + ] +} +``` + +**参数说明**: +- `message`: 用户发送的消息内容 +- `sessionId` (可选): 会话ID,如果不提供则创建新会话 +- `conversationHistory` (可选): 会话历史记录,用于AI理解上下文 + +**流式响应**: +接口返回Server-Sent Events流,每个事件包含AI回复的一部分内容: +``` +data: 学好Python编程需要 +data: 循序渐进地学习 +data: ,首先掌握基础语法 +data: ,然后通过实际项目练习 +data: [END] +``` + +**前端处理示例**: +```javascript +const response = await fetch('/ai/chat', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + token + }, + body: JSON.stringify(requestData) +}); + +const reader = response.body.getReader(); +const decoder = new TextDecoder(); + +while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value); + // 处理流式数据块 + console.log(chunk); +} +``` + +#### 7.1.2 获取聊天会话列表 +- **URL**: `/ai/sessions` +- **方法**: GET +- **描述**: 获取当前用户的聊天会话列表 +- **认证**: 需要JWT Token + +请求参数: +- **page** (query, 可选): 页码,默认为1 +- **size** (query, 可选): 每页大小,默认为20 + +响应结果: +```json +{ + "code": 200, + "message": "success", + "data": { + "sessions": [ + { + "id": "session_1234567890", + "title": "Python学习咨询", + "createdAt": "2025-01-27T10:00:00", + "updatedAt": "2025-01-27T15:30:00", + "lastMessageTime": "2025-01-27T15:30:00", + "messageCount": 8 + } + ], + "total": 5 + } +} +``` + +#### 7.1.3 获取会话消息历史 +- **URL**: `/ai/sessions/{sessionId}/messages` +- **方法**: GET +- **描述**: 获取指定会话的消息历史 +- **认证**: 需要JWT Token + +请求参数: +- **page** (query, 可选): 页码,默认为1 +- **size** (query, 可选): 每页大小,默认为50 + +响应结果: +```json +{ + "code": 200, + "message": "success", + "data": { + "messages": [ + { + "id": "msg_001", + "role": "user", + "content": "如何学好Python编程?", + "timestamp": "2025-01-27T10:00:00" + }, + { + "id": "msg_002", + "role": "assistant", + "content": "学好Python编程需要循序渐进...", + "timestamp": "2025-01-27T10:00:30" + } + ], + "total": 8, + "sessionInfo": { + "id": "session_1234567890", + "title": "Python学习咨询", + "createdAt": "2025-01-27T10:00:00", + "updatedAt": "2025-01-27T15:30:00", + "lastMessageTime": "2025-01-27T15:30:00", + "messageCount": 8 + } + } +} +``` + +#### 7.1.4 创建聊天会话 +- **URL**: `/ai/sessions` +- **方法**: POST +- **描述**: 创建新的AI聊天会话(sessionId由前端生成) +- **认证**: 需要JWT Token + +请求参数: +```json +{ + "sessionId": "session_1706177600_abc123", + "title": "新对话" +} +``` + +**参数说明**: +- `sessionId`: 会话ID,由前端生成(格式:session__) +- `title` (可选): 会话标题,默认为"新对话" + +响应结果: +```json +{ + "code": 200, + "message": "创建会话成功", + "data": { + "sessionId": "session_1706177600_abc123", + "title": "新对话" + } +} +``` + +#### 7.1.5 更新会话标题 +- **URL**: `/ai/sessions/{sessionId}` +- **方法**: PUT +- **描述**: 更新会话标题 +- **认证**: 需要JWT Token + +请求参数: +```json +{ + "title": "更新后的标题" +} +``` + +响应结果: +```json +{ + "code": 200, + "message": "更新成功", + "data": null +} +``` + +#### 7.1.6 清空会话消息 +- **URL**: `/ai/sessions/{sessionId}/messages` +- **方法**: DELETE +- **描述**: 清空指定会话的所有消息(保留会话) +- **认证**: 需要JWT Token + +响应结果: +```json +{ + "code": 200, + "message": "清空成功", + "data": null +} +``` + +#### 7.1.7 删除会话 +- **URL**: `/ai/sessions/{sessionId}` +- **方法**: DELETE +- **描述**: 删除指定会话及其所有消息 +- **认证**: 需要JWT Token + +响应结果: +```json +{ + "code": 200, + "message": "删除成功", + "data": null +} +``` + +### 7.2 数据库设计说明 + +#### AI聊天会话表 (ai_chat_sessions) +```sql +CREATE TABLE `ai_chat_sessions` ( + `id` VARCHAR(64) PRIMARY KEY COMMENT '会话ID(前端生成)', + `user_id` BIGINT NOT NULL COMMENT '用户ID', + `title` VARCHAR(100) NOT NULL DEFAULT '新对话' COMMENT '会话标题', + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + INDEX `idx_user_id` (`user_id`), + FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +); +``` + +#### AI聊天消息表 (ai_chat_messages) +```sql +CREATE TABLE `ai_chat_messages` ( + `id` VARCHAR(64) PRIMARY KEY COMMENT '消息ID(前端生成)', + `session_id` VARCHAR(64) NOT NULL COMMENT '会话ID', + `role` ENUM('user', 'assistant', 'system') NOT NULL COMMENT '角色', + `content` TEXT NOT NULL COMMENT '消息内容', + `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + INDEX `idx_session_id` (`session_id`), + FOREIGN KEY (`session_id`) REFERENCES `ai_chat_sessions` (`id`) ON DELETE CASCADE +); +``` + +### 7.3 流式响应特性说明 + +**核心特性**: +- 支持多会话管理,每个用户可以有多个独立的对话会话 +- 消息按时间顺序存储,支持完整的对话历史记录 +- 级联删除保证数据一致性 +- 支持用户、助手和系统三种角色的消息 +- 会话ID和消息ID由前端生成,确保前端能够立即使用 +- ID格式:session__ 和 msg__ +- **流式响应**: 使用Server-Sent Events实现实时的AI回复流式传输 + +**前端流式处理示例**: +```typescript +import { sendMessage } from '@/api/ai' + +// 使用流式API +await sendMessage({ + message: '你好', + sessionId: 'session_123', + conversationHistory: [] +}, (chunk: string) => { + // 处理每个数据块 + currentMessage.content += chunk + // 实时更新UI + updateMessageDisplay() +}) +``` + +**后端流式实现**: +```java +@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) +public Flux sendMessage(@RequestBody AiSendMessageDTO sendMessageDTO) { + return aiService.sendMessage(sendMessageDTO); +} +``` + +## 8. 待实现模块 以下模块尚未实现,将在后续开发中完成: - 搜索功能模块 -- AI辅助学习模块 - 积分系统模块 \ No newline at end of file diff --git a/unilife-frontend/src/api/ai.ts b/unilife-frontend/src/api/ai.ts index a77d227..df565aa 100644 --- a/unilife-frontend/src/api/ai.ts +++ b/unilife-frontend/src/api/ai.ts @@ -24,13 +24,6 @@ export interface SendMessageRequest { conversationHistory?: ChatMessage[] } -export interface SendMessageResponse { - messageId: string - content: string - sessionId: string - timestamp: string -} - export interface ChatHistoryResponse { sessions: ChatSession[] total: number @@ -42,9 +35,30 @@ export interface ChatMessagesResponse { sessionInfo: ChatSession } -// 发送消息给AI -export const sendMessage = (data: SendMessageRequest) => { - return api.post>('/ai/chat', data) +// 发送消息给AI(流式响应) +export const sendMessage = async ( + data: SendMessageRequest +): Promise> => { + const token = localStorage.getItem('token') + + const response = await fetch(`${api.defaults.baseURL}/ai/chat`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': token ? `Bearer ${token}` : '' + }, + body: JSON.stringify(data) + }) + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + + if (!response.body) { + throw new Error('Response body is not readable') + } + + return response.body.getReader() } // 获取聊天历史列表 @@ -62,8 +76,11 @@ export const getChatMessages = (sessionId: string, page = 1, size = 50) => { } // 创建新的聊天会话 -export const createChatSession = (title?: string) => { - return api.post>('/ai/sessions', { title }) +export const createChatSession = (sessionId: string, title?: string) => { + return api.post>('/ai/sessions', { + sessionId, + title + }) } // 删除聊天会话 diff --git a/unilife-frontend/src/components/TopNavbar.vue b/unilife-frontend/src/components/TopNavbar.vue new file mode 100644 index 0000000..db79997 --- /dev/null +++ b/unilife-frontend/src/components/TopNavbar.vue @@ -0,0 +1,172 @@ + + + + + \ No newline at end of file diff --git a/unilife-frontend/src/router/index.ts b/unilife-frontend/src/router/index.ts index 8e6fe76..0a9eb05 100644 --- a/unilife-frontend/src/router/index.ts +++ b/unilife-frontend/src/router/index.ts @@ -58,7 +58,7 @@ const router = createRouter({ { path: '/ai-assistant', name: 'ai-assistant', - component: () => import('@/views/AIAssistantView.vue'), + component: () => import('@/views/ai/AIAssistantView.vue'), meta: { requiresAuth: true } }, { diff --git a/unilife-frontend/src/utils/index.ts b/unilife-frontend/src/utils/index.ts new file mode 100644 index 0000000..45d371b --- /dev/null +++ b/unilife-frontend/src/utils/index.ts @@ -0,0 +1,19 @@ +/** + * 生成唯一的会话ID + * 格式:session__ + */ +export const generateSessionId = (): string => { + const timestamp = Date.now() + const random = Math.random().toString(36).substring(2, 8) + return `session_${timestamp}_${random}` +} + +/** + * 生成唯一的消息ID + * 格式:msg__ + */ +export const generateMessageId = (): string => { + const timestamp = Date.now() + const random = Math.random().toString(36).substring(2, 8) + return `msg_${timestamp}_${random}` +} \ No newline at end of file diff --git a/unilife-frontend/src/views/AIAssistantView.vue b/unilife-frontend/src/views/AIAssistantView.vue deleted file mode 100644 index 668ad2c..0000000 --- a/unilife-frontend/src/views/AIAssistantView.vue +++ /dev/null @@ -1,864 +0,0 @@ - - - - - \ No newline at end of file diff --git a/unilife-frontend/src/views/HomeView.vue b/unilife-frontend/src/views/HomeView.vue index bfb47fe..fd4c478 100644 --- a/unilife-frontend/src/views/HomeView.vue +++ b/unilife-frontend/src/views/HomeView.vue @@ -346,7 +346,7 @@ const steps = ref([ } .nav-container { - max-width: 1200px; + max-width: 1400px; margin: 0 auto; padding: 0 24px; display: flex; @@ -426,7 +426,7 @@ const steps = ref([ } .hero-content { - max-width: 1200px; + max-width: 1400px; margin: 0 auto; display: grid; grid-template-columns: 1fr 1fr; @@ -663,7 +663,7 @@ const steps = ref([ } .section-container { - max-width: 1200px; + max-width: 1400px; margin: 0 auto; } @@ -806,7 +806,7 @@ const steps = ref([ } .footer-container { - max-width: 1200px; + max-width: 1400px; margin: 0 auto; display: grid; grid-template-columns: 1fr 2fr; diff --git a/unilife-frontend/src/views/ai/AIAssistantView.vue b/unilife-frontend/src/views/ai/AIAssistantView.vue new file mode 100644 index 0000000..06d183f --- /dev/null +++ b/unilife-frontend/src/views/ai/AIAssistantView.vue @@ -0,0 +1,1654 @@ + + + + + \ No newline at end of file diff --git a/unilife-frontend/src/views/forum/ForumView.vue b/unilife-frontend/src/views/forum/ForumView.vue index 976e509..a096f8b 100644 --- a/unilife-frontend/src/views/forum/ForumView.vue +++ b/unilife-frontend/src/views/forum/ForumView.vue @@ -1,46 +1,7 @@