init commit.

This commit is contained in:
ljh01459796
2023-06-20 16:05:15 +08:00
commit 95150b6be7
38 changed files with 1965 additions and 0 deletions

View File

@@ -0,0 +1,31 @@
<?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">
<parent>
<groupId>com.orion.ops</groupId>
<artifactId>orion-ops-framework</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>orion-ops-common</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<description>项目公共基准包</description>
<url>https://github.com/lijiahangmax/orion-ops-pro</url>
<dependencies>
<dependency>
<groupId>io.github.lijiahangmax</groupId>
<artifactId>orion-all</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,16 @@
package com.orion.ops.framework.common.annotation;
import java.lang.annotation.*;
/**
* 不执行统一日志打印
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/4/20 10:33
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreLog {
}

View File

@@ -0,0 +1,16 @@
package com.orion.ops.framework.common.annotation;
import java.lang.annotation.*;
/**
* 无需包装返回
*
* @author Jiahang Li
* @version 1.0.0
* @since 2021/4/2 10:36
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreWrapper {
}

View File

@@ -0,0 +1,17 @@
package com.orion.ops.framework.common.annotation;
import java.lang.annotation.*;
/**
* 统一返回包装
*
* @author Jiahang Li
* @version 1.0.0
* @since 2021/4/2 12:34
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RestWrapper {
}

View File

@@ -0,0 +1,50 @@
package com.orion.ops.framework.common.enums;
/**
* 消息常量
*
* @author Jiahang Li
* @version 1.0.0
* @since 2021/6/4 18:26
*/
public interface ExceptionMessageConst {
String INVALID_PARAM = "非法参数";
String OPERATOR_ERROR = "操作失败";
String HTTP_API = "api 调用异常";
String NETWORK_FLUCTUATION = "当前环境网路波动";
String OPEN_TEMPLATE_ERROR = "模板解析失败 请检查模板和密码";
String PARSE_TEMPLATE_DATA_ERROR = "模板解析失败 请检查模板数据";
String REPOSITORY_OPERATOR_ERROR = "应用版本仓库操作执行失败";
String TASK_ERROR = "任务执行异常";
String CONNECT_ERROR = "建立连接失败";
String TIMEOUT_ERROR = "处理超时";
String INTERRUPT_ERROR = "操作中断";
String UNSAFE_OPERATOR = "不安全的操作";
String ENCRYPT_ERROR = "数据加密异常";
String DECRYPT_ERROR = "数据解密异常";
String EXCEPTION_MESSAGE = "系统异常";
String IO_EXCEPTION_MESSAGE = "网络异常";
String SQL_EXCEPTION_MESSAGE = "数据异常";
String FILE_TOO_LARGE = "文件过大";
String ERROR_EXPRESSION = "表达式错误";
}

View File

@@ -0,0 +1,16 @@
package com.orion.ops.framework.common.enums;
/**
* 过滤器排序常量
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/16 17:04
*/
public interface FilterOrderConst {
int CORS_FILTER = Integer.MIN_VALUE;
int TRICE_ID_FILTER = Integer.MIN_VALUE + 10;
}

View File

@@ -0,0 +1,14 @@
package com.orion.ops.framework.common.enums;
/**
* 拦截器排序常量
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/16 18:15
*/
public interface InterceptorOrderConst {
int LOG_FILTER = Integer.MIN_VALUE;
}

View File

@@ -0,0 +1,20 @@
package com.orion.ops.framework.common.enums;
/**
* 项目常量
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/19 18:56
*/
public interface OrionOpsProConst {
String GITHUB = "https://github.com/lijiahangmax/orion-ops-pro";
String GITEE = "https://gitee.com/lijiahangmax/orion-ops-pro";
String ISSUES = "https://gitee.com/lijiahangmax/orion-ops-pro/issues";
String VERSION = "1.0.0";
}

View File

