This commit is contained in:
Jerry
2020-05-24 22:59:25 +08:00
parent be7395cf82
commit 5d8aca463c
184 changed files with 4126 additions and 3508 deletions

View File

@@ -12,7 +12,7 @@ import java.util.Date;
* Controller的环绕拦截类。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@ControllerAdvice
public class MyControllerAdvice {

View File

@@ -1,9 +1,12 @@
package com.orange.admin.common.biz.advice;
import lombok.extern.slf4j.Slf4j;
import com.orange.admin.common.core.exception.InvalidClassFieldException;
import com.orange.admin.common.core.exception.InvalidDataFieldException;
import com.orange.admin.common.core.exception.InvalidDataModelException;
import com.orange.admin.common.core.constant.ErrorCodeEnum;
import com.orange.admin.common.core.exception.RedisCacheAccessException;
import com.orange.admin.common.core.object.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.PersistenceException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
@@ -19,26 +22,86 @@ import java.util.concurrent.TimeoutException;
* 用不同的函数,处理不同类型的异常。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
@RestControllerAdvice
public class MyExceptionHandler {
/**
* 通用异常处理方法。
*
* @param ex 异常对象。
* @param request http请求。
* @return 应答对象。
*/
@ExceptionHandler(value = Exception.class)
public ResponseResult<?> exceptionHandle(Exception ex, HttpServletRequest request) {
public ResponseResult<Void> exceptionHandle(Exception ex, HttpServletRequest request) {
log.error("Unhandled exception from URL [" + request.getRequestURI() + "]", ex);
return ResponseResult.error(ErrorCodeEnum.UNHANDLED_EXCEPTION);
}
/**
* 无效的实体对象异常。
*
* @param ex 异常对象。
* @param request http请求。
* @return 应答对象。
*/
@ExceptionHandler(value = InvalidDataModelException.class)
public ResponseResult<Void> invalidDataModelExceptionHandle(Exception ex, HttpServletRequest request) {
log.error("InvalidDataModelException exception from URL [" + request.getRequestURI() + "]", ex);
return ResponseResult.error(ErrorCodeEnum.INVALID_DATA_MODEL);
}
/**
* 无效的实体对象字段异常。
*
* @param ex 异常对象。
* @param request http请求。
* @return 应答对象。
*/
@ExceptionHandler(value = InvalidDataFieldException.class)
public ResponseResult<Void> invalidDataFieldExceptionHandle(Exception ex, HttpServletRequest request) {
log.error("InvalidDataFieldException exception from URL [" + request.getRequestURI() + "]", ex);
return ResponseResult.error(ErrorCodeEnum.INVALID_DATA_FIELD);
}
/**
* 无效类字段异常。
*
* @param ex 异常对象。
* @param request http请求。
* @return 应答对象。
*/
@ExceptionHandler(value = InvalidClassFieldException.class)
public ResponseResult<Void> invalidClassFieldExceptionHandle(Exception ex, HttpServletRequest request) {
log.error("InvalidClassFieldException exception from URL [" + request.getRequestURI() + "]", ex);
return ResponseResult.error(ErrorCodeEnum.INVALID_CLASS_FIELD);
}
/**
* 重复键异常处理方法。
*
* @param ex 异常对象。
* @param request http请求。
* @return 应答对象。
*/
@ExceptionHandler(value = DuplicateKeyException.class)
public ResponseResult<?> duplicateKeyExceptionHandle(Exception ex, HttpServletRequest request) {
public ResponseResult<Void> duplicateKeyExceptionHandle(Exception ex, HttpServletRequest request) {
log.error("DuplicateKeyException exception from URL [" + request.getRequestURI() + "]", ex);
return ResponseResult.error(ErrorCodeEnum.DUPLICATED_UNIQUE_KEY);
}
/**
* 数据访问失败异常处理方法。
*
* @param ex 异常对象。
* @param request http请求。
* @return 应答对象。
*/
@ExceptionHandler(value = DataAccessException.class)
public ResponseResult<?> dataAccessExceptionHandle(Exception ex, HttpServletRequest request) {
public ResponseResult<Void> dataAccessExceptionHandle(Exception ex, HttpServletRequest request) {
log.error("DataAccessException exception from URL [" + request.getRequestURI() + "]", ex);
if (ex.getCause() instanceof PersistenceException
&& ex.getCause().getCause() instanceof PermissionDeniedDataAccessException) {
@@ -47,8 +110,15 @@ public class MyExceptionHandler {
return ResponseResult.error(ErrorCodeEnum.DATA_ACCESS_FAILED);
}
/**
* Redis缓存访问异常处理方法。
*
* @param ex 异常对象。
* @param request http请求。
* @return 应答对象。
*/
@ExceptionHandler(value = RedisCacheAccessException.class)
public ResponseResult<?> redisCacheAccessExceptionHandle(Exception ex, HttpServletRequest request) {
public ResponseResult<Void> redisCacheAccessExceptionHandle(Exception ex, HttpServletRequest request) {
log.error("RedisCacheAccessException exception from URL [" + request.getRequestURI() + "]", ex);
if (ex.getCause() instanceof TimeoutException) {
return ResponseResult.error(ErrorCodeEnum.REDIS_CACHE_ACCESS_TIMEOUT);

View File

@@ -0,0 +1,41 @@
package com.orange.admin.common.biz.aop;
import com.orange.admin.common.core.object.GlobalThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* 禁用数据权限过滤的AOP处理类。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
@Aspect
@Component
@Order(1)
@Slf4j
public class DisableDataPermAspect {
/**
* 所有标记了DisableDataPerm注解的方法。
*/
@Pointcut("@annotation(com.orange.admin.common.core.annotation.DisableDataPerm)")
public void disableDataPermPointCut() {
// 空注释避免sonar警告
}
@Around("disableDataPermPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
boolean dataPermEnabled = GlobalThreadLocal.setDataPerm(false);
try {
return point.proceed();
} finally {
GlobalThreadLocal.setDataPerm(dataPermEnabled);
}
}
}

View File

@@ -0,0 +1,19 @@
package com.orange.admin.common.biz.base.service;
import com.orange.admin.common.core.base.service.BaseService;
import lombok.extern.slf4j.Slf4j;
/**
* 所有业务Service的共同基类。由于BaseService位于common-core模块内该模块很少涉及spring bean的集成
* 因此该类位于业务服务类和BaseService之间主要提供一些通用方法特别是与spring bean相关的业务代码。
* NOTE: 目前该类实现为空,主要是为了便于用户自行扩展,同时也能方便今后向微服务的升级,
*
* @param <M> Model对象的类型。
* @param <K> Model对象主键的类型。
* @author Stephen.Liu
* @date 2020-05-24
*/
@Slf4j
public abstract class BaseBizService<M, K> extends BaseService<M, K> {
}

View File

@@ -8,7 +8,7 @@ import org.springframework.context.annotation.Configuration;
* 应用程序自定义的通用属性配置文件。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Data
@Configuration

View File

@@ -6,12 +6,16 @@ import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import java.util.ArrayList;
import java.util.List;
/**
* DTA 对象数据格式转换器这里集成了alibaba的fastjson作为消息格式转换工具
* 对象数据格式转换器这里集成了alibaba的fastjson作为消息格式转换工具
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Configuration
public class FastjsonConfig {
@@ -19,9 +23,15 @@ public class FastjsonConfig {
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
fastConverter.setSupportedMediaTypes(supportedMediaTypes);
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
SerializerFeature.PrettyFormat, SerializerFeature.DisableCircularReferenceDetect);
SerializerFeature.PrettyFormat,
SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.IgnoreNonFieldGetter);
fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");
fastConverter.setFastJsonConfig(fastJsonConfig);
return new HttpMessageConverters(fastConverter);

View File

@@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
* tomcat配置对象。当前配置禁用了PUT和DELETE方法防止渗透攻击。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Configuration
public class TomcatConfig {

View File

@@ -3,6 +3,12 @@ package com.orange.admin.common.biz.constant;
import java.util.HashMap;
import java.util.Map;
/**
* 科目常量字典对象。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
public final class Subject {
/**
@@ -18,7 +24,7 @@ public final class Subject {
*/
public static final int ENGLISH = 2;
public static final Map<Object, String> DICT_MAP = new HashMap<>(3);
private static final Map<Object, String> DICT_MAP = new HashMap<>(3);
static {
DICT_MAP.put(CHINESE, "语文");
DICT_MAP.put(MATH, "数学");
@@ -31,8 +37,8 @@ public final class Subject {
* @param value 待验证的参数值。
* @return 合法返回true否则false。
*/
public static boolean isValid(int value) {
return DICT_MAP.containsKey(value);
public static boolean isValid(Integer value) {
return value != null && DICT_MAP.containsKey(value);
}
/**

View File

@@ -3,6 +3,12 @@ package com.orange.admin.common.biz.constant;
import java.util.HashMap;
import java.util.Map;
/**
* 是否常量字典对象。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
public final class YesNo {
/**
@@ -14,7 +20,7 @@ public final class YesNo {
*/
public static final int NO = 0;
public static final Map<Object, String> DICT_MAP = new HashMap<>(2);
private static final Map<Object, String> DICT_MAP = new HashMap<>(2);
static {
DICT_MAP.put(YES, "");
DICT_MAP.put(NO, "");
@@ -26,8 +32,8 @@ public final class YesNo {
* @param value 待验证的参数值。
* @return 合法返回true否则false。
*/
public static boolean isValid(int value) {
return DICT_MAP.containsKey(value);
public static boolean isValid(Integer value) {
return value != null && DICT_MAP.containsKey(value);
}
/**

View File

@@ -13,7 +13,7 @@ import javax.servlet.http.HttpServletResponse;
* 服务访问日志的拦截器,主要完成记录接口调用时长。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
public class AccessInterceptor implements HandlerInterceptor {
@@ -33,6 +33,7 @@ public class AccessInterceptor implements HandlerInterceptor {
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 这里需要加注释否则sonar不happy。
}
@Override

View File

@@ -9,9 +9,11 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
@@ -27,7 +29,7 @@ import java.util.*;
* 2、多个对象需要封装到一个对象里才可以用@RequestBody接收。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver {
@@ -47,10 +49,10 @@ public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver
}
/**
* 设置支持的方法参数类型
* 设置支持的方法参数类型
*
* @param parameter 方法参数
* @return 支持的类型
* @param parameter 方法参数
* @return 支持的类型
*/
@Override
public boolean supportsParameter(@NonNull MethodParameter parameter) {
@@ -58,10 +60,9 @@ public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver
}
/**
* 参数解析利用fastjson
* 注意非基本类型返回null会报空指针异常要通过反射或者JSON工具类创建一个空对象
* 参数解析利用fastjson
* 注意非基本类型返回null会报空指针异常要通过反射或者JSON工具类创建一个空对象
*/
@SuppressWarnings("unchecked")
@Override
public Object resolveArgument(
@NonNull MethodParameter parameter,
@@ -73,7 +74,7 @@ public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver
if (!HttpMethod.POST.name().equals(servletRequest.getMethod())) {
throw new IllegalArgumentException("Only POST method can be applied @MyRequestBody annotation");
}
if (!StringUtils.containsIgnoreCase(contentType, "application/json")) {
if (!StringUtils.containsIgnoreCase(contentType, MediaType.APPLICATION_JSON_VALUE)) {
throw new IllegalArgumentException(
"Only application/json Content-Type can be applied @MyRequestBody annotation");
}
@@ -106,37 +107,41 @@ public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver
// 基本类型包装类
if (isBasicDataTypes(parameterType)) {
return parseBasicTypeWrapper(parameterType, value);
// 字符串类型
} else if (parameterType == String.class) {
// 字符串类型
return value.toString();
}
// 数组类型
if (value instanceof JSONArray) {
Object o;
if (!parameterType.equals(List.class)) {
o = parameterType.newInstance();
parameterType = (Class<?>) ((ParameterizedType)
parameterType.getGenericSuperclass()).getActualTypeArguments()[0];
} else {
parameterType = parameterAnnotation.elementType();
if (parameterType.equals(Class.class)) {
throw new IllegalArgumentException(
String.format("List Type parameter %s MUST have elementType!", key));
}
o = new LinkedList<>();
}
if (!(o instanceof List)) {
throw new IllegalArgumentException(String.format("Required parameter %s is List!", key));
}
((List<Object>) o).addAll(((JSONArray) value).toJavaList(parameterType));
return o;
return parseArray(parameterType, parameterAnnotation.elementType(), key, value);
}
// 其他复杂对象
return JSONObject.toJavaObject((JSONObject) value, parameterType);
return JSON.toJavaObject((JSONObject) value, parameterType);
}
@SuppressWarnings("unchecked")
private Object parseArray(Class<?> parameterType, Class<?> elementType, String key, Object value)
throws IllegalAccessException, InstantiationException {
Object o;
if (!parameterType.equals(List.class)) {
o = parameterType.newInstance();
parameterType = (Class<?>) ((ParameterizedType)
parameterType.getGenericSuperclass()).getActualTypeArguments()[0];
} else {
parameterType = elementType;
if (parameterType.equals(Class.class)) {
throw new IllegalArgumentException(
String.format("List Type parameter %s MUST have elementType!", key));
}
o = new LinkedList<>();
}
if (!(o instanceof List)) {
throw new IllegalArgumentException(String.format("Required parameter %s is List!", key));
}
((List<Object>) o).addAll(((JSONArray) value).toJavaList(parameterType));
return o;
}
/**
* 基本类型解析
*/
private Object parsePrimitive(String parameterTypeName, Object value) {
final String booleanTypeName = "boolean";
if (booleanTypeName.equals(parameterTypeName)) {
@@ -173,9 +178,6 @@ public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver
return null;
}
/**
* 基本类型包装类解析
*/
private Object parseBasicTypeWrapper(Class<?> parameterType, Object value) {
if (Number.class.isAssignableFrom(parameterType)) {
if (value instanceof String) {
@@ -203,30 +205,20 @@ public class MyRequestArgumentResolver implements HandlerMethodArgumentResolver
return null;
}
/**
* 判断是否为基本数据类型包装类
*/
private boolean isBasicDataTypes(Class<?> clazz) {
return classSet.contains(clazz);
}
/**
* 获取请求体JSON字符串
*/
private JSONObject getRequestBody(NativeWebRequest webRequest) {
private JSONObject getRequestBody(NativeWebRequest webRequest) throws IOException {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
// 有就直接获取
JSONObject jsonObject = (JSONObject) webRequest.getAttribute(JSONBODY_ATTRIBUTE, NativeWebRequest.SCOPE_REQUEST);
JSONObject jsonObject = (JSONObject) webRequest.getAttribute(JSONBODY_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
// 没有就从请求中读取
if (jsonObject == null) {
try {
String jsonBody = IOUtils.toString(servletRequest.getReader());
jsonObject = JSON.parseObject(jsonBody);
if (jsonObject != null) {
webRequest.setAttribute(JSONBODY_ATTRIBUTE, jsonObject, NativeWebRequest.SCOPE_REQUEST);
}
} catch (IOException e) {
throw new RuntimeException(e);
String jsonBody = IOUtils.toString(servletRequest.getReader());
jsonObject = JSON.parseObject(jsonBody);
if (jsonObject != null) {
webRequest.setAttribute(JSONBODY_ATTRIBUTE, jsonObject, RequestAttributes.SCOPE_REQUEST);
}
}
return jsonObject;

View File

@@ -11,11 +11,12 @@ import java.util.Map;
* 应用程序启动后的事件监听对象。主要负责加载Model之间的字典关联和一对一关联所对应的Service结构关系。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Component
public class LoadCachedDataListener implements ApplicationListener<ApplicationReadyEvent> {
@SuppressWarnings("all")
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
Map<String, BaseDictService> serviceMap =

View File

@@ -11,11 +11,12 @@ import java.util.Map;
* 应用程序启动后的事件监听对象。主要负责加载Model之间的字典关联和一对一关联所对应的Service结构关系。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Component
public class LoadServiceRelationListener implements ApplicationListener<ApplicationReadyEvent> {
@SuppressWarnings("all")
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
Map<String, BaseService> serviceMap =

View File

@@ -14,7 +14,7 @@ import javax.annotation.PostConstruct;
* 升级为基于分布式Id生成服务的实现时仅需修改内部实现外部业务方法不会受到任何影响。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Component
public class BasicIdGenerator {