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

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记逻辑删除字段。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记数据权限中基于DeptId进行过滤的字段。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -0,0 +1,17 @@
package com.orange.admin.common.core.annotation;
import java.lang.annotation.*;
/**
* 作为DisableDataPermAspect的切点。
* 该注解仅能标记在方法上,方法内所有的查询语句,均不会被数据权限过滤。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DisableDataPerm {
}

View File

@@ -11,7 +11,7 @@ import java.lang.annotation.*;
* 将其中一个方法重新定义一个具体的接口方法,并重新设定其是否参与数据过滤。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记更新字段。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记Service所依赖的数据源类型。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -9,7 +9,7 @@ import java.lang.annotation.Target;
* 标记Controller中的方法参数参数解析器会根据该注解将请求中的JSON数据映射到参数中的绑定字段。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记无需Token验证的接口
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 标识Model和常量字典之间的关联关系。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 标识Model之间的字典关联关系。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -0,0 +1,36 @@
package com.orange.admin.common.core.annotation;
import java.lang.annotation.*;
/**
* 标注多对多的Model关系。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RelationManyToMany {
/**
* 多对多中间表的Mapper对象名称。
*
* @return 被关联的本地Service对象名称。
*/
String relationMapperName();
/**
* 多对多关联表Model对象的Class对象。
*
* @return 被关联Model对象的Class对象。
*/
Class<?> relationModelClass();
/**
* 多对多关联表Model对象中与主表关联的Id字段名称。
*
* @return 被关联Model对象的关联Id字段名称。
*/
String relationMasterIdField();
}

View File