@@ -0,0 +1,33 @@
package com.orion.ops.framework.common.meta;
/**
* traceId 持有者
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/16 17:35
*/
public class TraceIdHolder {
private TraceIdHolder() {
}
/**
* 请求序列
*/
private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
;
public static String get() {
return HOLDER.get();
}
public static void set(String traceId) {
HOLDER.set(traceId);
}
public static void remove() {
HOLDER.remove();
}
}

View File

@@ -0,0 +1,265 @@
package com.orion.ops.framework.common.utils;
/**
* ANSI 高亮颜色转义码
* <p>
* \u001B = \x1b = 27 = esc
* <p>
* 基本8色 基本高对比色 xterm 256 色
* 30 ~ 37 90 ~ 97 0 ~ 256
* <p>
* \033[0m 关闭所有属性
* \033[1m 设置高亮度
* \033[4m 下划线
* \033[5m 闪烁
* \033[7m 反显
* \033[8m 消隐
* \033[30m 至 \33[37m 设置前景色
* \033[40m 至 \33[47m 设置背景色
* \033[nA 光标上移n行
* \033[nB 光标下移n行
* \033[nC 光标右移n行
* \033[nD 光标左移n行
* \033[y;xH 设置光标位置
* \033[2J 清屏
* \033[K 清除从光标到行尾的内容
* \033[s 保存光标位置
* \033[u 恢复光标位置
* \033[?25l 隐藏光标
* \033[?25h 显示光标
* <p>
* TODO 后续直接使用kit
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/20 10:51
*/
public enum AnsiCode {
/**
* 黑色
*/
BLACK(30),
/**
* 红色
*/
RED(31),
/**
* 绿色
*/
GREEN(32),
/**
* 黄色
*/
YELLOW(33),
/**
* 蓝色
*/
BLUE(34),
/**
* 紫色
*/
PURPLE(35),
/**
* 青色
*/
CYAN(36),
/**
* 白色
*/
WHITE(37),
// -------------------- 背景色 --------------------
/**
* 黑色 背景色
*/
BG_BLACK(40),
/**
* 红色 背景色
*/
BG_RED(41),
/**
* 绿色 背景色
*/
BG_GREEN(42),
/**
* 黄色 背景色
*/
BG_YELLOW(43),
/**
* 蓝色 背景色
*/
BG_BLUE(44),
/**
* 紫色 背景色
*/
BG_PURPLE(45),
/**
* 青色 背景色
*/
BG_CYAN(46),
/**
* 白色 背景色
*/
BG_WHITE(47),
// -------------------- 亮色 --------------------
/**
* 亮黑色 (灰)
*/
GLOSS_BLACK(90),
/**
* 亮红色
*/
GLOSS_RED(91),
/**
* 亮绿色
*/
GLOSS_GREEN(92),
/**
* 亮黄色
*/
GLOSS_YELLOW(93),
/**
* 亮蓝色
*/
GLOSS_BLUE(94),
/**
* 亮紫色
*/
GLOSS_PURPLE(95),
/**
* 亮青色
*/
GLOSS_CYAN(96),
/**
* 亮白色
*/
GLOSS_WHITE(97),
// -------------------- 亮背景色 --------------------
/**
* 亮黑色 (灰) 背景色
*/
BG_GLOSS_BLACK(100),
/**
* 亮红色 背景色
*/
BG_GLOSS_RED(101),
/**
* 亮绿色 背景色
*/
BG_GLOSS_GREEN(102),
/**
* 亮黄色 背景色
*/
BG_GLOSS_YELLOW(103),
/**
* 亮蓝色 背景色
*/
BG_GLOSS_BLUE(104),
/**
* 亮紫色 背景色
*/
BG_GLOSS_PURPLE(105),
/**
* 亮青色 背景色
*/
BG_GLOSS_CYAN(106),
/**
* 亮白色 背景色
*/
BG_GLOSS_WHITE(107),
;
/**
* 颜色码
*/
public final int code;
/**
* 前缀
*/
public final String prefix;
/**
* 后缀
* \x1b[0m
*/
public static final String SUFFIX = (char) 27 + "[0m";
AnsiCode(int code) {
this.code = code;
this.prefix = getPrefix(code);
}
/**
* 文字着色
*
* @param s s
* @return s
*/
public String stain(String s) {
return prefix + s + SUFFIX;
}
/**
* 获取颜色前缀
* .e.g \x1b[31m
*
* @param code code
* @return 前缀
*/
public static String getPrefix(int code) {
return (char) 27 + "[" + code + "m";
}
/**
* 文字着色
*
* @param s s
* @param code code
* @return s
*/
public static String getStain(String s, int code) {
return getPrefix(code) + s + SUFFIX;
}
@Override
public String toString() {
return prefix;
}
}

