feat: 完善后端架构 - 标准化Controller层和Service层

 新功能:
- 创建了完整的Service层架构,包含所有业务实体的Service接口和实现类
- 新增8个标准化的Controller类,支持完整的CRUD操作
- 实现了统一的Request/Response模式和分页查询功能
- 创建了认证服务(AuthService)和令牌服务(TokenService)
- 添加了Redis配置和认证拦截器

🏗️ 架构优化:
- 移除Controller层所有try-catch块,使用全局异常处理机制
- 创建了专门的异常类(AuthException, TokenException, CaptchaException)
- 统一了API返回格式,完善了Result类的方法
- 实现了标准的分页查询和参数校验

📦 新增文件:
- 8个Controller类: Achievement, Comment, CommunityPost, Conversation, CozeApiCall, EmotionAnalysis, Reward, UserStats
- 12个Service接口和对应的实现类
- 标准化的DTO类(Request/Response)
- 异常处理类和拦截器
- 测试用例

🔧 重构优化:
- 重写了AuthController,移除所有业务逻辑到Service层
- 优化了MessageController,使用标准的Request/Response格式
- 更新了全局异常处理器,支持多种异常类型
- 完善了WebConfig配置,添加认证拦截器

📊 代码统计:
- 新增文件: 60+个
- 新增代码行数: 8000+行
- 重构代码行数: 1000+行
- 移除过时接口: 4个
This commit is contained in:
2025-07-24 07:38:40 +08:00
parent 880e0e3c88
commit 873b8e55da
67 changed files with 8619 additions and 850 deletions
@@ -1,293 +1,130 @@
package com.emotion.controller;
import com.emotion.common.Result;
import com.emotion.entity.User;
import com.emotion.service.UserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.emotion.dto.request.LoginRequest;
import com.emotion.dto.request.RegisterRequest;
import com.emotion.dto.response.AuthResponse;
import com.emotion.dto.response.CaptchaResponse;
import com.emotion.dto.response.UserInfoResponse;
import com.emotion.service.AuthService;
import com.emotion.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import com.emotion.util.JwtUtil;
import javax.servlet.http.HttpServletRequest;
/**
* 认证控制器
*
*
* @author emotion-museum
* @date 2025-07-22
* @date 2025-07-23
*/
@RestController
@RequestMapping("/auth")
public class AuthController {
private static final Logger log = LoggerFactory.getLogger(AuthController.class);
// 验证码存储(生产环境应使用Redis)
private static final Map<String, String> captchaStore = new ConcurrentHashMap<>();
@Autowired
private AuthService authService;
@Autowired
private UserService userService;
@Autowired
private JwtUtil jwtUtil;
private TokenService tokenService;
/**
* 用户登录
*/
@PostMapping("/login")
public Result<Map<String, Object>> login(@RequestBody Map<String, String> request) {
log.info("用户登录请求: {}", request.get("account"));
try {
String account = request.get("account");
String password = request.get("password");
String captcha = request.get("captcha");
String captchaKey = request.get("captchaKey");
if (account == null || password == null) {
return Result.error("账号和密码不能为空");
}
// 验证验证码
if (captcha == null || captchaKey == null) {
return Result.error("验证码不能为空");
}
String storedCaptcha = captchaStore.get(captchaKey);
if (storedCaptcha == null) {
return Result.error("验证码已过期");
}
if (!storedCaptcha.equals(captcha.toLowerCase())) {
captchaStore.remove(captchaKey); // 验证失败后移除验证码
return Result.error("验证码错误");
}
// 验证成功后移除验证码
captchaStore.remove(captchaKey);
// 查找用户
User user = userService.findByAccount(account);
if (user == null) {
return Result.error("用户不存在");
}
// 验证密码
if (!userService.validatePassword(password, user.getPassword())) {
return Result.error("密码错误");
}
// 更新最后活跃时间
userService.updateLastActiveTime(user.getId());
// 生成JWT token
String accessToken = jwtUtil.generateToken(user.getId(), user.getUsername());
String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername());
// 构建响应
Map<String, Object> response = new HashMap<>();
response.put("accessToken", accessToken);
response.put("refreshToken", refreshToken);
response.put("expiresIn", 86400L);
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", user.getId());
userInfo.put("username", user.getUsername());
userInfo.put("account", user.getAccount());
userInfo.put("nickname", user.getNickname());
userInfo.put("avatar", user.getAvatar());
userInfo.put("status", user.getStatus());
response.put("userInfo", userInfo);
response.put("loginTime", LocalDateTime.now());
return Result.success("登录成功", response);
} catch (Exception e) {
log.error("用户登录失败: {}", e.getMessage());
return Result.error("登录失败: " + e.getMessage());
}
public Result<AuthResponse> login(@RequestBody @Validated LoginRequest request) {
AuthResponse response = authService.login(request);
return Result.success("登录成功", response);
}
/**
* 用户注册
*/
@PostMapping("/register")
public Result<Map<String, Object>> register(@RequestBody Map<String, String> request) {
log.info("用户注册请求: {}", request.get("account"));
try {
String account = request.get("account");
String password = request.get("password");
String username = request.get("username");
String email = request.get("email");
String phone = request.get("phone");
String nickname = request.get("nickname");
String captcha = request.get("captcha");
String captchaKey = request.get("captchaKey");
if (account == null || password == null) {
return Result.error("账号和密码不能为空");
}
// 验证验证码
if (captcha == null || captchaKey == null) {
return Result.error("验证码不能为空");
}
String storedCaptcha = captchaStore.get(captchaKey);
if (storedCaptcha == null) {
return Result.error("验证码已过期");
}
if (!storedCaptcha.equals(captcha.toLowerCase())) {
captchaStore.remove(captchaKey); // 验证失败后移除验证码
return Result.error("验证码错误");
}
// 验证成功后移除验证码
captchaStore.remove(captchaKey);
// 检查账号是否已存在
if (userService.accountExists(account)) {
return Result.error("账号已存在");
}
// 创建用户
User user = new User();
user.setAccount(account);
user.setPassword(password);
user.setUsername(username != null ? username : account);
user.setEmail(email);
user.setPhone(phone);
user.setNickname(nickname != null ? nickname : username != null ? username : account);
User createdUser = userService.createUser(user);
// 生成JWT token(注册成功后自动登录)
String accessToken = jwtUtil.generateToken(createdUser.getId(), createdUser.getUsername());
String refreshToken = jwtUtil.generateRefreshToken(createdUser.getId(), createdUser.getUsername());
// 构建用户信息
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", createdUser.getId());
userInfo.put("username", createdUser.getUsername());
userInfo.put("account", createdUser.getAccount());
userInfo.put("nickname", createdUser.getNickname());
userInfo.put("avatar", createdUser.getAvatar());
userInfo.put("status", createdUser.getStatus());
userInfo.put("createTime", createdUser.getCreateTime());
// 构建完整响应(包含token信息)
Map<String, Object> response = new HashMap<>();
response.put("accessToken", accessToken);
response.put("refreshToken", refreshToken);
response.put("expiresIn", 86400L); // 24小时
response.put("userInfo", userInfo);
response.put("loginTime", LocalDateTime.now());
log.info("用户注册并自动登录成功: {}", createdUser.getAccount());
return Result.success("注册成功,已自动登录", response);
} catch (Exception e) {
log.error("用户注册失败: {}", e.getMessage());
return Result.error("注册失败: " + e.getMessage());
}
public Result<AuthResponse> register(@RequestBody @Validated RegisterRequest request) {
AuthResponse response = authService.register(request);
return Result.success("注册成功", response);
}
/**
* 获取当前用户信息
*/
@GetMapping("/user-info")
public Result<Map<String, Object>> getCurrentUserInfo(HttpServletRequest request) {
try {
// 从请求属性中获取用户信息(由JWT拦截器设置)
String userId = (String) request.getAttribute("userId");
String username = (String) request.getAttribute("username");
if (userId == null) {
return Result.error("用户未登录");
}
// 根据用户ID获取完整用户信息
User user = userService.findById(userId);
if (user == null) {
return Result.error("用户不存在");
}
// 构建用户信息响应
Map<String, Object> userInfo = new HashMap<>();
userInfo.put("id", user.getId());
userInfo.put("username", user.getUsername());
userInfo.put("account", user.getAccount());
userInfo.put("nickname", user.getNickname());
userInfo.put("avatar", user.getAvatar());
userInfo.put("email", user.getEmail());
userInfo.put("phone", user.getPhone());
userInfo.put("status", user.getStatus());
userInfo.put("createTime", user.getCreateTime());
return Result.success("获取用户信息成功", userInfo);
} catch (Exception e) {
log.error("获取用户信息失败: {}", e.getMessage());
return Result.error("获取用户信息失败");
}
@GetMapping("/user/info")
public Result<UserInfoResponse> getCurrentUserInfo(HttpServletRequest request) {
String token = extractToken(request);
UserInfoResponse userInfo = tokenService.getUserInfoByToken(token);
return Result.success(userInfo);
}
/**
* 获取验证码
* 生成验证码
*/
@GetMapping("/captcha")
public Result<Map<String, Object>> getCaptcha() {
log.info("获取验证码请求");
try {
// 生成验证码
SpecCaptcha captcha = new SpecCaptcha(130, 48, 4);
captcha.setCharType(Captcha.TYPE_DEFAULT);
// 生成验证码key
String captchaKey = "captcha_" + System.currentTimeMillis();
String captchaText = captcha.text().toLowerCase();
// 存储验证码(5分钟过期)
captchaStore.put(captchaKey, captchaText);
// 5分钟后清理验证码
new Thread(() -> {
try {
Thread.sleep(300000); // 5分钟
captchaStore.remove(captchaKey);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
Map<String, Object> response = new HashMap<>();
response.put("key", captchaKey);
response.put("image", captcha.toBase64().replace("data:image/png;base64,", ""));
response.put("expireTime", 300);
log.info("生成验证码成功,key: {}, text: {}", captchaKey, captchaText);
return Result.success("获取验证码成功", response);
} catch (Exception e) {
log.error("获取验证码失败: {}", e.getMessage());
return Result.error("获取验证码失败");
}
public Result<CaptchaResponse> generateCaptcha() {
CaptchaResponse response = authService.generateCaptcha();
return Result.success(response);
}
/**
* 用户登出
*/
@PostMapping("/logout")
public Result<String> logout(@RequestBody Map<String, String> request) {
log.info("用户登出请求: {}", request.get("userId"));
return Result.success("登出成功");
public Result<Void> logout(HttpServletRequest request) {
String token = extractToken(request);
authService.logoutByToken(token);
return Result.success();
}
}
/**
* 刷新访问令牌
*/
@PostMapping("/refresh")
public Result<AuthResponse> refreshToken(@RequestParam String refreshToken) {
AuthResponse response = authService.refreshToken(refreshToken);
return Result.success("令牌刷新成功", response);
}
/**
* 验证访问令牌
*/
@GetMapping("/validate")
public Result<Boolean> validateToken(HttpServletRequest request) {
String token = extractToken(request);
if (token == null) {
return Result.success(false);
}
boolean isValid = authService.validateToken(token);
return Result.success(isValid);
}
/**
* 获取用户名(通过令牌)
*/
@GetMapping("/username")
public Result<String> getUsernameFromToken(HttpServletRequest request) {
String token = extractToken(request);
String username = tokenService.getUsernameByToken(token);
return Result.success(username);
}
/**
* 从请求中提取访问令牌
*/
private String extractToken(HttpServletRequest request) {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
return authHeader.substring(7);
}
// 也可以从请求参数中获取
String tokenParam = request.getParameter("token");
if (tokenParam != null && !tokenParam.trim().isEmpty()) {
return tokenParam.trim();
}
return null;
}
}