diff --git a/.idea/AugmentWebviewStateStore.xml b/.idea/AugmentWebviewStateStore.xml index c02c66e..07ece10 100644 --- a/.idea/AugmentWebviewStateStore.xml +++ b/.idea/AugmentWebviewStateStore.xml @@ -3,7 +3,7 @@ diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 96add56..184a149 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -10,27 +10,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -40,10 +19,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 3a1f68c..6a31e7e 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,7 +1,7 @@ - + mysql.8 true com.mysql.cj.jdbc.Driver diff --git a/.idea/encodings.xml b/.idea/encodings.xml index c8f350f..a75da8d 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -3,6 +3,26 @@ + + + + + + + + + + + + + + + + + + + + @@ -25,8 +45,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index ef4b512..801de23 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -9,16 +9,16 @@ - - + + diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 4b8cfc5..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "java.compile.nullAnalysis.mode": "automatic", - "java.debug.settings.onBuildFailureProceed": true, - "terminal.integrated.profiles.windows": { - "PowerShell": { - "args": ["-NoExit", "-Command", "chcp.com 65001"] - }, - "Command Prompt": { - "args": ["/K", "chcp 65001"] - } - }, - "kiroAgent.configureMCP": "Disabled" -} \ No newline at end of file diff --git a/AUTH_LOGIN_OPTIMIZATION.md b/AUTH_LOGIN_OPTIMIZATION.md deleted file mode 100644 index 5b65282..0000000 --- a/AUTH_LOGIN_OPTIMIZATION.md +++ /dev/null @@ -1,396 +0,0 @@ -# 登录接口优化总结 - -## 🎯 优化目标 - -简化用户登录流程,使用手机号+短信验证码登录,若用户不存在则自动注册。 - -## ✅ 完成的优化 - -### 1. 简化LoginRequest - -**文件**: `backend-single/src/main/java/com/emotion/dto/request/LoginRequest.java` - -**修改前**:需要账号、密码、图形验证码、验证码key等字段 - -**修改后**:仅需2个字段 -```java -@Data -@EqualsAndHashCode(callSuper = true) -public class LoginRequest extends BaseRequest { - - /** - * 手机号 - */ - @NotBlank(message = "手机号不能为空") - @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") - private String phone; - - /** - * 短信验证码 - */ - @NotBlank(message = "验证码不能为空") - @Size(min = 6, max = 6, message = "验证码必须为6位") - private String smsCode; -} -``` - -### 2. 优化登录逻辑 - -**文件**: `backend-single/src/main/java/com/emotion/service/impl/AuthServiceImpl.java` - -#### 2.1 新的登录流程 -```java -@Override -public AuthResponse login(LoginRequest request) { - // 1. 验证短信验证码 - if (!validateSmsCode(request.getPhone(), request.getSmsCode())) { - throw new CaptchaException("验证码错误或已过期"); - } - - // 2. 根据手机号查询用户 - User user = userService.getByPhone(request.getPhone()); - - // 3. 如果用户不存在,则自动注册 - if (user == null) { - log.info("用户不存在,自动注册: phone={}", request.getPhone()); - user = autoRegisterUser(request.getPhone()); - } else { - // 检查用户状态 - if (user.getStatus() != 1) { - throw new AuthException("账号已被禁用"); - } - log.info("用户登录: phone={}, userId={}", request.getPhone(), user.getId()); - } - - // 4. 生成令牌并返回 - // ... -} -``` - -#### 2.2 自动注册用户 -```java -/** - * 自动注册用户(用于登录时用户不存在的情况) - * - * @param phone 手机号 - * @return 新创建的用户 - */ -private User autoRegisterUser(String phone) { - // 生成随机用户名:开心 + 6位随机大小写字母和数字 - String username = generateRandomUsername(); - - // 使用手机号作为账号 - String account = phone; - - // 生成随机密码(用户可以后续修改) - String randomPassword = generateRandomPassword(); - - // 创建用户 - User user = userService.createUser( - account, - username, - randomPassword, - null, // 邮箱为空 - phone - ); - - log.info("自动注册用户成功: phone={}, username={}, userId={}", phone, username, user.getId()); - return user; -} -``` - -#### 2.3 生成随机密码 -```java -/** - * 生成随机密码 - * 格式:8位随机大小写字母和数字 - * - * @return 随机密码 - */ -private String generateRandomPassword() { - String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - Random random = new Random(); - StringBuilder sb = new StringBuilder(); - - for (int i = 0; i < 8; i++) { - int index = random.nextInt(chars.length()); - sb.append(chars.charAt(index)); - } - - return sb.toString(); -} -``` - -### 3. 优化短信验证码发送 - -**修改前**:检查手机号是否已注册,已注册则抛出异常 - -**修改后**:根据是否已注册返回不同的提示信息 -```java -@Override -public SmsCodeResponse sendSmsCode(String phone) { - // 验证手机号格式 - if (!StringUtils.hasText(phone) || !phone.matches("^1[3-9]\\d{9}$")) { - throw new BusinessException("手机号格式不正确"); - } - - // 检查手机号是否已注册,用于提示信息 - boolean isRegistered = existsByPhone(phone); - String message; - if (isRegistered) { - message = "验证码已发送,用于登录验证,有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟"; - } else { - message = "验证码已发送,用于注册验证,有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟"; - } - - // TODO: 接入真实的短信服务商(阿里云、腾讯云等) - String code = DEFAULT_SMS_CODE; - - // 将验证码存储到Redis,有效期5分钟 - String key = SMS_CODE_PREFIX + phone; - redisTemplate.opsForValue().set(key, code, SMS_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES); - - log.info("发送短信验证码: phone={}, code={}, isRegistered={}", phone, code, isRegistered); - - // 构建响应 - return SmsCodeResponse.builder() - .code(code) // 开发环境返回验证码,生产环境应该删除此行 - .expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60) - .message(message) - .build(); -} -``` - -### 4. 更新Controller注解 - -**文件**: `backend-single/src/main/java/com/emotion/controller/AuthController.java` - -```java -/** - * 用户登录(简化版:手机号+验证码,不存在则自动注册) - */ -@PostMapping("/login") -@Operation(summary = "用户登录", description = "使用手机号和短信验证码登录,若用户不存在则自动注册") -public Result login(@Valid @RequestBody LoginRequest request) { - AuthResponse response = authService.login(request); - return Result.success("登录成功", response); -} -``` - -## 📊 优化对比 - -| 项目 | 优化前 | 优化后 | -|------|--------|--------| -| 必填字段 | 账号、密码、验证码、验证码key | 手机号、短信验证码 | -| 验证码类型 | 图形验证码 | 短信验证码 | -| 用户不存在 | 提示错误 | 自动注册 | -| 密码验证 | 需要 | 不需要 | -| 登录流程 | 复杂 | 简单快捷 | - -## 🔧 核心特性 - -### 1. 简化登录流程 -- ✅ 仅需2个字段:手机号、短信验证码 -- ✅ 无需记住密码 -- ✅ 用户不存在自动注册 -- ✅ 自动生成随机用户名和密码 - -### 2. 智能提示信息 -- ✅ 未注册用户:提示"用于注册验证" -- ✅ 已注册用户:提示"用于登录验证" -- ✅ 用户体验更友好 - -### 3. 自动注册机制 -- ✅ 自动生成用户名:开心 + 6位随机字符 -- ✅ 自动生成密码:8位随机字符 -- ✅ 使用手机号作为账号 -- ✅ 无缝登录体验 - -### 4. 安全性保障 -- ✅ 手机号格式验证 -- ✅ 短信验证码验证 -- ✅ 验证码过期检查 -- ✅ 验证码一次性使用 -- ✅ 用户状态检查 - -## 🚀 使用示例 - -### 场景1:新用户首次登录(自动注册) - -#### 步骤1:获取短信验证码 -**请求**: -```bash -curl -X GET "http://localhost:19089/api/auth/sms-code?phone=13900139000" -``` - -**响应**: -```json -{ - "code": 200, - "message": "验证码已发送", - "data": { - "code": "123456", - "expiresIn": 300, - "message": "验证码已发送,用于注册验证,有效期5分钟" - } -} -``` - -#### 步骤2:登录(自动注册) -**请求**: -```bash -curl -X POST "http://localhost:19089/api/auth/login" \ - -H "Content-Type: application/json" \ - -d '{ - "phone": "13900139000", - "smsCode": "123456" - }' -``` - -**响应**: -```json -{ - "code": 200, - "message": "登录成功", - "data": { - "accessToken": "eyJhbGciOiJIUzUxMiJ9...", - "refreshToken": "eyJhbGciOiJIUzUxMiJ9...", - "expiresIn": 86400, - "userInfo": { - "id": "f361c8648378565a91c6d799d641741f", - "account": "13900139000", - "username": "开心4FhbOl", - "nickname": "开心4FhbOl", - "phone": "13900139000", - "memberLevel": "free" - }, - "loginTime": "2025-10-06 23:14:57" - } -} -``` - -### 场景2:已注册用户登录 - -#### 步骤1:获取短信验证码 -**请求**: -```bash -curl -X GET "http://localhost:19089/api/auth/sms-code?phone=13900139000" -``` - -**响应**: -```json -{ - "code": 200, - "message": "验证码已发送", - "data": { - "code": "123456", - "expiresIn": 300, - "message": "验证码已发送,用于登录验证,有效期5分钟" - } -} -``` - -#### 步骤2:登录 -**请求**: -```bash -curl -X POST "http://localhost:19089/api/auth/login" \ - -H "Content-Type: application/json" \ - -d '{ - "phone": "13900139000", - "smsCode": "123456" - }' -``` - -**响应**: -```json -{ - "code": 200, - "message": "登录成功", - "data": { - "accessToken": "eyJhbGciOiJIUzUxMiJ9...", - "refreshToken": "eyJhbGciOiJIUzUxMiJ9...", - "expiresIn": 86400, - "userInfo": { - "id": "f361c8648378565a91c6d799d641741f", - "account": "13900139000", - "username": "开心4FhbOl", - "nickname": "开心4FhbOl", - "phone": "13900139000", - "memberLevel": "free" - }, - "loginTime": "2025-10-06 23:15:18" - } -} -``` - -## 📝 TODO事项 - -### 1. 接入真实短信服务 - -**位置**: `AuthServiceImpl.sendSmsCode()` 方法 - -**当前代码**: -```java -// TODO: 接入真实的短信服务商(阿里云、腾讯云等) -// 目前使用固定验证码 123456 -String code = DEFAULT_SMS_CODE; -``` - -**后续改进**: -```java -// 生成随机6位数字验证码 -String code = String.format("%06d", new Random().nextInt(1000000)); - -// 调用短信服务商API发送验证码 -// 示例:阿里云短信服务 -// smsService.sendSms(phone, code); -``` - -### 2. 生产环境优化 - -**需要修改的地方**: -1. **移除响应中的验证码**: - ```java - return SmsCodeResponse.builder() - // .code(code) // 生产环境删除此行 - .expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60) - .message(message) - .build(); - ``` - -2. **添加发送频率限制**: - - 同一手机号60秒内只能发送一次 - - 同一IP每天最多发送10次 - -3. **添加验证码尝试次数限制**: - - 同一手机号最多尝试5次 - - 超过次数后需要重新获取验证码 - -## ✅ 验证清单 - -- [x] 编译成功,无错误 -- [x] LoginRequest简化为2个字段 -- [x] 实现自动注册逻辑 -- [x] 实现随机密码生成 -- [x] 优化短信验证码提示信息 -- [x] 更新Controller注解 -- [x] 测试新用户登录(自动注册) -- [x] 测试已注册用户登录 -- [x] 添加详细的日志记录 - -## 🎉 总结 - -成功优化了登录接口,实现了以下目标: - -1. ✅ **简化登录流程** - 仅需手机号和短信验证码 -2. ✅ **自动注册机制** - 用户不存在时自动注册 -3. ✅ **智能提示信息** - 根据用户状态返回不同提示 -4. ✅ **无缝用户体验** - 无需区分注册和登录 -5. ✅ **良好的代码质量** - 清晰的注释、完善的日志、标准的异常处理 - -现在用户可以通过简单的2步完成登录或注册: -1. 输入手机号获取验证码 -2. 输入验证码完成登录(不存在则自动注册) - -登录成功后自动返回访问令牌和用户信息,用户体验大大提升! - diff --git a/AUTH_REGISTER_OPTIMIZATION.md b/AUTH_REGISTER_OPTIMIZATION.md deleted file mode 100644 index b40840a..0000000 --- a/AUTH_REGISTER_OPTIMIZATION.md +++ /dev/null @@ -1,433 +0,0 @@ -# 注册接口优化总结 - -## 🎯 优化目标 - -简化用户注册流程,仅需要手机号、密码和短信验证码即可完成注册。 - -## ✅ 完成的优化 - -### 1. 简化RegisterRequest - -**文件**: `backend-single/src/main/java/com/emotion/dto/request/RegisterRequest.java` - -**修改前**:需要账号、密码、确认密码、用户名、昵称、邮箱、手机号、图形验证码等多个字段 - -**修改后**:仅需3个字段 -```java -@Data -@EqualsAndHashCode(callSuper = true) -public class RegisterRequest extends BaseRequest { - - /** - * 手机号(作为账号) - */ - @NotBlank(message = "手机号不能为空") - @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确") - private String phone; - - /** - * 密码 - */ - @NotBlank(message = "密码不能为空") - @Size(min = 6, max = 20, message = "密码长度必须在6-20个字符之间") - private String password; - - /** - * 短信验证码 - */ - @NotBlank(message = "验证码不能为空") - @Size(min = 6, max = 6, message = "验证码必须为6位") - private String smsCode; -} -``` - -### 2. 新增SmsCodeResponse - -**文件**: `backend-single/src/main/java/com/emotion/dto/response/SmsCodeResponse.java` - -```java -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class SmsCodeResponse { - - /** - * 验证码(开发环境返回,生产环境不返回) - */ - private String code; - - /** - * 过期时间(秒) - */ - private Long expiresIn; - - /** - * 提示信息 - */ - private String message; -} -``` - -### 3. 新增获取短信验证码接口 - -**文件**: `backend-single/src/main/java/com/emotion/controller/AuthController.java` - -```java -/** - * 获取短信验证码(用于注册) - */ -@GetMapping("/sms-code") -@Operation(summary = "获取短信验证码", description = "用于注册时的短信验证码") -public Result getSmsCode( - @Parameter(description = "手机号", required = true) - @RequestParam String phone) { - SmsCodeResponse response = authService.sendSmsCode(phone); - return Result.success("验证码已发送", response); -} -``` - -**接口地址**: `GET /api/auth/sms-code?phone=13800138000` - -**响应示例**: -```json -{ - "code": 200, - "message": "验证码已发送", - "data": { - "code": "123456", - "expiresIn": 300, - "message": "验证码已发送,有效期5分钟" - } -} -``` - -### 4. 优化注册接口逻辑 - -**文件**: `backend-single/src/main/java/com/emotion/service/impl/AuthServiceImpl.java` - -#### 4.1 新增常量定义 -```java -private static final String SMS_CODE_PREFIX = "sms_code:"; -private static final int SMS_CODE_EXPIRE_MINUTES = 5; -private static final String DEFAULT_SMS_CODE = "123456"; -``` - -#### 4.2 简化注册逻辑 -```java -@Override -public AuthResponse register(RegisterRequest request) { - // 1. 验证短信验证码 - if (!validateSmsCode(request.getPhone(), request.getSmsCode())) { - throw new CaptchaException("验证码错误或已过期"); - } - - // 2. 检查手机号是否已存在 - if (userService.getByPhone(request.getPhone()) != null) { - throw new BusinessException("手机号已被注册"); - } - - // 3. 生成随机用户名:开心 + 6位随机大小写字母和数字 - String username = generateRandomUsername(); - - // 4. 使用手机号作为账号 - String account = request.getPhone(); - - // 5. 创建用户 - User user = userService.createUser( - account, - username, - request.getPassword(), - null, // 邮箱为空 - request.getPhone() - ); - - // 6. 生成令牌并返回 - // ... -} -``` - -#### 4.3 实现短信验证码发送 -```java -@Override -public SmsCodeResponse sendSmsCode(String phone) { - // 验证手机号格式 - if (!StringUtils.hasText(phone) || !phone.matches("^1[3-9]\\d{9}$")) { - throw new BusinessException("手机号格式不正确"); - } - - // 检查手机号是否已注册 - if (existsByPhone(phone)) { - throw new BusinessException("该手机号已被注册"); - } - - // TODO: 接入真实的短信服务商(阿里云、腾讯云等) - // 目前使用固定验证码 123456 - String code = DEFAULT_SMS_CODE; - - // 将验证码存储到Redis,有效期5分钟 - String key = SMS_CODE_PREFIX + phone; - redisTemplate.opsForValue().set(key, code, SMS_CODE_EXPIRE_MINUTES, TimeUnit.MINUTES); - - log.info("发送短信验证码: phone={}, code={}", phone, code); - - // 构建响应 - return SmsCodeResponse.builder() - .code(code) // 开发环境返回验证码,生产环境应该删除此行 - .expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60) - .message("验证码已发送,有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟") - .build(); -} -``` - -#### 4.4 实现短信验证码校验 -```java -@Override -public boolean validateSmsCode(String phone, String code) { - if (!StringUtils.hasText(phone) || !StringUtils.hasText(code)) { - return false; - } - - String key = SMS_CODE_PREFIX + phone; - String storedCode = (String) redisTemplate.opsForValue().get(key); - - if (storedCode == null) { - log.warn("短信验证码不存在或已过期: phone={}", phone); - return false; - } - - boolean isValid = storedCode.equals(code); - - // 验证成功后删除验证码(一次性使用) - if (isValid) { - redisTemplate.delete(key); - log.info("短信验证码验证成功: phone={}", phone); - } else { - log.warn("短信验证码错误: phone={}, expected={}, actual={}", phone, storedCode, code); - } - - return isValid; -} -``` - -#### 4.5 实现随机用户名生成 -```java -/** - * 生成随机用户名 - * 格式:开心 + 6位随机大小写字母和数字 - * - * @return 随机用户名 - */ -private String generateRandomUsername() { - String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - Random random = new Random(); - StringBuilder sb = new StringBuilder("开心"); - - for (int i = 0; i < 6; i++) { - int index = random.nextInt(chars.length()); - sb.append(chars.charAt(index)); - } - - return sb.toString(); -} -``` - -**用户名示例**: -- 开心A3bC9z -- 开心X7yK2m -- 开心P1qW5n - -### 5. 更新AuthService接口 - -**文件**: `backend-single/src/main/java/com/emotion/service/AuthService.java` - -新增方法定义: -```java -/** - * 发送短信验证码 - * - * @param phone 手机号 - * @return 短信验证码响应 - */ -SmsCodeResponse sendSmsCode(String phone); - -/** - * 验证短信验证码 - * - * @param phone 手机号 - * @param code 验证码 - * @return 是否验证成功 - */ -boolean validateSmsCode(String phone, String code); -``` - -## 📊 优化对比 - -| 项目 | 优化前 | 优化后 | -|------|--------|--------| -| 必填字段 | 账号、密码、确认密码、验证码、验证码key | 手机号、密码、短信验证码 | -| 验证码类型 | 图形验证码 | 短信验证码 | -| 用户名生成 | 手动输入或使用账号 | 自动生成(开心+6位随机) | -| 账号 | 手动输入 | 自动使用手机号 | -| 邮箱 | 可选输入 | 不需要 | -| 昵称 | 可选输入 | 自动使用用户名 | - -## 🔧 核心特性 - -### 1. 简化注册流程 -- ✅ 仅需3个字段:手机号、密码、短信验证码 -- ✅ 自动生成用户名:开心 + 6位随机字符 -- ✅ 手机号作为账号 -- ✅ 移除了确认密码、邮箱、昵称等非必要字段 - -### 2. 短信验证码机制 -- ✅ 默认验证码:123456(开发环境) -- ✅ 有效期:5分钟 -- ✅ 一次性使用:验证成功后自动删除 -- ✅ Redis存储:支持分布式部署 -- ✅ TODO标记:预留真实短信服务接入点 - -### 3. 安全性保障 -- ✅ 手机号格式验证 -- ✅ 手机号唯一性检查 -- ✅ 验证码过期检查 -- ✅ 验证码一次性使用 -- ✅ 密码加密存储 - -### 4. 用户体验优化 -- ✅ 注册流程简单快捷 -- ✅ 自动生成友好的用户名 -- ✅ 清晰的错误提示 -- ✅ 完整的API文档(Swagger) - -## 🚀 使用示例 - -### 1. 获取短信验证码 - -**请求**: -```bash -curl -X GET "http://localhost:19089/api/auth/sms-code?phone=13800138000" -``` - -**响应**: -```json -{ - "code": 200, - "message": "验证码已发送", - "data": { - "code": "123456", - "expiresIn": 300, - "message": "验证码已发送,有效期5分钟" - } -} -``` - -### 2. 用户注册 - -**请求**: -```bash -curl -X POST "http://localhost:19089/api/auth/register" \ - -H "Content-Type: application/json" \ - -d '{ - "phone": "13800138000", - "password": "password123", - "smsCode": "123456" - }' -``` - -**响应**: -```json -{ - "code": 200, - "message": "注册成功", - "data": { - "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", - "expiresIn": 86400, - "userInfo": { - "id": "1844234567890123456", - "account": "13800138000", - "username": "开心A3bC9z", - "nickname": "开心A3bC9z", - "phone": "13800138000", - "memberLevel": "free", - "status": 1 - }, - "loginTime": "2025-10-06 21:49:33" - } -} -``` - -## 📝 TODO事项 - -### 1. 接入真实短信服务 - -**位置**: `AuthServiceImpl.sendSmsCode()` 方法 - -**当前代码**: -```java -// TODO: 接入真实的短信服务商(阿里云、腾讯云等) -// 目前使用固定验证码 123456 -String code = DEFAULT_SMS_CODE; -``` - -**后续改进**: -```java -// 生成随机6位数字验证码 -String code = String.format("%06d", new Random().nextInt(1000000)); - -// 调用短信服务商API发送验证码 -// 示例:阿里云短信服务 -// smsService.sendSms(phone, code); -``` - -### 2. 生产环境优化 - -**需要修改的地方**: -1. **移除响应中的验证码**: - ```java - return SmsCodeResponse.builder() - // .code(code) // 生产环境删除此行 - .expiresIn((long) SMS_CODE_EXPIRE_MINUTES * 60) - .message("验证码已发送,有效期" + SMS_CODE_EXPIRE_MINUTES + "分钟") - .build(); - ``` - -2. **添加发送频率限制**: - - 同一手机号60秒内只能发送一次 - - 同一IP每天最多发送10次 - -3. **添加验证码尝试次数限制**: - - 同一手机号最多尝试5次 - - 超过次数后需要重新获取验证码 - -## ✅ 验证清单 - -- [x] 编译成功,无错误 -- [x] RegisterRequest简化为3个字段 -- [x] 新增SmsCodeResponse响应类 -- [x] 新增获取短信验证码接口 -- [x] 实现短信验证码发送逻辑 -- [x] 实现短信验证码校验逻辑 -- [x] 实现随机用户名生成 -- [x] 优化注册接口逻辑 -- [x] 添加Swagger API文档注解 -- [x] 添加详细的日志记录 - -## 🎉 总结 - -成功优化了注册接口,实现了以下目标: - -1. ✅ **简化注册流程** - 仅需手机号、密码和短信验证码 -2. ✅ **自动生成用户名** - 开心 + 6位随机大小写字母和数字 -3. ✅ **短信验证码机制** - 默认123456,预留真实短信服务接入点 -4. ✅ **完整的校验逻辑** - 手机号格式、唯一性、验证码有效性 -5. ✅ **良好的代码质量** - 清晰的注释、完善的日志、标准的异常处理 - -现在用户可以通过简单的3步完成注册: -1. 输入手机号获取验证码 -2. 输入密码和验证码 -3. 自动生成用户名并完成注册 - -注册成功后自动登录,返回访问令牌和用户信息! - diff --git a/BACKEND_SINGLE_STARTUP_FIX.md b/BACKEND_SINGLE_STARTUP_FIX.md deleted file mode 100644 index 37d7f3b..0000000 --- a/BACKEND_SINGLE_STARTUP_FIX.md +++ /dev/null @@ -1,369 +0,0 @@ -# Backend-Single微服务启动修复总结 - -## 🎯 问题描述 - -backend-single微服务在启动时编译失败,缺少UserService中的多个方法实现。 - -## ❌ 编译错误 - -### 错误信息 -``` -[ERROR] 找不到符号 - 符号: 方法 getByAccount(java.lang.String) - 位置: 类型为com.emotion.service.UserService的变量 userService - -[ERROR] 找不到符号 - 符号: 方法 updateLastActiveTime(java.lang.String,java.time.LocalDateTime) - 位置: 类型为com.emotion.service.UserService的变量 userService - -[ERROR] 找不到符号 - 符号: 方法 getByEmail(java.lang.String) - 位置: 类型为com.emotion.service.UserService的变量 userService - -[ERROR] 找不到符号 - 符号: 方法 getByPhone(java.lang.String) - 位置: 类型为com.emotion.service.UserService的变量 userService - -[ERROR] 找不到符号 - 符号: 方法 createUser(java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lang.String) - 位置: 类型为com.emotion.service.UserService的变量 userService -``` - -### 缺少的方法 -1. `getByAccount(String account)` - 根据账号获取用户 -2. `getByEmail(String email)` - 根据邮箱获取用户 -3. `getByPhone(String phone)` - 根据手机号获取用户 -4. `createUser(String, String, String, String, String)` - 创建用户 -5. `updateLastActiveTime(String, LocalDateTime)` - 更新最后活跃时间 - -## ✅ 解决方案 - -### 1. 修改UserService接口 - -**文件**: `backend-single/src/main/java/com/emotion/service/UserService.java` - -添加了以下方法定义: - -```java -/** - * 根据账号获取用户 - */ -User getByAccount(String account); - -/** - * 根据邮箱获取用户 - */ -User getByEmail(String email); - -/** - * 根据手机号获取用户 - */ -User getByPhone(String phone); - -/** - * 创建用户 - * - * @param account 账号 - * @param username 用户名 - * @param password 密码(明文,会在方法内加密) - * @param email 邮箱(可为null) - * @param phone 手机号(可为null) - * @return 创建的用户 - */ -User createUser(String account, String username, String password, String email, String phone); - -/** - * 更新用户最后活跃时间 - * - * @param userId 用户ID - * @param lastActiveTime 最后活跃时间 - */ -void updateLastActiveTime(String userId, LocalDateTime lastActiveTime); -``` - -### 2. 实现UserServiceImpl方法 - -**文件**: `backend-single/src/main/java/com/emotion/service/impl/UserServiceImpl.java` - -#### 2.1 getByAccount方法 -```java -@Override -public User getByAccount(String account) { - if (!StringUtils.hasText(account)) { - return null; - } - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(User::getAccount, account) - .eq(User::getIsDeleted, 0); - return this.getOne(wrapper); -} -``` - -#### 2.2 getByEmail方法 -```java -@Override -public User getByEmail(String email) { - if (!StringUtils.hasText(email)) { - return null; - } - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(User::getEmail, email) - .eq(User::getIsDeleted, 0); - return this.getOne(wrapper); -} -``` - -#### 2.3 getByPhone方法 -```java -@Override -public User getByPhone(String phone) { - if (!StringUtils.hasText(phone)) { - return null; - } - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.eq(User::getPhone, phone) - .eq(User::getIsDeleted, 0); - return this.getOne(wrapper); -} -``` - -#### 2.4 createUser方法 -```java -@Override -public User createUser(String account, String username, String password, String email, String phone) { - User user = new User(); - user.setAccount(account); - user.setUsername(username); - user.setNickname(username); // 默认昵称与用户名相同 - user.setPassword(passwordEncoder.encode(password)); // 加密密码 - user.setEmail(email); - user.setPhone(phone); - user.setMemberLevel("free"); // 默认免费会员 - user.setStatus(1); // 默认启用 - user.setIsVerified(0); // 默认未验证 - user.setLastActiveTime(LocalDateTime.now()); - - this.save(user); - return user; -} -``` - -#### 2.5 updateLastActiveTime方法 -```java -@Override -public void updateLastActiveTime(String userId, LocalDateTime lastActiveTime) { - if (!StringUtils.hasText(userId) || lastActiveTime == null) { - return; - } - User user = this.getById(userId); - if (user != null && user.getIsDeleted() == 0) { - user.setLastActiveTime(lastActiveTime); - this.updateById(user); - } -} -``` - -## 🔧 实现细节 - -### 1. 查询方法的共同特点 -- 参数校验:检查参数是否为空 -- 软删除过滤:只查询未删除的用户(`isDeleted = 0`) -- 使用MyBatis-Plus的LambdaQueryWrapper进行类型安全的查询 - -### 2. createUser方法的特点 -- **密码加密**:使用PasswordEncoder加密密码 -- **默认值设置**: - - 昵称默认与用户名相同 - - 会员等级默认为"free" - - 状态默认为1(启用) - - 验证状态默认为0(未验证) - - 最后活跃时间设为当前时间 -- **可选字段**:email和phone可以为null - -### 3. updateLastActiveTime方法的特点 -- 参数校验:检查userId和lastActiveTime是否为空 -- 存在性检查:确保用户存在且未删除 -- 只更新lastActiveTime字段 - -## 📊 编译和启动结果 - -### 编译成功 -```bash -[INFO] BUILD SUCCESS -[INFO] Total time: 9.692 s -[INFO] Finished at: 2025-10-06T21:29:28+08:00 -``` - -### 启动成功 -``` -======================================== -🎉 情感博物馆服务启动成功! -📋 服务信息: - - 服务名称: emotion-single - - 服务端口: 19089 - - 环境配置: local - - API文档: http://localhost:19089/api/health -======================================== -``` - -### 启动日志关键信息 -- ✅ Spring Boot版本: 2.7.18 -- ✅ Java版本: 17.0.15 -- ✅ 端口: 19089 -- ✅ Context Path: /api -- ✅ 环境: local -- ✅ MyBatis Mapper扫描: 17个Mapper成功注册 -- ✅ WebSocket配置: 成功启动 -- ✅ Security配置: 成功加载 - -## 🎯 方法使用场景 - -### 1. getByAccount -**使用场景**: 用户登录、账号唯一性检查 -```java -// AuthServiceImpl.login() -User user = userService.getByAccount(request.getAccount()); -if (user == null) { - throw new AuthException("账号不存在"); -} -``` - -### 2. getByEmail -**使用场景**: 邮箱唯一性检查、邮箱登录 -```java -// AuthServiceImpl.register() -if (StringUtils.hasText(request.getEmail()) && userService.getByEmail(request.getEmail()) != null) { - throw new BusinessException("邮箱已被使用"); -} -``` - -### 3. getByPhone -**使用场景**: 手机号唯一性检查、手机号登录 -```java -// AuthServiceImpl.register() -if (StringUtils.hasText(request.getPhone()) && userService.getByPhone(request.getPhone()) != null) { - throw new BusinessException("手机号已被使用"); -} -``` - -### 4. createUser -**使用场景**: 用户注册 -```java -// AuthServiceImpl.register() -User user = userService.createUser( - request.getAccount(), - username, - request.getPassword(), - email, - phone -); -``` - -### 5. updateLastActiveTime -**使用场景**: 用户登录后更新活跃时间 -```java -// AuthServiceImpl.login() -userService.updateLastActiveTime(user.getId(), LocalDateTime.now()); -``` - -## ✅ 验证清单 - -- [x] 编译成功,无错误 -- [x] 服务启动成功 -- [x] 端口19089正常监听 -- [x] WebSocket配置正常 -- [x] MyBatis Mapper扫描成功 -- [x] Security配置加载成功 -- [x] 所有Controller注册成功 - -## 📝 注意事项 - -### 1. 密码安全 -- createUser方法会自动加密密码 -- 使用PasswordEncoder(BCrypt)进行加密 -- 不要在调用前预先加密密码 - -### 2. 软删除 -- 所有查询方法都过滤了已删除的用户 -- 确保业务逻辑中正确处理软删除 - -### 3. 参数校验 -- 所有方法都进行了参数校验 -- 空值或null会被正确处理 - -### 4. 默认值 -- createUser方法设置了合理的默认值 -- 确保数据库字段允许这些默认值 - -## 🚀 后续建议 - -### 1. 添加单元测试 -```java -@Test -public void testGetByAccount() { - User user = userService.getByAccount("testuser"); - assertNotNull(user); - assertEquals("testuser", user.getAccount()); -} - -@Test -public void testCreateUser() { - User user = userService.createUser( - "newuser", - "New User", - "password123", - "new@example.com", - "13800138000" - ); - assertNotNull(user.getId()); - assertTrue(passwordEncoder.matches("password123", user.getPassword())); -} -``` - -### 2. 添加日志 -在关键方法中添加日志记录: -```java -@Override -public User createUser(String account, String username, String password, String email, String phone) { - log.info("创建用户: account={}, username={}, email={}, phone={}", - account, username, email, phone); - // ... 实现代码 - log.info("用户创建成功: userId={}", user.getId()); - return user; -} -``` - -### 3. 性能优化 -考虑添加缓存: -```java -@Cacheable(value = "users", key = "#account") -public User getByAccount(String account) { - // ... 实现代码 -} -``` - -## 📚 相关文件 - -### 修改的文件 -1. `backend-single/src/main/java/com/emotion/service/UserService.java` - - 添加了5个方法定义 - -2. `backend-single/src/main/java/com/emotion/service/impl/UserServiceImpl.java` - - 实现了5个方法 - -### 依赖的文件 -1. `backend-single/src/main/java/com/emotion/entity/User.java` - 用户实体 -2. `backend-single/src/main/java/com/emotion/mapper/UserMapper.java` - 用户Mapper -3. `backend-single/src/main/java/com/emotion/service/impl/AuthServiceImpl.java` - 认证服务(调用方) - -## 🎉 总结 - -成功修复了backend-single微服务的启动问题: - -1. ✅ **补充了缺失的方法** - 在UserService接口和实现中添加了5个方法 -2. ✅ **遵循了项目规范** - 使用MyBatis-Plus、软删除、参数校验等 -3. ✅ **实现了完整逻辑** - 包括密码加密、默认值设置、错误处理等 -4. ✅ **编译和启动成功** - 服务正常运行在19089端口 -5. ✅ **代码质量良好** - 清晰的注释、合理的实现、完善的校验 - -现在backend-single微服务可以正常启动和运行了! diff --git a/FRONTEND_TOKEN_API_MIGRATION_GUIDE.md b/FRONTEND_TOKEN_API_MIGRATION_GUIDE.md deleted file mode 100644 index bee3377..0000000 --- a/FRONTEND_TOKEN_API_MIGRATION_GUIDE.md +++ /dev/null @@ -1,307 +0,0 @@ -# 前端Token API迁移指南 - -## 📋 概述 - -TokenController接口已经从POST请求体传递token的方式优化为GET请求头传递token的标准方式。如果前端需要使用这些接口,请参考以下迁移指南。 - -## ✅ 当前状态 - -经过检查,前端代码中**没有直接使用**以下接口: -- `/token/user-info` -- `/token/username` -- `/token/validate` - -前端主要使用的是AuthController中的接口: -- `/auth/user/info` - 获取用户信息 -- `/auth/username` - 获取用户名 -- `/auth/validateToken` - 验证Token - -因此,**本次优化不需要修改前端代码**。 - -## 📚 接口使用指南(如果将来需要) - -### 1. 获取用户信息 - -#### 旧方式(已废弃) -```typescript -// ❌ 不推荐:POST请求,token在请求体中 -const response = await http.post('/token/user-info', { - token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' -}) -``` - -#### 新方式(推荐) -```typescript -// ✅ 推荐:GET请求,token在请求头中(自动添加) -const response = await http.get('/token/user-info') - -// 或者手动指定token(特殊场景) -const response = await http.get('/token/user-info', { - headers: { - 'Authorization': `Bearer ${token}` - } -}) -``` - -### 2. 获取用户名 - -#### 旧方式(已废弃) -```typescript -// ❌ 不推荐 -const response = await http.post('/token/username', { - token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' -}) -``` - -#### 新方式(推荐) -```typescript -// ✅ 推荐 -const response = await http.get('/token/username') -``` - -### 3. 验证Token - -#### 旧方式(已废弃) -```typescript -// ❌ 不推荐 -const response = await http.post('/token/validate', { - token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' -}) -``` - -#### 新方式(推荐) -```typescript -// ✅ 推荐 -const response = await http.get('/token/validate') -``` - -## 🔧 HTTP请求工具配置 - -### 确保请求拦截器自动添加Token - -大多数项目的HTTP工具已经配置了自动添加Authorization请求头的拦截器: - -```typescript -// utils/request.ts 或 utils/http.ts -import axios from 'axios' - -const http = axios.create({ - baseURL: import.meta.env.VITE_API_BASE_URL, - timeout: 10000 -}) - -// 请求拦截器:自动添加Token -http.interceptors.request.use( - (config) => { - // 从localStorage获取token - const token = localStorage.getItem('access_token') - - // 如果token存在,添加到请求头 - if (token) { - config.headers.Authorization = `Bearer ${token}` - } - - return config - }, - (error) => { - return Promise.reject(error) - } -) - -export { http } -``` - -### 如果项目还没有配置,请添加以下代码 - -```typescript -// 1. 在请求拦截器中添加token -http.interceptors.request.use((config) => { - const token = localStorage.getItem('access_token') - if (token && !config.headers.Authorization) { - config.headers.Authorization = `Bearer ${token}` - } - return config -}) - -// 2. 在响应拦截器中处理401错误 -http.interceptors.response.use( - (response) => response, - async (error) => { - if (error.response?.status === 401) { - // Token过期或无效,跳转到登录页 - localStorage.removeItem('access_token') - window.location.href = '/login' - } - return Promise.reject(error) - } -) -``` - -## 📝 API服务封装示例 - -如果需要使用TokenController的接口,可以创建以下服务: - -```typescript -// services/token.ts -import { http } from '@/utils/request' -import type { UserInfo } from '@/types' - -export const tokenApi = { - /** - * 获取用户信息 - * Token会自动从请求头中获取 - */ - getUserInfo(): Promise { - return http.get('/token/user-info') - }, - - /** - * 获取用户名 - * Token会自动从请求头中获取 - */ - getUsername(): Promise { - return http.get('/token/username') - }, - - /** - * 验证Token - * Token会自动从请求头中获取 - */ - validateToken(): Promise { - return http.get('/token/validate') - } -} -``` - -## 🎯 推荐使用AuthController接口 - -实际上,建议前端继续使用AuthController中的接口,因为它们提供了更完整的功能: - -```typescript -// services/auth.ts -import { http } from '@/utils/request' - -export const authApi = { - /** - * 获取当前用户信息 - * 推荐使用这个接口而不是 /token/user-info - */ - getUserInfo(): Promise { - return http.get('/auth/user/info') - }, - - /** - * 获取用户名 - * 推荐使用这个接口而不是 /token/username - */ - getUsername(): Promise { - return http.get('/auth/username') - }, - - /** - * 验证Token - * 推荐使用这个接口而不是 /token/validate - */ - validateToken(): Promise { - return http.get('/auth/validateToken') - } -} -``` - -## 🔍 接口对比 - -| 功能 | TokenController | AuthController | 推荐使用 | -|------|----------------|----------------|---------| -| 获取用户信息 | `GET /token/user-info` | `GET /auth/user/info` | AuthController | -| 获取用户名 | `GET /token/username` | `GET /auth/username` | AuthController | -| 验证Token | `GET /token/validate` | `GET /auth/validateToken` | AuthController | - -**推荐理由**: -- AuthController接口功能更完整 -- 已经在项目中广泛使用 -- 有更好的错误处理和日志记录 -- 与认证流程集成更紧密 - -## ⚠️ 注意事项 - -### 1. Token存储位置 -确保token存储在正确的位置: -```typescript -// 登录成功后存储token -localStorage.setItem('access_token', response.accessToken) - -// 登出时清除token -localStorage.removeItem('access_token') -``` - -### 2. Token格式 -确保token以正确的格式发送: -``` -Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... -``` - -### 3. 错误处理 -处理token相关的错误: -```typescript -try { - const userInfo = await tokenApi.getUserInfo() -} catch (error) { - if (error.response?.status === 401) { - // Token无效或过期 - console.error('Token无效,请重新登录') - // 跳转到登录页 - router.push('/login') - } -} -``` - -### 4. Token刷新 -实现token自动刷新机制: -```typescript -http.interceptors.response.use( - (response) => response, - async (error) => { - const originalRequest = error.config - - if (error.response?.status === 401 && !originalRequest._retry) { - originalRequest._retry = true - - try { - // 尝试刷新token - const refreshToken = localStorage.getItem('refresh_token') - const response = await authApi.refreshToken({ refreshToken }) - - // 更新token - localStorage.setItem('access_token', response.accessToken) - - // 重试原始请求 - originalRequest.headers.Authorization = `Bearer ${response.accessToken}` - return http(originalRequest) - } catch (refreshError) { - // 刷新失败,跳转到登录页 - localStorage.clear() - window.location.href = '/login' - return Promise.reject(refreshError) - } - } - - return Promise.reject(error) - } -) -``` - -## ✅ 总结 - -1. **当前状态**:前端代码不需要修改,因为没有使用TokenController接口 -2. **推荐做法**:继续使用AuthController接口 -3. **如果需要使用TokenController**: - - 使用GET方法 - - Token在请求头中自动传递 - - 确保HTTP工具配置了请求拦截器 -4. **最佳实践**: - - Token存储在localStorage - - 使用Bearer格式 - - 实现自动刷新机制 - - 处理401错误 - -这次优化使后端接口更加标准化和安全,为将来的扩展打下了良好的基础! diff --git a/README.md b/README.md new file mode 100644 index 0000000..31fe32d --- /dev/null +++ b/README.md @@ -0,0 +1,225 @@ +# 情绪博物馆 (Emotion Museum) + +情绪博物馆是一款基于AI技术的心理健康应用,通过智能对话、情绪分析、个性化成长方案等功能,帮助用户建立健康的情绪管理习惯。 + +## 📋 项目概述 + +### 🎯 产品定位 +情绪博物馆致力于为用户提供一个安全、私密的情绪表达空间,通过AI技术提供24/7的情绪陪伴与支持,帮助用户更好地理解和管理自己的情绪。 + +### 🚀 核心价值 +- **情绪陪伴**: AI驱动的智能对话系统,提供全天候情绪支持 +- **个性化成长**: 基于用户数据的个性化心理成长路径 +- **社区分享**: 安全的情绪表达和经验分享社区 +- **数据洞察**: 情绪数据可视化,帮助用户了解自己 + +## 🏗️ 技术架构 + +### 整体架构 +情绪博物馆采用现代化的前后端分离架构,包含iOS移动端、Web端和后端微服务系统。服务IP:101.200.208.45 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ iOS 移动端 (SwiftUI) │ +├─────────────────────────────────────────────────────────────┤ +│ Web 前端 (Vue3 + TS) │ +├─────────────────────────────────────────────────────────────┤ +│ API 网关 (Spring Cloud Gateway) │ +├─────────────────────────────────────────────────────────────┤ +│ 用户服务 │ AI服务 │ 记录服务 │ 成长服务 │ 探索服务 │ +├─────────────────────────────────────────────────────────────┤ +│ 公共组件 & 基础设施 │ +│ Nacos配置中心 │ MySQL数据库 │ Redis缓存 │ 日志系统 │ +└─────────────────────────────────────────────────────────────┘ +``` + +### 技术栈 + +#### 后端技术栈 +- **微服务框架**: Spring Cloud Alibaba 2022.0.0.0 +- **核心框架**: Spring Boot 3.0.2 +- **服务注册发现**: Nacos 2.2.0+ +- **数据库**: MySQL 8.0+ +- **缓存**: Redis 7.0+ +- **AI服务**: Coze API (GPT-4) +- **WebSocket**: Spring WebSocket +- **构建工具**: Maven 3.6+ + +#### 前端技术栈 +- **Web端**: Vue 3.4.21 + TypeScript 5.4.2 + Vite 5.1.6 +- **移动端**: SwiftUI (iOS原生) +- **UI框架**: Element Plus 2.6.1 + Tailwind CSS 3.4.1 +- **状态管理**: Pinia 2.1.7 +- **实时通信**: STOMP + 原生WebSocket +- **数据可视化**: ECharts 5.5.0 +- **构建工具**: Vite 5.1.6 + +## 📁 项目结构 + +``` +. +├── UniApp/ # UniApp跨平台移动应用 +├── backend/ # 后端微服务系统 +│ ├── emotion-gateway/ # API网关服务 +│ ├── emotion-user/ # 用户服务 +│ ├── emotion-ai/ # AI对话服务 +│ ├── emotion-record/ # 情绪记录服务 +│ ├── emotion-growth/ # 成长课题服务 +│ ├── emotion-explore/ # 地图探索服务 +│ ├── emotion-reward/ # 成就奖励服务 +│ ├── emotion-stats/ # 统计分析服务 +│ └── emotion-common/ # 公共模块 +├── backend-single/ # SpringBoot单体后端服务 +├── web/ # 旧版Web前端 +├── web-new/ # 新版Web前端(Vue3+TS) +└── 开心APP网页代码v1.1/ # 早期网页版本 +``` + +## 🚀 快速开始 + +### 后端微服务部署 + +#### 环境要求 +- JDK 17+ +- Maven 3.6+ +- MySQL 8.0+ +- Redis 7.0+ +- Nacos 2.2.0+ + +#### 启动步骤 +1. 启动Nacos配置中心 +2. 创建MySQL数据库并导入数据结构 +3. 启动Redis服务 +4. 配置Nacos命名空间和配置文件 +5. 启动各微服务模块 + +```bash +# 启动所有服务 +cd backend +./start-services.sh + +# 停止所有服务 +./stop-services.sh +``` + +### Web前端部署 + +#### 环境要求 +- Node.js 18+ +- npm 9+ + +#### 安装与启动 +```bash +# 进入新版本Web目录 +cd web-new + +# 安装依赖 +npm install + +# 启动开发服务器 +npm run dev + +# 构建生产版本 +npm run build +``` + +## 📊 核心功能模块 + +### 1. 记录模块 +**定位**: 情绪主页 + AI对话入口 +- 智能对话系统(支持语音、文字、图片输入) +- 情绪日历(情绪记录与回顾) +- 对话记录管理(自动保存、分类、搜索) + +### 2. 治愈模块 +**定位**: 个人成长档案 + 课题系统 +- 智能情绪洞察(基于对话内容的情绪分析) +- 成长课题系统(个性化成长任务推荐) +- 用户画像建模(五维能力评估模型) +- 互动奖励(积分、称号、皮肤) + +### 3. 探索模块 +**定位**: 情绪地图 + 社区分享 +- 智能地图推荐(基于情绪状态推荐地点) +- 社区笔记系统(图文分享、点赞评论) +- 位置服务集成(地理定位、周边推荐) + +### 4. 个人模块 +**定位**: 用户信息 + 成就系统 +- 用户资料管理 +- 成就面板(数据统计展示) +- 邀请模块(好友邀请和奖励) +- 设置中心(应用配置和隐私设置) + +## 🔧 开发指南 + +### 后端开发 +1. 使用Spring Cloud Alibaba微服务架构 +2. 遵循RESTful API设计规范 +3. 使用MyBatis-Plus进行数据访问 +4. 集成Nacos进行配置管理 +5. 使用Spring Security进行安全控制 + +### 前端开发 +1. 使用Vue 3 Composition API +2. 遵循TypeScript编码规范 +3. 使用Element Plus组件库 +4. 集成Tailwind CSS样式框架 +5. 使用Pinia进行状态管理 + +### API文档 +- 网关地址: http://localhost:8080 +- 用户服务: http://localhost:8081 +- AI服务: http://localhost:8082 +- 各服务健康检查: http://localhost:{port}/actuator/health + +## 🧪 测试策略 + +### 后端测试 +- 单元测试覆盖率 > 80% +- 集成测试覆盖核心业务流程 +- API测试验证接口正确性 +- 性能测试确保系统稳定性 + +### 前端测试 +- 组件单元测试 (Vitest) +- 端到端测试 (Cypress) +- 跨浏览器兼容性测试 +- 移动端响应式测试 + +## 📈 部署与监控 + +### 部署方式 +- Docker容器化部署 +- Kubernetes集群部署 +- CI/CD自动化部署 + +### 监控体系 +- 健康检查端点 +- Prometheus指标收集 +- Grafana可视化监控 +- 日志收集与分析 + +## 🤝 贡献指南 + +我们欢迎任何形式的贡献! + +1. Fork项目 +2. 创建功能分支 +3. 提交代码更改 +4. 编写测试用例 +5. 提交Pull Request + +## 📄 许可证 + +本项目采用MIT许可证 - 查看 [LICENSE](LICENSE) 文件了解详情。 + +## 📞 联系我们 + +如有任何问题或建议,请通过以下方式联系我们: +- 邮箱: [请填写邮箱地址] +- GitHub Issues: [项目Issues页面] + +--- + +*情绪博物馆 - 让每一种情绪都被温柔以待* \ No newline at end of file diff --git a/TOKEN_CONTROLLER_OPTIMIZATION.md b/TOKEN_CONTROLLER_OPTIMIZATION.md deleted file mode 100644 index a52a67e..0000000 --- a/TOKEN_CONTROLLER_OPTIMIZATION.md +++ /dev/null @@ -1,337 +0,0 @@ -# TokenController优化总结 - -## 🎯 优化目标 - -将TokenController从使用请求体传递token的方式改为标准的从请求头自动获取token的方式,符合RESTful API最佳实践。 - -## ✅ 完成的优化 - -### 1. 修改TokenController接口 - -#### 优化前 -```java -@PostMapping("/user-info") -public Result getUserInfoByToken(@RequestBody @Validated TokenRequest request) { - UserInfoResponse userInfo = tokenService.getUserInfoByToken(request.getToken()); - return Result.success(userInfo); -} -``` - -**问题**: -- ❌ 使用POST方法获取数据(不符合RESTful规范) -- ❌ Token在请求体中传递(不安全,不标准) -- ❌ 需要额外的TokenRequest类封装 -- ❌ 客户端需要构造请求体 - -#### 优化后 -```java -@GetMapping("/user-info") -@Operation(summary = "获取用户信息", description = "通过请求头中的token获取当前用户信息") -public Result getUserInfoByToken(HttpServletRequest request) { - UserInfoResponse userInfo = tokenService.getUserInfoByToken(request); - return Result.success(userInfo); -} -``` - -**改进**: -- ✅ 使用GET方法获取数据(符合RESTful规范) -- ✅ Token从请求头Authorization自动提取 -- ✅ 不需要额外的Request类 -- ✅ 客户端只需设置请求头 - -### 2. 删除TokenRequest类 - -**删除的文件**: -- `backend-single/src/main/java/com/emotion/dto/request/TokenRequest.java` - -**原因**: -- 不再需要通过请求体传递token -- 简化代码结构 -- 减少不必要的类 - -### 3. 添加API文档注解 - -```java -@Tag(name = "Token管理", description = "Token验证和用户信息获取") -public class TokenController { - - @GetMapping("/user-info") - @Operation(summary = "获取用户信息", description = "通过请求头中的token获取当前用户信息") - public Result getUserInfoByToken(HttpServletRequest request) { - // ... - } -} -``` - -## 📊 接口变更对比 - -### 接口1: 获取用户信息 - -#### 优化前 -- **请求方式**: `POST /token/user-info` -- **请求头**: 无 -- **请求体**: - ```json - { - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - ``` - -#### 优化后 -- **请求方式**: `GET /token/user-info` -- **请求头**: - ``` - Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - ``` -- **请求体**: 无 - ---- - -### 接口2: 获取用户名 - -#### 优化前 -- **请求方式**: `POST /token/username` -- **请求头**: 无 -- **请求体**: - ```json - { - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - ``` - -#### 优化后 -- **请求方式**: `GET /token/username` -- **请求头**: - ``` - Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - ``` -- **请求体**: 无 - ---- - -### 接口3: 验证Token - -#### 优化前 -- **请求方式**: `POST /token/validate` -- **请求头**: 无 -- **请求体**: - ```json - { - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - } - ``` - -#### 优化后 -- **请求方式**: `GET /token/validate` -- **请求头**: - ``` - Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... - ``` -- **请求体**: 无 - -## 🔧 Token提取机制 - -TokenUtil已经实现了标准的token提取逻辑: - -```java -public String extractToken(HttpServletRequest request) { - // 1. 优先从Authorization请求头获取(标准方式) - String authHeader = request.getHeader("Authorization"); - if (authHeader != null && authHeader.startsWith("Bearer ")) { - return authHeader.substring(7); - } - - // 2. 备用方案:从请求参数获取 - String tokenParam = request.getParameter("token"); - if (tokenParam != null && !tokenParam.trim().isEmpty()) { - return tokenParam.trim(); - } - - return null; -} -``` - -**支持的Token传递方式**: -1. **标准方式**(推荐):`Authorization: Bearer {token}` -2. **备用方式**:URL参数 `?token={token}` - -## 🚀 前端调用示例 - -### 优化前(不推荐) -```typescript -// 需要在请求体中传递token -const response = await http.post('/token/user-info', { - token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' -}) -``` - -### 优化后(推荐) -```typescript -// Token自动从localStorage获取并添加到请求头 -const response = await http.get('/token/user-info') - -// 或者手动设置(如果需要) -const response = await http.get('/token/user-info', { - headers: { - 'Authorization': `Bearer ${token}` - } -}) -``` - -## 📝 优化收益 - -### 1. 符合标准规范 -- ✅ 遵循RESTful API设计原则 -- ✅ 使用标准的Authorization请求头 -- ✅ GET方法用于查询,POST方法用于修改 - -### 2. 提升安全性 -- ✅ Token不在请求体中暴露 -- ✅ 减少token在日志中被记录的风险 -- ✅ 符合OAuth 2.0和JWT标准 - -### 3. 简化代码 -- ✅ 删除不必要的TokenRequest类 -- ✅ 减少代码维护成本 -- ✅ 统一token处理方式 - -### 4. 改善开发体验 -- ✅ 前端不需要构造请求体 -- ✅ 自动从请求头提取token -- ✅ 更清晰的API文档 - -### 5. 提高可维护性 -- ✅ 代码结构更清晰 -- ✅ 职责分离更明确 -- ✅ 便于后续扩展 - -## 🔍 相关文件变更 - -### 修改的文件 -1. `backend-single/src/main/java/com/emotion/controller/TokenController.java` - - 修改所有接口从POST改为GET - - 参数从`@RequestBody TokenRequest`改为`HttpServletRequest` - - 添加Swagger API文档注解 - - 添加详细的注释说明 - -### 删除的文件 -1. `backend-single/src/main/java/com/emotion/dto/request/TokenRequest.java` - - 不再需要此类 - -### 未修改的文件 -1. `TokenService.java` - 接口已经是标准方式 -2. `TokenServiceImpl.java` - 实现已经是标准方式 -3. `TokenUtil.java` - 工具类已经支持标准方式 - -## ✅ 测试验证 - -### 测试用例 - -#### 1. 测试获取用户信息 -```bash -curl -X GET http://localhost:8080/token/user-info \ - -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." -``` - -**预期结果**: -```json -{ - "code": 200, - "message": "操作成功", - "data": { - "userId": "user_123", - "username": "testuser", - "email": "test@example.com" - } -} -``` - -#### 2. 测试获取用户名 -```bash -curl -X GET http://localhost:8080/token/username \ - -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." -``` - -**预期结果**: -```json -{ - "code": 200, - "message": "操作成功", - "data": "testuser" -} -``` - -#### 3. 测试验证Token -```bash -curl -X GET http://localhost:8080/token/validate \ - -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." -``` - -**预期结果**: -```json -{ - "code": 200, - "message": "操作成功", - "data": "user_123" -} -``` - -#### 4. 测试无效Token -```bash -curl -X GET http://localhost:8080/token/user-info \ - -H "Authorization: Bearer invalid_token" -``` - -**预期结果**: -```json -{ - "code": 401, - "message": "访问令牌无效或已过期", - "data": null -} -``` - -#### 5. 测试缺少Token -```bash -curl -X GET http://localhost:8080/token/user-info -``` - -**预期结果**: -```json -{ - "code": 401, - "message": "未提供访问令牌", - "data": null -} -``` - -## 📚 最佳实践 - -### 1. Token传递方式 -- ✅ **推荐**:使用`Authorization: Bearer {token}`请求头 -- ⚠️ **备用**:使用URL参数`?token={token}`(仅在特殊场景) -- ❌ **不推荐**:在请求体中传递token - -### 2. HTTP方法选择 -- ✅ **GET**:获取资源(如获取用户信息、验证token) -- ✅ **POST**:创建资源(如登录、注册) -- ✅ **PUT/PATCH**:更新资源 -- ✅ **DELETE**:删除资源 - -### 3. 安全建议 -- 使用HTTPS传输token -- 设置合理的token过期时间 -- 实现token刷新机制 -- 记录token使用日志 - -## 🎯 总结 - -通过这次优化,TokenController已经完全符合RESTful API标准和安全最佳实践: - -1. ✅ **标准化**:使用标准的Authorization请求头传递token -2. ✅ **简化**:删除不必要的TokenRequest类 -3. ✅ **规范化**:GET方法用于查询操作 -4. ✅ **文档化**:添加完整的Swagger API文档 -5. ✅ **安全性**:Token不在请求体中暴露 - -现在TokenController的实现更加专业、安全、易用! diff --git a/TOKEN_CONTROLLER_REFACTOR_SUMMARY.md b/TOKEN_CONTROLLER_REFACTOR_SUMMARY.md deleted file mode 100644 index 259d981..0000000 --- a/TOKEN_CONTROLLER_REFACTOR_SUMMARY.md +++ /dev/null @@ -1,275 +0,0 @@ -# TokenController重构总结 - -## 🎯 重构目标 - -优化 `com.emotion.controller.TokenController` 中的相关接口,将token从请求体传递改为标准的从请求头自动获取的方式,删除不必要的 `TokenRequest` 类及相关引用。 - -## ✅ 完成的工作 - -### 1. 修改TokenController - -**文件**: `backend-single/src/main/java/com/emotion/controller/TokenController.java` - -#### 主要变更 - -| 变更项 | 修改前 | 修改后 | -|--------|--------|--------| -| 请求方式 | `@PostMapping` | `@GetMapping` | -| 参数类型 | `@RequestBody TokenRequest` | `HttpServletRequest` | -| Token获取 | `request.getToken()` | 自动从请求头提取 | -| 导入包 | `TokenRequest` | `HttpServletRequest` | -| API文档 | 无 | 添加Swagger注解 | - -#### 具体修改 - -```java -// 修改前 -@PostMapping("/user-info") -public Result getUserInfoByToken(@RequestBody @Validated TokenRequest request) { - UserInfoResponse userInfo = tokenService.getUserInfoByToken(request.getToken()); - return Result.success(userInfo); -} - -// 修改后 -@GetMapping("/user-info") -@Operation(summary = "获取用户信息", description = "通过请求头中的token获取当前用户信息") -public Result getUserInfoByToken(HttpServletRequest request) { - UserInfoResponse userInfo = tokenService.getUserInfoByToken(request); - return Result.success(userInfo); -} -``` - -### 2. 删除TokenRequest类 - -**删除的文件**: `backend-single/src/main/java/com/emotion/dto/request/TokenRequest.java` - -**原因**: -- 不再需要通过请求体传递token -- 简化代码结构 -- 减少不必要的类维护 - -### 3. 添加API文档注解 - -为TokenController添加了完整的Swagger文档注解: - -```java -@RestController -@RequestMapping("/token") -@Tag(name = "Token管理", description = "Token验证和用户信息获取") -public class TokenController { - - @GetMapping("/user-info") - @Operation(summary = "获取用户信息", description = "通过请求头中的token获取当前用户信息") - public Result getUserInfoByToken(HttpServletRequest request) { - // ... - } - - @GetMapping("/username") - @Operation(summary = "获取用户名", description = "通过请求头中的token获取当前用户名") - public Result getUsernameByToken(HttpServletRequest request) { - // ... - } - - @GetMapping("/validate") - @Operation(summary = "验证Token", description = "验证请求头中的token并返回用户ID") - public Result validateTokenAndGetUserId(HttpServletRequest request) { - // ... - } -} -``` - -## 📊 接口变更详情 - -### 接口1: 获取用户信息 - -| 项目 | 修改前 | 修改后 | -|------|--------|--------| -| 路径 | `/token/user-info` | `/token/user-info` | -| 方法 | POST | GET | -| 请求头 | 无 | `Authorization: Bearer {token}` | -| 请求体 | `{"token": "..."}` | 无 | -| 响应 | 相同 | 相同 | - -### 接口2: 获取用户名 - -| 项目 | 修改前 | 修改后 | -|------|--------|--------| -| 路径 | `/token/username` | `/token/username` | -| 方法 | POST | GET | -| 请求头 | 无 | `Authorization: Bearer {token}` | -| 请求体 | `{"token": "..."}` | 无 | -| 响应 | 相同 | 相同 | - -### 接口3: 验证Token - -| 项目 | 修改前 | 修改后 | -|------|--------|--------| -| 路径 | `/token/validate` | `/token/validate` | -| 方法 | POST | GET | -| 请求头 | 无 | `Authorization: Bearer {token}` | -| 请求体 | `{"token": "..."}` | 无 | -| 响应 | 相同 | 相同 | - -## 🔧 技术实现 - -### Token提取机制 - -TokenUtil已经实现了标准的token提取逻辑: - -```java -public String extractToken(HttpServletRequest request) { - // 1. 优先从Authorization请求头获取(标准方式) - String authHeader = request.getHeader("Authorization"); - if (authHeader != null && authHeader.startsWith("Bearer ")) { - return authHeader.substring(7); - } - - // 2. 备用方案:从请求参数获取 - String tokenParam = request.getParameter("token"); - if (tokenParam != null && !tokenParam.trim().isEmpty()) { - return tokenParam.trim(); - } - - return null; -} -``` - -### TokenService接口 - -TokenService接口已经是标准方式,无需修改: - -```java -public interface TokenService { - UserInfoResponse getUserInfoByToken(HttpServletRequest request); - String getUsernameByToken(HttpServletRequest request); - String validateTokenAndGetUserId(HttpServletRequest request); -} -``` - -### TokenServiceImpl实现 - -TokenServiceImpl实现已经是标准方式,无需修改: - -```java -@Override -public UserInfoResponse getUserInfoByToken(HttpServletRequest request) { - String userId = validateTokenAndGetUserId(request); - return authService.getCurrentUserInfo(userId); -} -``` - -## 📝 影响分析 - -### 后端影响 - -✅ **无破坏性影响** -- TokenService接口和实现已经是标准方式 -- 只修改了Controller层的参数接收方式 -- 业务逻辑完全不变 - -### 前端影响 - -✅ **无需修改前端代码** -- 经过检查,前端代码中没有使用这些接口 -- 前端主要使用AuthController中的接口 -- 如果将来需要使用,参考迁移指南即可 - -### 数据库影响 - -✅ **无影响** -- 不涉及数据库结构变更 -- 不涉及数据迁移 - -## 🚀 优化收益 - -### 1. 符合标准规范 -- ✅ 遵循RESTful API设计原则 -- ✅ 使用标准的Authorization请求头 -- ✅ GET方法用于查询,POST方法用于修改 - -### 2. 提升安全性 -- ✅ Token不在请求体中暴露 -- ✅ 减少token在日志中被记录的风险 -- ✅ 符合OAuth 2.0和JWT标准 - -### 3. 简化代码 -- ✅ 删除不必要的TokenRequest类 -- ✅ 减少代码维护成本 -- ✅ 统一token处理方式 - -### 4. 改善开发体验 -- ✅ 前端不需要构造请求体 -- ✅ 自动从请求头提取token -- ✅ 更清晰的API文档 - -### 5. 提高可维护性 -- ✅ 代码结构更清晰 -- ✅ 职责分离更明确 -- ✅ 便于后续扩展 - -## 📚 相关文档 - -1. **TOKEN_CONTROLLER_OPTIMIZATION.md** - 详细的优化说明 -2. **FRONTEND_TOKEN_API_MIGRATION_GUIDE.md** - 前端迁移指南(如果需要) - -## ✅ 测试建议 - -### 1. 单元测试 -```java -@Test -public void testGetUserInfoByToken() { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Authorization", "Bearer " + validToken); - - Result result = tokenController.getUserInfoByToken(request); - - assertEquals(200, result.getCode()); - assertNotNull(result.getData()); -} -``` - -### 2. 集成测试 -```bash -# 测试获取用户信息 -curl -X GET http://localhost:8080/token/user-info \ - -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - -# 测试获取用户名 -curl -X GET http://localhost:8080/token/username \ - -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." - -# 测试验证Token -curl -X GET http://localhost:8080/token/validate \ - -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." -``` - -### 3. 错误场景测试 -```bash -# 测试无效Token -curl -X GET http://localhost:8080/token/user-info \ - -H "Authorization: Bearer invalid_token" - -# 测试缺少Token -curl -X GET http://localhost:8080/token/user-info -``` - -## 🎯 总结 - -本次重构成功完成了以下目标: - -1. ✅ **标准化Token传递方式** - 从请求体改为请求头 -2. ✅ **简化代码结构** - 删除不必要的TokenRequest类 -3. ✅ **符合RESTful规范** - 使用GET方法获取资源 -4. ✅ **提升安全性** - Token不在请求体中暴露 -5. ✅ **完善API文档** - 添加Swagger注解 -6. ✅ **无破坏性影响** - 前端代码无需修改 - -重构后的TokenController更加专业、安全、易用,完全符合现代Web API的最佳实践! - -## 📅 重构信息 - -- **重构日期**: 2025-10-06 -- **重构人**: Augment Agent -- **影响范围**: TokenController及TokenRequest类 -- **向后兼容**: 是(TokenUtil支持URL参数备用方案) -- **需要前端修改**: 否