View File

@@ -0,0 +1,31 @@
<?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">
<parent>
<groupId>com.orion.ops</groupId>
<artifactId>orion-ops-framework</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>orion-ops-spring-boot-starter-banner</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<description>项目 banner 打印包</description>
<url>https://github.com/lijiahangmax/orion-ops-pro</url>
<dependencies>
<dependency>
<groupId>com.orion.ops</groupId>
<artifactId>orion-ops-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,25 @@
package com.orion.ops.framework.banner.config;
import com.orion.ops.framework.banner.core.BannerApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
/**
* banner 自动配置类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/15 16:16
*/
@AutoConfiguration
public class OrionBannerAutoConfiguration {
/**
* @return banner 打印器
*/
@Bean
public BannerApplicationRunner bannerApplicationRunner() {
return new BannerApplicationRunner();
}
}

View File

@@ -0,0 +1,44 @@
package com.orion.ops.framework.banner.core;
import com.orion.ops.framework.common.utils.AnsiCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
/**
* banner printer
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/15 16:18
*/
@Slf4j
public class BannerApplicationRunner implements ApplicationRunner {
@Value("${spring.profiles.active}")
private String env;
@Value("${orion.version}")
private String version;
@Value("${server.port}")
private String port;
@Value("${orion.api.prefix}")
private String apiPrefix;
@Override
public void run(ApplicationArguments args) {
String line = AnsiCode.GLOSS_GREEN.stain(":: orion-ops-server v" + version + " 服务已启动(" + env + ") ::\n") +
AnsiCode.GLOSS_GREEN.stain(":: swagger 文档 ") +
// TODO swagger 地址
AnsiCode.GLOSS_BLUE.stain("http://127.0.0.1:xxxx/dox.html\n") +
AnsiCode.GLOSS_GREEN.stain(":: server 心跳检测 ") +
AnsiCode.GLOSS_BLUE +
"curl -X GET --location \"http://127.0.0.1:" + port + apiPrefix + "/server/bootstrap/health\"" +
AnsiCode.SUFFIX;
System.out.println(line);
}
}

View File

@@ -0,0 +1 @@
com.orion.ops.framework.banner.config.OrionBannerAutoConfiguration

View File

@@ -0,0 +1,11 @@
${AnsiColor.BRIGHT_GREEN} _ ${AnsiColor.BLUE}
${AnsiColor.BRIGHT_GREEN} ____ _____(_)___ ____ ____ ____ _____ ${AnsiColor.BLUE} ____ _________
${AnsiColor.BRIGHT_GREEN} / __ \/ ___/ / __ \/ __ \ / __ \/ __ \/ ___/ ${AnsiColor.BLUE} / __ \/ ___/ __ \
${AnsiColor.BRIGHT_GREEN}/ /_/ / / / / /_/ / / / / / /_/ / /_/ (__ ) ${AnsiColor.BLUE} / /_/ / / / /_/ /
${AnsiColor.BRIGHT_GREEN}\____/_/ /_/\____/_/ /_/ \____/ .___/____/ ${AnsiColor.BLUE} / .___/_/ \____/
${AnsiColor.BRIGHT_GREEN} /_/ ${AnsiColor.BLUE} /_/
${AnsiColor.BRIGHT_GREEN}:: Application Name ${AnsiColor.BLUE}${spring.application.name}
${AnsiColor.BRIGHT_GREEN}:: Application Version ${AnsiColor.BLUE}${orion.version}
${AnsiColor.BRIGHT_GREEN}:: SpringBoot Version ${AnsiColor.BLUE}${spring-boot.version}
${AnsiColor.BRIGHT_GREEN}:: Active Profile ${AnsiColor.BLUE}${spring.profiles.active}

