From 2e243c7671cc109a370ae78cb46f1c2f91d5e4d0 Mon Sep 17 00:00:00 2001 From: huazhongmin Date: Sun, 26 Oct 2025 16:59:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dataSources.xml | 8 +- MVP功能需求文档.md | 267 --------------- backend-single/pom.xml | 4 +- .../com/emotion/EmotionSimpleApplication.java | 2 +- .../java/com/emotion/config/WebMvcConfig.java | 19 +- .../emotion/controller/HealthController.java | 4 +- .../dto/request/ResetPasswordRequest.java | 42 +++ .../dto/response/ResetPasswordResponse.java | 21 ++ .../com/emotion/mapper/MessageMapper.java | 68 +--- .../service/impl/MessageServiceImpl.java | 46 ++- .../auth/request/ResetPasswordRequest.java | 49 +++ .../auth/response/ResetPasswordResponse.java | 26 ++ web/src/services/chat.ts | 39 +-- web/src/services/message.ts | 27 +- web/src/services/stomp-websocket.ts | 6 +- web/src/stores/chat.ts | 22 +- web/src/views/Chat/index.vue | 170 ++-------- web/src/views/ForgotPassword/index.vue | 71 ++++ 开心APP网页代码v1.1/wnD97OS/chat-history.html | 43 --- 开心APP网页代码v1.1/wnD97OS/chat-history.js | 64 ---- 开心APP网页代码v1.1/wnD97OS/chat.css | 102 ------ 开心APP网页代码v1.1/wnD97OS/chat.html | 89 ----- 开心APP网页代码v1.1/wnD97OS/chat.js | 78 ----- 开心APP网页代码v1.1/wnD97OS/chat_manager.js | 80 ----- 开心APP网页代码v1.1/wnD97OS/data.js | 40 --- 开心APP网页代码v1.1/wnD97OS/diary.html | 63 ---- 开心APP网页代码v1.1/wnD97OS/diary.js | 180 ---------- 开心APP网页代码v1.1/wnD97OS/index.html | 169 ---------- 开心APP网页代码v1.1/wnD97OS/js/app_nav.js | 37 -- .../wnD97OS/js/chat_manager.js | 266 --------------- 开心APP网页代码v1.1/wnD97OS/js/shared.js | 58 ---- .../wnD97OS/life_milestones.html | 51 --- .../wnD97OS/life_milestones.js | 5 - .../wnD97OS/life_trajectory.html | 166 --------- .../wnD97OS/life_trajectory.js | 301 ----------------- 开心APP网页代码v1.1/wnD97OS/messages.html | 53 --- 开心APP网页代码v1.1/wnD97OS/messages.js | 77 ----- .../wnD97OS/personal_dashboard.html | 136 -------- .../wnD97OS/personal_dashboard.js | 309 ----------------- 开心APP网页代码v1.1/wnD97OS/script.js | 78 ----- 开心APP网页代码v1.1/wnD97OS/settings.html | 153 --------- 开心APP网页代码v1.1/wnD97OS/settings.js | 7 - 开心APP网页代码v1.1/wnD97OS/style.css | 163 --------- .../wnD97OS/topic_tracker.html | 125 ------- 开心APP网页代码v1.1/wnD97OS/topic_tracker.js | 319 ------------------ 45 files changed, 346 insertions(+), 3757 deletions(-) delete mode 100644 MVP功能需求文档.md create mode 100644 backend-single/src/main/java/com/emotion/dto/request/ResetPasswordRequest.java create mode 100644 backend-single/src/main/java/com/emotion/dto/response/ResetPasswordResponse.java create mode 100644 backend/auth/server/src/main/java/com/emotionmuseum/auth/request/ResetPasswordRequest.java create mode 100644 backend/auth/server/src/main/java/com/emotionmuseum/auth/response/ResetPasswordResponse.java create mode 100644 web/src/views/ForgotPassword/index.vue delete mode 100644 开心APP网页代码v1.1/wnD97OS/chat-history.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/chat-history.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/chat.css delete mode 100644 开心APP网页代码v1.1/wnD97OS/chat.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/chat.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/chat_manager.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/data.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/diary.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/diary.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/index.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/js/app_nav.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/js/chat_manager.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/js/shared.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/life_milestones.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/life_milestones.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/life_trajectory.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/life_trajectory.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/messages.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/messages.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/personal_dashboard.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/personal_dashboard.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/script.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/settings.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/settings.js delete mode 100644 开心APP网页代码v1.1/wnD97OS/style.css delete mode 100644 开心APP网页代码v1.1/wnD97OS/topic_tracker.html delete mode 100644 开心APP网页代码v1.1/wnD97OS/topic_tracker.js diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 6a31e7e..8745406 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,15 +1,13 @@ - + mysql.8 true com.mysql.cj.jdbc.Driver jdbc:mysql://localhost:3306 - - $ProjectFileDir$ @@ -19,12 +17,10 @@ true $PROJECT_DIR$/backend/emotion-websocket/src/main/resources/application-prod.yml com.mysql.cj.jdbc.Driver - jdbc:mysql://47.111.10.27:3306/?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT + jdbc:mysql://101.200.208.45:3306/?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT - - $ProjectFileDir$ diff --git a/MVP功能需求文档.md b/MVP功能需求文档.md deleted file mode 100644 index a5a5cfb..0000000 --- a/MVP功能需求文档.md +++ /dev/null @@ -1,267 +0,0 @@ -# MVP功能需求文档 - -**生成时间**: 2025-07-03 11:31:17 - -**源文件**: MVP功能.pdf - -**总页数**: 7 - -MVP功能 - -你已经为MVP构想出一个⾮常完整、用户体验导向的原型了!下面我将按照你提出的四个页面结构, - -把功能要点进行**模块化梳理**,方便你用来指引设计开发,也方便你在Cursor⾥逐步实现每个部分 - -的前端/后端逻辑。 - -### ✅整体结构:底部导航栏4个主页面 - -导航项:聊天|开心||探索|我的 - -## 📘页面一:记录(情绪主页+聊天入⼝) - -### ✅核心功能: - -- 左上⻆入⼝:聊天内容回顾页(情绪对话记录索引页)。 -- 右上⻆设置(主题切换) -- ⽇历组件(单行,可展开):支持情绪记录、历史回顾标记。 -- 中心展示AI疗愈师3DIP(静态MVP阶段可用插图代替) -- 底部对话框输入: -- 支持语⾳、文字、图⽚输入 -- 点发送后进入全屏对话模式(AI聊天),AIIP缩为头像 - -**技术建议:** - -| MVP功能 你已经为MVP构想出一个⾮常完整、用户体验导向的原型了!下面我将按照你提出的四个页面结构, 把功能要点进行**模块化梳理**,方便你用来指引设计开发,也方便你在Cursor⾥逐步实现每个部分 的前端/后端逻辑。 | -| --- | -| ✅整体结构:底部导航栏4个主页面 导航项:聊天|开心||探索|我的 | -| 📘页面一:记录(情绪主页+聊天入⼝) ✅核心功能: • 左上⻆入⼝:聊天内容回顾页(情绪对话记录索引页)。 • 右上⻆设置(主题切换) • ⽇历组件(单行,可展开):支持情绪记录、历史回顾标记。 • 中心展示AI疗愈师3DIP(静态MVP阶段可用插图代替) • 底部对话框输入: • 支持语⾳、文字、图⽚输入 • 点发送后进入全屏对话模式(AI聊天),AIIP缩为头像 技术建议: | - - ---- -- 可用 做语⾳转文本MVP -react-speech-recognition - -- 全屏聊天页面:使用GPT-4或现成情绪对话接⼝+表情动画反馈 -①左上⻆ - -②右上⻆ - -(⽇记本图 ③单行⽇历 折叠/展开 选择⽇期 - -(设置图标) - -标) - -可滚动页面 ⽇历数字变 弹出可选择的 主题设置 - -AI对话记录 为心情图标 选择心情图标 心情图标 ⾳乐设置 - -⾳效设置 - -选择记录 选择主题 - -调整⾳量 - -聊天记录 - -调整⾳量 - -随机打招呼文案 - -跳转聊天详情 界面主题变化 - -⾳乐⾳量变化 - -⾳效⾳量变化 - -AI总结 - -查看课题 表情动作变化 - -点击产生交互 ⽓泡文案变化 - -(跳转课题系统) - -语⾳跟随⽓泡 - -跟随手指移动 - -+ 图⽚ - -语⾳ - -输入文字... - -⻨克⻛ - -记录 聊天 - -AI 探索 发现 我的 - -分析心情 反馈⾄③,⽇历数字变为心情图标 - -AI智能分析 - -记录 内容收录⾄① - -(A) - -反馈⾄①,未读提示 - -分析内容 - -反馈⾄课题系统(若有)) - -语⾳ 聊天已收录 - -AI智能分析 顶部 反馈⾄页面2 - -聊天 - -(A) 弹窗 未读提示 - -解锁新课题 - -文字 - -课题进度有更新 - - ---- -语⾳聊天 文字聊天 - -切换全屏 切换全屏 - -收起 X - -切换文字聊天 收起 - -可滚动页面 - -+ 图⽚ - -语⾳ - -挂断 文本输入框 - -⻨克⻛ - -## 📗页面二:治愈(个人成长档案) - -### ✅模块结构: - -A.情绪洞察与成长课题板块 - -- 来自: -1. ⽇常聊天自动总结 -2. 主动探索测试 -3. 命盘(出生数据生成) -B.课题标签系统 - -- 每个课题一个标签,包含: -- 当前等级/进度 -| 语⾳聊天 文字聊天 切换全屏 切换全屏 收起 X 切换文字聊天 收起 可滚动页面 + 图⽚ 语⾳ 挂断 文本输入框 ⻨克⻛ | -| --- | -| 📗页面二:治愈(个人成长档案) ✅模块结构: A.情绪洞察与成长课题板块 • 来自: 1. ⽇常聊天自动总结 2. 主动探索测试 3. 命盘(出生数据生成) B.课题标签系统 • 每个课题一个标签,包含: • 当前等级/进度 | - - ---- -- 可点入:AI对话、知识文章、行动建议 -- 完成一次互动:课题升级/积分/⽪肤/称号掉落 -C.用户画像五维图(或视觉更优形式) - -- 根据用户的成长路径动态生成 -- 示例:自我感知|情绪韧性|行动力|共情力|生活热度 - -**技术建议:** - -- 使用radarchart(五边图)或Tag系统管理成长维度 -- Tag模块可作为数据库中的 表维护 -UserGrowthTopic - -| • 可点入:AI对话、知识文章、行动建议 • 完成一次互动:课题升级/积分/⽪肤/称号掉落 C.用户画像五维图(或视觉更优形式) • 根据用户的成长路径动态生成 • 示例:自我感知|情绪韧性|行动力|共情力|生活热度 技术建议: • 使用radarchart(五边图)或Tag系统管理成长维度 • Tag模块可作为数据库中的 表维护 UserGrowthTopic | -| --- | -| | -| | -| | - - -### 页面图片信息 - -- 图片 1: X163 - - ---- -## 📙页面三:探索(情绪地图+笔记分享) - -### ✅地图展示+推荐逻辑 - -- 地图上两类地标颜⾊: -1. 用户自⼰收藏/计划出行地 -2. AI根据情绪推荐场景(匹配情绪+地点) -- 地标弹窗内容: -- 场景图⽚ -- 评论(系统/用户) -- 社区笔记 -- 收藏/加入计划 -### ✅分享视图切换 - -- 地图左上⻆切换为**社区模式**(美好⻆落分享) -- 用户上传图⽚+文字标记城市疗愈⻆落 -- 可点赞、评论、转发 - ---- - -**技术建议:** - -- 使用Leaflet/Mapbox/⾼德地图开放平台 -- 笔记内容结构可参考小红书feed设计(图文瀑布流) -## 📕页面四:我的 - -### ✅用户信息模块 - -- 用户基础资料(注册天数) -- 会员中心(MVP可为静态页) -- 邀请好友模块(可做邀请返积分) -### ✅成就总结 - -- 本周: -| | -| --- | -| | -| 技术建议: • 使用Leaflet/Mapbox/⾼德地图开放平台 • 笔记内容结构可参考小红书feed设计(图文瀑布流) | -| 📕页面四:我的 ✅用户信息模块 • 用户基础资料(注册天数) • 会员中心(MVP可为静态页) • 邀请好友模块(可做邀请返积分) ✅成就总结 • 本周: | - - -### 页面图片信息 - -- 图片 1: X176 - - ---- -- 心情指数 -- 聊天次数 -- 成长轨迹 -- 打卡记录: -- 疗愈地标打卡数 -- 分享数 -- 他人访问你分享的次数 -#### 🧠额外建议(冷启动优先级) - - -**MVP推荐优先搭建模块顺序:** - -1. 记录页(对话+⽇历) -2. 治愈页(课题总结+标签互动) -3. 探索页(地图基础+笔记系统) -4. 我的页(基础资料+成就统计) -如你愿意,我可以现在就帮你用Cursor建一个最小结构的React项目框架,含底部导航和主页面布 - -局,并生成页面组件文件名结构、推荐依赖包清单和初步布局代码。你要不要我开始搭建这部分? - -| • 心情指数 • 聊天次数 • 成长轨迹 • 打卡记录: • 疗愈地标打卡数 • 分享数 • 他人访问你分享的次数 | -| --- | -| 🧠额外建议(冷启动优先级) MVP推荐优先搭建模块顺序: 1. 记录页(对话+⽇历) 2. 治愈页(课题总结+标签互动) 3. 探索页(地图基础+笔记系统) 4. 我的页(基础资料+成就统计) | -| 如你愿意,我可以现在就帮你用Cursor建一个最小结构的React项目框架,含底部导航和主页面布 局,并生成页面组件文件名结构、推荐依赖包清单和初步布局代码。你要不要我开始搭建这部分? | - diff --git a/backend-single/pom.xml b/backend-single/pom.xml index f7d8fc4..b5421c6 100644 --- a/backend-single/pom.xml +++ b/backend-single/pom.xml @@ -5,11 +5,11 @@ 4.0.0 com.emotion - emotion-single + backend-single 1.0.0 jar - emotion-single + backend-single 情感博物馆单体服务 diff --git a/backend-single/src/main/java/com/emotion/EmotionSimpleApplication.java b/backend-single/src/main/java/com/emotion/EmotionSimpleApplication.java index fba4c1c..a58d50c 100644 --- a/backend-single/src/main/java/com/emotion/EmotionSimpleApplication.java +++ b/backend-single/src/main/java/com/emotion/EmotionSimpleApplication.java @@ -23,7 +23,7 @@ public class EmotionSimpleApplication { System.out.println("========================================"); System.out.println("🎉 情感博物馆服务启动成功!"); System.out.println("📋 服务信息:"); - System.out.println(" - 服务名称: emotion-single"); + System.out.println(" - 服务名称: backend-single"); System.out.println(" - 服务端口: 19089"); System.out.println(" - 环境配置: " + System.getProperty("spring.profiles.active")); System.out.println(" - API文档: http://localhost:19089/api/health"); diff --git a/backend-single/src/main/java/com/emotion/config/WebMvcConfig.java b/backend-single/src/main/java/com/emotion/config/WebMvcConfig.java index 7b6f3b7..8b1a106 100644 --- a/backend-single/src/main/java/com/emotion/config/WebMvcConfig.java +++ b/backend-single/src/main/java/com/emotion/config/WebMvcConfig.java @@ -20,17 +20,18 @@ public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { + // 注意:已配置 server.servlet.context-path=/api,拦截器路径匹配不需要再带 /api 前缀 registry.addInterceptor(jwtAuthInterceptor) - .addPathPatterns("/api/**") // 拦截所有API请求 + .addPathPatterns("/**") // 拦截应用内的所有请求(已去掉 /api 前缀) .excludePathPatterns( - "/api/auth/login", // 登录接口 - "/api/auth/register", // 注册接口 - "/api/auth/captcha", // 图形验证码接口 - "/api/auth/sms-code", // 短信验证码接口(免登录) - "/api/auth/refresh-token", // 刷新token接口 - "/api/auth/resetPassword", // 重置密码接口(免登录) - "/api/health", // 健康检查接口 - "/api/ws/**", // WebSocket接口 + "/auth/login", // 登录接口 + "/auth/register", // 注册接口 + "/auth/captcha", // 图形验证码接口 + "/auth/sms-code", // 短信验证码接口(免登录) + "/auth/refresh-token", // 刷新token接口 + "/auth/resetPassword", // 重置密码接口(免登录) + "/health", // 健康检查接口 + "/ws/**", // WebSocket接口 "/swagger-ui/**", // Swagger UI "/v3/api-docs/**", // API文档 "/actuator/**" // 监控端点 diff --git a/backend-single/src/main/java/com/emotion/controller/HealthController.java b/backend-single/src/main/java/com/emotion/controller/HealthController.java index ca682b7..9c4bb7c 100644 --- a/backend-single/src/main/java/com/emotion/controller/HealthController.java +++ b/backend-single/src/main/java/com/emotion/controller/HealthController.java @@ -28,7 +28,7 @@ public class HealthController { log.info("健康检查请求"); Map response = new HashMap<>(); - response.put("service", "emotion-single"); + response.put("service", "backend-single"); response.put("message", "情感博物馆单体服务运行正常"); response.put("version", "1.0.0"); response.put("status", "UP"); @@ -45,7 +45,7 @@ public class HealthController { log.info("服务信息请求"); Map response = new HashMap<>(); - response.put("service", "emotion-single"); + response.put("service", "backend-single"); response.put("description", "情感博物馆单体服务"); response.put("version", "1.0.0"); response.put("author", "emotion-museum"); diff --git a/backend-single/src/main/java/com/emotion/dto/request/ResetPasswordRequest.java b/backend-single/src/main/java/com/emotion/dto/request/ResetPasswordRequest.java new file mode 100644 index 0000000..0aa6ebd --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/request/ResetPasswordRequest.java @@ -0,0 +1,42 @@ +package com.emotion.dto.request; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +/** + * 重置密码请求对象 + * 用于通过手机号+验证码重置用户登录密码(验证码本期固定为 123456) + * + * 作者: emotion-museum + * 日期: 2025-10-26 + * 版本: 1.0 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ResetPasswordRequest extends BaseRequest { + + /** + * 手机号 + */ + @NotBlank(message = "手机号不能为空") + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") + private String phone; + + /** + * 新密码(6-20位) + */ + @NotBlank(message = "新密码不能为空") + @Size(min = 6, max = 20, message = "新密码长度必须在6-20个字符之间") + private String newPassword; + + /** + * 验证码(本期固定为 123456) + */ + @NotBlank(message = "验证码不能为空") + private String captcha; +} + diff --git a/backend-single/src/main/java/com/emotion/dto/response/ResetPasswordResponse.java b/backend-single/src/main/java/com/emotion/dto/response/ResetPasswordResponse.java new file mode 100644 index 0000000..6817cca --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/response/ResetPasswordResponse.java @@ -0,0 +1,21 @@ +package com.emotion.dto.response; + +import lombok.Data; + +/** + * 重置密码响应对象 + * + * 作者: emotion-museum + * 日期: 2025-10-26 + * 版本: 1.0 + */ +@Data +public class ResetPasswordResponse { + + /** 是否成功 */ + private boolean success; + + /** 提示信息 */ + private String message; +} + diff --git a/backend-single/src/main/java/com/emotion/mapper/MessageMapper.java b/backend-single/src/main/java/com/emotion/mapper/MessageMapper.java index 8229a2c..5da37a2 100644 --- a/backend-single/src/main/java/com/emotion/mapper/MessageMapper.java +++ b/backend-single/src/main/java/com/emotion/mapper/MessageMapper.java @@ -3,14 +3,10 @@ package com.emotion.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.emotion.entity.Message; import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; - -import java.time.LocalDateTime; -import java.util.List; /** * 消息Mapper接口 + * 说明:统一使用 MyBatis-Plus 的 BaseMapper 与 Service 层的 LambdaQueryWrapper 构建条件, * * @author emotion-museum * @date 2025-07-23 @@ -18,66 +14,4 @@ import java.util.List; @Mapper public interface MessageMapper extends BaseMapper { - /** - * 根据用户ID和时间范围查询消息 - * 通过conversation表关联查询 - */ - @Select("SELECT m.* FROM message m " + - "INNER JOIN conversation c ON m.conversation_id = c.id " + - "WHERE c.user_id = #{userId} " + - "AND m.create_time BETWEEN #{startTime} AND #{endTime} " + - "AND m.is_deleted = 0 " + - "ORDER BY m.create_time ASC") - List getByUserIdAndTimeRange(@Param("userId") String userId, - @Param("startTime") LocalDateTime startTime, - @Param("endTime") LocalDateTime endTime); - - /** - * 根据用户ID分页查询消息 - * 通过conversation表关联查询 - */ - @Select("SELECT m.* FROM message m " + - "INNER JOIN conversation c ON m.conversation_id = c.id " + - "WHERE c.user_id = #{userId} " + - "AND m.is_deleted = 0 " + - "ORDER BY m.create_time DESC " + - "LIMIT #{offset}, #{size}") - List getByUserIdWithPageList(@Param("userId") String userId, - @Param("offset") Integer offset, - @Param("size") Integer size); - - /** - * 统计用户消息总数 - */ - @Select("SELECT COUNT(*) FROM message m " + - "INNER JOIN conversation c ON m.conversation_id = c.id " + - "WHERE c.user_id = #{userId} " + - "AND m.is_deleted = 0") - Long countByUserId(@Param("userId") String userId); - - /** - * 根据用户ID和关键词搜索消息 - */ - @Select("SELECT m.* FROM message m " + - "INNER JOIN conversation c ON m.conversation_id = c.id " + - "WHERE c.user_id = #{userId} " + - "AND m.content LIKE CONCAT('%', #{keyword}, '%') " + - "AND m.is_deleted = 0 " + - "ORDER BY m.create_time DESC " + - "LIMIT #{limit}") - List searchByUserIdAndKeyword(@Param("userId") String userId, - @Param("keyword") String keyword, - @Param("limit") Integer limit); - - /** - * 根据用户ID获取最近的消息 - */ - @Select("SELECT m.* FROM message m " + - "INNER JOIN conversation c ON m.conversation_id = c.id " + - "WHERE c.user_id = #{userId} " + - "AND m.is_deleted = 0 " + - "ORDER BY m.create_time DESC " + - "LIMIT #{limit}") - List getRecentByUserId(@Param("userId") String userId, - @Param("limit") Integer limit); } diff --git a/backend-single/src/main/java/com/emotion/service/impl/MessageServiceImpl.java b/backend-single/src/main/java/com/emotion/service/impl/MessageServiceImpl.java index 8e45715..641fe75 100644 --- a/backend-single/src/main/java/com/emotion/service/impl/MessageServiceImpl.java +++ b/backend-single/src/main/java/com/emotion/service/impl/MessageServiceImpl.java @@ -195,35 +195,49 @@ public class MessageServiceImpl extends ServiceImpl impl @Override public List getByUserIdAndTimeRange(String userId, LocalDateTime startTime, LocalDateTime endTime) { - // 由于Message表没有直接的userId字段,需要通过conversation表关联查询 - // 这里先通过conversationService获取用户的所有对话ID,然后查询这些对话的消息 - return this.baseMapper.getByUserIdAndTimeRange(userId, startTime, endTime); + // 使用 MyBatis-Plus 条件构造器,直接根据消息表的 user_id 字段查询 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Message::getUserId, userId) + .between(Message::getCreateTime, startTime, endTime) + .eq(Message::getIsDeleted, 0) + .orderByAsc(Message::getCreateTime); + return this.list(wrapper); } @Override public IPage getByUserIdWithPage(String userId, Integer current, Integer size) { - // 手动实现分页 - Integer offset = (current - 1) * size; - List records = this.baseMapper.getByUserIdWithPageList(userId, offset, size); - Long total = this.baseMapper.countByUserId(userId); - + // 使用 MyBatis-Plus 分页 + 条件构造器 Page page = new Page<>(current, size); - page.setRecords(records); - page.setTotal(total); - - return page; + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Message::getUserId, userId) + .eq(Message::getIsDeleted, 0) + .orderByDesc(Message::getCreateTime); + return this.page(page, wrapper); } @Override public List searchByUserIdAndKeyword(String userId, String keyword, Integer limit) { - // 通过conversation表关联查询用户的消息,根据关键词搜索 - return this.baseMapper.searchByUserIdAndKeyword(userId, keyword, limit); + // 使用 MyBatis-Plus 分页 + 条件构造器,避免硬编码 SQL + Page page = new Page<>(1, limit); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Message::getUserId, userId) + .eq(Message::getIsDeleted, 0) + .like(StringUtils.hasText(keyword), Message::getContent, keyword) + .orderByDesc(Message::getCreateTime); + IPage result = this.page(page, wrapper); + return result.getRecords(); } @Override public List getRecentByUserId(String userId, Integer limit) { - // 获取用户最近的消息,按时间倒序 - return this.baseMapper.getRecentByUserId(userId, limit); + // 使用 MyBatis-Plus 分页查询最近消息 + Page page = new Page<>(1, limit); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(Message::getUserId, userId) + .eq(Message::getIsDeleted, 0) + .orderByDesc(Message::getCreateTime); + IPage result = this.page(page, wrapper); + return result.getRecords(); } @Override diff --git a/backend/auth/server/src/main/java/com/emotionmuseum/auth/request/ResetPasswordRequest.java b/backend/auth/server/src/main/java/com/emotionmuseum/auth/request/ResetPasswordRequest.java new file mode 100644 index 0000000..0c09ee0 --- /dev/null +++ b/backend/auth/server/src/main/java/com/emotionmuseum/auth/request/ResetPasswordRequest.java @@ -0,0 +1,49 @@ +package com.emotionmuseum.auth.request; + +import com.emotionmuseum.common.request.BaseRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 重置密码请求 + * + *

用于未登录情况下通过手机号与验证码(本期固定为 123456)设置新密码。

+ * + * @author emotion-museum + * @since 2025-10-26 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "重置密码请求") +public class ResetPasswordRequest extends BaseRequest { + + private static final long serialVersionUID = 1L; + + /** + * 手机号 + */ + @Schema(description = "手机号", example = "13800138000") + @NotBlank(message = "手机号不能为空") + @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") + private String phone; + + /** + * 新密码 + */ + @Schema(description = "新密码", example = "NewPass_123") + @NotBlank(message = "新密码不能为空") + @Size(min = 6, max = 20, message = "密码长度必须在6-20位之间") + private String newPassword; + + /** + * 验证码(本期为固定值 123456) + */ + @Schema(description = "验证码(固定为123456)", example = "123456") + @NotBlank(message = "验证码不能为空") + private String captcha; +} + diff --git a/backend/auth/server/src/main/java/com/emotionmuseum/auth/response/ResetPasswordResponse.java b/backend/auth/server/src/main/java/com/emotionmuseum/auth/response/ResetPasswordResponse.java new file mode 100644 index 0000000..19c7686 --- /dev/null +++ b/backend/auth/server/src/main/java/com/emotionmuseum/auth/response/ResetPasswordResponse.java @@ -0,0 +1,26 @@ +package com.emotionmuseum.auth.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 重置密码响应 + * + *

不返回敏感信息,仅提供结果标识与提示。

+ * + * @author emotion-museum + * @since 2025-10-26 + */ +@Data +@Schema(description = "重置密码响应") +public class ResetPasswordResponse { + + /** 是否重置成功 */ + @Schema(description = "是否重置成功", example = "true") + private boolean success; + + /** 提示信息 */ + @Schema(description = "提示信息", example = "重置密码成功") + private String message; +} + diff --git a/web/src/services/chat.ts b/web/src/services/chat.ts index 2ac1146..147745c 100644 --- a/web/src/services/chat.ts +++ b/web/src/services/chat.ts @@ -52,7 +52,8 @@ export class ChatApiService { async createSession(userId: string, title?: string): Promise { try { console.log('📝 创建会话API调用:', { userId, title }) - const response = await http.post('/conversation', { + // backend-single: POST /conversation/create + const response = await http.post('/conversation/create', { userId, title: title || `对话${Date.now()}` }) @@ -88,25 +89,23 @@ export class ChatApiService { async getUserSessions(userId: string): Promise { try { console.log('📂 获取用户会话API调用:', { userId }) - const response = await http.get(`/conversation/user/${userId}`) + // backend-single: GET /conversation/page?userId=xxx + const response = await http.get('/conversation/page', { params: { userId, current: 1, size: 100 } }) console.log('📂 获取用户会话API响应:', response) - // 处理HTTP响应的data字段 - const data = (response as any).data || response + // 处理HTTP响应的data字段(PageResult) + const pageData = (response as any).data || response + const records = pageData.records || [] - // 后端返回ConversationResponse数组,需要转换为ChatSession格式 - if (Array.isArray(data)) { - return data.map((conv: any) => ({ - id: conv.id, - title: conv.title, - userId: conv.userId || conv.user_id, // 兼容不同的字段名 - createTime: conv.createTime || conv.create_time, - updateTime: conv.updateTime || conv.update_time, - messageCount: conv.messageCount || conv.message_count || 0 - })) - } - - return [] + // 转换为ChatSession数组 + return records.map((conv: any) => ({ + id: conv.id, + title: conv.title, + userId: conv.userId || conv.user_id, + createTime: conv.createTime || conv.create_time, + updateTime: conv.updateTime || conv.update_time, + messageCount: conv.messageCount || conv.message_count || 0 + })) } catch (error) { console.error('❌ 获取用户会话失败:', error) return [] @@ -133,7 +132,8 @@ export class ChatApiService { async deleteSession(sessionId: string): Promise { try { console.log('🗑️ 删除会话API调用:', { sessionId }) - await http.delete(`/conversation/${sessionId}`) + // backend-single: DELETE /conversation/delete?id=xxx + await http.delete('/conversation/delete', { params: { id: sessionId } }) console.log('✅ 删除会话成功') } catch (error) { console.error('❌ 删除会话失败:', error) @@ -147,7 +147,8 @@ export class ChatApiService { async updateSessionTitle(sessionId: string, title: string): Promise { try { console.log('✏️ 更新会话标题API调用:', { sessionId, title }) - await http.put(`/conversation/${sessionId}`, { title }) + // backend-single: PUT /conversation/update 传id和title + await http.put('/conversation/update', { id: sessionId, title }) console.log('✅ 更新会话标题成功') } catch (error) { console.error('❌ 更新会话标题失败:', error) diff --git a/web/src/services/message.ts b/web/src/services/message.ts index b2af61c..3861d11 100644 --- a/web/src/services/message.ts +++ b/web/src/services/message.ts @@ -57,7 +57,8 @@ export const messageApi = { // 获取用户消息分页 getUserMessages: async (current: number = 1, size: number = 20) => { console.log('📨 调用getUserMessages API:', { current, size }) - const response = await http.get(`/message/user/page`, { params: { current, size } }) + // backend-single: GET /message/page (后端根据token识别用户) + const response = await http.get(`/message/page`, { params: { current, size } }) console.log('📨 getUserMessages API响应:', response) return response }, @@ -65,23 +66,31 @@ export const messageApi = { // 搜索用户消息 searchUserMessages: async (keyword: string, limit: number = 50) => { console.log('🔍 调用searchUserMessages API:', { keyword, limit }) - const response = await http.post(`/message/user/search`, { keyword, limit }) - console.log('🔍 searchUserMessages API响应:', response) - return response + // backend-single: POST /message/search + const resp = await http.post(`/message/search`, { keyword, limit }) + console.log('🔍 searchUserMessages API响应:', resp) + // 统一返回数组,兼容控制器返回 PageResult 结构 + const data: any = (resp as any).data || resp + const records = data.records || data + return Array.isArray(records) ? records : [] }, - // 获取用户最近的聊天记录 - 修复:使用POST请求匹配后端接口 + // 获取用户最近的聊天记录 - 返回数组,兼容后端 PageResult 结构 getRecentMessages: async (limit: number = 10) => { console.log('📝 调用getRecentMessages API:', { limit }) - const response = await http.post(`/message/user/recent`, { limit }) - console.log('📝 getRecentMessages API响应:', response) - return response + // backend-single: POST /message/recent + const resp = await http.post(`/message/recent`, { limit }) + console.log('📝 getRecentMessages API响应:', resp) + const data: any = (resp as any).data || resp + const records = data.records || data + return Array.isArray(records) ? records : [] }, // 获取消息详情 getMessageById: async (id: string) => { console.log('📄 调用getMessageById API:', { id }) - const response = await http.get(`/message/${id}`) + // backend-single: GET /message/detail?id=xxx + const response = await http.get(`/message/detail`, { params: { id } }) console.log('📄 getMessageById API响应:', response) return response } diff --git a/web/src/services/stomp-websocket.ts b/web/src/services/stomp-websocket.ts index e6b5be1..95504b8 100644 --- a/web/src/services/stomp-websocket.ts +++ b/web/src/services/stomp-websocket.ts @@ -207,9 +207,9 @@ export class StompWebSocketService { console.log('📤 准备发送的聊天请求:', chatRequest) try { - // 发送到后端的/app/chat.send端点 + // 发送到后端的/app/chat/send端点(对应 @MessageMapping("/chat") + @MessageMapping("/send")) this.client.publish({ - destination: '/app/chat.send', + destination: '/app/chat/send', body: JSON.stringify(chatRequest) }) console.log('✅ STOMP聊天消息发送成功:', chatRequest) @@ -324,7 +324,7 @@ export class StompWebSocketService { try { this.client.publish({ - destination: '/app/chat.connect', + destination: '/app/chat/connect', body: JSON.stringify(connectRequest) }) console.log('✅ STOMP连接消息发送成功:', connectRequest) diff --git a/web/src/stores/chat.ts b/web/src/stores/chat.ts index f141560..3e74182 100644 --- a/web/src/stores/chat.ts +++ b/web/src/stores/chat.ts @@ -1,5 +1,5 @@ import { defineStore } from 'pinia' -import { ref, computed, watch } from 'vue' +import { ref, computed, watch, nextTick } from 'vue' import type { ChatMessage, ChatSession } from '@/types' import { stompWebSocketService, type WebSocketMessage, type ConnectionStatus } from '@/services/stomp-websocket' import { useAuthStore } from './auth' @@ -381,31 +381,35 @@ export const useChatStore = defineStore('chat', () => { } // 添加AI回复消息(直接显示完整内容) - const addAiReplyMessages = (content: string) => { + const addAiReplyMessages = async (content: string) => { // 停止输入状态 isTyping.value = false + // 使用 nextTick 确保 DOM 更新的顺序性,避免与定时同步并发 + await nextTick() + // 直接添加完整的AI回复 const aiMessage = addMessage({ content: content.trim(), type: 'ai', - sessionId: currentSession.value?.id + conversationId: currentSession.value?.id }) // 强制触发响应式更新 - console.log('AI消息已添加,当前消息总数:', messages.value.length) - console.log('最新AI消息:', aiMessage) + console.log('✅ AI消息已添加,当前消息总数:', messages.value.length) + console.log('📝 最新AI消息:', aiMessage) + console.log('📊 所有消息:', messages.value) } // WebSocket消息处理 - let handleWebSocketMessage = (wsMessage: WebSocketMessage) => { + let handleWebSocketMessage = async (wsMessage: WebSocketMessage) => { console.log('收到WebSocket消息:', wsMessage.type, wsMessage.senderType) switch (wsMessage.type) { case 'TEXT': if (wsMessage.senderType === 'AI') { // AI回复消息 - 支持分段显示 - addAiReplyMessages(wsMessage.content) + await addAiReplyMessages(wsMessage.content) } break @@ -602,8 +606,8 @@ export const useChatStore = defineStore('chat', () => { onMessage: (callback: (message: any) => void) => { // 简单的消息监听实现 const originalHandler = handleWebSocketMessage - handleWebSocketMessage = (message: any) => { - originalHandler(message) + handleWebSocketMessage = async (message: any) => { + await originalHandler(message) callback(message) } } diff --git a/web/src/views/Chat/index.vue b/web/src/views/Chat/index.vue index d9a0caa..69f4c73 100644 --- a/web/src/views/Chat/index.vue +++ b/web/src/views/Chat/index.vue @@ -67,8 +67,8 @@

加载对话记录中...

- -
+ +
开开
- -
+ +
-