Commit fd9fb2a6 authored by dqjdda's avatar dqjdda
Browse files

Merge branch '2.3dev'

parents 7895e547 1839ef8d
......@@ -21,13 +21,12 @@ eladmin基于 Spring Boot 2.1.0 、 Jpa、 Spring Security、redis、Vue的前
| | 后端源码 | 前端源码 |
|--- |--- | --- |
| github | https://github.com/elunez/eladmin | https://github.com/elunez/eladmin-qd |
| 码云 | https://gitee.com/elunez/eladmin | https://gitee.com/elunez/eladmin-qt |
| github | https://github.com/elunez/eladmin | https://github.com/elunez/eladmin-web |
| 码云 | https://gitee.com/elunez/eladmin | https://gitee.com/elunez/eladmin-web |
#### 系统功能
- 用户管理:提供用户的相关配置,新增用户后,默认密码为123456
- 角色管理:对权限与菜单进行分配,可根据部门设置角色的数据权限
- 权限管理:权限细化到接口,可以理解成按钮权限
- 菜单管理:已实现菜单动态路由,后端可配置化,支持多级菜单
- 部门管理:可配置系统组织架构,树形表格展示
- 岗位管理:配置各个部门的职位
......@@ -44,16 +43,19 @@ eladmin基于 Spring Boot 2.1.0 、 Jpa、 Spring Security、redis、Vue的前
- 支付宝支付:整合了支付宝支付并且提供了测试账号,可自行测试
#### 项目结构
项目采用分模块开发方式,将通用的配置放在公共模块,```system```模块为系统核心模块也是项目入口模块,```logging``` 模块为系统的日志模块,```tools``` 为第三方工具模块,包含了图床、邮件、七牛云、支付宝,```generator``` 为系统的代码生成模块
项目采用按功能分模块开发方式,将通用的配置放在公共模块,```system```模块为系统核心模块也是项目入口模块,```logging``` 模块为系统的日志模块,```tools``` 为第三方工具模块,包含了图床、邮件、七牛云、支付宝,```generator``` 为系统的代码生成模块
- eladmin-common 公共模块
- exception 项目统一异常的处理
- mapper mapstruct的通用mapper
- redis redis缓存相关配置
- swagger2 接口文档配置
- utils 系统通用工具类
- annotation 为系统自定义注解
- aspect 自定义注解的切面
- base 提供了Entity、DTO基类和mapstruct的通用mapper
- config 自定义权限实现、redis配置、swagger配置
- exception 项目统一异常的处理
- utils 系统通用工具类
- eladmin-system 系统核心模块(系统启动入口)
- config 配置跨域与静态资源,与数据权限
- modules 系统相关模块(登录授权、定时任务等)
- config 配置跨域与静态资源,与数据权限
- thread 线程池相关
- modules 系统相关模块(登录授权、系统监控、定时任务等)
- eladmin-logging 系统日志模块
- eladmin-tools 系统第三方工具模块
- eladmin-generator 系统代码生成模块
......@@ -78,6 +80,6 @@ eladmin基于 Spring Boot 2.1.0 、 Jpa、 Spring Security、redis、Vue的前
</table>
#### 项目捐赠
项目的发展离不开你的支持,请作者喝杯咖啡吧 ☕! [Donate](https://docs.auauz.net/#/jz)
项目的发展离不开你的支持,请作者喝杯咖啡吧!ps:辣条也行 ☕! [Donate](https://docs.auauz.net/#/jz)
#### 反馈交流
- QQ交流群:891137268
......@@ -5,11 +5,10 @@
<parent>
<artifactId>eladmin</artifactId>
<groupId>me.zhengjie</groupId>
<version>2.2</version>
<version>2.3</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eladmin-common</artifactId>
<name>公共模块</name>
</project>
\ No newline at end of file
package me.zhengjie.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author jacky
* 用于标记匿名访问方法
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnonymousAccess {
}
......@@ -13,45 +13,42 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface Query {
/** Dong ZhaoYang 2017/8/7 基本对象的属性名 */
// Dong ZhaoYang 2017/8/7 基本对象的属性名
String propName() default "";
/** Dong ZhaoYang 2017/8/7 查询方式 */
// Dong ZhaoYang 2017/8/7 查询方式
Type type() default Type.EQUAL;
/**
* 连接查询的属性名,如User类中的dept
* @return
*/
String joinName() default "";
/**
* 默认左连接
* @return
*/
Join join() default Join.LEFT;
/**
* 多字段模糊搜索,仅支持String类型字段,多个用逗号隔开, 如@Query(blurry = "email,username")
* @return
*/
String blurry() default "";
enum Type {
/** jie 2019/6/4 相等 */
// jie 2019/6/4 相等
EQUAL
/** Dong ZhaoYang 2017/8/7 大于等于 */
// Dong ZhaoYang 2017/8/7 大于等于
, GREATER_THAN
/** Dong ZhaoYang 2017/8/7 小于等于 */
// Dong ZhaoYang 2017/8/7 小于等于
, LESS_THAN
/** Dong ZhaoYang 2017/8/7 中模糊查询 */
// Dong ZhaoYang 2017/8/7 中模糊查询
, INNER_LIKE
/** Dong ZhaoYang 2017/8/7 左模糊查询 */
// Dong ZhaoYang 2017/8/7 左模糊查询
, LEFT_LIKE
/** Dong ZhaoYang 2017/8/7 右模糊查询 */
// Dong ZhaoYang 2017/8/7 右模糊查询
, RIGHT_LIKE
/** Dong ZhaoYang 2017/8/7 小于 */
// Dong ZhaoYang 2017/8/7 小于
, LESS_THAN_NQ
//** jie 2019/6/4 包含 */
// jie 2019/6/4 包含
, IN
}
......
......@@ -12,7 +12,6 @@ import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
......@@ -23,10 +22,13 @@ import java.lang.reflect.Method;
@Aspect
@Component
public class LimitAspect {
@Autowired
private RedisTemplate redisTemplate;
private final RedisTemplate<Object,Object> redisTemplate;
private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class);
public LimitAspect(RedisTemplate<Object,Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Pointcut("@annotation(me.zhengjie.annotation.Limit)")
public void pointcut() {
......@@ -41,20 +43,18 @@ public class LimitAspect {
LimitType limitType = limit.limitType();
String key = limit.key();
if (StringUtils.isEmpty(key)) {
switch (limitType) {
case IP:
key = StringUtils.getIP(request);
break;
default:
key = signatureMethod.getName();
if (limitType == LimitType.IP) {
key = StringUtils.getIp(request);
} else {
key = signatureMethod.getName();
}
}
ImmutableList keys = ImmutableList.of(StringUtils.join(limit.prefix(), "_", key, "_", request.getRequestURI().replaceAll("/","_")));
ImmutableList<Object> keys = ImmutableList.of(StringUtils.join(limit.prefix(), "_", key, "_", request.getRequestURI().replaceAll("/","_")));
String luaScript = buildLuaScript();
RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
Number count = (Number) redisTemplate.execute(redisScript, keys, limit.count(), limit.period());
Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period());
if (null != count && count.intValue() <= limit.count()) {
logger.info("第{}次访问key为 {},描述为 [{}] 的接口", count, keys, limit.name());
return joinPoint.proceed();
......
package me.zhengjie.aspect;
/**
* 限流枚举
* @author /
*/
public enum LimitType {
// 默认
CUSTOMER,
// by ip addr
// by ip addr
IP;
}
package me.zhengjie.base;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.sql.Timestamp;
/**
* @author Zheng Jie
* @Date 2019年10月24日20:48:53
*/
@Getter
@Setter
public class BaseDTO implements Serializable {
private Boolean isDelete;
private Timestamp createTime;
private Timestamp updateTime;
}
package me.zhengjie.base;
import lombok.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.io.Serializable;
import java.sql.Timestamp;
import java.lang.reflect.Field;
/**
* @author Zheng Jie
* @Date 2019年10月24日20:46:32
*/
@Getter
@Setter
@MappedSuperclass
public class BaseEntity implements Serializable {
// 删除标识
@Column(name = "is_delete", columnDefinition = "bit default 0")
private Boolean isDelete = false;
@Column(name = "create_time")
@CreationTimestamp
private Timestamp createTime;
@Column(name = "update_time")
@UpdateTimestamp
private Timestamp updateTime;
public @interface Update {}
@Override
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this);
Field[] fields = this.getClass().getDeclaredFields();
try {
for (Field f : fields) {
f.setAccessible(true);
builder.append(f.getName(), f.get(this)).append("\n");
}
} catch (Exception e) {
builder.append("toString builder encounter an error");
}
return builder.toString();
}
}
package me.zhengjie.mapper;
package me.zhengjie.base;
import java.util.List;
......@@ -6,33 +6,25 @@ import java.util.List;
* @author Zheng Jie
* @date 2018-11-23
*/
public interface EntityMapper<D, E> {
public interface BaseMapper<D, E> {
/**
* DTO转Entity
* @param dto
* @return
*/
E toEntity(D dto);
/**
* Entity转DTO
* @param entity
* @return
*/
D toDto(E entity);
/**
* DTO集合转Entity集合
* @param dtoList
* @return
*/
List <E> toEntity(List<D> dtoList);
/**
* Entity集合转DTO集合
* @param entityList
* @return
*/
List <D> toDto(List<E> entityList);
}
package me.zhengjie.config;
import me.zhengjie.utils.SecurityUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Service(value = "el")
public class ElPermissionConfig {
public Boolean check(String ...permissions){
// 如果是匿名访问的,就放行
String anonymous = "anonymous";
if(Arrays.asList(permissions).contains(anonymous)){
return true;
}
// 获取当前用户的所有权限
List<String> elPermissions = SecurityUtils.getUserDetails().getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
// 判断当前用户的所有权限是否包含接口上定义的权限
List<String> list = Arrays.stream(permissions).filter(elPermissions::contains).collect(Collectors.toList());
return elPermissions.contains("admin") || list.size() != 0;
}
}
package me.zhengjie.redis;
package me.zhengjie.config;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.utils.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
......@@ -19,7 +23,12 @@ import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
/**
* @author Zheng Jie
......@@ -28,21 +37,19 @@ import java.time.Duration;
@Slf4j
@Configuration
@EnableCaching
// 自动配置
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig extends CachingConfigurerSupport {
/**
* 设置 redis 数据默认过期时间,默认1天
* 设置 redis 数据默认过期时间,默认6小时
* 设置@cacheable 序列化方式
* @return
*/
@Bean
public RedisCacheConfiguration redisCacheConfiguration(){
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig();
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofDays(1));
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).entryTtl(Duration.ofHours(6));
return configuration;
}
......@@ -51,21 +58,20 @@ public class RedisConfig extends CachingConfigurerSupport {
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//序列化
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// 全局开启AutoType,不建议使用
// ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 全局开启AutoType,这里方便开发,使用全局的方式
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
// 建议使用这种方式,小范围指定白名单
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.system.service.dto");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.service.dto");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.system.domain");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.quartz.domain");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.monitor.domain");
ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.security.security");
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.domain");
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.system.service.dto");
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.service.dto");
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.system.domain");
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.quartz.domain");
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.monitor.domain");
// ParserConfig.getGlobalInstance().addAccept("me.zhengjie.modules.security.security");
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
......@@ -75,20 +81,27 @@ public class RedisConfig extends CachingConfigurerSupport {
/**
* 自定义缓存key生成策略,默认将使用该策略
* 使用方法 @Cacheable
* @return
*/
@Bean
@Override
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(JSON.toJSONString(obj).hashCode());
Map<String,Object> container = new HashMap<>();
Class<?> targetClassClass = target.getClass();
// 类地址
container.put("class",targetClassClass.toGenericString());
// 方法名称
container.put("methodName",method.getName());
// 包名称
container.put("package",targetClassClass.getPackage());
// 参数列表
for (int i = 0; i < params.length; i++) {
container.put(String.valueOf(i),params[i]);
}
return sb.toString();
// 转为JSON字符串
String jsonString = JSON.toJSONString(container);
// 做SHA256 Hash计算,得到一个SHA256摘要作为Key
return DigestUtils.sha256Hex(jsonString);
};
}
......@@ -97,7 +110,7 @@ public class RedisConfig extends CachingConfigurerSupport {
public CacheErrorHandler errorHandler() {
// 异常处理,当Redis发生异常时,打印日志,但是程序正常走
log.info("初始化 -> [{}]", "Redis CacheErrorHandler");
CacheErrorHandler cacheErrorHandler = new CacheErrorHandler() {
return new CacheErrorHandler() {
@Override
public void handleCacheGetError(RuntimeException e, Cache cache, Object key) {
log.error("Redis occur handleCacheGetError:key -> [{}]", key, e);
......@@ -118,7 +131,74 @@ public class RedisConfig extends CachingConfigurerSupport {
log.error("Redis occur handleCacheClearError:", e);
}
};
return cacheErrorHandler;
}
}
/**
* Value 序列化
*
* @author /
* @param <T>
*/
class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
private Class<T> clazz;
FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(StandardCharsets.UTF_8);
}
@Override
public T deserialize(byte[] bytes) {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, StandardCharsets.UTF_8);
return JSON.parseObject(str, clazz);
}
}
/**
* 重写序列化器
*
* @author /
*/
class StringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
StringRedisSerializer() {
this(StandardCharsets.UTF_8);
}
private StringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
}
@Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}
@Override
public byte[] serialize(Object object) {
String string = JSON.toJSONString(object);
if (StringUtils.isBlank(string)) {
return null;
}
string = string.replace("\"", "");
return string.getBytes(charset);
}
}
package me.zhengjie.swagger2;
package me.zhengjie.config;
import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Predicates;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.data.domain.Pageable;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.schema.AlternateTypeRule;
import springfox.documentation.schema.AlternateTypeRuleConvention;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
......@@ -15,6 +23,8 @@ import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
import static springfox.documentation.schema.AlternateTypeRules.newRule;
/**
* api页面 /swagger-ui.html
......@@ -33,9 +43,10 @@ public class SwaggerConfig {
private Boolean enabled;
@Bean
@SuppressWarnings("all")
public Docket createRestApi() {
ParameterBuilder ticketPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
List<Parameter> pars = new ArrayList<>();
ticketPar.name(tokenHeader).description("token")
.modelRef(new ModelRef("string"))
.parameterType("header")
......@@ -55,8 +66,43 @@ public class SwaggerConfig {
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("eladmin 接口文档")
.version("2.1")
.version("2.3")
.build();
}
}
/**
* 将Pageable转换展示在swagger中
*/
@Configuration
class SwaggerDataConfig {
@Bean
public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) {
return new AlternateTypeRuleConvention() {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public List<AlternateTypeRule> rules() {
return newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class)));
}
};
}
@ApiModel
@Data
private static class Page {
@ApiModelProperty("页码 (0..N)")
private Integer page;
@ApiModelProperty("每页显示的数目")
private Integer size;
@ApiModelProperty("以下列格式排序标准:property[,asc | desc]。 默认排序顺序为升序。 支持多种排序条件:如:id,asc")
private List<String> sort;
}
}
......@@ -2,7 +2,6 @@ package me.zhengjie.exception;
import lombok.Getter;
import org.springframework.http.HttpStatus;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
/**
......
......@@ -2,33 +2,18 @@ package me.zhengjie.exception;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;
/**
* @author Zheng Jie
* @date 2018-11-23
*/
public class EntityExistException extends RuntimeException {
public EntityExistException(Class clazz, Object... saveBodyParamsMap) {
super(EntityExistException.generateMessage(clazz.getSimpleName(), toMap(String.class, String.class, saveBodyParamsMap)));
}
private static String generateMessage(String entity, Map<String, String> saveBodyParams) {
return StringUtils.capitalize(entity) +
" 已存在 " +
saveBodyParams;
public EntityExistException(Class clazz, String field, String val) {
super(EntityExistException.generateMessage(clazz.getSimpleName(), field, val));
}
private static <K, V> Map<K, V> toMap(
Class<K> keyType, Class<V> valueType, Object... entries) {
if (entries.length % 2 == 1)
throw new IllegalArgumentException("Invalid entries");
return IntStream.range(0, entries.length / 2).map(i -> i * 2)
.collect(HashMap::new,
(m, i) -> m.put(keyType.cast(entries[i]), valueType.cast(entries[i + 1])),
Map::putAll);
private static String generateMessage(String entity, String field, String val) {
return StringUtils.capitalize(entity)
+ " with " + field + " "+ val + " existed";
}
}
\ No newline at end of file
......@@ -2,34 +2,18 @@ package me.zhengjie.exception;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.IntStream;
/**
* @author Zheng Jie
* @date 2018-11-23
*/
public class EntityNotFoundException extends RuntimeException {
public EntityNotFoundException(Class clazz, Object... searchParamsMap) {
super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), toMap(String.class, String.class, searchParamsMap)));
}
private static String generateMessage(String entity, Map<String, String> searchParams) {
return StringUtils.capitalize(entity) +
" 不存在 " +
searchParams;
public EntityNotFoundException(Class clazz, String field, String val) {
super(EntityNotFoundException.generateMessage(clazz.getSimpleName(), field, val));
}
private static <K, V> Map<K, V> toMap(
Class<K> keyType, Class<V> valueType, Object... entries) {
if (entries.length % 2 == 1)
throw new IllegalArgumentException("Invalid entries");
return IntStream.range(0, entries.length / 2).map(i -> i * 2)
.collect(HashMap::new,
(m, i) -> m.put(keyType.cast(entries[i]), valueType.cast(entries[i + 1])),
Map::putAll);
private static String generateMessage(String entity, String field, String val) {
return StringUtils.capitalize(entity)
+ " with " + field + " "+ val + " does not exist";
}
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ import java.time.LocalDateTime;
@Data
class ApiError {
private Integer status;
private Integer status = 400;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timestamp;
private String message;
......@@ -21,10 +21,17 @@ class ApiError {
timestamp = LocalDateTime.now();
}
public ApiError(Integer status,String message) {
this();
this.status = status;
this.message = message;
public static ApiError error(String message){
ApiError apiError = new ApiError();
apiError.setMessage(message);
return apiError;
}
public static ApiError error(Integer status, String message){
ApiError apiError = new ApiError();
apiError.setStatus(status);
apiError.setMessage(message);
return apiError;
}
}
......
......@@ -11,6 +11,7 @@ import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Objects;
import static org.springframework.http.HttpStatus.*;
/**
......@@ -23,91 +24,73 @@ public class GlobalExceptionHandler {
/**
* 处理所有不可知的异常
* @param e
* @return
*/
@ExceptionHandler(Throwable.class)
public ResponseEntity handleException(Throwable e){
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
ApiError apiError = new ApiError(BAD_REQUEST.value(),e.getMessage());
return buildResponseEntity(apiError);
return buildResponseEntity(ApiError.error(e.getMessage()));
}
/**
* 处理 接口无权访问异常AccessDeniedException
* @param e
* @return
*/
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity handleAccessDeniedException(AccessDeniedException e){
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
ApiError apiError = new ApiError(FORBIDDEN.value(),e.getMessage());
return buildResponseEntity(apiError);
return buildResponseEntity(ApiError.error(FORBIDDEN.value(),e.getMessage()));
}
/**
* 处理自定义异常
* @param e
* @return
*/
@ExceptionHandler(value = BadRequestException.class)
public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
ApiError apiError = new ApiError(e.getStatus(),e.getMessage());
return buildResponseEntity(apiError);
return buildResponseEntity(ApiError.error(e.getStatus(),e.getMessage()));
}
/**
* 处理 EntityExist
* @param e
* @return
*/
@ExceptionHandler(value = EntityExistException.class)
public ResponseEntity<ApiError> entityExistException(EntityExistException e) {
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
ApiError apiError = new ApiError(BAD_REQUEST.value(),e.getMessage());
return buildResponseEntity(apiError);
return buildResponseEntity(ApiError.error(e.getMessage()));
}
/**
* 处理 EntityNotFound
* @param e
* @return
*/
@ExceptionHandler(value = EntityNotFoundException.class)
public ResponseEntity<ApiError> entityNotFoundException(EntityNotFoundException e) {
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
ApiError apiError = new ApiError(NOT_FOUND.value(),e.getMessage());
return buildResponseEntity(apiError);
return buildResponseEntity(ApiError.error(NOT_FOUND.value(),e.getMessage()));
}
/**
* 处理所有接口数据验证异常
* @param e
* @returns
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiError> handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
// 打印堆栈信息
log.error(ThrowableUtil.getStackTrace(e));
String[] str = e.getBindingResult().getAllErrors().get(0).getCodes()[1].split("\\.");
StringBuffer msg = new StringBuffer(str[1]+":");
msg.append(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
ApiError apiError = new ApiError(BAD_REQUEST.value(),msg.toString());
return buildResponseEntity(apiError);
String[] str = Objects.requireNonNull(e.getBindingResult().getAllErrors().get(0).getCodes())[1].split("\\.");
String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
if("不能为空".equals(message)){
message = str[1] + ":" + message;
}
return buildResponseEntity(ApiError.error(message));
}
/**
* 统一返回
* @param apiError
* @return
*/
private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
return new ResponseEntity(apiError, HttpStatus.valueOf(apiError.getStatus()));
return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
}
}
package me.zhengjie.redis;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.nio.charset.Charset;
/**
* Value 序列化
*
* @author /
* @param <T>
*/
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
public FastJsonRedisSerializer(Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return (T) JSON.parseObject(str, clazz);
}
}
package me.zhengjie.redis;
import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.nio.charset.Charset;
/**
* 重写序列化器
*
* @author /
*/
public class StringRedisSerializer implements RedisSerializer<Object> {
private final Charset charset;
private final String target = "\"";
private final String replacement = "";
public StringRedisSerializer() {
this(Charset.forName("UTF8"));
}
public StringRedisSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
}
@Override
public String deserialize(byte[] bytes) {
return (bytes == null ? null : new String(bytes, charset));
}
@Override
public byte[] serialize(Object object) {
String string = JSON.toJSONString(object);
if (string == null) {
return null;
}
string = string.replace(target, replacement);
return string.getBytes(charset);
}
}
\ No newline at end of file
package me.zhengjie.swagger2; import com.fasterxml.classmate.TypeResolver;import io.swagger.annotations.ApiModel;import io.swagger.annotations.ApiModelProperty;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.Ordered;import org.springframework.data.domain.Pageable;import springfox.documentation.schema.AlternateTypeRule;import springfox.documentation.schema.AlternateTypeRuleConvention;import java.util.List;import static com.google.common.collect.Lists.newArrayList;import static springfox.documentation.schema.AlternateTypeRules.newRule; /** * 将Pageable转换展示在swagger中 */@Configurationpublic class SwaggerDataConfig { @Bean public AlternateTypeRuleConvention pageableConvention(final TypeResolver resolver) { return new AlternateTypeRuleConvention() { @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } @Override public List<AlternateTypeRule> rules() { return newArrayList(newRule(resolver.resolve(Pageable.class), resolver.resolve(Page.class))); } }; } @ApiModel static class Page { @ApiModelProperty("页码 (0..N)") private Integer page; @ApiModelProperty("每页显示的数目") private Integer size; @ApiModelProperty("以下列格式排序标准:property[,asc | desc]。 默认排序顺序为升序。 支持多种排序条件:如:id,asc") private List<String> sort; public Integer getPage() { return page; } public void setPage(Integer page) { this.page = page; } public Integer getSize() { return size; } public void setSize(Integer size) { this.size = size; } public List<String> getSort() { return sort; } public void setSort(List<String> sort) { this.sort = sort; } }}
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment