diff --git a/common/pom.xml b/common/pom.xml
index 15b455b..ed69a5c 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -18,9 +18,35 @@
+
+ net.rzdata
+ demo-core
+ 5.1.0-SNAPSHOT
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+ provided
+
com.baomidou
mybatis-plus-boot-starter
+ provided
+
+
+ cn.dev33
+ sa-token-core
+ provided
+
+
+ org.projectlombok
+ lombok
+ provided
diff --git a/common/src/main/java/net/rzdata/demo/exception/GlobalExceptionHandler.java b/common/src/main/java/net/rzdata/demo/exception/GlobalExceptionHandler.java
new file mode 100644
index 0000000..e6ab6fb
--- /dev/null
+++ b/common/src/main/java/net/rzdata/demo/exception/GlobalExceptionHandler.java
@@ -0,0 +1,150 @@
+package net.rzdata.demo.exception;
+
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.dev33.satoken.exception.NotPermissionException;
+import cn.dev33.satoken.exception.NotRoleException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.ConstraintViolationException;
+import lombok.extern.slf4j.Slf4j;
+import net.rzdata.domain.Result;
+import net.rzdata.exception.ClientException;
+import net.rzdata.exception.ServerException;
+import net.rzdata.exception.ThirdPartyException;
+import org.springframework.context.support.DefaultMessageSourceResolvable;
+import org.springframework.validation.BindException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+import java.util.stream.Collectors;
+
+/**
+ * 全局异常处理
+ */
+@Slf4j
+public class GlobalExceptionHandler {
+
+ protected final transient String errorMessage = "系统错误,请联系系统管理员";
+
+ /**
+ * 权限码异常
+ */
+ @ExceptionHandler(NotPermissionException.class)
+ public Result handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
+ ClientException ce = new ClientException("没有访问权限,请联系管理员授权", e);
+ return Result.fail(ce);
+ }
+
+ /**
+ * 角色权限异常
+ */
+ @ExceptionHandler(NotRoleException.class)
+ public Result handleNotRoleException(NotRoleException e, HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
+ ClientException ce = new ClientException("没有访问权限,请联系管理员授权", e);
+ return Result.fail(ce);
+ }
+
+ /**
+ * 认证失败
+ */
+ @ExceptionHandler(NotLoginException.class)
+ public Result handleNotLoginException(NotLoginException e, HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ log.error("请求地址'{}',认证失败'{}',无法访问系统资源", requestURI, e.getMessage());
+ ClientException ce = new ClientException("没有访问权限,请联系管理员授权", e);
+ return Result.fail(ce);
+ }
+
+ /**
+ * 请求方式不支持
+ */
+ @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+ public Result handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
+ ServerException se = new ServerException("请求方式不支持", e);
+ return Result.fail(se, errorMessage);
+ }
+
+ @ExceptionHandler(ClientException.class)
+ public Result handleClientException(ClientException e) {
+ log.error("客户端异常: {}", e.getMessage(), e);
+ return Result.fail(e);
+ }
+
+ @ExceptionHandler(ServerException.class)
+ public Result handleServerException(ServerException e) {
+ log.error("服务端异常: {}", e.getMessage(), e);
+ return Result.fail(e, errorMessage);
+ }
+
+ @ExceptionHandler(ThirdPartyException.class)
+ public Result handleThirdPartyException(ThirdPartyException e) {
+ log.error("第三方服务异常: {}", e.getMessage(), e);
+ return Result.fail(e, errorMessage);
+ }
+
+ /**
+ * 拦截未知的运行时异常
+ */
+ @ExceptionHandler(RuntimeException.class)
+ public Result handleRuntimeException(RuntimeException e, HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ log.error("请求地址'{}',发生未知异常.", requestURI, e);
+ ServerException se = new ServerException(errorMessage, e);
+ return Result.fail(se);
+ }
+
+ /**
+ * 系统异常
+ */
+ @ExceptionHandler(Exception.class)
+ public Result handleException(Exception e, HttpServletRequest request) {
+ String requestURI = request.getRequestURI();
+ log.error("请求地址'{}',发生系统异常.", requestURI, e);
+ ServerException se = new ServerException(errorMessage, e);
+ return Result.fail(se);
+ }
+
+ /**
+ * 自定义验证异常
+ */
+ @ExceptionHandler(BindException.class)
+ public Result handleBindException(BindException e) {
+ log.error(e.getMessage(), e);
+ String message = e.getAllErrors().parallelStream()
+ .map(DefaultMessageSourceResolvable::getDefaultMessage)
+ .collect(Collectors.joining(", "));
+ ClientException ce = new ClientException(message, e);
+ return Result.fail(ce);
+ }
+
+ /**
+ * 自定义验证异常
+ */
+ @ExceptionHandler(ConstraintViolationException.class)
+ public Result constraintViolationException(ConstraintViolationException e) {
+ log.error(e.getMessage(), e);
+ String message = e.getConstraintViolations().parallelStream()
+ .map(ConstraintViolation::getMessage)
+ .collect(Collectors.joining(", "));
+ ClientException ce = new ClientException(message, e);
+ return Result.fail(ce);
+ }
+
+ /**
+ * 自定义验证异常
+ */
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+ log.error(e.getMessage(), e);
+ String message = e.getBindingResult().getFieldError().getDefaultMessage();
+ ClientException ce = new ClientException(message, e);
+ return Result.fail(ce);
+ }
+}
diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000..474f2ff
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ net.rzdata
+ demo
+ 5.1.0-SNAPSHOT
+
+ demo-core
+ jar
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+
diff --git a/common/src/main/java/net/rzdata/demo/domain/Id.java b/core/src/main/java/net/rzdata/domain/Id.java
similarity index 76%
rename from common/src/main/java/net/rzdata/demo/domain/Id.java
rename to core/src/main/java/net/rzdata/domain/Id.java
index 8006ce3..2c4aa5b 100644
--- a/common/src/main/java/net/rzdata/demo/domain/Id.java
+++ b/core/src/main/java/net/rzdata/domain/Id.java
@@ -1,4 +1,4 @@
-package net.rzdata.demo.domain;
+package net.rzdata.domain;
public record Id(String id) {
diff --git a/core/src/main/java/net/rzdata/domain/Result.java b/core/src/main/java/net/rzdata/domain/Result.java
new file mode 100644
index 0000000..65e4a47
--- /dev/null
+++ b/core/src/main/java/net/rzdata/domain/Result.java
@@ -0,0 +1,42 @@
+package net.rzdata.domain;
+
+import lombok.*;
+import net.rzdata.exception.BaseException;
+
+@Getter
+@ToString
+@NoArgsConstructor
+@AllArgsConstructor(access = AccessLevel.PROTECTED)
+public class Result {
+
+ public static final String SUCCESS_CODE = "00000";
+ public static final String SUCCESS_MESSAGE = "操作成功";
+
+ private String code;
+ private String message;
+ private T data;
+
+ public static Result ok() {
+ return new Result<>(SUCCESS_CODE, SUCCESS_MESSAGE, null);
+ }
+
+ public static Result ok(T data) {
+ return new Result<>(SUCCESS_CODE, SUCCESS_MESSAGE, data);
+ }
+
+ public static Result fail(BaseException e) {
+ return new Result<>(e.getCode(), e.getMessage(), null);
+ }
+
+ public static Result fail(BaseException e, T data) {
+ return new Result<>(e.getCode(), e.getMessage(), data);
+ }
+
+ public static Result fail(BaseException e, String message) {
+ return new Result<>(e.getCode(), message, null);
+ }
+
+ public static Result fail(BaseException e, String message, T data) {
+ return new Result<>(e.getCode(), message, data);
+ }
+}
diff --git a/core/src/main/java/net/rzdata/exception/BaseException.java b/core/src/main/java/net/rzdata/exception/BaseException.java
new file mode 100644
index 0000000..48c21cf
--- /dev/null
+++ b/core/src/main/java/net/rzdata/exception/BaseException.java
@@ -0,0 +1,21 @@
+package net.rzdata.exception;
+
+public abstract sealed class BaseException extends RuntimeException
+ permits ClientException, ServerException, ThirdPartyException {
+
+ private final String code;
+
+ protected BaseException(String code, String message) {
+ super(message);
+ this.code = code;
+ }
+
+ protected BaseException(String code, String message, Throwable cause) {
+ super(message, cause);
+ this.code = code;
+ }
+
+ public String getCode() {
+ return this.code;
+ }
+}
diff --git a/core/src/main/java/net/rzdata/exception/ClientException.java b/core/src/main/java/net/rzdata/exception/ClientException.java
new file mode 100644
index 0000000..0a63d41
--- /dev/null
+++ b/core/src/main/java/net/rzdata/exception/ClientException.java
@@ -0,0 +1,25 @@
+package net.rzdata.exception;
+
+/**
+ * 客户端异常
+ */
+public non-sealed class ClientException extends BaseException {
+
+ public static final String CLIENT_ERROR_CODE = "A0001";
+
+ public ClientException(String message) {
+ super(CLIENT_ERROR_CODE, message);
+ }
+
+ public ClientException(String message, Throwable cause) {
+ super(CLIENT_ERROR_CODE, message, cause);
+ }
+
+ protected ClientException(String code, String message) {
+ super(code, message);
+ }
+
+ protected ClientException(String code, String message, Throwable cause) {
+ super(code, message, cause);
+ }
+}
diff --git a/core/src/main/java/net/rzdata/exception/ServerException.java b/core/src/main/java/net/rzdata/exception/ServerException.java
new file mode 100644
index 0000000..f436288
--- /dev/null
+++ b/core/src/main/java/net/rzdata/exception/ServerException.java
@@ -0,0 +1,25 @@
+package net.rzdata.exception;
+
+/**
+ * 服务端异常
+ */
+public non-sealed class ServerException extends BaseException {
+
+ public static final String SERVER_ERROR_CODE = "B0001";
+
+ public ServerException(String message) {
+ super(SERVER_ERROR_CODE, message);
+ }
+
+ public ServerException(String message, Throwable cause) {
+ super(SERVER_ERROR_CODE, message, cause);
+ }
+
+ protected ServerException(String code, String message) {
+ super(code, message);
+ }
+
+ protected ServerException(String code, String message, Throwable cause) {
+ super(code, message, cause);
+ }
+}
diff --git a/core/src/main/java/net/rzdata/exception/ThirdPartyException.java b/core/src/main/java/net/rzdata/exception/ThirdPartyException.java
new file mode 100644
index 0000000..dc287e3
--- /dev/null
+++ b/core/src/main/java/net/rzdata/exception/ThirdPartyException.java
@@ -0,0 +1,25 @@
+package net.rzdata.exception;
+
+/**
+ * 第三方服务异常
+ */
+public non-sealed class ThirdPartyException extends BaseException {
+
+ public static final String THIRD_PARTY_ERROR_CODE = "C0001";
+
+ public ThirdPartyException(String message) {
+ super(THIRD_PARTY_ERROR_CODE, message);
+ }
+
+ public ThirdPartyException(String message, Throwable cause) {
+ super(THIRD_PARTY_ERROR_CODE, message, cause);
+ }
+
+ protected ThirdPartyException(String code, String message) {
+ super(code, message);
+ }
+
+ protected ThirdPartyException(String code, String message, Throwable cause) {
+ super(code, message, cause);
+ }
+}
diff --git a/common/src/main/java/net/rzdata/demo/trait/IConverter.java b/core/src/main/java/net/rzdata/trait/IConverter.java
similarity index 82%
rename from common/src/main/java/net/rzdata/demo/trait/IConverter.java
rename to core/src/main/java/net/rzdata/trait/IConverter.java
index 7afe46f..94679f4 100644
--- a/common/src/main/java/net/rzdata/demo/trait/IConverter.java
+++ b/core/src/main/java/net/rzdata/trait/IConverter.java
@@ -1,4 +1,4 @@
-package net.rzdata.demo.trait;
+package net.rzdata.trait;
/**
* 实体转换类
diff --git a/common/src/main/java/net/rzdata/demo/trait/IQuery.java b/core/src/main/java/net/rzdata/trait/IQuery.java
similarity index 77%
rename from common/src/main/java/net/rzdata/demo/trait/IQuery.java
rename to core/src/main/java/net/rzdata/trait/IQuery.java
index faa8344..b2f891e 100644
--- a/common/src/main/java/net/rzdata/demo/trait/IQuery.java
+++ b/core/src/main/java/net/rzdata/trait/IQuery.java
@@ -1,4 +1,4 @@
-package net.rzdata.demo.trait;
+package net.rzdata.trait;
/**
* 请求实体类
diff --git a/pom.xml b/pom.xml
index d0730f6..2d8acb4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,7 @@
service
common
+ core