[+] 结构调整

This commit is contained in:
acgist
2022-11-11 19:28:02 +08:00
parent e668670da8
commit 605e0fbbe7
46 changed files with 841 additions and 215 deletions

View File

@@ -1,6 +1,7 @@
package com.acgist.taoyao.boot.config;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.util.Timer;
import java.util.TimerTask;
import java.util.stream.Collectors;
@@ -50,6 +51,7 @@ import org.springframework.web.servlet.NoHandlerFoundException;
import com.acgist.taoyao.boot.controller.TaoyaoControllerAdvice;
import com.acgist.taoyao.boot.controller.TaoyaoErrorController;
import com.acgist.taoyao.boot.interceptor.SecurityInterceptor;
import com.acgist.taoyao.boot.interceptor.SlowInterceptor;
import com.acgist.taoyao.boot.model.MessageCode;
import com.acgist.taoyao.boot.service.IdService;
import com.acgist.taoyao.boot.service.impl.IdServiceImpl;
@@ -73,7 +75,7 @@ import lombok.extern.slf4j.Slf4j;
@Configuration
@EnableScheduling
@EnableAspectJAutoProxy(exposeProxy = true)
@EnableConfigurationProperties({ TaoyaoProperties.class, WebrtcProperties.class, SecurityProperties.class })
@EnableConfigurationProperties({ IdProperties.class, TaoyaoProperties.class, WebrtcProperties.class, SecurityProperties.class })
public class BootAutoConfiguration {
@Value("${spring.application.name:taoyao}")
@@ -119,6 +121,12 @@ public class BootAutoConfiguration {
};
}
@Bean
@ConditionalOnMissingBean
public SlowInterceptor slowInterceptor() {
return new SlowInterceptor();
}
@Bean
@ConditionalOnMissingBean
public SecurityInterceptor securityInterceptor() {
@@ -139,15 +147,15 @@ public class BootAutoConfiguration {
@PostConstruct
public void init() {
final var runtime = Runtime.getRuntime();
final var runtimeMXBean = ManagementFactory.getRuntimeMXBean();
final Runtime runtime = Runtime.getRuntime();
final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
final String freeMemory = FileUtils.formatSize(runtime.freeMemory());
final String totalMemory = FileUtils.formatSize(runtime.totalMemory());
final String maxMemory = FileUtils.formatSize(runtime.maxMemory());
log.info("操作系统名称:{}", System.getProperty("os.name"));
log.info("操作系统架构:{}", System.getProperty("os.arch"));
log.info("操作系统版本:{}", System.getProperty("os.version"));
log.info("操作系统可用处理器数量:{}", runtime.availableProcessors());
log.info("可用处理器数量:{}", runtime.availableProcessors());
log.info("Java版本{}", System.getProperty("java.version"));
log.info("Java主目录{}", System.getProperty("java.home"));
log.info("Java库目录{}", System.getProperty("java.library.path"));
@@ -184,8 +192,8 @@ public class BootAutoConfiguration {
ErrorUtils.register(MessageCode.CODE_3400, MethodArgumentNotValidException.class);
ErrorUtils.register(MessageCode.CODE_3500, ConversionNotSupportedException.class);
ErrorUtils.register(MessageCode.CODE_3500, HttpMessageNotWritableException.class);
ErrorUtils.register(MessageCode.CODE_3415, HttpMediaTypeNotSupportedException.class);
ErrorUtils.register(MessageCode.CODE_3400, MissingServletRequestPartException.class);
ErrorUtils.register(MessageCode.CODE_3415, HttpMediaTypeNotSupportedException.class);
ErrorUtils.register(MessageCode.CODE_3406, HttpMediaTypeNotAcceptableException.class);
ErrorUtils.register(MessageCode.CODE_3405, HttpRequestMethodNotSupportedException.class);
ErrorUtils.register(MessageCode.CODE_3400, MissingServletRequestParameterException.class);

View File

@@ -0,0 +1,27 @@
package com.acgist.taoyao.boot.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import lombok.Getter;
import lombok.Setter;
/**
* ID配置
*
* @author acgist
*/
@Getter
@Setter
@ConfigurationProperties(prefix = "taoyao.id")
public class IdProperties {
/**
* 机器序号
*/
private Integer sn;
/**
* 最大序号
*/
private Integer maxIndex;
}

View File

@@ -1,5 +1,7 @@
package com.acgist.taoyao.boot.config;
import java.util.List;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@@ -9,8 +11,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
@@ -61,46 +63,45 @@ public class OpenApiAutoConfiguration {
public OpenAPI openAPI() {
return new OpenAPI()
.info(this.buildInfo())
.externalDocs(this.buildExternalDocumentation())
.addSecurityItem(this.buildSecurityRequirement())
.security(this.buildSecurity())
.components(this.buildComponents());
}
/**
* @return 文档基本信息
* @return 基本信息
*/
private Info buildInfo() {
return new Info()
.contact(this.buildContact())
.license(this.buildLicense())
.title(this.taoyaoProperties.getName())
.version(this.taoyaoProperties.getVersion())
.description(this.taoyaoProperties.getDescription())
.license(this.buildLicense());
.description(this.taoyaoProperties.getDescription());
}
/**
* @return 授权协议信息
* @return 联系方式
*/
private Contact buildContact() {
return new Contact()
.url(this.taoyaoProperties.getUrl())
.name(this.taoyaoProperties.getName());
}
/**
* @return 开源信息
*/
private License buildLicense() {
return new License()
.name("Apache 2.0")
.url("https://www.apache.org/licenses/LICENSE-2.0.html");
}
/**
* @return 外部文档信息
*/
private ExternalDocumentation buildExternalDocumentation() {
return new ExternalDocumentation()
.description(this.taoyaoProperties.getDescription())
.url(this.taoyaoProperties.getUrl());
}
/**
* @return 授权
*/
private SecurityRequirement buildSecurityRequirement() {
return new SecurityRequirement()
.addList(SecurityProperties.BASIC);
private List<SecurityRequirement> buildSecurity() {
return List.of(new SecurityRequirement().addList(SecurityProperties.BASIC));
}
/**

View File

@@ -36,5 +36,4 @@ public class TaoyaoProperties {
*/
private String description;
}

View File

@@ -1,11 +1,12 @@
package com.acgist.taoyao.boot.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import com.acgist.taoyao.boot.interceptor.SecurityInterceptor;
import com.acgist.taoyao.boot.interceptor.InterceptorAdapter;
import lombok.extern.slf4j.Slf4j;
@@ -19,12 +20,17 @@ import lombok.extern.slf4j.Slf4j;
public class WebMvcConfigurerAutoConfiguration implements WebMvcConfigurer {
@Autowired
private SecurityInterceptor securityInterceptor;
private ApplicationContext context;
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("加载拦截器securityInterceptor");
registry.addInterceptor(this.securityInterceptor).addPathPatterns("/**");
this.context.getBeansOfType(InterceptorAdapter.class).entrySet().stream()
.sorted((a, z) -> a.getValue().compareTo(z.getValue()))
.forEach(entry -> {
final InterceptorAdapter value = entry.getValue();
log.info("加载拦截器:{}-{}", entry.getKey(), value.name());
registry.addInterceptor(value).addPathPatterns(value.pathPattern());
});
}
}

View File

@@ -13,7 +13,7 @@ import lombok.Setter;
*/
@Getter
@Setter
@Schema(name = "WebRTC配置")
@Schema(title = "WebRTC配置", description = "WebRTC配置")
@ConfigurationProperties(prefix = "taoyao.webrtc")
public class WebrtcProperties {
@@ -42,15 +42,17 @@ public class WebrtcProperties {
/**
* 类型
*/
@Schema(name = "架构类型", description = "WebRTC架构类型")
@Schema(title = "架构类型", description = "WebRTC架构类型")
private Type type;
/**
* stun服务器
*/
@Schema(title = "stun服务器", description = "stun服务器")
private String[] stun;
/**
* turn服务器
*/
@Schema(title = "turn服务器", description = "turn服务器")
private String[] turn;
}

View File

@@ -0,0 +1,28 @@
package com.acgist.taoyao.boot.interceptor;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* 拦截器适配器
*
* @author acgist
*/
public abstract class InterceptorAdapter implements Ordered, HandlerInterceptor, Comparable<InterceptorAdapter> {
/**
* @return 名称
*/
public abstract String name();
/**
* @return 拦截地址
*/
public abstract String[] pathPattern();
@Override
public int compareTo(InterceptorAdapter o) {
return Integer.compare(this.getOrder(), o.getOrder());
}
}

View File

@@ -10,7 +10,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.HandlerInterceptor;
import com.acgist.taoyao.boot.config.SecurityProperties;
@@ -22,20 +21,29 @@ import lombok.extern.slf4j.Slf4j;
* @author acgist
*/
@Slf4j
public class SecurityInterceptor implements HandlerInterceptor {
public class SecurityInterceptor extends InterceptorAdapter {
/**
* 时间
*/
private ThreadLocal<Long> local = new ThreadLocal<>();
@Autowired
private SecurityProperties securityProperties;
@Override
public String name() {
return "安全拦截";
}
@Override
public String[] pathPattern() {
return new String[] { "/**" };
}
@Override
public int getOrder() {
return Integer.MIN_VALUE + 1;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(this.permit(request) || this.authorization(request)) {
this.local.set(System.currentTimeMillis());
return true;
}
response.setStatus(HttpStatus.UNAUTHORIZED.value());
@@ -85,14 +93,4 @@ public class SecurityInterceptor implements HandlerInterceptor {
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
final long duration;
final Long last = this.local.get();
if(last != null && (duration = System.currentTimeMillis() - last) > 1000) {
log.info("执行时间过慢:{}-{}", request.getRequestURI(), duration);
}
this.local.remove();
}
}

View File

@@ -0,0 +1,59 @@
package com.acgist.taoyao.boot.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import com.acgist.taoyao.boot.config.TaoyaoProperties;
import lombok.extern.slf4j.Slf4j;
/**
* 过慢请求统计拦截
*
* @author acgist
*/
@Slf4j
public class SlowInterceptor extends InterceptorAdapter {
/**
* 时间
*/
private ThreadLocal<Long> local = new ThreadLocal<>();
@Autowired
private TaoyaoProperties taoyaoProperties;
@Override
public String name() {
return "过慢请求统计拦截";
}
@Override
public String[] pathPattern() {
return new String[] { "/**" };
}
@Override
public int getOrder() {
return Integer.MIN_VALUE;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
this.local.set(System.currentTimeMillis());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) throws Exception {
final long duration;
final Long last = this.local.get();
if(last != null && (duration = System.currentTimeMillis() - last) > this.taoyaoProperties.getTimeout()) {
log.info("请求执行时间过慢:{}-{}", request.getRequestURI(), duration);
}
this.local.remove();
}
}

View File

@@ -2,6 +2,7 @@ package com.acgist.taoyao.boot.model;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
@@ -9,12 +10,13 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* 信令头部
* 请求响应头部
*
* @author acgist
*/
@Getter
@Setter
@Schema( title = "请求响应头部", description = "请求响应头部")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@@ -23,20 +25,24 @@ public class Header implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 信令版本
* 请求响应版本
*/
@Schema(title = "请求响应版本", description = "请求响应版本")
private String v;
/**
* 请求标识
* 请求响应标识
*/
@Schema(title = "请求响应标识", description = "请求响应标识")
private Long id;
/**
* 终端标识
*/
@Schema(title = "终端标识", description = "终端标识")
private String sn;
/**
* 协议标识
*/
@Schema(title = "协议标识", description = "协议标识")
private Integer pid;
}

View File

@@ -14,13 +14,13 @@ import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* 消息
* 请求响应消息
*
* @author acgist
*/
@Getter
@Setter
@Schema(name = "消息", description = "请求响应消息")
@Schema(title = "请求响应消息", description = "请求响应消息")
@Builder
@NoArgsConstructor
@AllArgsConstructor
@@ -31,22 +31,22 @@ public class Message implements Serializable {
/**
* 响应编码
*/
@Schema(name = "响应编码", description = "响应消息标识响应状态")
@Schema(title = "响应编码", description = "响应消息标识响应状态")
private String code;
/**
* 响应描述
*/
@Schema(name = "响应描述", description = "响应消息描述响应编码")
@Schema(title = "响应描述", description = "响应消息描述响应编码")
private String message;
/**
* 请求响应头部
*/
@Schema(name = "请求响应头部", description = "请求响应头部")
@Schema(title = "请求响应头部", description = "请求响应头部")
private Header header;
/**
* 请求响应主体
*/
@Schema(name = "请求响应主体", description = "请求响应主体")
@Schema(title = "请求响应主体", description = "请求响应主体")
private Object body;
/**

View File

@@ -5,10 +5,10 @@ import lombok.Getter;
/**
* 状态编码
*
* 1xxx=前置错误:前置校验错误(数据校验
* 2xxx=内部错误:服务内部错误
* 1xxx=前置错误:数据校验
* 2xxx=内部错误
* 3xxx=请求错误HTTP错误
* 9999=未知错误:没有适配异常
* 9999=未知错误
*
* @author acgist
*/
@@ -41,7 +41,7 @@ public enum MessageCode {
CODE_9999("9999", 500, "未知错误");
/**
* HTTP状态编码头部
* HTTP状态编码前缀
*/
public static final String HTTP_STATUS = "3";
@@ -54,7 +54,7 @@ public enum MessageCode {
*/
private final Integer status;
/**
* 状态信息
* 状态描述
*/
private final String message;
@@ -85,14 +85,7 @@ public enum MessageCode {
* @return 状态编码
*/
public static final MessageCode of(Integer status) {
final String code = HTTP_STATUS + status;
final MessageCode[] values = MessageCode.values();
for (MessageCode value : values) {
if (value.code.equals(code)) {
return value;
}
}
return of(String.valueOf(status));
return of(HTTP_STATUS + status);
}
}

View File

@@ -2,14 +2,16 @@ package com.acgist.taoyao.boot.model;
import java.util.Objects;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import lombok.Getter;
/**
* 状态编码异常
*
* @author acgist
*/
@Getter
public class MessageCodeException extends RuntimeException {
private static final long serialVersionUID = 1L;
@@ -20,104 +22,59 @@ public class MessageCodeException extends RuntimeException {
private final MessageCode code;
/**
* @param messages 错误消息
* @param message 错误消息
*
* @return 状态编码异常
*/
public static final MessageCodeException of(Object ... messages) {
return of(null, MessageCode.CODE_9999, messages);
public static final MessageCodeException of(String message) {
return of(null, null, message);
}
/**
* @param t 异常
* @param messages 错误消息
* @param message 错误消息
*
* @return 状态编码异常
*/
public static final MessageCodeException of(Throwable t, Object ... messages) {
return of(t, MessageCode.CODE_9999, messages);
public static final MessageCodeException of(Throwable t, String message) {
return of(t, null, message);
}
/**
* @param code 状态编码
* @param messages 错误消息
* @param message 错误消息
*
* @return 状态编码异常
*/
public static final MessageCodeException of(MessageCode code, Object ... messages) {
return of(null, code, messages);
public static final MessageCodeException of(MessageCode code, String message) {
return of(null, code, message);
}
/**
* @param t 异常
* @param code 状态编码
* @param messages 错误消息
* @param message 错误消息
*
* @return 状态编码异常
*/
public static final MessageCodeException of(Throwable t, MessageCode code, Object ... messages) {
final String message;
if(ArrayUtils.isEmpty(messages)) {
message = Objects.isNull(t) ? code.getMessage() : t.getMessage();
} else {
// 拼接错误描述
final StringBuilder builder = new StringBuilder();
for (Object value : messages) {
builder.append(value);
}
message = builder.toString();
public static final MessageCodeException of(Throwable t, MessageCode code, String message) {
if(code == null) {
code = MessageCode.CODE_9999;
}
return new MessageCodeException(code, message, t);
}
/**
* @param code 状态编码
*/
public MessageCodeException(MessageCode code) {
this(code, code.getMessage());
if(StringUtils.isEmpty(message)) {
message = Objects.isNull(t) ? code.getMessage() : t.getMessage();
}
return new MessageCodeException(t, code, message);
}
/**
* @param t 异常
* @param code 状态编码
* @param message 错误消息
*/
public MessageCodeException(MessageCode code, String message) {
this(code, message, null);
}
/**
* @param code 状态编码
* @param t 异常
*/
public MessageCodeException(MessageCode code, Throwable t) {
this(code, Objects.isNull(t) ? code.getMessage() : t.getMessage(), t);
}
/**
* @param code 状态编码
* @param message 错误消息
* @param t 异常
*/
public MessageCodeException(MessageCode code, String message, Throwable t) {
public MessageCodeException(Throwable t, MessageCode code, String message) {
super(message, t);
this.code = code;
}
/**
* @return 状态编码
*/
public MessageCode getCode() {
return this.code;
}
@Override
public String getMessage() {
final String message = super.getMessage();
if (StringUtils.isEmpty(message)) {
return this.code.getMessage();
} else {
return message;
}
}
}

View File

@@ -5,6 +5,7 @@ import java.time.LocalDateTime;
import com.acgist.taoyao.boot.utils.JSONUtils;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@@ -16,6 +17,7 @@ import lombok.Setter;
*/
@Getter
@Setter
@Schema(title = "模型", description = "模型")
@EqualsAndHashCode(callSuper = false, of = "id")
public abstract class Model implements Cloneable, Serializable {
@@ -24,14 +26,17 @@ public abstract class Model implements Cloneable, Serializable {
/**
* ID
*/
@Schema(title = "标识", description = "标识")
private Long id;
/**
* 创建时间
*/
@Schema(title = "创建时间", description = "创建时间")
private LocalDateTime createDate;
/**
* 修改时间
*/
@Schema(title = "修改时间", description = "修改时间")
private LocalDateTime modifyDate;
@Override

View File

@@ -1,7 +1,7 @@
package com.acgist.taoyao.boot.service;
/**
* ID
* ID生成器
*
* @author acgist
*/

View File

@@ -2,30 +2,25 @@ package com.acgist.taoyao.boot.service.impl;
import java.time.LocalDateTime;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.annotation.Autowired;
import com.acgist.taoyao.boot.config.IdProperties;
import com.acgist.taoyao.boot.service.IdService;
public class IdServiceImpl implements IdService {
/**
* 机器序号
*/
@Value("${taoyao.sn:0}")
private int sn = 9;
/**
* 当前索引
*/
private int index;
/**
* 最大索引
*/
private static final int MAX_INDEX = 999999;
@Autowired
private IdProperties idProperties;
@Override
public long id() {
synchronized (this) {
if (++this.index > MAX_INDEX) {
if (++this.index > this.idProperties.getMaxIndex()) {
this.index = 0;
}
}
@@ -38,7 +33,7 @@ public class IdServiceImpl implements IdService {
1000000000L * time.getMinute() +
10000000L * time.getSecond() +
// 机器序号一位
1000000L * this.sn +
1000000L * this.idProperties.getSn() +
// 每秒并发数量
this.index;
}

View File

@@ -125,7 +125,8 @@ public final class ErrorUtils {
请求参数:{}
请求方法:{}
错误信息:{}
""", path, query, method, message, globalError);
响应状态:{}
""", path, query, method, message, status, globalError);
} else {
log.warn("""
请求错误
@@ -133,8 +134,9 @@ public final class ErrorUtils {
请求参数:{}
请求方法:{}
错误信息:{}
响应状态:{}
原始信息:{}
""", path, query, method, message, globalError);
""", path, query, method, message, status, globalError);
}
return message;
}

View File

@@ -60,7 +60,7 @@ public final class JSONUtils {
try {
return MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw MessageCodeException.of(e, "Java转JSON失败", object);
throw MessageCodeException.of(e, "Java转JSON失败" + object);
}
}
@@ -81,7 +81,7 @@ public final class JSONUtils {
return MAPPER.readValue(json, new TypeReference<T>() {
});
} catch (IOException e) {
throw MessageCodeException.of(e, "JSON转Java失败", json);
throw MessageCodeException.of(e, "JSON转Java失败" + json);
}
}
@@ -102,7 +102,7 @@ public final class JSONUtils {
try {
return MAPPER.readValue(json, clazz);
} catch (IOException e) {
throw MessageCodeException.of(e, "JSON转Java失败", json);
throw MessageCodeException.of(e, "JSON转Java失败" + json);
}
}
@@ -124,7 +124,7 @@ public final class JSONUtils {
return MAPPER.readValue(json, new TypeReference<Map<K, V>>() {
});
} catch (IOException e) {
throw MessageCodeException.of(e, "JSON转Map失败", json);
throw MessageCodeException.of(e, "JSON转Map失败" + json);
}
}
@@ -145,7 +145,7 @@ public final class JSONUtils {
return MAPPER.readValue(json, new TypeReference<List<T>>() {
});
} catch (IOException e) {
throw MessageCodeException.of(e, "JSON转List失败", json);
throw MessageCodeException.of(e, "JSON转List失败" + json);
}
}
@@ -169,7 +169,7 @@ public final class JSONUtils {
return MAPPER.readValue(json, new TypeReference<List<T>>() {
});
} catch (IOException e) {
throw MessageCodeException.of(e, "JSON转List失败", json);
throw MessageCodeException.of(e, "JSON转List失败" + json);
}
}