diff --git a/README.md b/README.md index 581f555..13c5017 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,102 @@ -# UniLife —— 有你生活,优你生活 -## 项目简介 -UniLife 是一款面向学生的在线论坛,致力于提升校园生活体验。其口号为 “有你生活,优你生活”,核心功能包括论坛交流、学习资源共享、课程管理、AI 学习辅助等。项目初期以网站形式开发,后续可能扩展为移动端应用。 +# UniLife - 大学生活综合平台 + +## 最新更新 🎉 + +### v1.4.0 - AI助手重构 (2025-05-30) +- ✅ **流式AI对话**: 实现真正的实时流式响应,告别等待时间 +- ✅ **优化用户体验**: 文字逐字显示,就像真实对话一样自然 +- ✅ **技术优化**: 使用Spring AI + WebFlux + 原生fetch API,确保稳定性 +- ✅ **美观界面**: 保持现代化设计,支持Markdown渲染和代码高亮 ## 功能特性 -### 1.用户系统 ✅ -+ 账号注册/登录(支持邮箱验证码登录) -+ 个人资料管理 -+ JWT认证机制 - -### 2.论坛功能 ✅ -+ 主题发布/评论/点赞 -+ 话题分类(学习、校园生活、兴趣交流等) -+ 嵌套评论系统 - -### 3.学习资源共享 ✅ -+ 资源上传/下载 -+ 资源分类管理 -+ 文件存储(支持阿里云OSS) - -### 4.课程表 & 个人行程安排 ✅ -+ 课程信息管理 -+ 个人日程管理 -+ 课程冲突检测 -+ 日程提醒功能 - -### 5.搜索功能 ✅ -+ 综合搜索(帖子/资源/用户) -+ 分类搜索和过滤 -+ 搜索建议 -+ 热门搜索词 - -### 6.AI 辅助学习 🚧 -+ 学习计划制定(开发中) -+ 智能任务提醒(开发中) -+ AI问答助手(开发中) - -### 7.积分系统 🚧 -+ 贡献积分系统(规划中) -+ 成就系统(规划中) -+ 积分排行榜(规划中) - -### 8.实时通知 🚧 -+ WebSocket实时推送(规划中) -+ 消息中心(规划中) -+ 私信功能(规划中) - -## 小组成员: -王雨菲 蔡子钦 钟宏烨 贾瀚翔 胡天琦 刘宇航 +### 🤖 AI智能助手 +- **实时流式对话**: 支持连续对话,AI回复实时显示 +- **多会话管理**: 创建多个独立的对话会话 +- **学习辅导**: 专业的学习计划制定和学习方法指导 +- **智能问答**: 快速获得各类学习问题的解答 + +### 📚 学习资源共享 +- 课件、笔记、考试资料上传下载 +- 资源分类管理和搜索 +- 用户评分和推荐系统 + +### 💬 学术论坛 +- 学习交流和问题讨论 +- 帖子发布、评论、点赞 +- 热门话题和精华内容推荐 + +### 📅 课程表与日程管理 +- 个人课程表管理 +- 学习任务和截止时间提醒 +- 日程安排和时间规划 + +## 技术架构 + +### 后端技术栈 +- **Spring Boot 3.x**: 主应用框架 +- **Spring AI**: AI集成框架,支持多种AI提供商 +- **WebFlux**: 响应式Web框架,支持流式响应 +- **MySQL**: 主数据库 +- **JWT**: 身份认证 +- **OSS**: 文件存储 + +### 前端技术栈 +- **Vue 3**: 前端框架 +- **TypeScript**: 类型安全 +- **Element Plus**: UI组件库 +- **Pinia**: 状态管理 +- **md-editor-v3**: Markdown编辑器 +- **Native Fetch API**: 流式响应处理 + +## 快速开始 + +### 1. 克隆项目 +```bash +git clone https://github.com/your-repo/unilife.git +cd unilife +``` + +### 2. 启动后端 +```bash +cd unilife-server +mvn spring-boot:run -Dmaven.test.skip=true +``` + +### 3. 启动前端 +```bash +cd unilife-frontend +npm install +npm run dev +``` + +### 4. 访问应用 +- 前端地址: http://localhost:5175 +- 后端地址: http://localhost:8087 + +## AI助手使用指南 + +### 基本使用 +1. 登录系统后,点击导航栏的"AI助手" +2. 在输入框中输入你的问题或需求 +3. 按Enter发送,AI会实时回复 + +### 功能示例 +- **学习计划**: "帮我制定一个学期的学习计划" +- **课程推荐**: "推荐一些适合我专业的课程" +- **学习方法**: "分享一些高效的学习方法" +- **答疑解惑**: "如何学好Python编程?" + +### 技术特点 +- **实时响应**: 无需等待,AI回复逐字显示 +- **上下文理解**: 支持连续对话,记住前面的对话内容 +- **多会话管理**: 可以创建多个不同主题的对话 +- **Markdown支持**: AI回复支持代码高亮、表格等格式 + +## 贡献指南 + +欢迎提交Issue和Pull Request来帮助改进项目! + +## 许可证 + +本项目基于MIT许可证开源。 diff --git a/UniLife技术要点与开发指南.md b/UniLife技术要点与开发指南.md index 1f6a519..ad3a5ad 100644 --- a/UniLife技术要点与开发指南.md +++ b/UniLife技术要点与开发指南.md @@ -153,32 +153,86 @@ public class Result { ### 🤖 AI辅助学习模块 #### 技术选型建议 -- **AI服务**:百度文心一言、阿里通义千问 -- **集成方式**:HTTP API调用 -- **功能设计**: - - 学习计划生成 - - 智能问答 - - 学习进度分析 +- **AI服务**:Spring AI + ChatClient(支持多种AI提供商) +- **流式响应**:WebFlux Flux +- **集成方式**:Spring Boot ReactiveWeb + +#### 流式响应核心技术 +```java +// 后端实现要点 +@RequestMapping(value = "/chat", produces = "text/html;charset=UTF-8") +public Flux sendMessage(@RequestParam("prompt") String prompt) { + return chatClient.prompt(prompt) + .stream() + .content(); +} +``` + +```javascript +// 前端实现要点 +const sendMessage = async (prompt) => { + const params = new URLSearchParams() + params.append('prompt', prompt) + + const response = await fetch('/ai/chat', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: params + }) + + const reader = response.body.getReader() + const decoder = new TextDecoder('utf-8') + let accumulatedContent = '' + + while (true) { + const { value, done } = await reader.read() + if (done) break + + accumulatedContent += decoder.decode(value) + // 实时更新UI + updateMessage(accumulatedContent) + } +} +``` + +#### 关键技术要点 +1. **使用原生fetch而不是axios**: axios对流式响应支持有限 +2. **URLSearchParams参数传递**: 比JSON格式更稳定 +3. **UTF-8编码处理**: 确保中文字符正确显示 +4. **累积内容渲染**: 避免字符被截断 +5. **错误处理机制**: 流式传输中的异常捕获 + +#### 功能设计 +- 实时流式AI对话 +- 多会话管理 +- 学习计划生成 +- 智能问答 +- 学习进度分析 #### 数据库设计 ```sql --- 学习计划表 -CREATE TABLE study_plans ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - user_id BIGINT NOT NULL, - title VARCHAR(100) NOT NULL, - content TEXT, - ai_generated TINYINT DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP +-- AI聊天会话表 +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对话记录 -CREATE TABLE ai_conversations ( - id BIGINT PRIMARY KEY AUTO_INCREMENT, - user_id BIGINT NOT NULL, - question TEXT NOT NULL, - answer TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP +-- AI聊天消息表 +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 ); ``` diff --git a/UniLife接口文档.md b/UniLife接口文档.md index b2c115f..e0a9be9 100644 --- a/UniLife接口文档.md +++ b/UniLife接口文档.md @@ -2,6 +2,14 @@ ## 更新日志 +### v1.4.0 (2025-05-30) +- **重构AI助手流式响应**: 完全重构AI聊天功能,实现真正的流式显示 +- **优化接口参数格式**: AI聊天接口改用`application/x-www-form-urlencoded`格式,简化参数传递 +- **改进前端实现**: 使用原生fetch API替代axios,确保流式响应的稳定性 +- **参考成功案例**: 基于已验证的流式响应实现模式进行重构 +- **UI体验提升**: 保持美观界面的同时,实现真正的实时流式文字显示 +- **响应格式优化**: 从Server-Sent Events改为直接文本流,提高兼容性 + ### v1.3.0 (2025-01-27) - **新增AI辅助学习模块**: 实现了完整的AI聊天助手功能 - **AI会话管理**: 支持创建、查询、更新、删除聊天会话 @@ -1666,62 +1674,26 @@ CREATE TABLE `schedules` ( - **方法**: POST - **描述**: 向AI发送消息并获取流式回复 - **认证**: 需要JWT Token -- **响应类型**: `text/event-stream`(Server-Sent Events) +- **Content-Type**: `application/x-www-form-urlencoded` +- **响应类型**: `text/html;charset=UTF-8`(流式文本响应) -请求参数: -```json -{ - "message": "如何学好Python编程?", - "sessionId": "session_1234567890", - "conversationHistory": [ - { - "id": "msg_001", - "role": "user", - "content": "你好", - "timestamp": "2025-01-27T10:00:00" - } - ] -} +请求参数(Form Data格式): +``` +prompt=如何学好Python编程? +sessionId=session_1234567890 ``` **参数说明**: -- `message`: 用户发送的消息内容 -- `sessionId` (可选): 会话ID,如果不提供则创建新会话 -- `conversationHistory` (可选): 会话历史记录,用于AI理解上下文 +- `prompt`: 用户发送的消息内容(必填) +- `sessionId`: 会话ID,如果不提供则由后端创建新会话(可选) **流式响应**: -接口返回Server-Sent Events流,每个事件包含AI回复的一部分内容: +接口返回原始文本流,AI的回复内容会逐字符或逐词流式返回: ``` -data: 学好Python编程需要 -data: 循序渐进地学习 -data: ,首先掌握基础语法 -data: ,然后通过实际项目练习 -data: [END] +学好Python编程需要循序渐进地学习,首先掌握基础语法,然后通过实际项目练习... ``` -**前端处理示例**: -```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` @@ -1937,9 +1909,45 @@ await sendMessage({ **后端流式实现**: ```java -@PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) -public Flux sendMessage(@RequestBody AiSendMessageDTO sendMessageDTO) { - return aiService.sendMessage(sendMessageDTO); +@RestController +@RequestMapping("/ai") +public class AiController { + + private final AiService aiService; + + @Operation(summary = "发送消息给AI") + @RequestMapping(value = "/chat", produces = "text/html;charset=UTF-8") + public Flux sendMessage( + @RequestParam("prompt") String prompt, + @RequestParam(value = "sessionId", required = false) String sessionId) { + log.info("发送消息给AI: {}", prompt); + + AiSendMessageDTO sendMessageDTO = new AiSendMessageDTO(); + sendMessageDTO.setMessage(prompt); + sendMessageDTO.setSessionId(sessionId); + + return aiService.sendMessage(sendMessageDTO); + } +} +``` + +**Service层实现**: +```java +@Service +public class AiServiceImpl implements AiService { + + @Autowired + private ChatClient chatClient; + + @Override + public Flux sendMessage(AiSendMessageDTO sendMessageDTO) { + log.info("发送消息给AI: {}", sendMessageDTO.getMessage()); + + // 使用Spring AI ChatClient的流式响应 + return chatClient.prompt(sendMessageDTO.getMessage()) + .stream() + .content(); + } } ``` diff --git a/unilife-frontend/src/api/ai.ts b/unilife-frontend/src/api/ai.ts index df565aa..e9ba72a 100644 --- a/unilife-frontend/src/api/ai.ts +++ b/unilife-frontend/src/api/ai.ts @@ -37,17 +37,30 @@ export interface ChatMessagesResponse { // 发送消息给AI(流式响应) export const sendMessage = async ( - data: SendMessageRequest + prompt: string, + sessionId?: string | null ): Promise> => { const token = localStorage.getItem('token') + // 使用URLSearchParams构建请求参数 + const params = new URLSearchParams() + params.append('prompt', prompt) + if (sessionId && sessionId.trim()) { + params.append('sessionId', sessionId.trim()) + console.log('API请求包含sessionId:', sessionId.trim()) + } else { + console.log('API请求不包含sessionId') + } + + console.log('发送到后端的参数:', params.toString()) + const response = await fetch(`${api.defaults.baseURL}/ai/chat`, { method: 'POST', headers: { - 'Content-Type': 'application/json', + 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': token ? `Bearer ${token}` : '' }, - body: JSON.stringify(data) + body: params }) if (!response.ok) { diff --git a/unilife-frontend/src/views/ai/AIAssistantView.vue b/unilife-frontend/src/views/ai/AIAssistantView.vue index 06d183f..2dafc7b 100644 --- a/unilife-frontend/src/views/ai/AIAssistantView.vue +++ b/unilife-frontend/src/views/ai/AIAssistantView.vue @@ -3,10 +3,10 @@ - +
- + - +
-
+
- +