View File

@@ -0,0 +1,43 @@
<?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">
<parent>
<groupId>com.orion.ops</groupId>
<artifactId>orion-ops-framework</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>orion-ops-spring-boot-starter-web</artifactId>
<name>${project.artifactId}</name>
<packaging>jar</packaging>
<description>项目 web 包</description>
<url>https://github.com/lijiahangmax/orion-ops-pro</url>
<dependencies>
<dependency>
<groupId>com.orion.ops</groupId>
<artifactId>orion-ops-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,33 @@
package com.orion.ops.framework.web.config;
import com.orion.ops.framework.common.enums.InterceptorOrderConst;
import com.orion.ops.framework.web.core.interceptor.LogPrintInterceptor;
import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
/**
* 全局日志打印配置类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/16 18:18
*/
@AutoConfiguration
public class OrionLogPrinterConfiguration {
@Bean
public LogPrintInterceptor logPrintInterceptor() {
return new LogPrintInterceptor();
}
@Bean
public AspectJExpressionPointcutAdvisor logPrinterAdvisor(LogPrintInterceptor logPrintInterceptor) {
AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor();
advisor.setExpression("execution (* com.orion.ops.**.controller.*.*(..)) && !@annotation(com.orion.ops.framework.common.annotation.IgnoreLog)");
advisor.setAdvice(logPrintInterceptor);
advisor.setOrder(InterceptorOrderConst.LOG_FILTER);
return advisor;
}
}

View File

@@ -0,0 +1,136 @@
package com.orion.ops.framework.web.config;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.orion.lang.utils.collect.Lists;
import com.orion.ops.framework.common.enums.FilterOrderConst;
import com.orion.ops.framework.web.core.filter.TraceIdFilter;
import com.orion.ops.framework.web.core.handler.GlobalExceptionHandler;
import com.orion.ops.framework.web.core.handler.WrapperResultHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.Filter;
import java.util.List;
/**
* web 配置类
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/16 16:26
*/
@AutoConfiguration
public class OrionWebAutoConfiguration implements WebMvcConfigurer {
@Value("${orion.api.prefix}")
private String orionApiPrefix;
// TODO XSS
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
// 公共 api 前缀
configurer.addPathPrefix(orionApiPrefix, clazz -> clazz.isAnnotationPresent(RestController.class));
}
/**
* @return 全局异常处理器
*/
@Bean
public GlobalExceptionHandler globalExceptionHandler() {
return new GlobalExceptionHandler();
}
/**
* @return 通用返回结果处理器
*/
@Bean
public WrapperResultHandler wrapperResultHandler() {
return new WrapperResultHandler();
}
/**
* @return http json 转换器
*/
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
// 转换器
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
// 配置
FastJsonConfig config = new FastJsonConfig();
// 支持的类型
List<MediaType> mediaTypes = Lists.of(
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_FORM_URLENCODED,
MediaType.APPLICATION_XHTML_XML,
MediaType.TEXT_PLAIN,
MediaType.TEXT_HTML,
MediaType.TEXT_XML
);
converter.setSupportedMediaTypes(mediaTypes);
// 序列化配置
config.setSerializerFeatures(
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.IgnoreNonFieldGetter
);
converter.setFastJsonConfig(config);
return new HttpMessageConverters(converter);
}
/**
* @return 跨域配置
*/
@Bean
@ConditionalOnProperty(value = "orion.api.cors", havingValue = "true")
public FilterRegistrationBean<CorsFilter> corsFilterBean() {
// 跨域配置
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.setMaxAge(3600L);
// 创建 UrlBasedCorsConfigurationSource 对象
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return createFilterBean(new CorsFilter(source), FilterOrderConst.CORS_FILTER);
}
/**
* @return traceId 配置
*/
@Bean
public FilterRegistrationBean<TraceIdFilter> traceIdFilterBean() {
return createFilterBean(new TraceIdFilter(), FilterOrderConst.TRICE_ID_FILTER);
}
/**
* 创建过滤器
*
* @param filter filter
* @param order order
* @param <T> type
* @return filter bean
*/
public static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
bean.setOrder(order);
return bean;
}
}

