服务层重构与优化:补全所有ServiceImpl实现类,修复RestTemplate注入,完善DTO与配置,保证编译与启动通过

This commit is contained in:
2025-07-24 14:15:31 +08:00
parent 873b8e55da
commit cf4d73ceff
95 changed files with 5889 additions and 2282 deletions
@@ -0,0 +1,62 @@
package com.emotionmuseum.common.exception;
import com.emotionmuseum.common.result.ResultCode;
import lombok.Getter;
/**
* 认证异常
*
* @author emotion-museum
* @since 2025-07-24
*/
@Getter
public class AuthException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private final Integer code;
/**
* 错误消息
*/
private final String message;
public AuthException(String message) {
super(message);
this.code = ResultCode.UNAUTHORIZED.getCode();
this.message = message;
}
public AuthException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
public AuthException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public AuthException(String message, Throwable cause) {
super(message, cause);
this.code = ResultCode.UNAUTHORIZED.getCode();
this.message = message;
}
public AuthException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
this.message = message;
}
public AuthException(ResultCode resultCode, Throwable cause) {
super(resultCode.getMessage(), cause);
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
}
@@ -0,0 +1,68 @@
package com.emotionmuseum.common.exception;
import com.emotionmuseum.common.result.ResultCode;
import lombok.Getter;
/**
* 业务异常
*
* @author emotion-museum
* @since 2025-07-24
*/
@Getter
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private final Integer code;
/**
* 错误消息
*/
private final String message;
public BusinessException(String message) {
super(message);
this.code = ResultCode.BUSINESS_ERROR.getCode();
this.message = message;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
public BusinessException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public BusinessException(ResultCode resultCode, String message) {
super(message);
this.code = resultCode.getCode();
this.message = message;
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
this.code = ResultCode.BUSINESS_ERROR.getCode();
this.message = message;
}
public BusinessException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
this.message = message;
}
public BusinessException(ResultCode resultCode, Throwable cause) {
super(resultCode.getMessage(), cause);
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
}
@@ -0,0 +1,62 @@
package com.emotionmuseum.common.exception;
import com.emotionmuseum.common.result.ResultCode;
import lombok.Getter;
/**
* 验证码异常
*
* @author emotion-museum
* @since 2025-07-24
*/
@Getter
public class CaptchaException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private final Integer code;
/**
* 错误消息
*/
private final String message;
public CaptchaException(String message) {
super(message);
this.code = ResultCode.CAPTCHA_ERROR.getCode();
this.message = message;
}
public CaptchaException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
public CaptchaException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public CaptchaException(String message, Throwable cause) {
super(message, cause);
this.code = ResultCode.CAPTCHA_ERROR.getCode();
this.message = message;
}
public CaptchaException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
this.message = message;
}
public CaptchaException(ResultCode resultCode, Throwable cause) {
super(resultCode.getMessage(), cause);
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
}
@@ -0,0 +1,153 @@
package com.emotionmuseum.common.exception;
import com.emotionmuseum.common.result.Result;
import com.emotionmuseum.common.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.Set;
/**
* 全局异常处理器
*
* @author emotion-museum
* @since 2025-07-24
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理认证异常
*/
@ExceptionHandler(AuthException.class)
public Result<Void> handleAuthException(AuthException e, HttpServletRequest request) {
log.warn("认证异常: {} {} - {}", request.getMethod(), request.getRequestURI(), e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
/**
* 处理令牌异常
*/
@ExceptionHandler(TokenException.class)
public Result<Void> handleTokenException(TokenException e, HttpServletRequest request) {
log.warn("令牌异常: {} {} - {}", request.getMethod(), request.getRequestURI(), e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
/**
* 处理验证码异常
*/
@ExceptionHandler(CaptchaException.class)
public Result<Void> handleCaptchaException(CaptchaException e, HttpServletRequest request) {
log.warn("验证码异常: {} {} - {}", request.getMethod(), request.getRequestURI(), e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e, HttpServletRequest request) {
log.warn("业务异常: {} {} - {}", request.getMethod(), request.getRequestURI(), e.getMessage());
return Result.error(e.getCode(), e.getMessage());
}
/**
* 处理参数校验异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
log.warn("参数校验失败: {} {}", request.getMethod(), request.getRequestURI(), e);
StringBuilder message = new StringBuilder("参数校验失败: ");
for (FieldError error : e.getBindingResult().getFieldErrors()) {
message.append(error.getField()).append(" ").append(error.getDefaultMessage()).append("; ");
}
return Result.error(ResultCode.PARAM_VALIDATION_ERROR.getCode(), message.toString());
}
/**
* 处理Bean校验异常
*/
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<Void> handleBindException(BindException e, HttpServletRequest request) {
log.warn("参数绑定失败: {} {}", request.getMethod(), request.getRequestURI(), e);
StringBuilder message = new StringBuilder("参数绑定失败: ");
for (FieldError error : e.getBindingResult().getFieldErrors()) {
message.append(error.getField()).append(" ").append(error.getDefaultMessage()).append("; ");
}
return Result.error(ResultCode.PARAM_VALIDATION_ERROR.getCode(), message.toString());
}
/**
* 处理约束校验异常
*/
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<Void> handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
log.warn("约束校验失败: {} {}", request.getMethod(), request.getRequestURI(), e);
StringBuilder message = new StringBuilder("约束校验失败: ");
Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
for (ConstraintViolation<?> violation : violations) {
message.append(violation.getPropertyPath()).append(" ").append(violation.getMessage()).append("; ");
}
return Result.error(ResultCode.PARAM_VALIDATION_ERROR.getCode(), message.toString());
}
/**
* 处理非法参数异常
*/
@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Result<Void> handleIllegalArgumentException(IllegalArgumentException e, HttpServletRequest request) {
log.warn("非法参数: {} {}", request.getMethod(), request.getRequestURI(), e);
return Result.error(ResultCode.BAD_REQUEST.getCode(), "参数错误: " + e.getMessage());
}
/**
* 处理空指针异常
*/
@ExceptionHandler(NullPointerException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result<Void> handleNullPointerException(NullPointerException e, HttpServletRequest request) {
log.error("空指针异常: {} {}", request.getMethod(), request.getRequestURI(), e);
return Result.error(ResultCode.INTERNAL_SERVER_ERROR.getCode(), "系统内部错误");
}
/**
* 处理运行时异常
*/
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request) {
log.error("运行时异常: {} {}", request.getMethod(), request.getRequestURI(), e);
return Result.error(ResultCode.INTERNAL_SERVER_ERROR.getCode(), "系统运行异常: " + e.getMessage());
}
/**
* 处理所有其他异常
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Result<Void> handleException(Exception e, HttpServletRequest request) {
log.error("未知异常: {} {}", request.getMethod(), request.getRequestURI(), e);
return Result.error(ResultCode.INTERNAL_SERVER_ERROR.getCode(), "系统异常,请联系管理员");
}
}
@@ -0,0 +1,62 @@
package com.emotionmuseum.common.exception;
import com.emotionmuseum.common.result.ResultCode;
import lombok.Getter;
/**
* Token异常
*
* @author emotion-museum
* @since 2025-07-24
*/
@Getter
public class TokenException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private final Integer code;
/**
* 错误消息
*/
private final String message;
public TokenException(String message) {
super(message);
this.code = ResultCode.TOKEN_INVALID.getCode();
this.message = message;
}
public TokenException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
public TokenException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public TokenException(String message, Throwable cause) {
super(message, cause);
this.code = ResultCode.TOKEN_INVALID.getCode();
this.message = message;
}
public TokenException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
this.message = message;
}
public TokenException(ResultCode resultCode, Throwable cause) {
super(resultCode.getMessage(), cause);
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
}
@@ -0,0 +1,55 @@
package com.emotionmuseum.common.request;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
/**
* 基础分页请求类
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "基础分页请求")
public abstract class BasePageRequest extends BaseRequest {
private static final long serialVersionUID = 1L;
/**
* 页码,从1开始
*/
@Schema(description = "页码,从1开始", example = "1", minimum = "1")
@Min(value = 1, message = "页码必须大于0")
private Integer pageNum = 1;
/**
* 每页大小
*/
@Schema(description = "每页大小", example = "20", minimum = "1", maximum = "100")
@Min(value = 1, message = "每页大小必须大于0")
@Max(value = 100, message = "每页大小不能超过100")
private Integer pageSize = 20;
/**
* 排序字段
*/
@Schema(description = "排序字段", example = "create_time")
private String sortField;
/**
* 排序方向
*/
@Schema(description = "排序方向", example = "DESC", allowableValues = {"ASC", "DESC"})
private String sortOrder = "DESC";
/**
* 搜索关键词
*/
@Schema(description = "搜索关键词", example = "关键词")
private String keyword;
}
@@ -0,0 +1,60 @@
package com.emotionmuseum.common.request;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotBlank;
import java.io.Serializable;
/**
* 基础请求类
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@Schema(description = "基础请求")
public abstract class BaseRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 请求ID,用于链路追踪
*/
@Schema(description = "请求ID", example = "req_123456789")
private String requestId;
/**
* 客户端IP地址
*/
@Schema(description = "客户端IP地址", example = "192.168.1.100")
private String clientIp;
/**
* 用户代理信息
*/
@Schema(description = "用户代理信息", example = "Mozilla/5.0...")
private String userAgent;
/**
* 请求时间戳
*/
@Schema(description = "请求时间戳", example = "1721808000000")
private Long timestamp;
/**
* 设备类型
*/
@Schema(description = "设备类型", example = "WEB", allowableValues = {"WEB", "MOBILE", "APP"})
private String deviceType;
/**
* 应用版本
*/
@Schema(description = "应用版本", example = "1.0.0")
private String appVersion;
public BaseRequest() {
this.timestamp = System.currentTimeMillis();
}
}
@@ -0,0 +1,101 @@
package com.emotionmuseum.common.response;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
/**
* 基础分页响应类
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(description = "基础分页响应")
public class BasePageResponse<T> extends BaseResponse {
private static final long serialVersionUID = 1L;
/**
* 当前页码
*/
@Schema(description = "当前页码", example = "1")
private Long current;
/**
* 每页大小
*/
@Schema(description = "每页大小", example = "20")
private Long size;
/**
* 总记录数
*/
@Schema(description = "总记录数", example = "100")
private Long total;
/**
* 总页数
*/
@Schema(description = "总页数", example = "5")
private Long pages;
/**
* 数据列表
*/
@Schema(description = "数据列表")
private List<T> records;
/**
* 是否有下一页
*/
@Schema(description = "是否有下一页", example = "true")
private Boolean hasNext;
/**
* 是否有上一页
*/
@Schema(description = "是否有上一页", example = "false")
private Boolean hasPrevious;
public BasePageResponse() {
super();
}
public BasePageResponse(IPage<T> page) {
super();
this.current = page.getCurrent();
this.size = page.getSize();
this.total = page.getTotal();
this.pages = page.getPages();
this.records = page.getRecords();
this.hasNext = page.hasNext();
this.hasPrevious = page.hasPrevious();
}
/**
* 静态工厂方法
*/
public static <T> BasePageResponse<T> of(IPage<T> page) {
return new BasePageResponse<>(page);
}
/**
* 静态工厂方法
*/
public static <T> BasePageResponse<T> of(List<T> records, Long current, Long size, Long total) {
BasePageResponse<T> response = new BasePageResponse<>();
response.setRecords(records);
response.setCurrent(current);
response.setSize(size);
response.setTotal(total);
response.setPages((total + size - 1) / size);
response.setHasNext(current < response.getPages());
response.setHasPrevious(current > 1);
return response;
}
}
@@ -0,0 +1,73 @@
package com.emotionmuseum.common.response;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 基础响应类
*
* @author emotion-museum
* @since 2025-07-24
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@Schema(description = "基础响应")
public abstract class BaseResponse implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 响应时间戳
*/
@Schema(description = "响应时间戳", example = "1721808000000")
private Long timestamp;
/**
* 请求ID,用于链路追踪
*/
@Schema(description = "请求ID", example = "req_123456789")
private String requestId;
/**
* 服务器处理时间(毫秒)
*/
@Schema(description = "服务器处理时间(毫秒)", example = "150")
private Long processingTime;
/**
* 服务节点标识
*/
@Schema(description = "服务节点标识", example = "node-001")
private String serverNode;
public BaseResponse() {
this.timestamp = System.currentTimeMillis();
}
/**
* 设置请求ID
*/
public BaseResponse requestId(String requestId) {
this.requestId = requestId;
return this;
}
/**
* 设置处理时间
*/
public BaseResponse processingTime(Long processingTime) {
this.processingTime = processingTime;
return this;
}
/**
* 设置服务节点
*/
public BaseResponse serverNode(String serverNode) {
this.serverNode = serverNode;
return this;
}
}
@@ -127,4 +127,60 @@ public class Result<T> implements Serializable {
this.requestId = requestId;
return this;
}
/**
* 未授权响应
*/
public static <T> Result<T> unauthorized() {
return new Result<>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage());
}
/**
* 未授权响应(自定义消息)
*/
public static <T> Result<T> unauthorized(String message) {
return new Result<>(ResultCode.UNAUTHORIZED.getCode(), message);
}
/**
* 禁止访问响应
*/
public static <T> Result<T> forbidden() {
return new Result<>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage());
}
/**
* 禁止访问响应(自定义消息)
*/
public static <T> Result<T> forbidden(String message) {
return new Result<>(ResultCode.FORBIDDEN.getCode(), message);
}
/**
* 请求参数错误响应
*/
public static <T> Result<T> badRequest() {
return new Result<>(ResultCode.BAD_REQUEST.getCode(), ResultCode.BAD_REQUEST.getMessage());
}
/**
* 请求参数错误响应(自定义消息)
*/
public static <T> Result<T> badRequest(String message) {
return new Result<>(ResultCode.BAD_REQUEST.getCode(), message);
}
/**
* 资源不存在响应
*/
public static <T> Result<T> notFound() {
return new Result<>(ResultCode.NOT_FOUND.getCode(), ResultCode.NOT_FOUND.getMessage());
}
/**
* 资源不存在响应(自定义消息)
*/
public static <T> Result<T> notFound(String message) {
return new Result<>(ResultCode.NOT_FOUND.getCode(), message);
}
}