UniLife AI 学习助手

-

基于智能检索的个性化学习伙伴

+

基于智能检索的个性化学习伙伴

- +
智能资源搜索
- +
个性化推荐
- +
学习路径规划
@@ -99,9 +99,9 @@ @click="sendExampleQuestion(action.prompt)" >
- - - + + +
{{ action.text }} @@ -111,10 +111,10 @@
-
+
@@ -128,31 +128,17 @@
-
-
- {{ message.content }} +
+
{{ message.content }}
+
+
+
+
+ {{ message.content }}| +
- -
-
- - - -
- AI正在思考... -
- - -
-
- {{ message.content }}| -
-
- -
- +
- -
- - - 清空对话 - - - - 导出对话 - -
- - -
-
- -
- -
- {{ attachment.name }} - {{ formatFileSize(attachment.size) }} -
- - - -
- - -
-
- -
-
- {{ attachment.name }} - {{ formatFileSize(attachment.size) }} -
- - - -
-
-
- -
-
- -
-
- -

拖拽文件到这里上传

- 支持图片、文档等格式 -
-
- +
+
- +
- -
- - - - - - - - - - - - - -
- - - +
@@ -337,11 +198,8 @@ Enter 发送消息 - - 支持拖拽上传图片和文件 - - - {{ inputMessage.length }}/2000 + + {{ userInput.length }}/2000
@@ -354,88 +212,46 @@ \ No newline at end of file + \ No newline at end of file diff --git a/unilife-server/src/main/java/com/unilife/controller/AiController.java b/unilife-server/src/main/java/com/unilife/controller/AiController.java index 09a54ef..8c53bf7 100644 --- a/unilife-server/src/main/java/com/unilife/controller/AiController.java +++ b/unilife-server/src/main/java/com/unilife/controller/AiController.java @@ -27,9 +27,16 @@ public class AiController { private final AiService aiService; @Operation(summary = "发送消息给AI") - @PostMapping(value = "/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE) - public Flux sendMessage(@RequestBody AiSendMessageDTO sendMessageDTO) { - log.info("发送消息给AI: {}", sendMessageDTO.getMessage()); + @RequestMapping(value = "/chat", produces = "text/html;charset=UTF-8") + public Flux sendMessage( + @RequestParam("prompt") String prompt, + @RequestParam(value = "sessionId", required = false) String sessionId) { + log.info("发送消息给AI: {}", prompt); + + AiSendMessageDTO sendMessageDTO = new AiSendMessageDTO(); + sendMessageDTO.setMessage(prompt); + sendMessageDTO.setSessionId(sessionId); + return aiService.sendMessage(sendMessageDTO); } diff --git a/问题修复和stagewise集成说明.md b/问题修复和stagewise集成说明.md deleted file mode 100644 index 97e5e4d..0000000 --- a/问题修复和stagewise集成说明.md +++ /dev/null @@ -1,182 +0,0 @@ -# 问题修复和Stagewise集成说明 - -## 🔧 已修复的问题 - -### 1. Schedule实体类reminder字段类型错误 -**问题描述**: -``` -Value '1440' is outside of valid range for type java.lang.Byte -``` - -**根本原因**: -- 数据库中schedules表的reminder字段已修改为INT类型 -- 但Java实体类中的reminder字段仍然是Byte类型(范围-128到127) -- 测试数据中有1440、720等超出Byte范围的值 - -**解决方案**: -修改了以下文件中的reminder字段类型从Byte改为Integer: -- `unilife-server/src/main/java/com/unilife/model/entity/Schedule.java` -- `unilife-server/src/main/java/com/unilife/model/vo/ScheduleVO.java` -- `unilife-server/src/main/java/com/unilife/model/dto/CreateScheduleDTO.java` - -### 2. 评论显示问题分析 -**问题描述**: -前端显示评论数量9条,但实际只显示2条评论 - -**初步分析**: -- 数据库中确实存在评论数据 -- 一级评论和回复查询逻辑正常 -- 可能是前端渲染或API调用的问题 - -**需要进一步调试**: -- 检查浏览器网络请求 -- 查看前端控制台错误 -- 验证API返回数据 - -### 3. 点赞按钮样式优化 -**问题描述**: -已点赞状态的蓝色背景过于突兀,与整体设计不协调 - -**解决方案**: -- 替换突兀的蓝色为柔和的紫色主题 -- 使用半透明背景代替实色背景 -- 添加悬停动效和平滑过渡 -- 保持状态清晰可辨的同时更加优雅 - -### 4. 发布帖子功能优化 -**问题描述**: -原发布帖子界面简陋,仅支持纯文本,用户体验不佳 - -**优化内容**: -- ✅ **集成Markdown编辑器**:使用`md-editor-v3`支持富文本编辑 -- ✅ **改进界面设计**:现代化的对话框布局和样式 -- ✅ **增强表单验证**:完整的客户端验证和错误提示 -- ✅ **优化用户体验**:字数限制、清空功能、响应式设计 -- ✅ **Markdown语法提示**:帮助用户了解支持的语法 - -**技术实现**: -```bash -npm install md-editor-v3 --save -``` - -**新增功能**: -- 实时预览Markdown内容 -- 工具栏支持各种格式化操作 -- 支持代码高亮、表格、链接等 -- 字数统计和限制(标题最多100字符) -- 表单验证和错误处理 -- 响应式设计适配移动端 - -### 5. 帖子详情页面优化 -**问题描述**: -帖子详情页面UI不够美观,且不支持Markdown内容渲染 - -**优化内容**: -- ✅ **支持Markdown渲染**:使用`MdPreview`组件渲染Markdown内容 -- ✅ **改善UI设计**:优化内容展示区域的样式和布局 -- ✅ **增强可读性**:改进字体、间距、颜色等视觉元素 -- ✅ **代码高亮**:支持代码块的语法高亮 -- ✅ **表格样式**:美化表格显示效果 -- ✅ **图片展示**:优化图片显示和圆角效果 - -**技术实现**: -- 复用已安装的`md-editor-v3`库的预览组件 -- 自定义CSS样式覆盖默认主题 -- 使用紫色主题保持设计一致性 -- 优化各种Markdown元素的显示效果 - -**视觉改进**: -- 独立的白色背景内容区域 -- 更好的标题层次和分割线 -- 紫色主题的引用块和链接 -- 优雅的代码块样式 -- 现代化的表格设计 - -## 🚀 Stagewise开发工具集成 - -### 安装的包 -```bash -npm install @stagewise/toolbar-vue --save-dev -``` - -### 集成位置 -修改了 `unilife-frontend/src/App.vue` 文件: - -```vue - - - -``` - -### 特性 -1. ✅ **仅在开发模式运行**:使用 `import.meta.env.DEV` 检查 -2. ✅ **动态导入**:避免在生产环境中包含stagewise代码 -3. ✅ **错误处理**:如果包未安装或导入失败,会优雅降级 -4. ✅ **TypeScript支持**:正确的类型定义避免编译错误 -5. ✅ **Vue 3兼容**:使用组合式API和动态组件 - -### 使用方法 -1. 启动开发服务器:`npm run dev` -2. 打开浏览器访问应用 -3. stagewise工具栏将在开发模式下自动显示 -4. 可以选择页面元素并添加AI编辑注释 - -## 📝 后续步骤 - -### 立即处理 -1. **重启后端服务**以应用Schedule实体类修改 -2. **测试日程功能**确保reminder字段正常工作 -3. **调试评论显示问题** - -### 评论问题调试步骤 -1. 在浏览器中访问帖子详情页 -2. 打开开发者工具检查网络请求 -3. 查看 `/comments/post/{postId}` API的返回数据 -4. 检查前端控制台是否有JavaScript错误 -5. 验证CommentVO的数据映射是否正确 - -### Stagewise工具测试 -1. 确认工具栏在开发模式下可见 -2. 测试选择页面元素功能 -3. 验证不会在生产构建中包含 - -## 🎯 预期结果 - -1. **Schedule功能**:日程提醒时间可以正常设置为大于127分钟的值 -2. **评论功能**:所有评论应该正确显示,包括回复 -3. **Stagewise工具**:在开发模式下提供AI驱动的页面编辑能力 - ---- - -*最后更新:2025年5月29日* \ No newline at end of file