fix: 重新设计依赖,添加多个模块
This commit is contained in:
30
aioj-backend-common/aioj-backend-common-core/pom.xml
Normal file
30
aioj-backend-common/aioj-backend-common-core/pom.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cn.meowrain</groupId>
|
||||
<artifactId>aioj-backend-common</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>aioj-backend-common-core</artifactId>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<description>aioj 公共工具类核心包</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!--hutool-->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,70 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.banner;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EnvironmentBanner implements ApplicationListener<ApplicationReadyEvent> {
|
||||
|
||||
@Value("${spring.application.name:unknown}")
|
||||
private String appName;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationReadyEvent event) {
|
||||
|
||||
Environment env = event.getApplicationContext().getEnvironment();
|
||||
|
||||
// Active profiles
|
||||
String profiles = String.join(",", env.getActiveProfiles());
|
||||
if (profiles.isEmpty()) {
|
||||
profiles = "default";
|
||||
}
|
||||
|
||||
// Port
|
||||
String port = env.getProperty("server.port", "unknown");
|
||||
|
||||
// JVM info
|
||||
String jvm = System.getProperty("java.version") + " (" + System.getProperty("java.vendor") + ")";
|
||||
|
||||
// PID
|
||||
String pid = ManagementFactory.getRuntimeMXBean().getPid() + "";
|
||||
|
||||
// Time
|
||||
String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
|
||||
// Git commit id (如果没有 git.properties,不会报错)
|
||||
String gitCommit = env.getProperty("git.commit.id.abbrev", "N/A");
|
||||
|
||||
printBanner(appName, profiles, port, jvm, pid, time, gitCommit);
|
||||
}
|
||||
|
||||
private void printBanner(String appName,
|
||||
String profiles,
|
||||
String port,
|
||||
String jvm,
|
||||
String pid,
|
||||
String time,
|
||||
String gitCommit) {
|
||||
|
||||
String banner = "\n" +
|
||||
"------------------------------------------------------------\n" +
|
||||
" ✨AI Online Judge✨ - " + appName + "\n" +
|
||||
" Environment : " + profiles + "\n" +
|
||||
" Port : " + port + "\n" +
|
||||
" Git Commit : " + gitCommit + "\n" +
|
||||
" JVM : " + jvm + "\n" +
|
||||
" PID : " + pid + "\n" +
|
||||
" Started At : " + time + "\n" +
|
||||
"------------------------------------------------------------\n";
|
||||
System.out.println(banner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.config;
|
||||
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.exception.handler.GlobalExceptionHandler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
/**
|
||||
* 注册为bean,全局异常拦截器
|
||||
*/
|
||||
public class WebAutoConfiguration {
|
||||
@Bean
|
||||
public GlobalExceptionHandler globalExceptionHandler() {
|
||||
return new GlobalExceptionHandler();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.designpattern.chains;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
public interface AbstractChianHandler<T> extends Ordered {
|
||||
/**
|
||||
* 执行责任链逻辑
|
||||
*
|
||||
* @param requestParam 责任链执行入参
|
||||
*/
|
||||
void handle(T requestParam);
|
||||
|
||||
/**
|
||||
* 责任链组件标识
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
String mark();
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.designpattern.chains;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 公共责任链容器
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class CommonChainContext<T> implements ApplicationContextAware, CommandLineRunner {
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
private final Map<String, List<AbstractChianHandler<T>>> abstractChainHandlerMap = new HashMap<>();
|
||||
|
||||
public void handler(String mark, T requestParam) {
|
||||
List<AbstractChianHandler<T>> merchantAdminAbstractChainHandlers = abstractChainHandlerMap.get(mark);
|
||||
if (merchantAdminAbstractChainHandlers == null || merchantAdminAbstractChainHandlers.isEmpty()) {
|
||||
throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark));
|
||||
}
|
||||
merchantAdminAbstractChainHandlers.forEach(h -> {
|
||||
h.handle(requestParam);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
log.info("【责任链路初始化】开始加载并分组所有处理器...");
|
||||
applicationContext.getBeansOfType(AbstractChianHandler.class)
|
||||
.values()
|
||||
.forEach(handler -> {
|
||||
// 打印当前处理器的类名和它所属的 ChainMark
|
||||
String handlerName = handler.getClass().getSimpleName();
|
||||
String mark = handler.mark();
|
||||
log.info(" -> 发现处理器:{},归属链路:{}", handlerName, mark);
|
||||
abstractChainHandlerMap
|
||||
.computeIfAbsent(handler.mark(), k -> new ArrayList<>())
|
||||
.add(handler);
|
||||
});
|
||||
|
||||
// 步骤 2: 对每个链路中的处理器进行排序 (Sort 阶段)
|
||||
abstractChainHandlerMap.forEach((mark, handlers) -> {
|
||||
handlers.sort(Comparator.comparing(Ordered::getOrder));
|
||||
|
||||
// 打印排序后的 Bean 列表
|
||||
String sortedList = handlers.stream()
|
||||
.map(h -> String.format("%s (Order:%d)", h.getClass().getSimpleName(), h.getOrder()))
|
||||
.collect(Collectors.joining(" -> "));
|
||||
|
||||
log.info(" ✅ 链路 {} 排序完成:{}", mark, sortedList);
|
||||
});
|
||||
|
||||
log.info("【责任链路初始化】所有处理器链已完全就绪。");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.errorcode;
|
||||
|
||||
public enum ErrorCode implements IErrorCode {
|
||||
SUCCESS("0", "ok"),
|
||||
PARAMS_ERROR("40000", "请求参数错误"),
|
||||
NOT_LOGIN_ERROR("40100", "未登录"),
|
||||
NO_AUTH_ERROR("40101", "无权限"),
|
||||
NOT_FOUND_ERROR("40400", "请求数据不存在"),
|
||||
FORBIDDEN_ERROR("40300", "禁止访问"),
|
||||
SYSTEM_ERROR("50000", "系统内部异常"),
|
||||
OPERATION_ERROR("50001", "操作失败"),
|
||||
API_REQUEST_ERROR("50010", "接口调用失败");
|
||||
/**
|
||||
* 状态码
|
||||
*/
|
||||
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
* 信息
|
||||
*/
|
||||
private final String message;
|
||||
|
||||
ErrorCode(String code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String message() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.errorcode;
|
||||
|
||||
public interface IErrorCode {
|
||||
String code();
|
||||
String message();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.exception;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.IErrorCode;
|
||||
import lombok.Getter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 抽象错误处理Exception,基于这个抽象类,我们能创建很多其它类型的exception,定义错误类型
|
||||
*/
|
||||
@Getter
|
||||
|
||||
public class AbstractException extends RuntimeException {
|
||||
public final String errorCode;
|
||||
public final String errorMessage;
|
||||
|
||||
public AbstractException(String message, Throwable throwable, IErrorCode errorCode) {
|
||||
super(message);
|
||||
this.errorCode = errorCode.code();
|
||||
this.errorMessage = Optional.ofNullable(StringUtils.hasLength(message) ? message : null)
|
||||
.orElse(errorCode.message());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.exception;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.IErrorCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 客户端异常
|
||||
*/
|
||||
@ToString
|
||||
public class ClientException extends AbstractException{
|
||||
public ClientException(String message, Throwable throwable, IErrorCode errorCode) {
|
||||
super(message, throwable, errorCode);
|
||||
}
|
||||
|
||||
public ClientException(IErrorCode errorCode) {
|
||||
this(null, null, errorCode);
|
||||
}
|
||||
|
||||
public ClientException(String message, IErrorCode errorCode) {
|
||||
this(message, null, errorCode);
|
||||
}
|
||||
|
||||
public ClientException(String message) {
|
||||
this(message, null, ErrorCode.PARAMS_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.exception;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.IErrorCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 调用第三方服务异常
|
||||
*/
|
||||
@ToString
|
||||
public class RemoteException extends AbstractException{
|
||||
public RemoteException(IErrorCode errorCode) {
|
||||
this(null, null, errorCode);
|
||||
}
|
||||
|
||||
public RemoteException(String message, IErrorCode errorCode) {
|
||||
this(message, null, errorCode);
|
||||
}
|
||||
|
||||
public RemoteException(String message, Throwable throwable, IErrorCode errorCode) {
|
||||
super(message, throwable, errorCode);
|
||||
}
|
||||
public RemoteException(String message) {
|
||||
this(message, null, ErrorCode.API_REQUEST_ERROR);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.exception;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.IErrorCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 系统执行异常
|
||||
*/
|
||||
@ToString
|
||||
public class ServiceException extends AbstractException {
|
||||
public ServiceException(String message, IErrorCode errorCode) {
|
||||
this(message, null, errorCode);
|
||||
}
|
||||
|
||||
public ServiceException(String message) {
|
||||
this(message, null, ErrorCode.SYSTEM_ERROR);
|
||||
}
|
||||
|
||||
public ServiceException(IErrorCode errorCode) {
|
||||
this(null, null, errorCode);
|
||||
}
|
||||
|
||||
public ServiceException(String message, Throwable throwable, IErrorCode errorCode) {
|
||||
super(message, throwable, errorCode);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.exception.handler;
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.exception.AbstractException;
|
||||
import cn.meowrain.aioj.backend.framework.core.web.Result;
|
||||
import cn.meowrain.aioj.backend.framework.core.web.Results;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.validation.BindingResult;
|
||||
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 java.util.List;
|
||||
|
||||
/**
|
||||
* 全局错误捕获器
|
||||
*/
|
||||
@Slf4j
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
/**
|
||||
* 捕获所有参数错误,然后统一捕获并且抛出
|
||||
* @param request {@link HttpServletRequest}
|
||||
* @param ex {@link org.springframework.validation.method.MethodValidationException}
|
||||
* @return {@link Result<Void>}
|
||||
*/
|
||||
@ExceptionHandler(value = MethodArgumentNotValidException.class)
|
||||
public Result<Void> validExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) {
|
||||
BindingResult bindingResult = ex.getBindingResult();
|
||||
// 收集所有错误字段
|
||||
List<String> errorMessages = bindingResult.getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage).toList();
|
||||
String exceptionMessage = String.join(",", errorMessages);
|
||||
log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionMessage);
|
||||
return Results.paramsValidFailure();
|
||||
}
|
||||
|
||||
/**
|
||||
* 抽象异常捕获其
|
||||
* @param request {@link HttpServletRequest}
|
||||
* @param ex {@link AbstractException}
|
||||
* @return {@link Result<Void>}
|
||||
*/
|
||||
@ExceptionHandler(value = {AbstractException.class})
|
||||
public Result<Void> abstractExceptionHandler(HttpServletRequest request,AbstractException ex ) {
|
||||
if (ex.getCause() != null) {
|
||||
log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex, ex.getCause());
|
||||
return Results.failure(ex);
|
||||
}
|
||||
StringBuilder stackTraceBuilder = new StringBuilder();
|
||||
stackTraceBuilder.append(ex.getClass().getName()).append(": ").append(ex.getErrorMessage()).append("\n");
|
||||
StackTraceElement[] stackTrace = ex.getStackTrace();
|
||||
for (int i = 0; i < Math.min(5, stackTrace.length); i++) {
|
||||
stackTraceBuilder.append("\tat ").append(stackTrace[i]).append("\n");
|
||||
}
|
||||
log.error("[{}] {} [ex] {} \n\n{}", request.getMethod(), request.getRequestURL().toString(), ex,
|
||||
stackTraceBuilder);
|
||||
return Results.failure(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截未捕获异常
|
||||
*/
|
||||
@ExceptionHandler(value = Throwable.class)
|
||||
public Result<Void> defaultErrorHandler(HttpServletRequest request, Throwable throwable) {
|
||||
log.error("[{}] {} ", request.getMethod(), getUrl(request), throwable);
|
||||
return Results.failure();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求URL
|
||||
* @param request {@link HttpServletRequest}
|
||||
* @return String
|
||||
*/
|
||||
private String getUrl(HttpServletRequest request) {
|
||||
if (!StringUtils.hasText(request.getQueryString())) {
|
||||
return request.getRequestURI();
|
||||
}
|
||||
return request.getRequestURL() + "?" + request.getQueryString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.web;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 全局返回对象
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class Result<T> implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* 正确返回码
|
||||
* */
|
||||
public static final String SUCCESS_CODE = "0";
|
||||
/**
|
||||
* 响应码
|
||||
*/
|
||||
private String code;
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
private T data;
|
||||
/**
|
||||
* 响应信息
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 返回是否是正确响应
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return SUCCESS_CODE.equals(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回是否是错误响应
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isFail() {
|
||||
return !isSuccess();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package cn.meowrain.aioj.backend.framework.core.web;
|
||||
|
||||
|
||||
import cn.meowrain.aioj.backend.framework.core.errorcode.ErrorCode;
|
||||
import cn.meowrain.aioj.backend.framework.core.exception.AbstractException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 构建响应的工具类
|
||||
*/
|
||||
public final class Results {
|
||||
/**
|
||||
* 成功响应,不返回任何信息
|
||||
*
|
||||
* @return {@link Result<Void>}
|
||||
*/
|
||||
public static Result<Void> success() {
|
||||
return new Result<Void>().setCode(Result.SUCCESS_CODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功响应 返回数据
|
||||
*
|
||||
* @param data 返回的响应体信息
|
||||
* @param <T> 泛型
|
||||
* @return {@link Result<T>}
|
||||
*/
|
||||
public static <T> Result<T> success(T data) {
|
||||
return new Result<T>().setCode(Result.SUCCESS_CODE)
|
||||
.setData(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端请求参数错误
|
||||
* @return {@link Result<Void>}
|
||||
*/
|
||||
public static Result<Void> paramsValidFailure() {
|
||||
return failure(ErrorCode.PARAMS_ERROR.code(), ErrorCode.PARAMS_ERROR.message());
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务端错误默认响应 -- <b>内部错误</b>
|
||||
*
|
||||
* @return {@link Result<Void>}
|
||||
*/
|
||||
public static Result<Void> failure() {
|
||||
return new Result<Void>().setCode(ErrorCode.SYSTEM_ERROR.code())
|
||||
.setMessage(ErrorCode.SYSTEM_ERROR.message());
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务端错误响应 - 接收一个AbstractException,里面定义了错误码和错误信息
|
||||
*
|
||||
* @param exception {@link AbstractException}
|
||||
* @return {@link Result<Void>}
|
||||
*/
|
||||
public static Result<Void> failure(AbstractException exception) {
|
||||
String errorCode = Optional.ofNullable(exception.getErrorCode()).orElse(ErrorCode.SYSTEM_ERROR.code());
|
||||
String errorMessage = Optional.ofNullable(exception.getMessage()).orElse(ErrorCode.SYSTEM_ERROR.message());
|
||||
|
||||
return new Result<Void>()
|
||||
.setCode(errorCode)
|
||||
.setMessage(errorMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务端错误响应,自定义错误码和错误信息
|
||||
*
|
||||
* @param errorCode {@link String}
|
||||
* @param errorMessage {@link String}
|
||||
* @return {@link Result<Void>}
|
||||
*/
|
||||
public static Result<Void> failure(String errorCode, String errorMessage) {
|
||||
return new Result<Void>()
|
||||
.setCode(errorCode)
|
||||
.setMessage(errorMessage);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user