@@ -3,10 +3,10 @@ package com.orange.admin.common.core.annotation;
import java.lang.annotation.*;
/**
* 主要用于多对多的Model关系。标注通过从表关联字段或者关联表关联字段计算主表虚拟字段的规则。
* 主要用于多对多的Model关系。标注通过从表关联字段或者关联表关联字段计算主表聚合计算字段的规则。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -3,10 +3,10 @@ package com.orange.admin.common.core.annotation;
import java.lang.annotation.*;
/**
* 主要用于一对多的Model关系。标注通过从表关联字段计算主表虚拟字段的规则。
* 主要用于一对多的Model关系。标注通过从表关联字段计算主表聚合计算字段的规则。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 标识Model之间的一对一关联关系。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -6,7 +6,7 @@ import java.lang.annotation.*;
* 主要用于标记数据权限中基于UserId进行过滤的字段。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -14,7 +14,7 @@ import java.util.Map;
*
* @param <M> 主Model实体对象。
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@RegisterMapper
public interface BaseDaoMapper<M> extends Mapper<M>, InsertListMapper<M> {
@@ -39,7 +39,7 @@ public interface BaseDaoMapper<M> extends Mapper<M>, InsertListMapper<M> {
+ " GROUP BY ${groupBy}"
+ "</if>"
+ "</script>")
<T> List<Map<String, Object>> getGroupedListByCondition(
List<Map<String, Object>> getGroupedListByCondition(
@Param("selectTable") String selectTable,
@Param("selectFields") String selectFields,
@Param("whereClause") String whereClause,
@@ -65,7 +65,7 @@ public interface BaseDaoMapper<M> extends Mapper<M>, InsertListMapper<M> {
+ " ORDER BY ${orderBy}"
+ "</if>"
+ "</script>")
<T> List<Map<String, Object>> getListByCondition(
List<Map<String, Object>> getListByCondition(
@Param("selectTable") String selectTable,
@Param("selectFields") String selectFields,
@Param("whereClause") String whereClause,

View File

@@ -1,9 +1,10 @@
package com.orange.admin.common.core.base.mapper;
import cn.hutool.core.bean.BeanUtil;
import org.apache.commons.collections4.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
* Model对象到Domain类型对象的相互转换。实现类通常声明在Model实体类中。
@@ -11,7 +12,7 @@ import java.util.Map;
* @param <D> Domain域对象类型。
* @param <M> Model实体对象类型。
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public interface BaseModelMapper<D, M> {
@@ -50,35 +51,69 @@ public interface BaseModelMapper<D, M> {
/**
* 转换bean到map
*
* @param bean bean对象。
* @param bean bean对象。
* @param ignoreNullValue 值为null的字段是否转换到Map。
* @param <T> bean类型。
* @param <T> bean类型。
* @return 转换后的map对象。
*/
default <T> Map<String, Object> beanToMap(T bean, boolean ignoreNullValue) {
return BeanUtil.beanToMap(bean);
return BeanUtil.beanToMap(bean, false, ignoreNullValue);
}
/**
* 转换bean集合到map集合
*
* @param dataList bean对象集合。
* @param ignoreNullValue 值为null的字段是否转换到Map。
* @param <T> bean类型。
* @return 转换后的map对象集合。
*/
default <T> List<Map<String, Object>> beanToMap(List<T> dataList, boolean ignoreNullValue) {
if (CollectionUtils.isEmpty(dataList)) {
return new LinkedList<>();
}
return dataList.stream()
.map(o -> BeanUtil.beanToMap(o, false, ignoreNullValue))
.collect(Collectors.toList());
}
/**
* 转换map到bean。
*
* @param map map对象。
* @param map map对象。
* @param beanClazz bean的Class对象。
* @param <T> bean类型。
* @param <T> bean类型。
* @return 转换后的bean对象。
*/
default <T> T mapToBean(Map<String, Object> map, Class<T> beanClazz) {
return BeanUtil.mapToBean(map, beanClazz, true);
}
/**
* 转换map集合到bean集合。
*
* @param mapList map对象集合。
* @param beanClazz bean的Class对象。
* @param <T> bean类型。
* @return 转换后的bean对象集合。
*/
default <T> List<T> mapToBean(List<Map<String, Object>> mapList, Class<T> beanClazz) {
if (CollectionUtils.isEmpty(mapList)) {
return new LinkedList<>();
}
return mapList.stream()
.map(m -> BeanUtil.mapToBean(m, beanClazz, true))
.collect(Collectors.toList());
}
/**
* 对于Map字段到Map字段的映射场景MapStruct会根据方法签名自动选择该函数
* 作为对象copy的函数。由于该函数是直接返回的因此没有对象copy效率更高。
* 如果没有该函数MapStruct会生成如下代码
* Map<String, Object> map = courseDto.getTeacherIdDictMap();
* if ( map != null ) {
* course.setTeacherIdDictMap( new HashMap<String, Object>( map ) );
* }
* Map<String, Object> map = courseDto.getTeacherIdDictMap();
* if ( map != null ) {
* course.setTeacherIdDictMap( new HashMap<String, Object>( map ) );
* }
*
* @param map map对象。
* @return 直接返回的map。

View File

@@ -8,7 +8,7 @@ import java.util.List;
*
* @param <M> 数据类型。
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class DummyModelMapper<M> implements BaseModelMapper<M, M> {

View File

@@ -16,7 +16,7 @@ import java.util.List;
* @param <M> Model实体对象的类型。
* @param <K> Model对象主键的类型。
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
public abstract class BaseDictService<M, K> extends BaseService<M, K> {
@@ -47,16 +47,23 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> {
*/
public void loadCachedData() {
if (loadOnStartup()) {
reloadCachedData();
reloadCachedData(false);
}
}
/**
* 重新加载数据库中所有当前表数据到系统内存。
*
* @param force true则强制刷新如果false当缓存中存在数据时不刷新。
*/
public void reloadCachedData() {
public void reloadCachedData(boolean force) {
// 在非强制刷新情况下。
// 先行判断缓存中是否存在数据,如果有就不加载了。
if (!force && dictionaryCache.getCount() > 0) {
return;
}
List<M> allList = super.getAllList();
dictionaryCache.reload(allList);
dictionaryCache.reload(allList, force);
}
/**
@@ -94,7 +101,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> {
/**
* 返回符合 inFilterField in (inFilterValues) 条件的所有数据。蜀国property是主键则从缓存中读取。
*
* @param inFilterField 参与(In-list)过滤的Java字段。
* @param inFilterField 参与(In-list)过滤的Java字段。
* @param inFilterValues 参与(In-list)过滤的Java字段值集合。
* @return 检索后的数据列表。
*/
@@ -110,7 +117,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> {
/**
* 判断参数值列表中的所有数据是否全部存在。另外keyName字段在数据表中必须是唯一键值否则返回结果会出现误判。
*
* @param inFilterField 待校验的数据字段这里使用Java对象中的属性如courseId而不是数据字段名course_id。
* @param inFilterField 待校验的数据字段这里使用Java对象中的属性如courseId而不是数据字段名course_id。
* @param inFilterValues 数据值集合。
* @return 全部存在返回true否则false。
*/

View File

@@ -9,7 +9,7 @@ import java.util.Set;
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public interface DictionaryCache<K, V> {
@@ -39,8 +39,9 @@ public interface DictionaryCache<K, V> {
* 重新加载先清空原有数据在执行putAll的操作。
*
* @param dataList 待缓存的数据列表。
* @param force true则强制刷新如果false当缓存中存在数据时不刷新。
*/
void reload(List<V> dataList);
void reload(List<V> dataList, boolean force);
/**
* 从缓存中获取指定的数据。
@@ -53,7 +54,7 @@ public interface DictionaryCache<K, V> {
/**
* 将数据存入缓存。
*
* @param key 通常为字典数据的主键。
* @param key 通常为字典数据的主键。
* @param object 字典数据对象。
*/
void put(K key, V object);
@@ -81,7 +82,7 @@ public interface DictionaryCache<K, V> {
void invalidateSet(Set<K> keys);
/**
* 清空缓存
* 清空缓存
*/
void invalidateAll();
}

View File

@@ -9,7 +9,7 @@ import java.util.function.Function;
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class MapDictionaryCache<K, V> implements DictionaryCache<K, V> {
@@ -26,8 +26,8 @@ public class MapDictionaryCache<K, V> implements DictionaryCache<K, V> {
* 当前对象的构造器函数。
*
* @param idGetter 获取当前类主键字段值的函数对象。
* @param <K> 字典主键类型。
* @param <V> 字典对象类型
* @param <K> 字典主键类型。
* @param <V> 字典对象类型
* @return 实例化后的字典内存缓存对象。
*/
public static <K, V> MapDictionaryCache<K, V> create(Function<V, K> idGetter) {
@@ -37,10 +37,20 @@ public class MapDictionaryCache<K, V> implements DictionaryCache<K, V> {
return new MapDictionaryCache<>(idGetter);
}
/**
* 构造函数。
*
* @param idGetter 主键Id的获取函数对象。
*/
public MapDictionaryCache(Function<V, K> idGetter) {
this.idGetter = idGetter;
}
/**
* 按照数据插入的顺序返回全部字典对象的列表。
*
* @return 全部字段数据列表。
*/
@Override
public synchronized List<V> getAll() {
List<V> resultList = new LinkedList<>();
@@ -50,6 +60,12 @@ public class MapDictionaryCache<K, V> implements DictionaryCache<K, V> {
return resultList;
}
/**
* 获取缓存中与键列表对应的对象列表。
*
* @param keys 主键集合。
* @return 对象列表。
*/
@Override
public synchronized List<V> getInList(Set<K> keys) {
List<V> resultList = new LinkedList<>();
@@ -62,6 +78,11 @@ public class MapDictionaryCache<K, V> implements DictionaryCache<K, V> {
return resultList;
}
/**
* 将参数List中的数据保存到缓存中同时保证getAll返回的数据列表与参数列表中数据项的顺序保持一致。
*
* @param dataList 待缓存的数据列表。
*/
@Override
public synchronized void putAll(List<V> dataList) {
if (dataList == null) {
@@ -73,32 +94,69 @@ public class MapDictionaryCache<K, V> implements DictionaryCache<K, V> {
});
}
/**
* 重新加载先清空原有数据在执行putAll的操作。
*
* @param dataList 待缓存的数据列表。
* @param force true则强制刷新如果false当缓存中存在数据时不刷新。
*/
@Override
public synchronized void reload(List<V> dataList) {
public synchronized void reload(List<V> dataList, boolean force) {
if (!force && this.getCount() > 0) {
return;
}
this.invalidateAll();
this.putAll(dataList);
}
/**
* 从缓存中获取指定的数据。
*
* @param id 数据的key。
* @return 获取到的数据如果没有返回null。
*/
@Override
public synchronized V get(K id) {
return id == null ? null : dataMap.get(id);
}
/**
* 将数据存入缓存。
*
* @param id 通常为字典数据的主键。
* @param object 字典数据对象。
*/
@Override
public synchronized void put(K id, V object) {
dataMap.put(id, object);
}
/**
* 获取缓存中数据条目的数量。
*
* @return 返回缓存的数据数量。
*/
@Override
public synchronized int getCount() {
return dataMap.size();
}
/**
* 删除缓存中指定的键。
*
* @param id 待删除数据的主键。
* @return 返回被删除的对象如果主键不存在返回null。
*/
@Override
public synchronized V invalidate(K id) {
return id == null ? null : dataMap.remove(id);
}
/**
* 删除缓存中,参数列表中包含的键。
*
* @param keys 待删除数据的主键集合。
*/
@Override
public synchronized void invalidateSet(Set<K> keys) {
keys.forEach(id -> {
@@ -108,6 +166,9 @@ public class MapDictionaryCache<K, V> implements DictionaryCache<K, V> {
});
}
/**
* 清空缓存。
*/
@Override
public synchronized void invalidateAll() {
dataMap.clear();

View File

@@ -12,10 +12,13 @@ import java.util.function.Function;
* @param <K> 字典表主键类型。
* @param <V> 字典表对象类型。
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {
/**
* 树形数据存储对象。
*/
private final Multimap<K, V> allTreeMap = LinkedHashMultimap.create();
/**
* 获取字典父主键数据的函数对象。
@@ -25,10 +28,10 @@ public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {
/**
* 当前对象的构造器函数。
*
* @param idGetter 获取当前类主键字段值的函数对象。
* @param idGetter 获取当前类主键字段值的函数对象。
* @param parentIdGetter 获取当前类父主键字段值的函数对象。
* @param <K> 字典主键类型。
* @param <V> 字典对象类型
* @param <K> 字典主键类型。
* @param <V> 字典对象类型
* @return 实例化后的树形字典内存缓存对象。
*/
public static <K, V> MapTreeDictionaryCache<K, V> create(Function<V, K> idGetter, Function<V, K> parentIdGetter) {
@@ -41,6 +44,12 @@ public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {
return new MapTreeDictionaryCache<>(idGetter, parentIdGetter);
}
/**
* 构造函数。
*
* @param idGetter 获取当前类主键字段值的函数对象。
* @param parentIdGetter 获取当前类父主键字段值的函数对象。
*/
public MapTreeDictionaryCache(Function<V, K> idGetter, Function<V, K> parentIdGetter) {
super(idGetter);
this.parentIdGetter = parentIdGetter;
@@ -56,6 +65,11 @@ public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {
return new LinkedList<>(allTreeMap.get(parentId));
}
/**
* 将参数List中的数据保存到缓存中同时保证getAll返回的数据列表与参数列表中数据项的顺序保持一致。
*
* @param dataList 待缓存的数据列表。
*/
@Override
public synchronized void putAll(List<V> dataList) {
if (dataList == null) {
@@ -69,6 +83,12 @@ public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {
});
}
/**
* 将数据存入缓存。
*
* @param id 通常为字典数据的主键。
* @param data 字典数据对象。
*/
@Override
public synchronized void put(K id, V data) {
super.put(id, data);
@@ -77,6 +97,12 @@ public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {
allTreeMap.put(parentId, data);
}
/**
* 删除缓存中指定的键。
*
* @param id 待删除数据的主键。
* @return 返回被删除的对象如果主键不存在返回null。
*/
@Override
public synchronized V invalidate(K id) {
V v = super.invalidate(id);
@@ -87,6 +113,11 @@ public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {
return v;
}
/**
* 删除缓存中,参数列表中包含的键。
*
* @param keys 待删除数据的主键集合。
*/
@Override
public synchronized void invalidateSet(Set<K> keys) {
keys.forEach(id -> {
@@ -100,6 +131,9 @@ public class MapTreeDictionaryCache<K, V> extends MapDictionaryCache<K, V> {
});
}
/**
* 清空缓存。
*/
@Override
public synchronized void invalidateAll() {
super.invalidateAll();

View File

@@ -7,32 +7,32 @@ import java.util.Map;
* 聚合计算的常量类型对象。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public final class AggregationType {
/**
* sum 计数
*/
public final static int SUM = 0;
public static final int SUM = 0;
/**
* count 汇总
*/
public final static int COUNT = 1;
public static final int COUNT = 1;
/**
* average 平均值
*/
public final static int AVG = 2;
public static final int AVG = 2;
/**
* min 最小值
*/
public final static int MIN = 3;
public static final int MIN = 3;
/**
* max 最大值
*/
public final static int MAX = 4;
public static final int MAX = 4;
public static final Map<Object, String> DICT_MAP = new HashMap<>(5);
private static final Map<Object, String> DICT_MAP = new HashMap<>(5);
static {
DICT_MAP.put(0, "累计总和");
DICT_MAP.put(1, "数量总和");
@@ -48,7 +48,7 @@ public final class AggregationType {
* @return 合法返回true否则false。
*/
public static boolean isValid(Integer value) {
return DICT_MAP.containsKey(value);
return value != null && DICT_MAP.containsKey(value);
}
/**
@@ -72,4 +72,10 @@ public final class AggregationType {
throw new IllegalArgumentException("无效的聚合类型!");
}
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private AggregationType() {
}
}

View File

@@ -4,16 +4,41 @@ package com.orange.admin.common.core.constant;
* 应用程序的常量声明对象。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public interface ApplicationConstant {
public final class ApplicationConstant {
/**
* 图片文件上传的父目录。
*/
String UPLOAD_IMAGE_PARENT_PATH = "/image";
public static final String UPLOAD_IMAGE_PARENT_PATH = "image";
/**
* 附件文件上传的父目录。
*/
String UPLOAD_ATTACHMENT_PARENT_PATH = "/attachment";
public static final String UPLOAD_ATTACHMENT_PARENT_PATH = "attachment";
/**
* CSV文件扩展名。
*/
public static final String CSV_EXT = "csv";
/**
* XLSX文件扩展名。
*/
public static final String XLSX_EXT = "xlsx";
/**
* 统计分类计算时,按天聚合计算的常量值。(前端在MyOrderParam和MyGroupParam中传给后台)
*/
public static final String DAY_AGGREGATION = "day";
/**
* 统计分类计算时,按月聚合计算的常量值。(前端在MyOrderParam和MyGroupParam中传给后台)
*/
public static final String MONTH_AGGREGATION = "month";
/**
* 统计分类计算时,按年聚合计算的常量值。(前端在MyOrderParam和MyGroupParam中传给后台)
*/
public static final String YEAR_AGGREGATION = "year";
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private ApplicationConstant() {
}
}

View File

@@ -7,7 +7,7 @@ import java.util.Map;
* 数据权限规则类型常量类。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public final class DataPermRuleType {
@@ -31,7 +31,7 @@ public final class DataPermRuleType {
*/
public static final int TYPE_CUSTOM_DETP_LIST = 5;
public static final Map<Object, String> DICT_MAP = new HashMap<>(4);
private static final Map<Object, String> DICT_MAP = new HashMap<>(4);
static {
DICT_MAP.put(0, "查看全部");
DICT_MAP.put(1, "仅查看当前用户");
@@ -45,8 +45,8 @@ public final class DataPermRuleType {
* @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

@@ -4,11 +4,17 @@ package com.orange.admin.common.core.constant;
* 返回应答中的错误代码和错误信息。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public enum ErrorCodeEnum {
/**
* 没有错误
*/
NO_ERROR("没有错误"),
/**
* 未处理的异常!
*/
UNHANDLED_EXCEPTION("未处理的异常!"),
ARGUMENT_NULL_EXIST("数据验证失败,接口调用参数存在空值,请核对!"),
@@ -37,24 +43,35 @@ public enum ErrorCodeEnum {
DATA_PARENT_LEVEL_ID_NOT_EXIST("数据验证失败父级别关联Id不存在请刷新后重试"),
DATA_PARENT_ID_NOT_EXIST("数据验证失败ParentId不存在请核对"),
INVALID_RELATED_RECORD_ID("数据验证失败,关联数据并不存在,请刷新后重试!"),
INVALID_DATA_FIELD("数据验证失败,无效的数据字段"),
INVALID_DATA_MODEL("数据验证失败,无效的数据实体对象"),
INVALID_DATA_FIELD("数据验证失败,无效的数据实体对象字段!"),
INVALID_CLASS_FIELD("数据验证失败,无效的类对象字段!"),
SERVER_INTERNAL_ERROR("服务器内部错误,请联系管理员!"),
REDIS_CACHE_ACCESS_TIMEOUT("Redis缓存数据访问超时请刷新后重试"),
REDIS_CACHE_ACCESS_STATE_ERROR("Redis缓存数据访问状态错误请刷新后重试");
// 下面的枚举值为特定枚举值,即开发者可以根据自己的项目需求定义更多的非通用枚举值
/**
* 构造函数。
*
* @param errorMessage 错误消息。
*/
ErrorCodeEnum(String errorMessage) {
this.errorMessage = errorMessage;
}
/**
* 错误信息。
*/
private String errorMessage;
/**
* 获取错误信息。
*
* @return 错误信息。
*/
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
}

View File

@@ -4,16 +4,22 @@ package com.orange.admin.common.core.constant;
* 数据记录逻辑删除标记常量。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public interface GlobalDeletedFlag {
public final class GlobalDeletedFlag {
/**
* 表示数据表记录已经删除
*/
int DELETED = -1;
public static final int DELETED = -1;
/**
* 数据记录正常
*/
int NORMAL = 1;
public static final int NORMAL = 1;
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private GlobalDeletedFlag() {
}
}

View File

@@ -4,14 +4,22 @@ package com.orange.admin.common.core.exception;
* 数据验证失败的自定义异常。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class DataValidationException extends RuntimeException {
/**
* 构造函数。
*/
public DataValidationException() {
}
/**
* 构造函数。
*
* @param msg 错误信息。
*/
public DataValidationException(String msg) {
super(msg);
}

View File

@@ -0,0 +1,30 @@
package com.orange.admin.common.core.exception;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 无效的类对象字段的自定义异常。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class InvalidClassFieldException extends RuntimeException {
private final String className;
private final String fieldName;
/**
* 构造函数。
*
* @param className 对象名。
* @param fieldName 字段名。
*/
public InvalidClassFieldException(String className, String fieldName) {
super("Invalid FieldName [" + fieldName + "] in Class [" + className + "].");
this.className = className;
this.fieldName = fieldName;
}
}

View File

@@ -7,15 +7,21 @@ import lombok.EqualsAndHashCode;
* 无效的实体对象字段的自定义异常。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class InvalidDataFieldException extends RuntimeException {
private String modelName;
private String fieldName;
private final String modelName;
private final String fieldName;
/**
* 构造函数。
*
* @param modelName 实体对象名。
* @param fieldName 字段名。
*/
public InvalidDataFieldException(String modelName, String fieldName) {
super("Invalid FieldName [" + fieldName + "] in Model Class [" + modelName + "].");
this.modelName = modelName;

View File

@@ -7,14 +7,19 @@ import lombok.EqualsAndHashCode;
* 无效的实体对象的自定义异常。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class InvalidDataModelException extends RuntimeException {
private String modelName;
private final String modelName;
/**
* 构造函数。
*
* @param modelName 实体对象名。
*/
public InvalidDataModelException(String modelName) {
super("Invalid Model Class [" + modelName + "].");
this.modelName = modelName;

View File

@@ -0,0 +1,36 @@
package com.orange.admin.common.core.exception;
/**
* 自定义的运行时异常,在需要抛出运行时异常时,可使用该异常。
* NOTE主要是为了避免SonarQube进行代码质量扫描时给出警告。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
public class MyRuntimeException extends RuntimeException {
/**
* 构造函数。
*/
public MyRuntimeException() {
}
/**
* 构造函数。
*
* @param throwable 引发异常对象。
*/
public MyRuntimeException(Throwable throwable) {
super(throwable);
}
/**
* 构造函数。
*
* @param msg 错误信息。
*/
public MyRuntimeException(String msg) {
super(msg);
}
}

View File

@@ -4,14 +4,22 @@ package com.orange.admin.common.core.exception;
* 没有数据被修改的自定义异常。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class NoDataAffectException extends RuntimeException {
/**
* 构造函数。
*/
public NoDataAffectException() {
}
/**
* 构造函数。
*
* @param msg 错误信息。
*/
public NoDataAffectException(String msg) {
super(msg);
}

View File

@@ -4,14 +4,22 @@ package com.orange.admin.common.core.exception;
* 没有数据访问权限的自定义异常。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class NoDataPermException extends RuntimeException {
/**
* 构造函数。
*/
public NoDataPermException() {
}
/**
* 构造函数。
*
* @param msg 错误信息。
*/
public NoDataPermException(String msg) {
super(msg);
}

View File

@@ -4,10 +4,16 @@ package com.orange.admin.common.core.exception;
* Redis缓存访问失败。比如获取分布式数据锁超时、等待线程中断等。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class RedisCacheAccessException extends RuntimeException {
/**
* 构造函数。
*
* @param msg 错误信息。
* @param cause 原始异常。
*/
public RedisCacheAccessException(String msg, Throwable cause) {
super(msg, cause);
}

View File

@@ -8,11 +8,15 @@ import lombok.Data;
* 同时为了提升效率减少查询次数可以根据具体的需求将部分验证关联对象存入data字段以供Controller使用
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Data
public class VerifyResult {
public class CallResult {
/**
* 为了优化性能所有没有携带数据的正确结果均可用该对象表示
*/
private static final CallResult OK = new CallResult();
/**
* 是否成功标记
*/
@@ -32,7 +36,7 @@ public class VerifyResult {
* @param errorMessage 错误描述信息
* @return 如果参数为空表示成功否则返回代码错误信息的错误对象实例
*/
public static VerifyResult create(String errorMessage) {
public static CallResult create(String errorMessage) {
return errorMessage == null ? ok() : error(errorMessage);
}
@@ -40,10 +44,10 @@ public class VerifyResult {
* 创建验证结果对象
*
* @param errorMessage 错误描述信息
* @param data 附带的数据对象
* @param data 附带的数据对象
* @return 如果参数为空表示成功否则返回代码错误信息的错误对象实例
*/
public static VerifyResult create(String errorMessage, JSONObject data) {
public static CallResult create(String errorMessage, JSONObject data) {
return errorMessage == null ? ok(data) : error(errorMessage);
}
@@ -52,8 +56,8 @@ public class VerifyResult {
*
* @return 验证成功对象实例
*/
public static VerifyResult ok() {
return new VerifyResult();
public static CallResult ok() {
return OK;
}
/**
@@ -62,8 +66,8 @@ public class VerifyResult {
* @param data 附带的数据对象
* @return 验证成功对象实例
*/
public static VerifyResult ok(JSONObject data) {
VerifyResult result = new VerifyResult();
public static CallResult ok(JSONObject data) {
CallResult result = new CallResult();
result.data = data;
return result;
}
@@ -74,11 +78,10 @@ public class VerifyResult {
* @param errorMessage 错误描述
* @return 验证失败对象实例
*/
public static VerifyResult error(String errorMessage) {
VerifyResult verifyResult = new VerifyResult();
verifyResult.success = false;
verifyResult.errorMessage = errorMessage;
return verifyResult;
public static CallResult error(String errorMessage) {
CallResult result = new CallResult();
result.success = false;
result.errorMessage = errorMessage;
return result;
}
}

View File

@@ -6,7 +6,7 @@ import cn.hutool.core.util.BooleanUtil;
* 线程本地化数据管理的工具类。可根据需求自行添加更多的线程本地化变量及其操作方法。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class GlobalThreadLocal {
@@ -42,4 +42,10 @@ public class GlobalThreadLocal {
public static void clearDataPerm() {
DATA_PERM_ENABLE.remove();
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private GlobalThreadLocal() {
}
}

View File

@@ -7,7 +7,7 @@ import lombok.Data;
* Mybatis Mapper.xml中所需的分组条件对象。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Data
@AllArgsConstructor

View File

@@ -1,6 +1,10 @@
package com.orange.admin.common.core.object;
import cn.hutool.core.util.ReflectUtil;
import com.orange.admin.common.core.constant.ApplicationConstant;
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.util.MyModelUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -16,7 +20,7 @@ import java.util.List;
* 查询分组参数请求对象。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@EqualsAndHashCode(callSuper = true)
@Slf4j
@@ -51,62 +55,15 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
StringBuilder groupSelectBuilder = new StringBuilder(128);
int i = 0;
for (GroupInfo groupInfo : groupParam) {
String modelName, fieldName, tableName, columnName;
String[] stringArray = StringUtils.split(groupInfo.fieldName,'.');
if (stringArray.length == 1) {
modelName = modelClazz.getSimpleName();
fieldName = groupInfo.fieldName;
tableName = MyModelUtil.mapToTableName(modelClazz);
columnName = MyModelUtil.mapToColumnName(fieldName, modelClazz);
} else {
Field field = ReflectUtil.getField(modelClazz, stringArray[0]);
if (field == null) {
log.error("Relation Field [{}] doesn't exist in Class [{}]!",
stringArray[0], modelClazz.getSimpleName());
return null;
}
Class<?> fieldClazz = field.getType();
modelName = fieldClazz.getSimpleName();
fieldName = stringArray[1];
tableName = MyModelUtil.mapToTableName(fieldClazz);
columnName = MyModelUtil.mapToColumnName(fieldName, fieldClazz);
GroupBaseData groupBaseData = parseGroupBaseData(groupInfo, modelClazz);
if (StringUtils.isBlank(groupBaseData.tableName)) {
throw new InvalidDataModelException(groupBaseData.modelName);
}
if (StringUtils.isBlank(tableName)) {
log.error("ModelName [{}] can't be mapped to Table!", modelName);
return null;
if (StringUtils.isBlank(groupBaseData.columnName)) {
throw new InvalidDataFieldException(groupBaseData.modelName, groupBaseData.fieldName);
}
if (StringUtils.isBlank(columnName)) {
log.error("FieldName [{}] in Class [{}`] can't be mapped to Column!", fieldName, modelName);
return null;
}
if (StringUtils.isNotBlank(groupInfo.dateAggregateBy)) {
groupByBuilder.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
groupSelectBuilder.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
if ("day".equals(groupInfo.dateAggregateBy)) {
groupByBuilder.append(", '%Y-%m-%d')");
groupSelectBuilder.append(", '%Y-%m-%d')");
} else if ("month".equals(groupInfo.dateAggregateBy)) {
groupByBuilder.append(", '%Y-%m-01')");
groupSelectBuilder.append(", '%Y-%m-01')");
} else if ("year".equals(groupInfo.dateAggregateBy)) {
groupByBuilder.append(", '%Y-01-01')");
groupSelectBuilder.append(", '%Y-01-01')");
} else {
throw new IllegalArgumentException("Illegal DATE_FORMAT for GROUP ID list.");
}
if (StringUtils.isNotBlank(groupInfo.aliasName)) {
groupSelectBuilder.append(" ").append(groupInfo.aliasName);
} else {
groupSelectBuilder.append(" ").append(columnName);
}
} else {
groupByBuilder.append(tableName).append(".").append(columnName);
groupSelectBuilder.append(tableName).append(".").append(columnName);
if (StringUtils.isNotBlank(groupInfo.aliasName)) {
groupSelectBuilder.append(" ").append(groupInfo.aliasName);
}
}
String aliasName = StringUtils.isBlank(groupInfo.aliasName) ? fieldName : groupInfo.aliasName;
processGroupInfo(groupInfo, groupBaseData, groupByBuilder, groupSelectBuilder);
String aliasName = StringUtils.isBlank(groupInfo.aliasName) ? groupInfo.fieldName : groupInfo.aliasName;
// selectGroupFieldList中的元素目前只是被export操作使用。会根据集合中的元素名称匹配导出表头。
groupParam.selectGroupFieldList.add(aliasName);
if (++i < groupParam.size()) {
@@ -118,6 +75,67 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
return groupParam;
}
private static GroupBaseData parseGroupBaseData(GroupInfo groupInfo, Class<?> modelClazz) {
GroupBaseData baseData = new GroupBaseData();
String[] stringArray = StringUtils.split(groupInfo.fieldName,'.');
if (stringArray.length == 1) {
baseData.modelName = modelClazz.getSimpleName();
baseData.fieldName = groupInfo.fieldName;
baseData.tableName = MyModelUtil.mapToTableName(modelClazz);
baseData.columnName = MyModelUtil.mapToColumnName(groupInfo.fieldName, modelClazz);
} else {
Field field = ReflectUtil.getField(modelClazz, stringArray[0]);
if (field == null) {
throw new InvalidClassFieldException(modelClazz.getSimpleName(), stringArray[0]);
}
Class<?> fieldClazz = field.getType();
baseData.modelName = fieldClazz.getSimpleName();
baseData.fieldName = stringArray[1];
baseData.tableName = MyModelUtil.mapToTableName(fieldClazz);
baseData.columnName = MyModelUtil.mapToColumnName(baseData.fieldName, fieldClazz);
}
return baseData;
}
private static void processGroupInfo(
GroupInfo groupInfo,
GroupBaseData baseData,
StringBuilder groupByBuilder,
StringBuilder groupSelectBuilder) {
String tableName = baseData.tableName;
String columnName = baseData.columnName;
if (StringUtils.isNotBlank(groupInfo.dateAggregateBy)) {
groupByBuilder.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
groupSelectBuilder.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
if (ApplicationConstant.DAY_AGGREGATION.equals(groupInfo.dateAggregateBy)) {
groupByBuilder.append(", '%Y-%m-%d')");
groupSelectBuilder.append(", '%Y-%m-%d')");
} else if (ApplicationConstant.MONTH_AGGREGATION.equals(groupInfo.dateAggregateBy)) {
groupByBuilder.append(", '%Y-%m-01')");
groupSelectBuilder.append(", '%Y-%m-01')");
} else if (ApplicationConstant.YEAR_AGGREGATION.equals(groupInfo.dateAggregateBy)) {
groupByBuilder.append(", '%Y-01-01')");
groupSelectBuilder.append(", '%Y-01-01')");
} else {
throw new IllegalArgumentException("Illegal DATE_FORMAT for GROUP ID list.");
}
if (StringUtils.isNotBlank(groupInfo.aliasName)) {
groupSelectBuilder.append(" ").append(groupInfo.aliasName);
} else {
groupSelectBuilder.append(" ").append(columnName);
}
} else {
groupByBuilder.append(tableName).append(".").append(columnName);
groupSelectBuilder.append(tableName).append(".").append(columnName);
if (StringUtils.isNotBlank(groupInfo.aliasName)) {
groupSelectBuilder.append(" ").append(groupInfo.aliasName);
}
}
}
/**
* 分组信息对象。
*/
@Data
public static class GroupInfo {
/**
@@ -140,4 +158,11 @@ public class MyGroupParam extends ArrayList<MyGroupParam.GroupInfo> {
*/
private String dateAggregateBy;
}
private static class GroupBaseData {
private String modelName;
private String fieldName;
private String tableName;
private String columnName;
}
}

View File

@@ -1,6 +1,10 @@
package com.orange.admin.common.core.object;
import cn.hutool.core.util.ReflectUtil;
import com.orange.admin.common.core.constant.ApplicationConstant;
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.util.MyModelUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -14,13 +18,14 @@ import java.util.*;
* Controller参数中的排序请求对象。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@EqualsAndHashCode(callSuper = true)
@Slf4j
@Data
public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
private static final String DICT_MAP = "DictMap.";
/**
* 基于排序对象中的JSON数据构建SQL中order by从句可以直接使用的排序字符串。
*
@@ -32,65 +37,21 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
if (orderParam == null) {
return null;
}
String exceptionMessage;
if (modelClazz == null) {
throw new IllegalArgumentException("modelClazz Argument can't be NULL");
throw new IllegalArgumentException(
"modelClazz Argument in MyOrderParam.buildOrderBy can't be NULL");
}
int i = 0;
StringBuilder orderBy = new StringBuilder(128);
for (OrderInfo orderInfo : orderParam) {
String modelName, fieldName = orderInfo.fieldName, tableName, columnName;
int pos = fieldName.indexOf("DictMap.");
if (pos != -1) {
fieldName = fieldName.substring(0, pos);
OrderBaseData orderBaseData = parseOrderBaseData(orderInfo, modelClazz);
if (StringUtils.isBlank(orderBaseData.tableName)) {
throw new InvalidDataModelException(orderBaseData.modelName);
}
String[] stringArray = StringUtils.split(fieldName, '.');
if (stringArray.length == 1) {
modelName = modelClazz.getSimpleName();
tableName = MyModelUtil.mapToTableName(modelClazz);
columnName = MyModelUtil.mapToColumnName(fieldName, modelClazz);
} else {
Field field = ReflectUtil.getField(modelClazz, stringArray[0]);
if (field == null) {
exceptionMessage = String.format(
"Relation Field [%s] doesn't exist in Class [%s]!", stringArray[0], modelClazz.getSimpleName());
log.error(exceptionMessage);
throw new IllegalArgumentException(exceptionMessage);
}
Class<?> fieldClazz = field.getType();
modelName = fieldClazz.getSimpleName();
fieldName = stringArray[1];
tableName = MyModelUtil.mapToTableName(fieldClazz);
columnName = MyModelUtil.mapToColumnName(fieldName, fieldClazz);
}
if (StringUtils.isBlank(tableName)) {
exceptionMessage = String.format("ModelName [%s] can't be mapped to Table!", modelName);
log.error(exceptionMessage);
throw new IllegalArgumentException(exceptionMessage);
}
if (StringUtils.isBlank(columnName)) {
exceptionMessage = String.format(
"FieldName [%s] in Class [%s] can't be mapped to Column!", fieldName, modelName);
log.error(exceptionMessage);
throw new IllegalArgumentException(exceptionMessage);
}
if (StringUtils.isNotBlank(orderInfo.dateAggregateBy)) {
orderBy.append("DATE_FORMAT(").append(tableName).append(".").append(columnName);
if ("day".equals(orderInfo.dateAggregateBy)) {
orderBy.append(", '%Y-%m-%d')");
} else if ("month".equals(orderInfo.dateAggregateBy)) {
orderBy.append(", '%Y-%m-01')");
} else if ("year".equals(orderInfo.dateAggregateBy)) {
orderBy.append(", '%Y-01-01')");
} else {
throw new IllegalArgumentException("Illegal DATE_FORMAT for GROUP ID list.");
}
} else {
orderBy.append(tableName).append(".").append(columnName);
}
if (orderInfo.asc != null && !orderInfo.asc) {
orderBy.append(" DESC");
if (StringUtils.isBlank(orderBaseData.columnName)) {
throw new InvalidDataFieldException(orderBaseData.modelName, orderBaseData.fieldName);
}
processOrderInfo(orderInfo, orderBaseData, orderBy);
if (++i < orderParam.size()) {
orderBy.append(", ");
}
@@ -98,12 +59,56 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
return orderBy.toString();
}
private static void processOrderInfo(
OrderInfo orderInfo, OrderBaseData orderBaseData, StringBuilder orderByBuilder) {
if (StringUtils.isNotBlank(orderInfo.dateAggregateBy)) {
orderByBuilder.append("DATE_FORMAT(")
.append(orderBaseData.tableName).append(".").append(orderBaseData.columnName);
if (ApplicationConstant.DAY_AGGREGATION.equals(orderInfo.dateAggregateBy)) {
orderByBuilder.append(", '%Y-%m-%d')");
} else if (ApplicationConstant.MONTH_AGGREGATION.equals(orderInfo.dateAggregateBy)) {
orderByBuilder.append(", '%Y-%m-01')");
} else if (ApplicationConstant.YEAR_AGGREGATION.equals(orderInfo.dateAggregateBy)) {
orderByBuilder.append(", '%Y-01-01')");
} else {
throw new IllegalArgumentException("Illegal DATE_FORMAT for GROUP ID list.");
}
} else {
orderByBuilder.append(orderBaseData.tableName).append(".").append(orderBaseData.columnName);
}
if (orderInfo.asc != null && !orderInfo.asc) {
orderByBuilder.append(" DESC");
}
}
private static OrderBaseData parseOrderBaseData(OrderInfo orderInfo, Class<?> modelClazz) {
OrderBaseData orderBaseData = new OrderBaseData();
orderBaseData.fieldName = StringUtils.substringBefore(orderInfo.fieldName, DICT_MAP);
String[] stringArray = StringUtils.split(orderBaseData.fieldName, '.');
if (stringArray.length == 1) {
orderBaseData.modelName = modelClazz.getSimpleName();
orderBaseData.tableName = MyModelUtil.mapToTableName(modelClazz);
orderBaseData.columnName = MyModelUtil.mapToColumnName(orderBaseData.fieldName, modelClazz);
} else {
Field field = ReflectUtil.getField(modelClazz, stringArray[0]);
if (field == null) {
throw new InvalidClassFieldException(modelClazz.getSimpleName(), stringArray[0]);
}
Class<?> fieldClazz = field.getType();
orderBaseData.modelName = fieldClazz.getSimpleName();
orderBaseData.fieldName = stringArray[1];
orderBaseData.tableName = MyModelUtil.mapToTableName(fieldClazz);
orderBaseData.columnName = MyModelUtil.mapToColumnName(orderBaseData.fieldName, fieldClazz);
}
return orderBaseData;
}
/**
* 在排序列表中,可能存在基于指定表字段的排序,该函数将获取指定表的所有排序字段。
* 返回的字符串可直接用于SQL中的ORDER BY从句。
* 返回的字符串可直接用于SQL中的ORDER BY从句。
*
* @param orderParam 排序参数对象。
* @param modelClazz 查询主表对应的主对象的Class。
* @param orderParam 排序参数对象。
* @param modelClazz 查询主表对应的主对象的Class。
* @param relationModelName 与关联表对应的Model的名称如my_course_paper表应对的Java对象CoursePaper。
* 如果该值为null或空字符串则获取所有主表的排序字段。
* @return 返回的是表字段而非Java对象的属性多个字段之间逗号分隔。
@@ -114,73 +119,72 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
return null;
}
if (modelClazz == null) {
throw new IllegalArgumentException("modelClazz Argument can't be NULL");
throw new IllegalArgumentException(
"modelClazz Argument in MyOrderParam.getOrderClauseByModelName can't be NULL");
}
String exceptionMessage;
List<String> fieldNameList = new LinkedList<>();
String prefix = null;
if (StringUtils.isNotBlank(relationModelName)) {
prefix = relationModelName + ".";
}
for (OrderInfo orderInfo : orderParam) {
boolean found = false;
String modelName = null, fieldName = orderInfo.fieldName, tableName = null, columnName = null;
int pos = fieldName.indexOf("DictMap.");
if (pos != -1) {
fieldName = fieldName.substring(0, pos);
}
if (prefix != null) {
if (fieldName.startsWith(prefix)) {
Field field = ReflectUtil.getField(modelClazz, relationModelName);
if (field == null) {
exceptionMessage = String.format(
"Relation Field [%s] doesn't exist in Class [%s]!", relationModelName, modelClazz.getSimpleName());
log.error(exceptionMessage);
throw new IllegalArgumentException(exceptionMessage);
}
Class<?> fieldClazz = field.getType();
modelName = fieldClazz.getSimpleName();
fieldName = StringUtils.removeStart(fieldName, prefix);
tableName = MyModelUtil.mapToTableName(fieldClazz);
columnName = MyModelUtil.mapToColumnName(fieldName, fieldClazz);
found = true;
}
} else {
if (!fieldName.contains(".")) {
modelName = modelClazz.getSimpleName();
tableName = MyModelUtil.mapToTableName(modelClazz);
columnName = MyModelUtil.mapToColumnName(fieldName, modelClazz);
found = true;
}
}
if (found) {
if (StringUtils.isBlank(tableName)) {
exceptionMessage = String.format("ModelName [%s] can't be mapped to Table!", modelName);
log.error(exceptionMessage);
throw new IllegalArgumentException(exceptionMessage);
}
if (StringUtils.isBlank(columnName)) {
exceptionMessage = String.format(
"FieldName [%s] in Class [%s] can't be mapped to Column!", fieldName, modelName);
log.error(exceptionMessage);
throw new IllegalArgumentException(exceptionMessage);
}
StringBuilder orderBy = new StringBuilder(128);
orderBy.append(tableName).append(".").append(columnName);
if (orderInfo.asc != null && !orderInfo.asc) {
orderBy.append(" DESC");
}
fieldNameList.add(orderBy.toString());
OrderBaseData baseData = parseOrderBaseData(orderInfo, modelClazz, prefix, relationModelName);
if (baseData != null) {
fieldNameList.add(makeOrderBy(baseData, orderInfo.asc));
}
}
return StringUtils.join(fieldNameList, ", ");
}
private static OrderBaseData parseOrderBaseData(
OrderInfo orderInfo, Class<?> modelClazz, String prefix, String relationModelName) {
OrderBaseData baseData = null;
String fieldName = StringUtils.substringBefore(orderInfo.fieldName, DICT_MAP);
if (prefix != null) {
if (fieldName.startsWith(prefix)) {
baseData = new OrderBaseData();
Field field = ReflectUtil.getField(modelClazz, relationModelName);
if (field == null) {
throw new InvalidClassFieldException(modelClazz.getSimpleName(), relationModelName);
}
Class<?> fieldClazz = field.getType();
baseData.modelName = fieldClazz.getSimpleName();
baseData.fieldName = StringUtils.removeStart(fieldName, prefix);
baseData.tableName = MyModelUtil.mapToTableName(fieldClazz);
baseData.columnName = MyModelUtil.mapToColumnName(fieldName, fieldClazz);
}
} else {
String dotLimitor = ".";
if (!fieldName.contains(dotLimitor)) {
baseData = new OrderBaseData();
baseData.modelName = modelClazz.getSimpleName();
baseData.tableName = MyModelUtil.mapToTableName(modelClazz);
baseData.columnName = MyModelUtil.mapToColumnName(fieldName, modelClazz);
}
}
return baseData;
}
private static String makeOrderBy(OrderBaseData baseData, Boolean asc) {
if (StringUtils.isBlank(baseData.tableName)) {
throw new InvalidDataModelException(baseData.modelName);
}
if (StringUtils.isBlank(baseData.columnName)) {
throw new InvalidDataFieldException(baseData.modelName, baseData.fieldName);
}
StringBuilder orderBy = new StringBuilder(128);
orderBy.append(baseData.tableName).append(".").append(baseData.columnName);
if (asc != null && !asc) {
orderBy.append(" DESC");
}
return orderBy.toString();
}
/**
* 在排序列表中,可能存在基于指定表字段的排序,该函数将删除指定表的所有排序字段。
*
* @param orderParam 排序参数对象。
* @param modelClazz 查询主表对应的主对象的Class。
* @param orderParam 排序参数对象。
* @param modelClazz 查询主表对应的主对象的Class。
* @param relationModelName 与关联表对应的Model的名称如my_course_paper表应对的Java对象CoursePaper。
* 如果该值为null或空字符串则获取所有主表的排序字段。
*/
@@ -190,7 +194,8 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
return;
}
if (modelClazz == null) {
throw new IllegalArgumentException("modelClazz Argument can't be NULL");
throw new IllegalArgumentException(
"modelClazz Argument in MyOrderParam.removeOrderClauseByModelName can't be NULL");
}
List<Integer> fieldIndexList = new LinkedList<>();
String prefix = null;
@@ -199,11 +204,7 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
}
int i = 0;
for (OrderInfo orderInfo : orderParam) {
String fieldName = orderInfo.fieldName;
int pos = fieldName.indexOf("DictMap.");
if (pos != -1) {
fieldName = fieldName.substring(0, pos);
}
String fieldName = StringUtils.substringBefore(orderInfo.fieldName, DICT_MAP);
if (prefix != null) {
if (fieldName.startsWith(prefix)) {
fieldIndexList.add(i);
@@ -220,6 +221,9 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
}
}
/**
* 排序信息对象。
*/
@Data
public static class OrderInfo {
/**
@@ -244,4 +248,11 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
*/
private String dateAggregateBy;
}
private static class OrderBaseData {
private String modelName;
private String fieldName;
private String tableName;
private String columnName;
}
}

View File

@@ -6,13 +6,14 @@ import lombok.Getter;
* Controller参数中的分页请求对象
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Getter
public class MyPageParam {
public static final int DEFAULT_PAGE_NUM = 1;
public static final int DEFAULT_PAGE_SIZE = 10;
public static final int DEFAULT_MAX_SIZE = 100;
/**
* 分页号码从1开始计数。
@@ -48,7 +49,7 @@ public class MyPageParam {
if (pageSize == null) {
return;
}
if (pageSize <= 0 || pageSize > 100) {
if (pageSize <= 0 || pageSize > DEFAULT_MAX_SIZE) {
pageSize = DEFAULT_PAGE_SIZE;
}
this.pageSize = pageSize;

View File

@@ -0,0 +1,84 @@
package com.orange.admin.common.core.object;
import lombok.Builder;
import lombok.Data;
/**
* 实体对象数据组装参数构建器。
* BaseService中的实体对象数据组装函数会根据该参数对象进行数据组装。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
@Data
@Builder
public class MyRelationParam {
/**
* 是否组装字典关联的标记。
* 组装RelationDict和RelationConstDict注解标记的字段。
*/
private boolean buildDict;
/**
* 是否组装一对一关联的标记。
* 组装RelationOneToOne注解标记的字段。
*/
private boolean buildOneToOne;
/**
* 在组装一对一关联的同时,是否继续关联从表中的字典。
* 从表中RelationDict和RelationConstDict注解标记的字段。
* 该字段为true时无需设置buildOneToOne了。
*/
private boolean buildOneToOneWithDict;
/**
* 是否组装主表对多对多中间表关联的标记。
* 组装RelationManyToMany注解标记的字段。
*/
private boolean buildRelationManyToMany;
/**
* 是否组装聚合计算关联的标记。
* 组装RelationOneToManyAggregation和RelationManyToManyAggregation注解标记的字段。
*/
private boolean buildRelationAggregation;
/**
* 便捷方法,返回仅做字典关联的参数对象。
*
* @return 返回仅做字典关联的参数对象。
*/
public static MyRelationParam dictOnly() {
return MyRelationParam.builder().buildDict(true).build();
}
/**
* 便捷方法,返回仅做字典关联、一对一从表及其字典和聚合计算的参数对象。
* NOTE: 对于一对多和多对多,这种从表数据是列表结果的关联,均不返回。
*
* @return 返回仅做字典关联、一对一从表及其字典和聚合计算的参数对象。
*/
public static MyRelationParam normal() {
return MyRelationParam.builder()
.buildDict(true)
.buildOneToOneWithDict(true)
.buildRelationAggregation(true)
.build();
}
/**
* 便捷方法,返回全部关联的参数对象。
*
* @return 返回全部关联的参数对象。
*/
public static MyRelationParam full() {
return MyRelationParam.builder()
.buildDict(true)
.buildOneToOneWithDict(true)
.buildRelationAggregation(true)
.buildRelationManyToMany(true)
.build();
}
}

View File

@@ -16,7 +16,7 @@ import java.util.List;
* Where中的条件语句。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
@Data
@@ -103,7 +103,7 @@ public class MyWhereCriteria {
* @param value 条件过滤值。
* @return 验证结果对象,如果有错误将会返回具体的错误信息。
*/
public VerifyResult setCriteria(String fieldName, Integer operatorType, Object value) {
public CallResult setCriteria(String fieldName, Integer operatorType, Object value) {
this.operatorType = operatorType;
this.fieldName = fieldName;
this.value = value;
@@ -119,7 +119,7 @@ public class MyWhereCriteria {
* @param value 条件过滤值。
* @return 验证结果对象,如果有错误将会返回具体的错误信息。
*/
public VerifyResult setCriteria(Class<?> modelClazz, String fieldName, Integer operatorType, Object value) {
public CallResult setCriteria(Class<?> modelClazz, String fieldName, Integer operatorType, Object value) {
this.modelClazz = modelClazz;
this.operatorType = operatorType;
this.fieldName = fieldName;
@@ -133,35 +133,31 @@ public class MyWhereCriteria {
*
* @return 验证结果对象,如果有错误将会返回具体的错误信息。
*/
public VerifyResult doVerify() {
public CallResult doVerify() {
if (fieldName == null) {
return VerifyResult.error("过滤字段名称 [fieldName] 不能为空!");
return CallResult.error("过滤字段名称 [fieldName] 不能为空!");
}
if (modelClazz != null) {
if (ReflectUtil.getField(modelClazz, fieldName) == null) {
return VerifyResult.error(
"过滤字段 [" + fieldName + "] 在实体对象 [" + modelClazz.getSimpleName() + "] 中并不存在!");
}
if (modelClazz != null && ReflectUtil.getField(modelClazz, fieldName) == null) {
return CallResult.error(
"过滤字段 [" + fieldName + "] 在实体对象 [" + modelClazz.getSimpleName() + "] 中并不存在!");
}
if (!checkOperatorType()) {
return VerifyResult.error("无效的操作符类型 [" + operatorType + "]!");
return CallResult.error("无效的操作符类型 [" + operatorType + "]!");
}
// 其他操作符必须包含value值
if (operatorType != OPERATOR_IS_NULL && operatorType != OPERATOR_NOT_NULL) {
if (value == null) {
String operatorString = this.getOperatorString();
return VerifyResult.error("操作符 [" + operatorString + "] 的条件值不能为空!");
}
if (operatorType != OPERATOR_IS_NULL && operatorType != OPERATOR_NOT_NULL && value == null) {
String operatorString = this.getOperatorString();
return CallResult.error("操作符 [" + operatorString + "] 的条件值不能为空!");
}
if (this.operatorType == OPERATOR_IN) {
if (!(value instanceof Collection)) {
return VerifyResult.error("操作符 [IN] 的条件值必须为集合对象!");
return CallResult.error("操作符 [IN] 的条件值必须为集合对象!");
}
if (CollectionUtils.isEmpty((Collection<?>) value)) {
return VerifyResult.error("操作符 [IN] 的条件值不能为空!");
return CallResult.error("操作符 [IN] 的条件值不能为空!");
}
}
return VerifyResult.ok();
return CallResult.ok();
}
/**
@@ -237,33 +233,7 @@ public class MyWhereCriteria {
if (tableName == null) {
throw new InvalidDataModelException(modelClazz.getSimpleName());
}
StringBuilder sb = new StringBuilder(64);
sb.append(tableName).append(".").append(fieldInfo.getFirst()).append(getOperatorString());
if (operatorType == OPERATOR_IN) {
Collection<?> filterValues = (Collection<?>) value;
sb.append("(");
int i = 0;
for (Object filterValue : filterValues) {
if (fieldInfo.getSecond().equals(MyModelUtil.NUMERIC_FIELD_TYPE)) {
sb.append(filterValue);
} else {
sb.append("'").append(filterValue).append("'");
}
if (i++ != filterValues.size() - 1) {
sb.append(", ");
}
}
sb.append(")");
} else {
if (value != null) {
if (fieldInfo.getSecond().equals(MyModelUtil.NUMERIC_FIELD_TYPE)) {
sb.append(value);
} else {
sb.append("'").append(value).append("'");
}
}
}
return sb.toString();
return this.buildClauseString(tableName, fieldInfo.getFirst(), fieldInfo.getSecond());
}
/**
@@ -305,4 +275,34 @@ public class MyWhereCriteria {
}
return sb.length() == 0 ? null : sb.toString();
}
private String buildClauseString(String tableName, String columnName, Integer columnType) {
StringBuilder sb = new StringBuilder(64);
sb.append(tableName).append(".").append(columnName).append(getOperatorString());
if (operatorType == OPERATOR_IN) {
Collection<?> filterValues = (Collection<?>) value;
sb.append("(");
int i = 0;
for (Object filterValue : filterValues) {
if (columnType.equals(MyModelUtil.NUMERIC_FIELD_TYPE)) {
sb.append(filterValue);
} else {
sb.append("'").append(filterValue).append("'");
}
if (i++ != filterValues.size() - 1) {
sb.append(", ");
}
}
sb.append(")");
return sb.toString();
}
if (value != null) {
if (columnType.equals(MyModelUtil.NUMERIC_FIELD_TYPE)) {
sb.append(value);
} else {
sb.append("'").append(value).append("'");
}
}
return sb.toString();
}
}

View File

@@ -4,14 +4,18 @@ import com.orange.admin.common.core.constant.ErrorCodeEnum;
import lombok.Data;
/**
* 接口返回对象
* 接口返回对象
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Data
public class ResponseResult<T> {
/**
* 为了优化性能,所有没有携带数据的正确结果,均可用该对象表示。
*/
private static final ResponseResult<Void> OK = new ResponseResult<>();
/**
* 是否成功标记。
*/
@@ -33,10 +37,10 @@ public class ResponseResult<T> {
* 根据参数errorCodeEnum的枚举值判断创建成功对象还是错误对象。
* 如果返回错误对象errorCode 和 errorMessage 分别取自于参数 errorCodeEnum 的 name() 和 getErrorMessage()。
*
* @param errorCodeEnum - 错误码枚举
* @param errorCodeEnum 错误码枚举
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> create(ErrorCodeEnum errorCodeEnum) {
public static ResponseResult<Void> create(ErrorCodeEnum errorCodeEnum) {
return create(errorCodeEnum, errorCodeEnum.getErrorMessage());
}
@@ -44,38 +48,24 @@ public class ResponseResult<T> {
* 根据参数errorCodeEnum的枚举值判断创建成功对象还是错误对象。
* 如果返回错误对象errorCode 和 errorMessage 分别取自于参数 errorCodeEnum 的 name() 和参数 errorMessage。
*
* @param errorCodeEnum - 错误码枚举。
* @param errorMessage - 如果该参数为null错误信息取自errorCodeEnum参数内置的errorMessage否则使用当前参数。
* @param errorCodeEnum 错误码枚举。
* @param errorMessage 如果该参数为null错误信息取自errorCodeEnum参数内置的errorMessage否则使用当前参数。
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> create(ErrorCodeEnum errorCodeEnum, String errorMessage) {
return errorCodeEnum == ErrorCodeEnum.NO_ERROR ? success()
: error(errorCodeEnum.name(), errorMessage != null ? errorMessage : errorCodeEnum.getErrorMessage());
}
/**
* 根据参数errorCodeEnum的枚举值判断创建成功对象还是错误对象。
* 如果返回错误对象errorCode 和 errorMessage 分别取自于参数 errorCodeEnum 的 name() 和参数 errorMessage。
* 如果返回的是成功对象dataObject数据对象将被连同返回。
*
* @param errorCodeEnum - 错误码枚举。
* @param errorMessage - 如果该参数为null错误信息取自errorCodeEnum参数内置的errorMessage否则使用当前参数。
* @param data - 返回的数据对象。
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> create(ErrorCodeEnum errorCodeEnum, String errorMessage, T data) {
return errorCodeEnum == ErrorCodeEnum.NO_ERROR ? success(data) : create(errorCodeEnum, errorMessage);
public static ResponseResult<Void> create(ErrorCodeEnum errorCodeEnum, String errorMessage) {
errorMessage = errorMessage != null ? errorMessage : errorCodeEnum.getErrorMessage();
return errorCodeEnum == ErrorCodeEnum.NO_ERROR ? success() : error(errorCodeEnum.name(), errorMessage);
}
/**
* 根据参数errorCode是否为空判断创建成功对象还是错误对象。
* 如果返回错误对象errorCode 和 errorMessage 分别取自于参数 errorCode 和参数 errorMessage。
*
* @param errorCode - 自定义的错误码
* @param errorMessage - 自定义的错误信息
* @param errorCode 自定义的错误码
* @param errorMessage 自定义的错误信息
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> create(String errorCode, String errorMessage) {
public static ResponseResult<Void> create(String errorCode, String errorMessage) {
return errorCode == null ? success() : error(errorCode, errorMessage);
}
@@ -85,14 +75,14 @@ public class ResponseResult<T> {
*
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> success() {
return success(null);
public static ResponseResult<Void> success() {
return OK;
}
/**
* 创建带有返回数据的成功对象。
*
* @param data - 返回的数据对象
* @param data 返回的数据对象
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> success(T data) {
@@ -105,7 +95,7 @@ public class ResponseResult<T> {
* 创建错误对象。
* 如果返回错误对象errorCode 和 errorMessage 分别取自于参数 errorCodeEnum 的 name() 和 getErrorMessage()。
*
* @param errorCodeEnum - 错误码枚举
* @param errorCodeEnum 错误码枚举
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> error(ErrorCodeEnum errorCodeEnum) {
@@ -116,8 +106,8 @@ public class ResponseResult<T> {
* 创建错误对象。
* 如果返回错误对象errorCode 和 errorMessage 分别取自于参数 errorCodeEnum 的 name() 和参数 errorMessage。
*
* @param errorCodeEnum - 错误码枚举
* @param errorMessage - 自定义的错误信息
* @param errorCodeEnum 错误码枚举
* @param errorMessage 自定义的错误信息
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> error(ErrorCodeEnum errorCodeEnum, String errorMessage) {
@@ -128,14 +118,24 @@ public class ResponseResult<T> {
* 创建错误对象。
* 如果返回错误对象errorCode 和 errorMessage 分别取自于参数 errorCode 和参数 errorMessage。
*
* @param errorCode - 自定义的错误码
* @param errorMessage - 自定义的错误信息
* @param errorCode 自定义的错误码
* @param errorMessage 自定义的错误信息
* @return 返回创建的ResponseResult实例对象
*/
public static <T> ResponseResult<T> error(String errorCode, String errorMessage) {
return new ResponseResult<>(false, errorCode, errorMessage);
}
/**
* 根据参数的errorCode和errorMessage创建新的错误应答对象。
*
* @param errorCause 导致错误原因的应答对象。
* @return 返回创建的ResponseResult实例对象。
*/
public static <T, E> ResponseResult<T> errorFrom(ResponseResult<E> errorCause) {
return error(errorCause.errorCode, errorCause.getErrorMessage());
}
/**
* 是否成功。
*
@@ -154,5 +154,4 @@ public class ResponseResult<T> {
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
}

View File

@@ -10,7 +10,7 @@ import javax.servlet.http.HttpServletRequest;
* 基于Jwt用于前后端传递的令牌对象。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Data
@ToString

View File

@@ -4,7 +4,7 @@ package com.orange.admin.common.core.object;
* 二元组对象。主要用于可以一次返回多个结果的场景,同时还能避免强制转换。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class Tuple2<T1, T2> {

View File

@@ -1,6 +1,6 @@
package com.orange.admin.common.core.util;
import org.springframework.beans.BeansException;
import com.orange.admin.common.core.exception.MyRuntimeException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
@@ -10,7 +10,7 @@ import org.springframework.stereotype.Component;
* Spring 系统启动应用感知对象主要用于获取Spring Bean的上下文对象后续的代码中可以直接查找系统中加载的Bean对象。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
@@ -21,11 +21,10 @@ public class ApplicationContextHolder implements ApplicationContextAware {
* Spring 启动的过程中会自动调用,并将应用上下文对象赋值进来。
*
* @param applicationContext 应用上下文对象可通过该对象查找Spring中已经加载的Bean。
* @throws BeansException Bean处理相关的异常。
*/
@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
ApplicationContextHolder.applicationContext = applicationContext;
public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
doSetApplicationContext(applicationContext);
}
/**
@@ -65,8 +64,11 @@ public class ApplicationContextHolder implements ApplicationContextAware {
private static void assertApplicationContext() {
if (ApplicationContextHolder.applicationContext == null) {
throw new RuntimeException("applicaitonContext属性为null,请检查是否注入了ApplicationContextHolder!");
throw new MyRuntimeException("applicaitonContext属性为null,请检查是否注入了ApplicationContextHolder!");
}
}
private static void doSetApplicationContext(ApplicationContext applicationContext) {
ApplicationContextHolder.applicationContext = applicationContext;
}
}

View File

@@ -10,7 +10,7 @@ import javax.servlet.http.HttpServletResponse;
* 获取Servlet HttpRequest和HttpResponse的工具类。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class ContextUtil {
@@ -41,4 +41,9 @@ public class ContextUtil {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private ContextUtil() {
}
}

View File

@@ -4,6 +4,9 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.jimmyshi.beanquery.BeanQuery;
import com.orange.admin.common.core.constant.ApplicationConstant;
import com.orange.admin.common.core.exception.MyRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
@@ -20,17 +23,18 @@ import java.util.*;
* 导出工具类目前支持xlsx和csv两种类型。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
public class ExportUtil {
/**
* 数据导出。目前仅支持xlsx和csv。
*
* @param dataList 导出数据列表。
* @param dataList 导出数据列表。
* @param selectFieldMap 导出的数据字段key为对象字段名称value为中文标题名称。
* @param filename 导出文件名。
* @param <T> 数据对象类型。
* @param filename 导出文件名。
* @param <T> 数据对象类型。
* @throws IOException 文件操作失败。
*/
public static <T> void doExport(
@@ -51,7 +55,7 @@ public class ExportUtil {
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
if ("xlsx".equals(FilenameUtils.getExtension(filename))) {
if (ApplicationConstant.XLSX_EXT.equals(FilenameUtils.getExtension(filename))) {
ServletOutputStream out = response.getOutputStream();
ExcelWriter writer = ExcelUtil.getWriter(true);
writer.setRowHeight(-1, 30);
@@ -61,7 +65,7 @@ public class ExportUtil {
writer.flush(out);
writer.close();
IoUtil.close(out);
} else if ("csv".equals(FilenameUtils.getExtension(filename))) {
} else if (ApplicationConstant.CSV_EXT.equals(FilenameUtils.getExtension(filename))) {
Collection<String> headerList = selectFieldMap.values();
String[] headerArray = new String[headerList.size()];
headerList.toArray(headerArray);
@@ -76,10 +80,16 @@ public class ExportUtil {
}
printer.flush();
} catch (Exception e) {
e.printStackTrace();
log.error("Failed to call ExportUtil.doExport", e);
}
} else {
throw new RuntimeException("不支持的导出文件类型!");
throw new MyRuntimeException("不支持的导出文件类型!");
}
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private ExportUtil() {
}
}

View File

@@ -4,6 +4,9 @@ import cn.hutool.core.util.ReflectUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.sax.Excel07SaxReader;
import com.orange.admin.common.core.constant.ApplicationConstant;
import com.orange.admin.common.core.exception.MyRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
@@ -19,10 +22,13 @@ import java.util.stream.Collectors;
* 导入工具类目前支持xlsx和csv两种类型。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
public class ImportUtil {
private static final String IMPORT_EXCEPTION_ERROR = "Failed to call ImportUtil.doImport";
private static final String UNSUPPORT_FILE_EXT_ERROR = "不支持的导入文件类型!";
/**
* 同步导入方式。
*
@@ -30,11 +36,11 @@ public class ImportUtil {
* @return 导入数据列表。
*/
public static List<Map<String, Object>> doImport(String filename) {
if ("xlsx".equals(FilenameUtils.getExtension(filename))) {
if (ApplicationConstant.XLSX_EXT.equals(FilenameUtils.getExtension(filename))) {
try (ExcelReader reader = ExcelUtil.getReader(filename)) {
return reader.readAll();
}
} else if ("csv".equals(FilenameUtils.getExtension(filename))) {
} else if (ApplicationConstant.CSV_EXT.equals(FilenameUtils.getExtension(filename))) {
List<Map<String, Object>> resultList = new LinkedList<>();
try (FileReader reader = new FileReader(filename)) {
CSVParser parser = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(reader);
@@ -50,11 +56,11 @@ public class ImportUtil {
resultList.add(rowMap);
}
} catch (Exception e) {
e.printStackTrace();
log.error(IMPORT_EXCEPTION_ERROR, e);
}
return resultList;
}
throw new RuntimeException("不支持的导入文件类型!");
throw new MyRuntimeException(UNSUPPORT_FILE_EXT_ERROR);
}
/**
@@ -65,12 +71,12 @@ public class ImportUtil {
* @throws IOException 文件处理异常。
*/
public static <T> void doImport(String filename, BaseImporter<T> importer) throws IOException {
if ("xlsx".equals(FilenameUtils.getExtension(filename))) {
if (ApplicationConstant.XLSX_EXT.equals(FilenameUtils.getExtension(filename))) {
Excel07SaxReader reader = new MyExcel07SaxReader<>(importer);
try (InputStream in = new FileInputStream(filename)) {
reader.read(in, 0);
}
} else if ("csv".equals(FilenameUtils.getExtension(filename))) {
} else if (ApplicationConstant.CSV_EXT.equals(FilenameUtils.getExtension(filename))) {
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
int rowIndex = 0;
do {
@@ -81,15 +87,12 @@ public class ImportUtil {
}
String[] dataArray = StringUtils.split(rowData, ",");
importer.doImport(rowIndex++, Arrays.asList(dataArray));
if (importer.doInterrupt()) {
break;
}
} while (true);
} while (!importer.doInterrupt());
} catch (Exception e) {
e.printStackTrace();
log.error(IMPORT_EXCEPTION_ERROR, e);
}
}
throw new RuntimeException("不支持的导入文件类型!");
throw new MyRuntimeException(UNSUPPORT_FILE_EXT_ERROR);
}
/**
@@ -97,7 +100,7 @@ public class ImportUtil {
*
* @param <T> 导入数据对象类型。
*/
public static abstract class BaseImporter<T> {
public abstract static class BaseImporter<T> {
private Class<T> beanType;
private List<T> batchRowList = new LinkedList<>();
private int batchSize;
@@ -117,7 +120,7 @@ public class ImportUtil {
* 导入操作执行函数。
*
* @param rowIndex 当前行号。
* @param row 当前行数据列表对象。
* @param row 当前行数据列表对象。
*/
public void doImport(int rowIndex, List<Object> row) {
if (row == null) {
@@ -154,7 +157,7 @@ public class ImportUtil {
}
batchRowList.add(data);
} catch (Exception e) {
e.printStackTrace();
log.error(IMPORT_EXCEPTION_ERROR, e);
}
if (rowIndex % batchSize == 0) {
doProcess(batchRowList);
@@ -193,7 +196,7 @@ public class ImportUtil {
public void endElement(String uri, String localName, String qName) {
super.endElement(uri, localName, qName);
if (importer.doInterrupt()) {
throw new RuntimeException();
throw new MyRuntimeException();
}
}
@@ -202,4 +205,10 @@ public class ImportUtil {
importer.doImport(-1, null);
}
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private ImportUtil() {
}
}

View File

@@ -16,7 +16,7 @@ import java.util.List;
* Ip工具类。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
public class IpUtil {
@@ -81,12 +81,9 @@ public class IpUtil {
Enumeration<InetAddress> allAddress = ni.getInetAddresses();
while (allAddress.hasMoreElements()) {
InetAddress address = allAddress.nextElement();
if (address.isLoopbackAddress()) {
// skip the loopback addr
continue;
}
if (address instanceof Inet6Address) {
// skip the IPv6 addr
// skip the IPv6 addr
// skip the IPv6 addr
if (address.isLoopbackAddress() || address instanceof Inet6Address) {
continue;
}
String hostAddress = address.getHostAddress();
@@ -95,4 +92,10 @@ public class IpUtil {
}
return ipList;
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private IpUtil() {
}
}

View File

@@ -3,6 +3,7 @@ package com.orange.admin.common.core.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.Map;
@@ -11,8 +12,9 @@ import java.util.Map;
* 基于JWT的Token生成工具类
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
public class JwtUtil {
private static final String TOKEN_PREFIX = "Bearer:";
@@ -71,7 +73,7 @@ public class JwtUtil {
try {
claims = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(tokenKey).getBody();
} catch (Exception e) {
System.out.println("Token Expired");
log.error("Token Expired", e);
}
return claims;
}
@@ -100,4 +102,9 @@ public class JwtUtil {
return createTime == null || System.currentTimeMillis() - createTime > REFRESH_TOKEN_INTERVAL;
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private JwtUtil() {
}
}

View File

@@ -0,0 +1,33 @@
package com.orange.admin.common.core.util;
/**
* 拼接日志消息的工具类。
* 主要目标是,尽量保证日志输出的统一性,同时也可以有效减少与日志信息相关的常量字符串,
* 提高代码的规范度和可维护性。
*
* @author Stephen.Liu
* @date 2020-05-24
*/
public class LogMessageUtil {
/**
* RPC调用错误格式。
*/
private static final String RPC_ERROR_MSG_FORMAT = "RPC Failed with Error message [%s]";
/**
* 组装RPC调用的错误信息。
*
* @param errorMsg 具体的错误信息。
* @return 格式化后的错误信息。
*/
public static String makeRpcError(String errorMsg) {
return String.format(RPC_ERROR_MSG_FORMAT, errorMsg);
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private LogMessageUtil() {
}
}

View File

@@ -1,6 +1,6 @@
package com.orange.admin.common.core.util;
import org.apache.commons.codec.digest.DigestUtils;
import cn.hutool.crypto.digest.DigestUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -16,7 +16,7 @@ import java.util.UUID;
* 脚手架中常用的基本工具方法集合,一般而言工程内部使用的方法。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class MyCommonUtil {
@@ -32,17 +32,18 @@ public class MyCommonUtil {
* @return 返回uuid。
*/
public static String generateUuid() {
return UUID.randomUUID().toString().replaceAll("-", "");
return UUID.randomUUID().toString().replace("-", "");
}
/**
* 对用户密码进行加盐后加密。
*
* @param password 明文密码。
* @param password 明文密码。
* @param passwordSalt 盐值。
* @return 加密后的密码。
*/
public static String encrptedPassword(String password, String passwordSalt) {
return DigestUtils.md5Hex(password + passwordSalt);
return DigestUtil.md5Hex(password + passwordSalt);
}
/**
@@ -119,7 +120,7 @@ public class MyCommonUtil {
/**
* 拼接参数中的字符串列表,用指定分隔符进行分割,同时每个字符串对象用单引号括起来。
*
* @param dataList 字符串集合。
* @param dataList 字符串集合。
* @param separator 分隔符。
* @return 拼接后的字符串。
*/
@@ -134,4 +135,10 @@ public class MyCommonUtil {
}
return sb.toString();
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private MyCommonUtil() {
}
}

View File

@@ -16,7 +16,7 @@ import static org.joda.time.PeriodType.days;
* 日期工具类主要封装了部分joda-time中的方法让很多代码一行完成同时统一了日期到字符串的pattern格式。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class MyDateUtil {
@@ -172,4 +172,10 @@ public class MyDateUtil {
public static Date truncateToYear(Date date) {
return DateUtils.truncate(date, Calendar.YEAR);
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private MyDateUtil() {
}
}

View File

@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ReflectUtil;
import com.orange.admin.common.core.annotation.RelationDict;
import com.orange.admin.common.core.annotation.RelationOneToOne;
import com.orange.admin.common.core.exception.MyRuntimeException;
import com.orange.admin.common.core.object.Tuple2;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -24,7 +25,7 @@ import java.util.stream.Collectors;
* 负责Model数据操作、类型转换和关系关联等行为的工具类。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
public class MyModelUtil {
@@ -94,9 +95,9 @@ public class MyModelUtil {
// 这里缺省情况下都是按照整型去处理,因为他覆盖太多的类型了。
// 如Integer/Long/Double/BigDecimal可根据实际情况完善和扩充。
Integer type = NUMERIC_FIELD_TYPE;
if ("String".equals(typeName)) {
if (String.class.getSimpleName().equals(typeName)) {
type = STRING_FIELD_TYPE;
} else if ("Date".equals(typeName)) {
} else if (Date.class.getSimpleName().equals(typeName)) {
type = DATE_FIELD_TYPE;
}
columnInfo = new Tuple2<>(columnName, type);
@@ -156,7 +157,7 @@ public class MyModelUtil {
* @param <R> 从表对象类型。
*/
public static <T, R> void makeDictRelation(
Class<T> thisClazz, List<? extends T> thisModelList, List<R> thatModelList, String thisRelationField) {
Class<T> thisClazz, List<T> thisModelList, List<R> thatModelList, String thisRelationField) {
if (CollectionUtils.isEmpty(thatModelList)
|| CollectionUtils.isEmpty(thisModelList)) {
return;
@@ -200,7 +201,7 @@ public class MyModelUtil {
* @param <R> 从表对象类型。
*/
public static <T, R> void makeDictRelation(
Class<T> thisClazz, List<? extends T> thisModelList, Map<Object, R> thatMadelMap, String thisRelationField) {
Class<T> thisClazz, List<T> thisModelList, Map<Object, R> thatMadelMap, String thisRelationField) {
if (MapUtils.isEmpty(thatMadelMap)
|| CollectionUtils.isEmpty(thisModelList)) {
return;
@@ -210,7 +211,6 @@ public class MyModelUtil {
RelationDict r = thisTargetField.getAnnotation(RelationDict.class);
Field masterIdField = ReflectUtil.getField(thisClazz, r.masterIdField());
Class<?> thatClass = r.slaveModelClass();
Field slaveIdField = ReflectUtil.getField(thatClass, r.slaveIdField());
Field slaveNameField = ReflectUtil.getField(thatClass, r.slaveNameField());
thisModelList.forEach(thisModel -> {
if (thisModel != null) {
@@ -238,7 +238,7 @@ public class MyModelUtil {
* @param <R> 从表对象类型。
*/
public static <T, R> void makeOneToOneRelation(
Class<T> thisClazz, List<? extends T> thisModelList, List<R> thatModelList, String thisRelationField) {
Class<T> thisClazz, List<T> thisModelList, List<R> thatModelList, String thisRelationField) {
if (CollectionUtils.isEmpty(thatModelList)
|| CollectionUtils.isEmpty(thisModelList)) {
return;
@@ -290,7 +290,7 @@ public class MyModelUtil {
*/
public static <T, R> void makeOneToOneRelation(
Class<T> thisClazz,
List<? extends T> thisModelList,
List<T> thisModelList,
Function<T, Object> thisIdGetterFunc,
List<R> thatModelList,
Function<R, Object> thatIdGetterFunc,
@@ -316,60 +316,45 @@ public class MyModelUtil {
*/
public static <T, R> void makeOneToOneRelation(
Class<T> thisClazz,
List<? extends T> thisModelList,
List<T> thisModelList,
Function<T, Object> thisIdGetterFunc,
List<R> thatModelList,
Function<R, Object> thatIdGetterFunc,
String thisRelationField,
boolean orderByThatList) {
Field thisTargetField = ReflectUtil.getField(thisClazz, thisRelationField);
boolean isMap = thisTargetField.getType().equals(Map.class);
if (orderByThatList) {
List<T> newThisModelList = new LinkedList<>();
Map<Object, ? extends T> thisModelMap =
thisModelList.stream().collect(Collectors.toMap(thisIdGetterFunc, c -> c));
if (thisTargetField.getType().equals(Map.class)) {
thatModelList.forEach(thatModel -> {
Object thatId = thatIdGetterFunc.apply(thatModel);
T thisModel = thisModelMap.get(thatId);
if (thisModel != null) {
ReflectUtil.setFieldValue(thisModel, thisTargetField, BeanUtil.beanToMap(thatModel));
newThisModelList.add(thisModel);
}
});
} else {
thatModelList.forEach(thatModel -> {
Object thatId = thatIdGetterFunc.apply(thatModel);
T thisModel = thisModelMap.get(thatId);
if (thisModel != null) {
ReflectUtil.setFieldValue(thisModel, thisTargetField, thatModel);
newThisModelList.add(thisModel);
}
});
}
thisModelList = newThisModelList;
thatModelList.forEach(thatModel -> {
Object thatId = thatIdGetterFunc.apply(thatModel);
T thisModel = thisModelMap.get(thatId);
if (thisModel != null) {
ReflectUtil.setFieldValue(thisModel, thisTargetField, normalize(isMap, thatModel));
newThisModelList.add(thisModel);
}
});
thisModelList.clear();
thisModelList.addAll(newThisModelList);
} else {
Map<Object, R> thatMadelMap =
thatModelList.stream().collect(Collectors.toMap(thatIdGetterFunc, c -> c));
if (thisTargetField.getType().equals(Map.class)) {
thisModelList.forEach(thisModel -> {
Object thisId = thisIdGetterFunc.apply(thisModel);
R thatModel = thatMadelMap.get(thisId);
if (thatModel != null) {
ReflectUtil.setFieldValue(thisModel, thisTargetField, BeanUtil.beanToMap(thatModel));
}
});
} else {
thisModelList.forEach(thisModel -> {
Object thisId = thisIdGetterFunc.apply(thisModel);
R thatModel = thatMadelMap.get(thisId);
if (thatModel != null) {
ReflectUtil.setFieldValue(thisModel, thisTargetField, thatModel);
}
});
}
thisModelList.forEach(thisModel -> {
Object thisId = thisIdGetterFunc.apply(thisModel);
R thatModel = thatMadelMap.get(thisId);
if (thatModel != null) {
ReflectUtil.setFieldValue(thisModel, thisTargetField, normalize(isMap, thatModel));
}
});
}
}
private static <M> Object normalize(boolean isMap, M model) {
return isMap ? BeanUtil.beanToMap(model) : model;
}
/**
* 转换过滤对象到与其等效的Example对象。
*
@@ -392,7 +377,7 @@ public class MyModelUtil {
int modifiers = field.getModifiers();
// transient类型的字段不能作为查询条件
if ((modifiers & 128) == 0) {
field.setAccessible(true);
ReflectUtil.setAccessible(field);
try {
Object o = field.get(filterModel);
if (o != null) {
@@ -400,10 +385,16 @@ public class MyModelUtil {
}
} catch (IllegalAccessException ex) {
log.error("Failed to call reflection code.", ex);
throw new RuntimeException(ex);
throw new MyRuntimeException(ex);
}
}
}
return e;
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private MyModelUtil() {
}
}

View File

@@ -10,22 +10,25 @@ import java.util.List;
* 生成带有分页信息的数据列表
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class MyPageUtil {
private static final String DATA_LIST_LITERAL = "dataList";
private static final String TOTAL_COUNT_LITERAL = "totalCount";
/**
* 用户构建带有分页信息的数据列表。
*
* @param dataList 数据列表该参数必须是调用PageHelper.startPage之后立即执行mybatis查询操作的结果集。
* @param dataList 数据列表该参数必须是调用PageMethod.startPage之后立即执行mybatis查询操作的结果集。
* @param includeFields 结果集中需要返回到前端的字段,多个字段之间逗号分隔。
* @return 返回只是包含includeFields字段的数据列表以及结果集TotalCount。
*/
public static <T> JSONObject makeResponseData(List<T> dataList, String includeFields) {
JSONObject pageData = new JSONObject();
pageData.put("dataList", BeanQuery.select(includeFields).from(dataList).execute());
pageData.put(DATA_LIST_LITERAL, BeanQuery.select(includeFields).from(dataList).execute());
if (dataList instanceof Page) {
pageData.put("totalCount", ((Page<?>)dataList).getTotal());
pageData.put(TOTAL_COUNT_LITERAL, ((Page<?>)dataList).getTotal());
}
return pageData;
}
@@ -33,14 +36,14 @@ public class MyPageUtil {
/**
* 用户构建带有分页信息的数据列表。
*
* @param dataList 数据列表该参数必须是调用PageHelper.startPage之后立即执行mybatis查询操作的结果集。
* @param dataList 数据列表该参数必须是调用PageMethod.startPage之后立即执行mybatis查询操作的结果集。
* @return 返回结果集和TotalCount。
*/
public static <T> JSONObject makeResponseData(List<T> dataList) {
JSONObject pageData = new JSONObject();
pageData.put("dataList", dataList);
pageData.put(DATA_LIST_LITERAL, dataList);
if (dataList instanceof Page) {
pageData.put("totalCount", ((Page<?>)dataList).getTotal());
pageData.put(TOTAL_COUNT_LITERAL, ((Page<?>)dataList).getTotal());
}
return pageData;
}
@@ -48,17 +51,22 @@ public class MyPageUtil {
/**
* 用户构建带有分页信息的数据列表。
*
* @param dataList 数据列表该参数必须是调用PageHelper.startPage之后立即执行mybatis查询操作的结果集。
* @param dataList 数据列表该参数必须是调用PageMethod.startPage之后立即执行mybatis查询操作的结果集。
* @param totalCount 总数量。
* @return 返回结果集和TotalCount。
*/
public static <T> JSONObject makeResponseData(List<T> dataList, Long totalCount) {
JSONObject pageData = new JSONObject();
pageData.put("dataList", dataList);
pageData.put(DATA_LIST_LITERAL, dataList);
if (totalCount != null) {
pageData.put("totalCount", totalCount);
pageData.put(TOTAL_COUNT_LITERAL, totalCount);
}
return pageData;
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private MyPageUtil() {
}
}

View File

@@ -0,0 +1,114 @@
package com.orange.admin.common.core.util;
import javax.crypto.Cipher;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* Java RSA 加密工具类
*
* @author Stephen.Liu
* @date 2020-05-24
*/
public class RsaUtil {
/**
* 密钥长度 于原文长度对应 以及越长速度越慢
*/
private static final int KEY_SIZE = 1024;
/**
* 用于封装随机产生的公钥与私钥
*/
private static Map<Integer, String> keyMap = new HashMap<>();
/**
* 随机生成密钥对。
*/
public static void genKeyPair() throws NoSuchAlgorithmException {
// KeyPairGenerator类用于生成公钥和私钥对基于RSA算法生成对象
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 初始化密钥对生成器
keyPairGen.initialize(KEY_SIZE, new SecureRandom());
// 生成一个密钥对保存在keyPair中
KeyPair keyPair = keyPairGen.generateKeyPair();
// 得到私钥
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
// 得到公钥
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());
// 得到私钥字符串
String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());
// 将公钥和私钥保存到Map
//0表示公钥
keyMap.put(0, publicKeyString);
//1表示私钥
keyMap.put(1, privateKeyString);
}
/**
* RSA公钥加密。
*
* @param str 加密字符串
* @param publicKey 公钥
* @return 密文
* @throws Exception 加密过程中的异常信息
*/
public static String encrypt(String str, String publicKey) throws Exception {
//base64编码的公钥
byte[] decoded = Base64.getDecoder().decode(publicKey);
RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RSA加密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8)));
}
/**
* RSA私钥解密。
*
* @param str 加密字符串
* @param privateKey 私钥
* @return 明文
* @throws Exception 解密过程中的异常信息
*/
public static String decrypt(String str, String privateKey) throws Exception {
//64位解码加密后的字符串
byte[] inputByte = Base64.getDecoder().decode(str);
//base64编码的私钥
byte[] decoded = Base64.getDecoder().decode(privateKey);
RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
//RSA解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priKey);
return new String(cipher.doFinal(inputByte));
}
public static void main(String[] args) throws Exception {
long temp = System.currentTimeMillis();
//生成公钥和私钥
genKeyPair();
//加密字符串
System.out.println("公钥:" + keyMap.get(0));
System.out.println("私钥:" + keyMap.get(1));
System.out.println("生成密钥消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "");
System.out.println("生成后的公钥前端使用!");
System.out.println("生成后的私钥后台使用!");
String message = "RSA测试ABCD~!@#$";
System.out.println("原文:" + message);
temp = System.currentTimeMillis();
String messageEn = encrypt(message, keyMap.get(0));
System.out.println("密文:" + messageEn);
System.out.println("加密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "");
temp = System.currentTimeMillis();
String messageDe = decrypt(messageEn, keyMap.get(1));
System.out.println("解密:" + messageDe);
System.out.println("解密消耗时间:" + (System.currentTimeMillis() - temp) / 1000.0 + "");
}
}

View File

@@ -15,7 +15,7 @@ import java.util.function.Function;
* @param <K> 节点之间关联键的类型。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Data
public class TreeNode<T, K> {
@@ -28,12 +28,12 @@ public class TreeNode<T, K> {
/**
* 将列表结构组建为树结构的工具方法。
*
* @param dataList 数据列表结构。
* @param idFunc 获取关联id的函数对象。
* @param dataList 数据列表结构。
* @param idFunc 获取关联id的函数对象。
* @param parentIdFunc 获取关联ParentId的函数对象。
* @param root 根节点。
* @param <T> 数据对象类型。
* @param <K> 节点之间关联键的类型。
* @param root 根节点。
* @param <T> 数据对象类型。
* @param <K> 节点之间关联键的类型。
* @return 源数据对象的树结构存储。
*/
public static <T, K> List<TreeNode<T, K>> build(
@@ -90,5 +90,4 @@ public class TreeNode<T, K> {
private void add(TreeNode<T, K> node) {
childList.add(node);
}
}

View File

@@ -1,7 +1,6 @@
package com.orange.admin.common.core.util;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON;
import com.orange.admin.common.core.constant.ApplicationConstant;
import com.orange.admin.common.core.constant.ErrorCodeEnum;
import com.orange.admin.common.core.object.ResponseResult;
@@ -19,12 +18,13 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
/**
* 上传或下载附件文件的工具类。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Slf4j
public class UpDownloadUtil {
@@ -47,8 +47,8 @@ public class UpDownloadUtil {
Boolean asImage,
HttpServletResponse response) {
StringBuilder uploadPathBuilder = new StringBuilder(128);
uploadPathBuilder.append(rootBaseDir);
if (asImage) {
uploadPathBuilder.append(rootBaseDir).append("/");
if (Boolean.TRUE.equals(asImage)) {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_IMAGE_PARENT_PATH);
} else {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_ATTACHMENT_PARENT_PATH);
@@ -64,8 +64,8 @@ public class UpDownloadUtil {
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
byte[] buff = new byte[2048];
try (OutputStream os = response.getOutputStream()) {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
try (OutputStream os = response.getOutputStream();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
int i = bis.read(buff);
while (i != -1) {
os.write(buff, 0, buff.length);
@@ -73,7 +73,7 @@ public class UpDownloadUtil {
i = bis.read(buff);
}
} catch (IOException e) {
e.printStackTrace();
log.error("Failed to call UpDownloadUtil.doDownload", e);
}
}
@@ -97,9 +97,14 @@ public class UpDownloadUtil {
HttpServletResponse response) throws IOException {
PrintWriter out = response.getWriter();
response.setContentType("application/json; charset=utf-8");
if (Objects.isNull(uploadFile) || uploadFile.isEmpty() || MyCommonUtil.isBlankOrNull(fieldName)) {
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
out.print(JSON.toJSONString(ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FILE_ARGUMENT)));
return;
}
StringBuilder uploadPathBuilder = new StringBuilder(128);
uploadPathBuilder.append(rootBaseDir);
if (asImage) {
uploadPathBuilder.append(rootBaseDir).append("/");
if (Boolean.TRUE.equals(asImage)) {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_IMAGE_PARENT_PATH);
} else {
uploadPathBuilder.append(ApplicationConstant.UPLOAD_ATTACHMENT_PARENT_PATH);
@@ -129,10 +134,10 @@ public class UpDownloadUtil {
} catch (IOException e) {
log.error("Failed to write uploaded file [" + uploadFile.getOriginalFilename() + " ].", e);
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
out.print(JSONObject.toJSONString(ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FILE_IOERROR)));
out.print(JSON.toJSONString(ResponseResult.error(ErrorCodeEnum.INVALID_UPLOAD_FILE_IOERROR)));
return;
}
out.print(JSONObject.toJSONString(ResponseResult.success(fileInfo)));
out.print(JSON.toJSONString(ResponseResult.success(fileInfo)));
out.flush();
out.close();
}
@@ -148,7 +153,7 @@ public class UpDownloadUtil {
if (StringUtils.isAnyBlank(fileInfoJson, filename)) {
return false;
}
List<UploadFileInfo> fileInfoList = JSONArray.parseArray(fileInfoJson, UploadFileInfo.class);
List<UploadFileInfo> fileInfoList = JSON.parseArray(fileInfoJson, UploadFileInfo.class);
if (CollectionUtils.isNotEmpty(fileInfoList)) {
for (UploadFileInfo fileInfo : fileInfoList) {
if (StringUtils.equals(filename, fileInfo.filename)) {
@@ -159,6 +164,12 @@ public class UpDownloadUtil {
return false;
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private UpDownloadUtil() {
}
@Data
static class UploadFileInfo {
private String downloadUri;

View File

@@ -4,7 +4,7 @@ package com.orange.admin.common.core.validator;
* 数据增加的验证分组。通常用于数据新增场景。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public interface AddGroup {
}

View File

@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
* 定义在Model对象中标注字段值引用自指定的常量字典和ConstDictRefValidator对象配合完成数据验证。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -10,7 +10,7 @@ import java.lang.reflect.Method;
* 数据字段自定义验证用于验证Model中字符串字段的最大长度和最小长度。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class ConstDictValidator implements ConstraintValidator<ConstDictRef, Object> {

View File

@@ -11,7 +11,7 @@ import java.lang.annotation.Target;
* 定义在Model或Dto对象中UTF-8编码的字符串字段长度的上限和下限和TextLengthValidator对象配合完成数据验证。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

View File

@@ -9,7 +9,7 @@ import javax.validation.ConstraintValidatorContext;
* 数据字段自定义验证用于验证Model中UTF-8编码的字符串字段的最大长度和最小长度。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public class TextLengthValidator implements ConstraintValidator<TextLength, String> {

View File

@@ -4,7 +4,7 @@ package com.orange.admin.common.core.validator;
* 数据修改的验证分组。通常用于数据更新的场景。
*
* @author Stephen.Liu
* @date 2020-04-11
* @date 2020-05-24
*/
public interface UpdateGroup {