feat(biji-houdaun): 实现用户注册、登录、 Markdown 文件和图片上传功能
- 新增用户注册、登录接口及服务实现 - 添加 Markdown 文件创建、更新接口及服务实现 - 实现图片上传、获取接口及服务实现 - 集成 Snowflake ID 生成器 - 添加全局异常处理和统一返回结果封装 - 配置跨域访问和静态资源处理 - 实现基础的 XSS 防护
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package com.test.bijihoudaun.common.advice;
|
||||
|
||||
import com.test.bijihoudaun.common.exception.BaseException;
|
||||
|
||||
import com.test.bijihoudaun.common.response.R;
|
||||
import com.test.bijihoudaun.common.response.ResultCode;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
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.RestControllerAdvice;
|
||||
import org.springframework.web.multipart.MaxUploadSizeExceededException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 处理业务异常
|
||||
*/
|
||||
@ExceptionHandler(BaseException.class)
|
||||
public R<Void> handleBaseException(BaseException e, HttpServletRequest request) {
|
||||
return R.fail(e.getResultCode().getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理文件大小超出限制异常
|
||||
*/
|
||||
@ExceptionHandler(MaxUploadSizeExceededException.class)
|
||||
public R<String> handleFileSizeLimitExceeded() {
|
||||
return R.fail("文件大小超过限制");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数校验异常
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public R<List<String>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
|
||||
List<String> errors = e.getBindingResult().getFieldErrors()
|
||||
.stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.toList());
|
||||
return R.fail(ResultCode.VALIDATE_FAILED.getCode(), errors.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理绑定异常
|
||||
*/
|
||||
@ExceptionHandler(BindException.class)
|
||||
public R<List<String>> handleBindException(BindException e) {
|
||||
List<String> errors = e.getBindingResult().getFieldErrors()
|
||||
.stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.toList());
|
||||
return R.fail(ResultCode.VALIDATE_FAILED.getCode(), errors.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理其他异常
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public R<Void> handleException(Exception e) {
|
||||
return R.fail(ResultCode.FAILED.getCode(), "系统繁忙,请稍后再试");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.test.bijihoudaun.common.exception;
|
||||
|
||||
import com.test.bijihoudaun.common.response.ResultCode;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 基础业务异常类
|
||||
*/
|
||||
@Getter
|
||||
public class BaseException extends RuntimeException {
|
||||
private final ResultCode resultCode;
|
||||
|
||||
public BaseException(ResultCode resultCode) {
|
||||
super(resultCode.getMsg());
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
public BaseException(ResultCode resultCode, String message) {
|
||||
super(message);
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
public BaseException(ResultCode resultCode, Throwable cause) {
|
||||
super(resultCode.getMsg(), cause);
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
public BaseException(ResultCode resultCode, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.test.bijihoudaun.common.response;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ProblemDetail;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* 统一返回结果类
|
||||
* @param <T> 数据类型
|
||||
*/
|
||||
@Data
|
||||
public class R<T> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Integer code; // 状态码
|
||||
private String msg; // 消息
|
||||
private T data; // 数据
|
||||
|
||||
// 成功返回
|
||||
public static <T> R<T> success() {
|
||||
return success(null);
|
||||
}
|
||||
|
||||
public static <T> R<T> success(T data) {
|
||||
R<T> r = new R<>();
|
||||
r.setCode(ResultCode.SUCCESS.getCode());
|
||||
r.setMsg(ResultCode.SUCCESS.getMsg());
|
||||
r.setData(data);
|
||||
return r;
|
||||
}
|
||||
|
||||
// 分页成功返回
|
||||
public static <T> R<T> pageSuccess(long total, T data) {
|
||||
PageR<T> r = new PageR<>();
|
||||
r.setCode(ResultCode.SUCCESS.getCode());
|
||||
r.setMsg(ResultCode.SUCCESS.getMsg());
|
||||
r.setData(data);
|
||||
r.setTotal(total);
|
||||
return r;
|
||||
}
|
||||
|
||||
// 失败返回
|
||||
public static <T> R<T> fail() {
|
||||
return fail(ResultCode.FAILED);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(ResultCode resultCode) {
|
||||
return fail(resultCode.getCode(), resultCode.getMsg());
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(String msg) {
|
||||
return fail(ResultCode.FAILED.getCode(), msg);
|
||||
}
|
||||
|
||||
public static <T> R<T> fail(Integer code, String msg) {
|
||||
R<T> r = new R<>();
|
||||
r.setCode(code);
|
||||
r.setMsg(msg);
|
||||
return r;
|
||||
}
|
||||
|
||||
// 转换为ProblemDetail
|
||||
public static ProblemDetail toProblemDetail(ResultCode resultCode, HttpStatus status) {
|
||||
ProblemDetail problemDetail = ProblemDetail.forStatus(status);
|
||||
problemDetail.setType(URI.create("https://api.test.com/errors/" + resultCode.name().toLowerCase()));
|
||||
problemDetail.setTitle(resultCode.getMsg());
|
||||
problemDetail.setDetail(resultCode.getMsg());
|
||||
problemDetail.setProperty("code", resultCode.getCode());
|
||||
return problemDetail;
|
||||
}
|
||||
|
||||
// 分页返回结果类
|
||||
private static class PageR<T> extends R<T> {
|
||||
private long total;
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long total) {
|
||||
this.total = total;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.test.bijihoudaun.common.response;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 返回状态码枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum ResultCode {
|
||||
// 基础状态码
|
||||
SUCCESS(200, "操作成功"),
|
||||
FAILED(500, "操作失败"),
|
||||
VALIDATE_FAILED(400, "参数校验失败"),
|
||||
UNAUTHORIZED(401, "未授权"),
|
||||
FORBIDDEN(403, "禁止访问"),
|
||||
NOT_FOUND(404, "资源不存在"),
|
||||
|
||||
// 参数相关错误
|
||||
PARAM_IS_INVALID(1001, "参数无效"),
|
||||
PARAM_IS_BLANK(1002, "参数为空"),
|
||||
PARAM_TYPE_BIND_ERROR(1003, "参数类型错误"),
|
||||
PARAM_NOT_COMPLETE(1004, "参数缺失"),
|
||||
|
||||
// 加密相关错误
|
||||
ENCRYPTION_FAILED(2001, "加密失败"),
|
||||
DECRYPTION_FAILED(2002, "解密失败"),
|
||||
KEY_GENERATION_FAILED(2003, "密钥生成失败"),
|
||||
INVALID_KEY_FORMAT(2004, "密钥格式无效"),
|
||||
|
||||
// 编码相关
|
||||
BASE64_ENCODE_FAILED(4001, "Base64编码失败"),
|
||||
BASE64_DECODE_FAILED(4002, "Base64解码失败"),
|
||||
|
||||
|
||||
|
||||
// ID生成相关
|
||||
ID_GENERATION_FAILED(3001, "ID生成失败"),
|
||||
UUID_GENERATION_FAILED(3002, "UUID生成失败"),
|
||||
SNOWFLAKE_PARAM_INVALID(3003, "雪花ID参数无效"), // 用于workerId/datacenterId越界
|
||||
CLOCK_BACKWARD_ERROR(3004, "系统时钟回拨异常"), // 处理时钟回拨场景
|
||||
SEQUENCE_OVERFLOW(3005, "ID序列号溢出"); // 序列号超过4095时的异常
|
||||
|
||||
|
||||
private final Integer code;
|
||||
private final String msg;
|
||||
|
||||
ResultCode(Integer code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user