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
@@ -0,0 +1,113 @@
package com.emotion.interceptor;
import com.emotion.service.AuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 认证拦截器
*
* @author emotion-museum
* @date 2025-07-23
*/
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private AuthService authService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 跨域预检请求直接放行
if ("OPTIONS".equals(request.getMethod())) {
return true;
}
// 获取请求路径
String requestURI = request.getRequestURI();
// 白名单路径,不需要认证
if (isWhiteList(requestURI)) {
return true;
}
// 提取访问令牌
String token = extractToken(request);
if (token == null) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"message\":\"未提供访问令牌\",\"data\":null}");
return false;
}
// 验证访问令牌
if (!authService.validateToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"message\":\"访问令牌无效或已过期\",\"data\":null}");
return false;
}
// 将用户ID存储到请求属性中,供后续使用
String userId = authService.getUserIdFromToken(token);
request.setAttribute("userId", userId);
request.setAttribute("token", token);
return true;
}
/**
* 检查是否为白名单路径
*/
private boolean isWhiteList(String requestURI) {
// 认证相关接口
if (requestURI.startsWith("/auth/")) {
return true;
}
// 静态资源
if (requestURI.startsWith("/static/") ||
requestURI.startsWith("/css/") ||
requestURI.startsWith("/js/") ||
requestURI.startsWith("/images/")) {
return true;
}
// Swagger文档
if (requestURI.startsWith("/swagger-") ||
requestURI.startsWith("/v2/api-docs") ||
requestURI.startsWith("/webjars/")) {
return true;
}
// 健康检查
if (requestURI.equals("/health") || requestURI.equals("/actuator/health")) {
return true;
}
return false;
}
/**
* 从请求中提取访问令牌
*/
private String extractToken(HttpServletRequest request) {
// 从Authorization头中获取
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;
}
}