View File

@@ -0,0 +1,39 @@
package com.orion.ops.framework.web.core.filter;
import com.orion.lang.id.UUIds;
import com.orion.ops.framework.common.meta.TraceIdHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* traceId 过滤器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/16 17:45
*/
public class TraceIdFilter extends OncePerRequestFilter {
private static final String TRACE_ID_HEADER = "trace-id";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
// 获 traceId
String traceId = UUIds.random32();
TraceIdHolder.set(traceId);
// 设置响应头
response.setHeader(TRACE_ID_HEADER, traceId);
// 执行请求
filterChain.doFilter(request, response);
} finally {
TraceIdHolder.remove();
}
}
}

View File

@@ -0,0 +1,185 @@
package com.orion.ops.framework.web.core.handler;
import com.orion.lang.define.wrapper.HttpWrapper;
import com.orion.lang.exception.*;
import com.orion.lang.exception.argument.CodeArgumentException;
import com.orion.lang.exception.argument.HttpWrapperException;
import com.orion.lang.exception.argument.InvalidArgumentException;
import com.orion.lang.exception.argument.RpcWrapperException;
import com.orion.lang.utils.Exceptions;
import com.orion.ops.framework.common.enums.ExceptionMessageConst;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.EncryptedDocumentException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
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.multipart.MaxUploadSizeExceededException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.sql.SQLException;
/**
* 全局异常处理器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/15 17:19
*/
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
public HttpWrapper<?> normalExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("normalExceptionHandler url: {}, 抛出异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.EXCEPTION_MESSAGE).data(ex.getMessage());
}
@ExceptionHandler(value = ApplicationException.class)
public HttpWrapper<?> applicationExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("applicationExceptionHandler url: {}, 抛出异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ex.getMessage());
}
// TODO datasource starter
// @ExceptionHandler(value = DataAccessResourceFailureException.class)
public HttpWrapper<?> dataAccessResourceFailureExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("dataAccessResourceFailureExceptionHandler url: {}, 抛出异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.NETWORK_FLUCTUATION);
}
@ExceptionHandler(value = {HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class,
HttpMessageNotReadableException.class, MethodArgumentNotValidException.class, BindException.class})
public HttpWrapper<?> httpRequestExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("httpRequestExceptionHandler url: {}, http请求异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.INVALID_PARAM);
}
@ExceptionHandler(value = {HttpRequestException.class})
public HttpWrapper<?> httpApiRequestExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("httpApiRequestExceptionHandler url: {}, http-api请求异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.HTTP_API);
}
@ExceptionHandler(value = {InvalidArgumentException.class, IllegalArgumentException.class, DisabledException.class})
public HttpWrapper<?> invalidArgumentExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("invalidArgumentExceptionHandler url: {}, 参数异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ex.getMessage());
}
@ExceptionHandler(value = {IOException.class, IORuntimeException.class})
public HttpWrapper<?> ioExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("ioExceptionHandler url: {}, io异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.IO_EXCEPTION_MESSAGE).data(ex.getMessage());
}
@ExceptionHandler(value = SQLException.class)
public HttpWrapper<?> sqlExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("sqlExceptionHandler url: {}, sql异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.SQL_EXCEPTION_MESSAGE);
}
@ExceptionHandler(value = {SftpException.class, com.jcraft.jsch.SftpException.class})
public HttpWrapper<?> sftpExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("sftpExceptionHandler url: {}, sftp处理异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.OPERATOR_ERROR).data(ex.getMessage());
}
@ExceptionHandler(value = ParseRuntimeException.class)
public HttpWrapper<?> parseExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("parseExceptionHandler url: {}, 解析异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
if (Exceptions.isCausedBy(ex, EncryptedDocumentException.class)) {
// excel 密码错误
return HttpWrapper.error(ExceptionMessageConst.OPEN_TEMPLATE_ERROR).data(ex.getMessage());
} else {
return HttpWrapper.error(ExceptionMessageConst.PARSE_TEMPLATE_DATA_ERROR).data(ex.getMessage());
}
}
@ExceptionHandler(value = EncryptException.class)
public HttpWrapper<?> encryptExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("encryptExceptionHandler url: {}, 数据加密异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.ENCRYPT_ERROR).data(ex.getMessage());
}
@ExceptionHandler(value = DecryptException.class)
public HttpWrapper<?> decryptExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("decryptExceptionHandler url: {}, 数据解密异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.DECRYPT_ERROR).data(ex.getMessage());
}
@ExceptionHandler(value = VcsException.class)
public HttpWrapper<?> vcsExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("vcsExceptionHandler url: {}, vcs处理异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.REPOSITORY_OPERATOR_ERROR).data(ex.getMessage());
}
@ExceptionHandler(value = {TaskExecuteException.class, ExecuteException.class})
public HttpWrapper<?> taskExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("taskExceptionHandler url: {}, task处理异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.TASK_ERROR).data(ex.getMessage());
}
@ExceptionHandler(value = ConnectionRuntimeException.class)
public HttpWrapper<?> connectionExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("connectionExceptionHandler url: {}, connect异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.CONNECT_ERROR).data(ex.getMessage());
}
@ExceptionHandler(value = {TimeoutException.class, java.util.concurrent.TimeoutException.class})
public HttpWrapper<?> timeoutExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("timeoutExceptionHandler url: {}, timeout异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.TIMEOUT_ERROR).data(ex.getMessage());
}
@ExceptionHandler(value = {InterruptedException.class, InterruptedRuntimeException.class, InterruptedIOException.class})
public HttpWrapper<?> interruptExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("interruptExceptionHandler url: {}, interrupt异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.INTERRUPT_ERROR).data(ex.getMessage());
}
@ExceptionHandler(value = UnsafeException.class)
public HttpWrapper<?> unsafeExceptionHandler(HttpServletRequest request, Exception ex) {
log.error("unsafeExceptionHandler url: {}, unsafe异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.UNSAFE_OPERATOR).data(ex.getMessage());
}
@ExceptionHandler(value = LogException.class)
public HttpWrapper<?> logExceptionHandler(HttpServletRequest request, LogException ex) {
log.error("logExceptionHandler url: {}, 处理异常打印日志: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.EXCEPTION_MESSAGE).data(ex.getMessage());
}
@ExceptionHandler(value = ParseCronException.class)
public HttpWrapper<?> parseCronExceptionHandler(ParseCronException ex) {
return HttpWrapper.error(ExceptionMessageConst.ERROR_EXPRESSION).data(ex.getMessage());
}
@ExceptionHandler(value = MaxUploadSizeExceededException.class)
public HttpWrapper<?> maxUploadSizeExceededExceptionHandler(HttpServletRequest request, MaxUploadSizeExceededException ex) {
log.error("maxUploadSizeExceededExceptionHandler url: {}, 上传异常: {}, message: {}", request.getRequestURI(), ex.getClass(), ex.getMessage(), ex);
return HttpWrapper.error(ExceptionMessageConst.FILE_TOO_LARGE).data(ex.getMessage());
}
@ExceptionHandler(value = CodeArgumentException.class)
public HttpWrapper<?> codeArgumentExceptionHandler(CodeArgumentException ex) {
return HttpWrapper.error(ex.getCode(), ex.getMessage());
}
@ExceptionHandler(value = HttpWrapperException.class)
public HttpWrapper<?> httpWrapperExceptionHandler(HttpWrapperException ex) {
return ex.getWrapper();
}
@ExceptionHandler(value = RpcWrapperException.class)
public HttpWrapper<?> rpcWrapperExceptionHandler(RpcWrapperException ex) {
return ex.getWrapper().toHttpWrapper();
}
}

View File

@@ -0,0 +1,54 @@
package com.orion.ops.framework.web.core.handler;
import com.orion.lang.constant.StandardContentType;
import com.orion.lang.define.wrapper.HttpWrapper;
import com.orion.lang.define.wrapper.RpcWrapper;
import com.orion.ops.framework.common.annotation.IgnoreWrapper;
import com.orion.ops.framework.common.annotation.RestWrapper;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* 返回值处理器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2023/6/15 17:38
*/
@ControllerAdvice
public class WrapperResultHandler implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, @NotNull Class converterType) {
// 统一返回值
if (!methodParameter.getContainingClass().isAnnotationPresent(RestWrapper.class)) {
return false;
}
return !methodParameter.hasMethodAnnotation(IgnoreWrapper.class);
// && methodParameter.getExecutable().getAnnotatedReturnType().getType() != Void.TYPE;
}
@Override
public Object beforeBodyWrite(Object body, @NotNull MethodParameter methodParameter, @NotNull MediaType selectedContentType, @NotNull Class selectedConverterType,
@NotNull ServerHttpRequest request, @NotNull ServerHttpResponse response) {
HttpWrapper<?> wrapper;
if (body instanceof HttpWrapper) {
wrapper = (HttpWrapper<?>) body;
} else if (body instanceof RpcWrapper) {
wrapper = ((RpcWrapper<?>) body).toHttpWrapper();
} else {
wrapper = new HttpWrapper<>().data(body);
}
if (response instanceof ServletServerHttpResponse) {
((ServletServerHttpResponse) response).getServletResponse().setContentType(StandardContentType.APPLICATION_JSON);
}
return wrapper;
}
}

