feat: 用户相关api接口
This commit is contained in:
@@ -9,10 +9,6 @@ public record ApiResponse<T>(int code, String message, T data) {
|
||||
|
||||
/**
|
||||
* 成功响应
|
||||
*
|
||||
* @param data 响应数据
|
||||
* @param <T> 数据类型
|
||||
* @return ApiResponse
|
||||
*/
|
||||
public static <T> ApiResponse<T> ok(T data) {
|
||||
return new ApiResponse<>(0, "success", data);
|
||||
@@ -20,11 +16,6 @@ public record ApiResponse<T>(int code, String message, T data) {
|
||||
|
||||
/**
|
||||
* 失败响应
|
||||
*
|
||||
* @param code 业务状态码
|
||||
* @param message 错误消息
|
||||
* @param <T> 数据类型
|
||||
* @return ApiResponse
|
||||
*/
|
||||
public static <T> ApiResponse<T> fail(int code, String message) {
|
||||
return new ApiResponse<>(code, message, null);
|
||||
|
||||
@@ -2,25 +2,18 @@ package com.webgame.webgamebackend.common.dto.account;
|
||||
|
||||
import com.webgame.webgamebackend.common.dto.base.BaseVo;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 登录请求
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class LoginRequest extends BaseVo {
|
||||
public record LoginRequest(
|
||||
@NotNull(message = "用户名不能为null")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
String username,
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password;
|
||||
@NotNull(message = "密码不能为null")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
String password
|
||||
) implements BaseVo {
|
||||
}
|
||||
|
||||
@@ -3,4 +3,5 @@ package com.webgame.webgamebackend.common.dto.account;
|
||||
/**
|
||||
* 登录/注册响应
|
||||
*/
|
||||
public record LoginResponse(String token) {}
|
||||
public record LoginResponse(String token) {
|
||||
}
|
||||
|
||||
@@ -2,28 +2,21 @@ package com.webgame.webgamebackend.common.dto.account;
|
||||
|
||||
import com.webgame.webgamebackend.common.dto.base.BaseVo;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 注册请求
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class RegisterRequest extends BaseVo {
|
||||
public record RegisterRequest(
|
||||
@NotNull(message = "用户名不能为null")
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 32, message = "用户名长度3-32位")
|
||||
String username,
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
@Size(min = 3, max = 32, message = "用户名长度3-32位")
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 64, message = "密码长度6-64位")
|
||||
private String password;
|
||||
@NotNull(message = "密码不能为null")
|
||||
@NotBlank(message = "密码不能为空")
|
||||
@Size(min = 6, max = 64, message = "密码长度6-64位")
|
||||
String password
|
||||
) implements BaseVo {
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package com.webgame.webgamebackend.common.dto.base;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 请求参数基类,所有入参 VO 均继承此类
|
||||
* 入参 VO 标记接口,所有请求参数 record 均实现此接口
|
||||
*/
|
||||
public abstract class BaseVo implements Serializable {
|
||||
public interface BaseVo extends Serializable {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.webgame.webgamebackend.common.dto.user;
|
||||
|
||||
import com.webgame.webgamebackend.entities.UserEntity;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 用户信息响应
|
||||
*/
|
||||
public record UserInfoResponse(
|
||||
String nickName,
|
||||
String avatar,
|
||||
String signature,
|
||||
Integer age,
|
||||
Integer sex,
|
||||
LocalDate birthday
|
||||
) {
|
||||
/**
|
||||
* 从实体转换
|
||||
*/
|
||||
public static UserInfoResponse from(UserEntity user) {
|
||||
return new UserInfoResponse(
|
||||
user.getNickName(),
|
||||
user.getAvatar(),
|
||||
user.getSignature(),
|
||||
user.getAge(),
|
||||
user.getSex(),
|
||||
user.getBirthday()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,9 @@ public enum ErrorCode {
|
||||
// ========== 账号模块 ==========
|
||||
USERNAME_EXISTS(1001, "用户名已存在"),
|
||||
BAD_CREDENTIALS(1002, "用户名或密码错误"),
|
||||
|
||||
// ========== 用户模块 ==========
|
||||
USER_NOT_FOUND(2001, "用户不存在"),
|
||||
;
|
||||
|
||||
private final int code;
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
package com.webgame.webgamebackend.common.handler;
|
||||
|
||||
import cn.dev33.satoken.exception.NotLoginException;
|
||||
import cn.dev33.satoken.exception.NotPermissionException;
|
||||
import cn.dev33.satoken.exception.NotRoleException;
|
||||
import com.webgame.webgamebackend.common.dto.ApiResponse;
|
||||
import com.webgame.webgamebackend.common.exception.BusinessException;
|
||||
import com.webgame.webgamebackend.common.exception.ErrorCode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
|
||||
/**
|
||||
* 全局异常处理,统一返回 ApiResponse 格式
|
||||
@@ -15,6 +22,8 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
// ==================== 业务异常 ====================
|
||||
|
||||
/**
|
||||
* 处理业务异常
|
||||
*/
|
||||
@@ -24,18 +33,89 @@ public class GlobalExceptionHandler {
|
||||
return ApiResponse.fail(ex.getCode(), ex.getMessage());
|
||||
}
|
||||
|
||||
// ==================== 参数校验异常 ====================
|
||||
|
||||
/**
|
||||
* 处理参数校验异常
|
||||
* 处理 @Valid 参数校验失败,合并所有字段的错误消息
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ApiResponse<Void> handleValidation(MethodArgumentNotValidException ex) {
|
||||
String message = ex.getBindingResult().getFieldErrors().stream()
|
||||
.findFirst()
|
||||
.map(e -> e.getDefaultMessage())
|
||||
.map(e -> e.getField() + " " + e.getDefaultMessage())
|
||||
.reduce((a, b) -> a + "; " + b)
|
||||
.orElse(ErrorCode.BAD_REQUEST.getDefaultMessage());
|
||||
return ApiResponse.fail(ErrorCode.BAD_REQUEST.getCode(), message);
|
||||
}
|
||||
|
||||
// ==================== Sa-Token 鉴权异常 ====================
|
||||
|
||||
/**
|
||||
* 未登录 / token 无效
|
||||
*/
|
||||
@ExceptionHandler(NotLoginException.class)
|
||||
public ApiResponse<Void> handleNotLogin(NotLoginException ex) {
|
||||
log.warn("未登录: {}", ex.getMessage());
|
||||
return ApiResponse.fail(401, "请先登录");
|
||||
}
|
||||
|
||||
/**
|
||||
* 无权限
|
||||
*/
|
||||
@ExceptionHandler(NotPermissionException.class)
|
||||
public ApiResponse<Void> handleNotPermission(NotPermissionException ex) {
|
||||
log.warn("无权限: {}", ex.getMessage());
|
||||
return ApiResponse.fail(403, "无权限");
|
||||
}
|
||||
|
||||
/**
|
||||
* 无角色
|
||||
*/
|
||||
@ExceptionHandler(NotRoleException.class)
|
||||
public ApiResponse<Void> handleNotRole(NotRoleException ex) {
|
||||
log.warn("无角色: {}", ex.getMessage());
|
||||
return ApiResponse.fail(403, "无权限");
|
||||
}
|
||||
|
||||
// ==================== Spring MVC 异常 ====================
|
||||
|
||||
/**
|
||||
* 请求方法不支持(如 GET 访问 POST 接口)
|
||||
*/
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ApiResponse<Void> handleMethodNotSupported(HttpRequestMethodNotSupportedException ex) {
|
||||
log.warn("请求方法不支持: {}", ex.getMessage());
|
||||
return ApiResponse.fail(405, "请求方法不支持");
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求体不可读(如 JSON 格式错误)
|
||||
*/
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ApiResponse<Void> handleMessageNotReadable(HttpMessageNotReadableException ex) {
|
||||
log.warn("请求体解析失败: {}", ex.getMessage());
|
||||
return ApiResponse.fail(ErrorCode.BAD_REQUEST.getCode(), "请求体格式错误");
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径参数类型不匹配
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
public ApiResponse<Void> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
|
||||
log.warn("参数类型不匹配: {}", ex.getMessage());
|
||||
return ApiResponse.fail(ErrorCode.BAD_REQUEST.getCode(), "参数类型错误");
|
||||
}
|
||||
|
||||
/**
|
||||
* 资源不存在(404)
|
||||
*/
|
||||
@ExceptionHandler(NoResourceFoundException.class)
|
||||
public ApiResponse<Void> handleNoResourceFound(NoResourceFoundException ex) {
|
||||
log.warn("资源不存在: {}", ex.getMessage());
|
||||
return ApiResponse.fail(404, "资源不存在");
|
||||
}
|
||||
|
||||
// ==================== 兜底 ====================
|
||||
|
||||
/**
|
||||
* 处理其他未捕获异常
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user