View File

@@ -0,0 +1,154 @@
package com.orion.ops.framework.web.core.interceptor;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.orion.lang.constant.Const;
import com.orion.lang.utils.Arrays1;
import com.orion.lang.utils.Exceptions;
import com.orion.lang.utils.time.Dates;
import com.orion.ops.framework.common.meta.TraceIdHolder;
import com.orion.web.servlet.web.Servlets;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Optional;
/**
* 日志打印拦截器
*
* @author Jiahang Li
* @version 1.0.0
* @since 2022/7/14 18:25
*/
@Slf4j
@Order(10)
public class LogPrintInterceptor implements MethodInterceptor {
// TODO @OperatorLog(bizName = "")
// TODO 日志规格模型 SIMPLIFY FULL
// https://blog.csdn.net/gaojie_csdn/article/details/127810618
// 加注解
// TODO 改为正则?
@Value("#{'${log.interceptor.ignore.fields:}'.split(',')}")
private String[] ignoreFields;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Date startTime = new Date();
String traceId = TraceIdHolder.get();
// 打印开始日志
this.beforeLogPrint(startTime, traceId, invocation);
try {
// 执行方法
Object ret = invocation.proceed();
// 返回打印
this.afterReturnLogPrint(startTime, traceId, ret);
return ret;
} catch (Throwable t) {
// 异常打印
this.afterThrowingLogPrint(startTime, traceId, t);
throw t;
}
}
/**
* 方法进入打印
*
* @param startTime 开始时间
* @param traceId trace
* @param invocation invocation
*/
public void beforeLogPrint(Date startTime, String traceId, MethodInvocation invocation) {
StringBuilder requestLog = new StringBuilder("\napi请求-开始-traceId: ").append(traceId).append('\n');
// TODO 登陆用户
// requestLog.append("\t当前用户: ").append(JSON.toJSONString(UserHolder.get())).append('\n');
// TODO @OperatorLog
// http请求信息
Optional.ofNullable(RequestContextHolder.getRequestAttributes())
.map(s -> (ServletRequestAttributes) s)
.map(ServletRequestAttributes::getRequest)
.ifPresent(request -> {
// url
requestLog.append("\t").append(Servlets.getMethod(request)).append(" ")
.append(Servlets.getRequestUrl(request)).append('\n');
// query
requestLog.append("\tip: ").append(Servlets.getRemoteAddr(request)).append('\n')
.append("\tquery: ").append(Servlets.getQueryString(request)).append('\n');
// header
Servlets.getHeaderMap(request).forEach((hk, hv) -> requestLog.append('\t')
.append(hk).append(": ")
.append(hv).append('\n'));
});
// 方法信息
Method method = invocation.getMethod();
requestLog.append("\t开始时间: ").append(Dates.format(startTime, Dates.YMD_HMSS)).append('\n')
.append("\t方法签名: ").append(method.getDeclaringClass().getName()).append('#')
.append(method.getName()).append("\n")
.append("\t请求参数: ").append(this.argsToString(invocation.getArguments()));
log.info(requestLog.toString());
}
/**
* 返回打印
*
* @param startTime 开始时间
* @param traceId traceId
* @param ret return
*/
private void afterReturnLogPrint(Date startTime, String traceId, Object ret) {
Date endTime = new Date();
// 响应日志
StringBuilder responseLog = new StringBuilder("\napi请求-结束-traceId: ").append(traceId).append('\n');
responseLog.append("\t结束时间: ").append(Dates.format(endTime, Dates.YMD_HMSS))
.append(" used: ").append(endTime.getTime() - startTime.getTime()).append("ms \n")
.append("\t响应结果: ").append(this.argsToString(ret));
log.info(responseLog.toString());
}
/**
* 异常打印
*
* @param startTime 开始时间
* @param traceId trace
* @param throwable ex
*/
private void afterThrowingLogPrint(Date startTime, String traceId, Throwable throwable) {
Date endTime = new Date();
// 响应日志
StringBuilder responseLog = new StringBuilder("\napi请求-异常-traceId: ").append(traceId).append('\n');
responseLog.append("\t结束时间: ").append(Dates.format(endTime, Dates.YMD_HMSS))
.append(" used: ").append(endTime.getTime() - startTime.getTime()).append("ms \n")
.append("\t异常摘要: ").append(Exceptions.getDigest(throwable));
log.error(responseLog.toString());
}
/**
* 参数转json
*
* @param o object
* @return json
*/
private String argsToString(Object o) {
try {
if (ignoreFields.length == 1 && Const.EMPTY.equals(ignoreFields[0])) {
// 不过滤
return JSON.toJSONString(o);
} else {
return JSON.toJSONString(o, (PropertyFilter) (object, name, value) -> !Arrays1.contains(ignoreFields, name));
}
} catch (Exception e) {
return String.valueOf(o);
}
}
}

View File

@@ -0,0 +1,2 @@
com.orion.ops.framework.web.config.OrionWebAutoConfiguration
com.orion.ops.framework.web.config.OrionLogPrinterConfiguration

View File

@@ -0,0 +1,24 @@
<?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">
<parent>
<groupId>com.orion.ops</groupId>
<artifactId>orion-ops-pro</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>orion-ops-framework</artifactId>
<packaging>pom</packaging>
<description>项目组件包</description>
<url>https://github.com/lijiahangmax/orion-ops-pro</url>
<modules>
<module>orion-ops-common</module>
<module>orion-ops-spring-boot-starter-banner</module>
<module>orion-ops-spring-boot-starter-web</module>
</modules>
</project>