mirror of
https://gitee.com/orangeform/orange-admin.git
synced 2026-01-17 18:46:36 +08:00
同步到1.8.0
This commit is contained in:
@@ -96,9 +96,9 @@
|
||||
<version>${druid.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>tk.mybatis</groupId>
|
||||
<artifactId>mapper-spring-boot-starter</artifactId>
|
||||
<version>${mybatis-mapper.version}</version>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatisplus.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
package com.orange.demo.common.core.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 主要用于标记逻辑删除字段。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface DeletedFlagColumn {
|
||||
|
||||
}
|
||||
@@ -36,13 +36,13 @@ public class DataSourceAspect {
|
||||
Class<?> clazz = point.getTarget().getClass();
|
||||
MyDataSource ds = clazz.getAnnotation(MyDataSource.class);
|
||||
// 通过判断 DataSource 中的值来判断当前方法应用哪个数据源
|
||||
DataSourceContextHolder.setDataSourceType(ds.value());
|
||||
Integer originalType = DataSourceContextHolder.setDataSourceType(ds.value());
|
||||
log.debug("set datasource is " + ds.value());
|
||||
try {
|
||||
return point.proceed();
|
||||
} finally {
|
||||
DataSourceContextHolder.clear();
|
||||
log.debug("clean datasource");
|
||||
DataSourceContextHolder.unset(originalType);
|
||||
log.debug("unset datasource is " + originalType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,13 +50,13 @@ public class DataSourceResolveAspect {
|
||||
}
|
||||
int type = resolver.resolve(dsr.arg(), point.getArgs());
|
||||
// 通过判断 DataSource 中的值来判断当前方法应用哪个数据源
|
||||
DataSourceContextHolder.setDataSourceType(type);
|
||||
Integer originalType = DataSourceContextHolder.setDataSourceType(type);
|
||||
log.debug("set datasource is " + type);
|
||||
try {
|
||||
return point.proceed();
|
||||
} finally {
|
||||
DataSourceContextHolder.clear();
|
||||
log.debug("clean datasource");
|
||||
DataSourceContextHolder.unset(originalType);
|
||||
log.debug("unset datasource is " + originalType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 字典缓存同步的AOP。该AOP的优先级必须比事务切面的优先级高,因此会在事务外执行该切面的代码。
|
||||
*
|
||||
@@ -39,23 +41,23 @@ public class DictCacheSyncAspect {
|
||||
Object arg = joinPoint.getArgs()[0];
|
||||
if ("saveNew".equals(methodName)) {
|
||||
Object data = joinPoint.proceed();
|
||||
BaseDictService<Object, Object> service =
|
||||
(BaseDictService<Object, Object>) joinPoint.getTarget();
|
||||
BaseDictService<Object, Serializable> service =
|
||||
(BaseDictService<Object, Serializable>) joinPoint.getTarget();
|
||||
// 这里参数必须使用saveNew方法的返回对象,因为里面包含实际主键值。
|
||||
service.putDictionaryCache(data);
|
||||
return data;
|
||||
} else if ("update".equals(methodName)) {
|
||||
Object data = joinPoint.proceed();
|
||||
BaseDictService<Object, Object> service =
|
||||
(BaseDictService<Object, Object>) joinPoint.getTarget();
|
||||
BaseDictService<Object, Serializable> service =
|
||||
(BaseDictService<Object, Serializable>) joinPoint.getTarget();
|
||||
// update的方法返回的是boolean,因此这里的参数需要使用第一个参数即可。
|
||||
service.putDictionaryCache(arg);
|
||||
return data;
|
||||
} else {
|
||||
// remove
|
||||
BaseDictService<Object, Object> service =
|
||||
(BaseDictService<Object, Object>) joinPoint.getTarget();
|
||||
service.removeDictionaryCache(arg);
|
||||
BaseDictService<Object, Serializable> service =
|
||||
(BaseDictService<Object, Serializable>) joinPoint.getTarget();
|
||||
service.removeDictionaryCache((Serializable) arg);
|
||||
return joinPoint.proceed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.orange.demo.common.core.base.dao;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import tk.mybatis.mapper.additional.insert.InsertListMapper;
|
||||
import tk.mybatis.mapper.annotation.RegisterMapper;
|
||||
import tk.mybatis.mapper.common.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -16,8 +14,7 @@ import java.util.Map;
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@RegisterMapper
|
||||
public interface BaseDaoMapper<M> extends Mapper<M>, InsertListMapper<M> {
|
||||
public interface BaseDaoMapper<M> extends BaseMapper<M> {
|
||||
|
||||
/**
|
||||
* 根据指定的表名、显示字段列表、过滤条件字符串和分组字段,返回聚合计算后的查询结果。
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.orange.demo.common.core.base.service;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.orange.demo.common.core.constant.GlobalDeletedFlag;
|
||||
import com.orange.demo.common.core.exception.MyRuntimeException;
|
||||
import com.orange.demo.common.core.cache.DictionaryCache;
|
||||
@@ -8,8 +9,8 @@ import com.orange.demo.common.core.object.TokenData;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tk.mybatis.mapper.entity.Example;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -22,7 +23,7 @@ import java.util.*;
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseDictService<M, K> extends BaseService<M, K> implements IBaseDictService<M, K> {
|
||||
public abstract class BaseDictService<M, K extends Serializable> extends BaseService<M, K> implements IBaseDictService<M, K> {
|
||||
|
||||
/**
|
||||
* 缓存池对象。
|
||||
@@ -89,15 +90,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
|
||||
if (tenantIdField != null) {
|
||||
ReflectUtil.setFieldValue(data, tenantIdField, TokenData.takeFromRequest().getTenantId());
|
||||
}
|
||||
if (deletedFlagFieldName != null) {
|
||||
try {
|
||||
setDeletedFlagMethod.invoke(data, GlobalDeletedFlag.NORMAL);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to call reflection [setDeletedFlagMethod] in BaseDictService.update.", e);
|
||||
throw new MyRuntimeException(e);
|
||||
}
|
||||
}
|
||||
return mapper().updateByPrimaryKey(data) == 1;
|
||||
return mapper().updateById(data) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +102,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean remove(K id) {
|
||||
return this.removeById(id);
|
||||
return mapper().deleteById(id) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,15 +111,16 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
|
||||
* @param id 主键Id。
|
||||
* @return 主键关联的数据,不存在返回null。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public M getById(K id) {
|
||||
M data = dictionaryCache.get(id);
|
||||
public M getById(Serializable id) {
|
||||
M data = dictionaryCache.get((K) id);
|
||||
if (data != null) {
|
||||
return data;
|
||||
}
|
||||
data = super.getById(id);
|
||||
if (data != null) {
|
||||
this.dictionaryCache.put(id, data);
|
||||
this.dictionaryCache.put((K) id, data);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
@@ -188,7 +182,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回符合 inFilterField in (inFilterValues) 条件的所有数据。蜀国property是主键,则从缓存中读取。
|
||||
* 返回符合 inFilterField in (inFilterValues) 条件的所有数据。属性property是主键,则从缓存中读取。
|
||||
*
|
||||
* @param inFilterField 参与(In-list)过滤的Java字段。
|
||||
* @param inFilterValues 参与(In-list)过滤的Java字段值集合。
|
||||
@@ -200,7 +194,7 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
|
||||
if (inFilterField.equals(this.idFieldName)) {
|
||||
return this.getInList((Set<K>) inFilterValues);
|
||||
}
|
||||
return this.getInList(inFilterField, inFilterValues);
|
||||
return super.getInList(inFilterField, inFilterValues);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,8 +214,10 @@ public abstract class BaseDictService<M, K> extends BaseService<M, K> implements
|
||||
List<M> dataList = this.getInList((Set<K>) inFilterValues);
|
||||
return dataList.size() == inFilterValues.size();
|
||||
}
|
||||
Example e = this.makeDefaultInListExample(inFilterField, inFilterValues, null);
|
||||
return mapper().selectCountByExample(e) == inFilterValues.size();
|
||||
String columnName = this.safeMapToColumnName(inFilterField);
|
||||
QueryWrapper<M> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.in(columnName, inFilterValues);
|
||||
return mapper().selectCount(queryWrapper) == inFilterValues.size();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package com.orange.demo.common.core.base.service;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.orange.demo.common.core.annotation.*;
|
||||
import com.orange.demo.common.core.base.dao.BaseDaoMapper;
|
||||
import com.orange.demo.common.core.constant.AggregationType;
|
||||
import com.orange.demo.common.core.constant.GlobalDeletedFlag;
|
||||
import com.orange.demo.common.core.exception.InvalidDataFieldException;
|
||||
import com.orange.demo.common.core.exception.MyRuntimeException;
|
||||
import com.orange.demo.common.core.object.*;
|
||||
import com.orange.demo.common.core.util.AopTargetUtil;
|
||||
@@ -14,13 +20,9 @@ import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import tk.mybatis.mapper.entity.Example;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
@@ -39,7 +41,7 @@ import static java.util.stream.Collectors.*;
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
public abstract class BaseService<M, K extends Serializable> extends ServiceImpl<BaseDaoMapper<M>, M> implements IBaseService<M, K> {
|
||||
/**
|
||||
* 当前Service关联的主Model实体对象的Class。
|
||||
*/
|
||||
@@ -133,6 +135,11 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
private static final String AGGREGATED_VALUE = "aggregatedValue";
|
||||
private static final String AND_OP = " AND ";
|
||||
|
||||
@Override
|
||||
public BaseDaoMapper<M> getBaseMapper() {
|
||||
return mapper();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造函数,在实例化的时候,一次性完成所有有关主Model对象信息的加载。
|
||||
*/
|
||||
@@ -140,7 +147,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
public BaseService() {
|
||||
modelClass = (Class<M>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
idFieldClass = (Class<K>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
|
||||
this.tableName = modelClass.getAnnotation(Table.class).name();
|
||||
this.tableName = modelClass.getAnnotation(TableName.class).value();
|
||||
Field[] fields = ReflectUtil.getFields(modelClass);
|
||||
for (Field field : fields) {
|
||||
initializeField(field);
|
||||
@@ -148,10 +155,10 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
}
|
||||
|
||||
private void initializeField(Field field) {
|
||||
if (idFieldName == null && null != field.getAnnotation(Id.class)) {
|
||||
if (idFieldName == null && null != field.getAnnotation(TableId.class)) {
|
||||
idFieldName = field.getName();
|
||||
Column c = field.getAnnotation(Column.class);
|
||||
idColumnName = c == null ? idFieldName : c.name();
|
||||
TableId c = field.getAnnotation(TableId.class);
|
||||
idColumnName = c == null ? idFieldName : c.value();
|
||||
setIdFieldMethod = ReflectUtil.getMethod(
|
||||
modelClass, "set" + StringUtils.capitalize(idFieldName), idFieldClass);
|
||||
getIdFieldMethod = ReflectUtil.getMethod(
|
||||
@@ -159,21 +166,18 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
}
|
||||
if (updateTimeFieldName == null && null != field.getAnnotation(JobUpdateTimeColumn.class)) {
|
||||
updateTimeFieldName = field.getName();
|
||||
Column c = field.getAnnotation(Column.class);
|
||||
updateTimeColumnName = c == null ? updateTimeFieldName : c.name();
|
||||
updateTimeColumnName = this.safeMapToColumnName(updateTimeFieldName);
|
||||
}
|
||||
if (deletedFlagFieldName == null && null != field.getAnnotation(DeletedFlagColumn.class)) {
|
||||
if (deletedFlagFieldName == null && null != field.getAnnotation(TableLogic.class)) {
|
||||
deletedFlagFieldName = field.getName();
|
||||
Column c = field.getAnnotation(Column.class);
|
||||
deletedFlagColumnName = c == null ? deletedFlagFieldName : c.name();
|
||||
deletedFlagColumnName = this.safeMapToColumnName(deletedFlagFieldName);
|
||||
setDeletedFlagMethod = ReflectUtil.getMethod(
|
||||
modelClass, "set" + StringUtils.capitalize(deletedFlagFieldName), Integer.class);
|
||||
}
|
||||
if (tenantIdFieldName == null && null != field.getAnnotation(TenantFilterColumn.class)) {
|
||||
tenantIdField = field;
|
||||
tenantIdFieldName = field.getName();
|
||||
Column c = field.getAnnotation(Column.class);
|
||||
tenantIdColumnName = c == null ? tenantIdFieldName : c.name();
|
||||
tenantIdColumnName = this.safeMapToColumnName(tenantIdFieldName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,28 +189,15 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
protected abstract BaseDaoMapper<M> mapper();
|
||||
|
||||
/**
|
||||
* 基于主键Id删除数据。如果包含逻辑删除字段,则进行逻辑删除。
|
||||
* 根据过滤条件删除数据。
|
||||
*
|
||||
* @param id 主键Id值。
|
||||
* @return true删除成功,false数据不存在。
|
||||
* @param filter 过滤对象。
|
||||
* @return 删除数量。
|
||||
*/
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public boolean removeById(K id) {
|
||||
if (this.deletedFlagFieldName == null) {
|
||||
return mapper().deleteByPrimaryKey(id) == 1;
|
||||
}
|
||||
try {
|
||||
Example e = new Example(modelClass);
|
||||
Example.Criteria c = e.createCriteria().andEqualTo(idFieldName, id);
|
||||
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
|
||||
M data = modelClass.newInstance();
|
||||
setDeletedFlagMethod.invoke(data, GlobalDeletedFlag.DELETED);
|
||||
return mapper().updateByExampleSelective(data, e) == 1;
|
||||
} catch (Exception ex) {
|
||||
log.error("Failed to call reflection method in BaseService.removeById.", ex);
|
||||
throw new MyRuntimeException(ex);
|
||||
}
|
||||
public Integer removeBy(M filter) {
|
||||
return mapper().delete(new QueryWrapper<>(filter));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,9 +214,8 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
if (fieldName.equals(this.idFieldName)) {
|
||||
return this.existId((K) fieldValue);
|
||||
}
|
||||
Example e = new Example(modelClass);
|
||||
e.createCriteria().andEqualTo(fieldName, fieldValue);
|
||||
return mapper().selectCountByExample(e) == 1;
|
||||
String columnName = MyModelUtil.mapToColumnName(fieldName, modelClass);
|
||||
return mapper().selectCount(new QueryWrapper<M>().eq(columnName, fieldValue)) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,24 +229,6 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
return getById(id) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主键Id关联的数据。
|
||||
*
|
||||
* @param id 主键Id。
|
||||
* @return 主键关联的数据,不存在返回null。
|
||||
*/
|
||||
@Override
|
||||
public M getById(K id) {
|
||||
if (deletedFlagFieldName == null) {
|
||||
return mapper().selectByPrimaryKey(id);
|
||||
}
|
||||
Example e = new Example(modelClass);
|
||||
e.createCriteria()
|
||||
.andEqualTo(this.idFieldName, id)
|
||||
.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
|
||||
return mapper().selectOneByExample(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回符合 filterField = filterValue 条件的一条数据。
|
||||
*
|
||||
@@ -270,12 +242,9 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
if (filterField.equals(idFieldName)) {
|
||||
return this.getById((K) filterValue);
|
||||
}
|
||||
Example e = new Example(modelClass);
|
||||
Example.Criteria c = e.createCriteria().andEqualTo(filterField, filterValue);
|
||||
if (deletedFlagFieldName != null) {
|
||||
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
|
||||
}
|
||||
return mapper().selectOneByExample(e);
|
||||
String columnName = this.safeMapToColumnName(filterField);
|
||||
QueryWrapper<M> queryWrapper = new QueryWrapper<M>().eq(columnName, filterValue);
|
||||
return mapper().selectOne(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,12 +268,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
*/
|
||||
@Override
|
||||
public List<M> getAllList() {
|
||||
if (deletedFlagFieldName == null) {
|
||||
return mapper().selectAll();
|
||||
}
|
||||
Example e = new Example(modelClass);
|
||||
e.createCriteria().andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
|
||||
return mapper().selectByExample(e);
|
||||
return mapper().selectList(Wrappers.emptyWrapper());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -315,14 +279,11 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
*/
|
||||
@Override
|
||||
public List<M> getAllListByOrder(String... orderByProperties) {
|
||||
Example e = new Example(modelClass);
|
||||
for (String orderByProperty : orderByProperties) {
|
||||
e.orderBy(orderByProperty);
|
||||
String[] columns = new String[orderByProperties.length];
|
||||
for (int i = 0; i < orderByProperties.length; i++) {
|
||||
columns[i] = this.safeMapToColumnName(orderByProperties[i]);
|
||||
}
|
||||
if (deletedFlagFieldName != null) {
|
||||
e.and().andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
|
||||
}
|
||||
return mapper().selectByExample(e);
|
||||
return mapper().selectList(new QueryWrapper<M>().orderByAsc(columns));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,8 +312,8 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
if (CollectionUtils.isEmpty(inFilterValues)) {
|
||||
return true;
|
||||
}
|
||||
Example e = this.makeDefaultInListExample(inFilterField, inFilterValues, null);
|
||||
return mapper().selectCountByExample(e) == inFilterValues.size();
|
||||
String column = this.safeMapToColumnName(inFilterField);
|
||||
return mapper().selectCount(new QueryWrapper<M>().in(column, inFilterValues)) == inFilterValues.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -391,8 +352,12 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
if (CollectionUtils.isEmpty(inFilterValues)) {
|
||||
return new LinkedList<>();
|
||||
}
|
||||
Example e = this.makeDefaultInListExample(inFilterField, inFilterValues, orderBy);
|
||||
return mapper().selectByExample(e);
|
||||
String column = this.safeMapToColumnName(inFilterField);
|
||||
QueryWrapper<M> queryWrapper = new QueryWrapper<M>().in(column, inFilterValues);
|
||||
if (StringUtils.isNotBlank(orderBy)) {
|
||||
queryWrapper.last(orderBy);
|
||||
}
|
||||
return mapper().selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,16 +368,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
*/
|
||||
@Override
|
||||
public int getCountByFilter(M filter) {
|
||||
if (deletedFlagFieldName == null) {
|
||||
return mapper().selectCount(filter);
|
||||
}
|
||||
try {
|
||||
setDeletedFlagMethod.invoke(filter, GlobalDeletedFlag.NORMAL);
|
||||
return mapper().selectCount(filter);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to call reflection [setDeletedFlagMethod] in BaseService.getCountByFilter.", e);
|
||||
throw new MyRuntimeException(e);
|
||||
}
|
||||
return mapper().selectCount(new QueryWrapper<>(filter));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,42 +390,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
*/
|
||||
@Override
|
||||
public List<M> getListByFilter(M filter) {
|
||||
if (filter == null) {
|
||||
return this.getAllList();
|
||||
}
|
||||
if (deletedFlagFieldName == null) {
|
||||
return mapper().select(filter);
|
||||
}
|
||||
try {
|
||||
setDeletedFlagMethod.invoke(filter, GlobalDeletedFlag.NORMAL);
|
||||
return mapper().select(filter);
|
||||
} catch (Exception ex) {
|
||||
log.error("Failed to call reflection code of BaseService.getListByFilter.", ex);
|
||||
throw new MyRuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void assembleCriteriaByFilter(M filter, Field field, Example.Criteria c) {
|
||||
int modifiers = field.getModifiers();
|
||||
// transient类型的字段不能作为查询条件
|
||||
int transientMask = 128;
|
||||
if ((modifiers & transientMask) != 0 || Modifier.isStatic(modifiers)) {
|
||||
return;
|
||||
}
|
||||
if (field.getName().equals(deletedFlagFieldName)) {
|
||||
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
|
||||
} else {
|
||||
ReflectUtil.setAccessible(field);
|
||||
try {
|
||||
Object o = field.get(filter);
|
||||
if (o != null) {
|
||||
c.andEqualTo(field.getName(), field.get(filter));
|
||||
}
|
||||
} catch (IllegalAccessException ex) {
|
||||
log.error("Failed to call reflection code of BaseService.getListByFilter.", ex);
|
||||
throw new MyRuntimeException(ex);
|
||||
}
|
||||
}
|
||||
return mapper().selectList(new QueryWrapper<>(filter));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,17 +402,14 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
*/
|
||||
@Override
|
||||
public List<M> getListByParentId(String parentIdFieldName, K parentId) {
|
||||
Example e = new Example(modelClass);
|
||||
Example.Criteria c = e.createCriteria();
|
||||
QueryWrapper<M> queryWrapper = new QueryWrapper<>();
|
||||
String parentIdColumn = this.safeMapToColumnName(parentIdFieldName);
|
||||
if (parentId != null) {
|
||||
c.andEqualTo(parentIdFieldName, parentId);
|
||||
queryWrapper.eq(parentIdColumn, parentId);
|
||||
} else {
|
||||
c.andIsNull(parentIdFieldName);
|
||||
queryWrapper.isNull(parentIdColumn);
|
||||
}
|
||||
if (deletedFlagFieldName != null) {
|
||||
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
|
||||
}
|
||||
return mapper().selectByExample(e);
|
||||
return mapper().selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -521,32 +439,21 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
*/
|
||||
@Override
|
||||
public List<M> getListByCondition(List<String> selectList, M filter, String whereClause, String orderBy) {
|
||||
Example e = new Example(modelClass);
|
||||
Example.Criteria c = null;
|
||||
QueryWrapper<M> queryWrapper = new QueryWrapper<>(filter);
|
||||
if (CollectionUtils.isNotEmpty(selectList)) {
|
||||
String[] selectFields = new String[selectList.size()];
|
||||
selectList.toArray(selectFields);
|
||||
e.selectProperties(selectFields);
|
||||
}
|
||||
if (StringUtils.isNotBlank(orderBy)) {
|
||||
e.setOrderByClause(orderBy);
|
||||
}
|
||||
if (filter != null) {
|
||||
c = e.createCriteria();
|
||||
Field[] fields = ReflectUtil.getFields(modelClass);
|
||||
for (Field field : fields) {
|
||||
if (field.getAnnotation(Transient.class) == null) {
|
||||
this.assembleCriteriaByFilter(filter, field, c);
|
||||
}
|
||||
String[] columns = new String[selectList.size()];
|
||||
for (int i = 0; i < selectList.size(); i++) {
|
||||
columns[i] = this.safeMapToColumnName(selectList.get(i));
|
||||
}
|
||||
queryWrapper.select(columns);
|
||||
}
|
||||
if (StringUtils.isNotBlank(whereClause)) {
|
||||
if (c == null) {
|
||||
c = e.createCriteria();
|
||||
}
|
||||
c.andCondition(whereClause);
|
||||
queryWrapper.apply(whereClause);
|
||||
}
|
||||
return mapper().selectByExample(e);
|
||||
if (StringUtils.isNotBlank(orderBy)) {
|
||||
queryWrapper.last(" ORDER BY " + orderBy);
|
||||
}
|
||||
return mapper().selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -753,9 +660,10 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
continue;
|
||||
}
|
||||
Object masterIdValue = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
|
||||
Example e = new Example(relationStruct.relationManyToMany.relationModelClass());
|
||||
e.createCriteria().andEqualTo(relationStruct.masterIdField.getName(), masterIdValue);
|
||||
List<?> manyToManyList = relationStruct.manyToManyMapper.selectByExample(e);
|
||||
String masterIdColumn = this.safeMapToColumnName(relationStruct.masterIdField.getName());
|
||||
Map<String, Object> filterMap = new HashMap<>(1);
|
||||
filterMap.put(masterIdColumn, masterIdValue);
|
||||
List<?> manyToManyList = relationStruct.manyToManyMapper.selectByMap(filterMap);
|
||||
ReflectUtil.setFieldValue(dataObject, relationStruct.relationField, manyToManyList);
|
||||
}
|
||||
}
|
||||
@@ -904,7 +812,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
.collect(toSet());
|
||||
// 从主表集合中,抽取主表关联字段的集合,再以in list形式去从表中查询。
|
||||
if (CollectionUtils.isNotEmpty(masterIdSet)) {
|
||||
BaseService<Object, Object> relationService = relationStruct.service;
|
||||
BaseService<Object, Serializable> relationService = relationStruct.service;
|
||||
List<Object> relationList =
|
||||
relationService.getInList(relationStruct.relationOneToOne.slaveIdField(), masterIdSet);
|
||||
MyModelUtil.makeOneToOneRelation(
|
||||
@@ -913,8 +821,8 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
if (withDict && relationStruct.relationOneToOne.loadSlaveDict()
|
||||
&& CollectionUtils.isNotEmpty(relationList)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
BaseService<Object, Object> proxyTarget =
|
||||
(BaseService<Object, Object>) AopTargetUtil.getTarget(relationService);
|
||||
BaseService<Object, Serializable> proxyTarget =
|
||||
(BaseService<Object, Serializable>) AopTargetUtil.getTarget(relationService);
|
||||
// 关联本地字典。
|
||||
proxyTarget.buildDictForDataList(relationList, false, ignoreFields);
|
||||
// 关联常量字典
|
||||
@@ -941,14 +849,14 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
}
|
||||
Object id = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
|
||||
if (id != null) {
|
||||
BaseService<Object, Object> relationService = relationStruct.service;
|
||||
BaseService<Object, Serializable> relationService = relationStruct.service;
|
||||
Object relationObject = relationService.getOne(relationStruct.relationOneToOne.slaveIdField(), id);
|
||||
ReflectUtil.setFieldValue(dataObject, relationStruct.relationField, relationObject);
|
||||
// 仅仅当需要加载从表字典关联时,才去加载。
|
||||
if (withDict && relationStruct.relationOneToOne.loadSlaveDict() && relationObject != null) {
|
||||
@SuppressWarnings("unchecked")
|
||||
BaseService<Object, Object> proxyTarget =
|
||||
(BaseService<Object, Object>) AopTargetUtil.getTarget(relationService);
|
||||
BaseService<Object, Serializable> proxyTarget =
|
||||
(BaseService<Object, Serializable>) AopTargetUtil.getTarget(relationService);
|
||||
// 关联本地字典
|
||||
proxyTarget.buildDictForData(relationObject, false, ignoreFields);
|
||||
// 关联常量字典
|
||||
@@ -978,7 +886,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
.collect(toSet());
|
||||
// 从主表集合中,抽取主表关联字段的集合,再以in list形式去从表中查询。
|
||||
if (CollectionUtils.isNotEmpty(masterIdSet)) {
|
||||
BaseService<Object, Object> relationService = relationStruct.service;
|
||||
BaseService<Object, Serializable> relationService = relationStruct.service;
|
||||
List<Object> relationList =
|
||||
relationService.getInList(relationStruct.relationOneToMany.slaveIdField(), masterIdSet);
|
||||
MyModelUtil.makeOneToManyRelation(
|
||||
@@ -1003,7 +911,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
}
|
||||
Object id = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
|
||||
if (id != null) {
|
||||
BaseService<Object, Object> relationService = relationStruct.service;
|
||||
BaseService<Object, Serializable> relationService = relationStruct.service;
|
||||
Set<Object> masterIdSet = new HashSet<>(1);
|
||||
masterIdSet.add(id);
|
||||
List<Object> relationObject = relationService.getInList(
|
||||
@@ -1289,32 +1197,97 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过(In-list)条件和orderBy条件,构建Example对象,以供后续的查询操作使用。
|
||||
* 因为Mybatis Plus中QueryWrapper的条件方法都要求传入数据表字段名,因此提供该函数将
|
||||
* Java实体对象的字段名转换为数据表字段名,如果不存在会抛出异常。
|
||||
* 另外在MyModelUtil.mapToColumnName有一级缓存,对于查询过的对象字段都会放到缓存中,
|
||||
* 下次映射转换的时候,会直接从缓存获取。
|
||||
*
|
||||
* @param inFilterField 参与(In-list)过滤的Java字段。
|
||||
* @param inFilterValues 参与(In-list)过滤的Java字段值集合。
|
||||
* @param orderBy 排序字段。
|
||||
* @param <T> in 属性字段的类型。
|
||||
* @return 构建后的Example对象。
|
||||
* @param fieldName Java实体对象的字段名。
|
||||
* @return 对应的数据表字段名。
|
||||
*/
|
||||
protected <T> Example makeDefaultInListExample(String inFilterField, Collection<T> inFilterValues, String orderBy) {
|
||||
Set<T> inFilterValueSet;
|
||||
Example e = new Example(modelClass);
|
||||
if (StringUtils.isNotBlank(orderBy)) {
|
||||
e.setOrderByClause(orderBy);
|
||||
protected String safeMapToColumnName(String fieldName) {
|
||||
String columnName = MyModelUtil.mapToColumnName(fieldName, modelClass);
|
||||
if (columnName == null) {
|
||||
throw new InvalidDataFieldException(modelClass.getSimpleName(), fieldName);
|
||||
}
|
||||
if (inFilterValues instanceof Set) {
|
||||
inFilterValueSet = (Set<T>) inFilterValues;
|
||||
} else {
|
||||
inFilterValueSet = new HashSet<>(inFilterValues.size());
|
||||
inFilterValueSet.addAll(inFilterValues);
|
||||
return columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 因为Mybatis Plus在update的时候,不能将实体对象中值为null的字段,更新为null,
|
||||
* 而且忽略更新,在全部更新场景下,这个是非常重要的,所以我们写了这个函数绕开这一问题。
|
||||
* 该函数会遍历实体对象中,所有不包含@Transient注解,没有transient修饰符的字段,如果
|
||||
* 当前对象的该字段值为null,则会调用UpdateWrapper的set方法,将该字段赋值为null。
|
||||
* 相比于其他重载方法,该方法会将参数中的主键id,设置到UpdateWrapper的过滤条件中。
|
||||
*
|
||||
* @param o 实体对象。
|
||||
* @param id 实体对象的主键值。
|
||||
* @return 创建后的UpdateWrapper。
|
||||
*/
|
||||
protected UpdateWrapper<M> createUpdateQueryForNullValue(M o, K id) {
|
||||
UpdateWrapper<M> uw = createUpdateQueryForNullValue(o, modelClass);
|
||||
try {
|
||||
M filter = modelClass.newInstance();
|
||||
this.setIdFieldMethod.invoke(filter, id);
|
||||
uw.setEntity(filter);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to call reflection code of BaseService.createUpdateQueryForNullValue.", e);
|
||||
throw new MyRuntimeException(e);
|
||||
}
|
||||
Example.Criteria c = e.createCriteria();
|
||||
c.andIn(inFilterField, inFilterValueSet);
|
||||
if (deletedFlagFieldName != null) {
|
||||
c.andEqualTo(deletedFlagFieldName, GlobalDeletedFlag.NORMAL);
|
||||
return uw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 因为Mybatis Plus在update的时候,不能将实体对象中值为null的字段,更新为null,
|
||||
* 而且忽略更新,在全部更新场景下,这个是非常重要的,所以我们写了这个函数绕开这一问题。
|
||||
* 该函数会遍历实体对象中,所有不包含@Transient注解,没有transient修饰符的字段,如果
|
||||
* 当前对象的该字段值为null,则会调用UpdateWrapper的set方法,将该字段赋值为null。
|
||||
*
|
||||
* @param o 实体对象。
|
||||
* @return 创建后的UpdateWrapper。
|
||||
*/
|
||||
protected UpdateWrapper<M> createUpdateQueryForNullValue(M o) {
|
||||
return createUpdateQueryForNullValue(o, modelClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* 因为Mybatis Plus在update的时候,不能将实体对象中值为null的字段,更新为null,
|
||||
* 而且忽略更新,在全部更新场景下,这个是非常重要的,所以我们写了这个函数绕开这一问题。
|
||||
* 该函数会遍历实体对象中,所有不包含@Transient注解,没有transient修饰符的字段,如果
|
||||
* 当前对象的该字段值为null,则会调用UpdateWrapper的set方法,将该字段赋值为null。
|
||||
*
|
||||
* @param o 实体对象。
|
||||
* @param clazz 实体对象的class。
|
||||
* @return 创建后的UpdateWrapper。
|
||||
*/
|
||||
public static <T> UpdateWrapper<T> createUpdateQueryForNullValue(T o, Class<T> clazz) {
|
||||
UpdateWrapper<T> uw = new UpdateWrapper<>();
|
||||
Field[] fields = ReflectUtil.getFields(clazz);
|
||||
List<String> nullColumnList = new LinkedList<>();
|
||||
for (Field field : fields) {
|
||||
TableField tableField = field.getAnnotation(TableField.class);
|
||||
if (tableField == null || tableField.exist()) {
|
||||
int modifiers = field.getModifiers();
|
||||
// transient类型的字段不能作为查询条件,静态字段和逻辑删除都不考虑。
|
||||
int transientMask = 128;
|
||||
if ((modifiers & transientMask) == 1
|
||||
|| Modifier.isStatic(modifiers)
|
||||
|| field.getAnnotation(TableLogic.class) != null) {
|
||||
continue;
|
||||
}
|
||||
// 仅当实体对象参数中,当前字段值为null的时候,才会赋值给UpdateWrapper。
|
||||
// 以便在后续的更新中,可以将这些null字段的值设置到数据库表对应的字段中。
|
||||
if (ReflectUtil.getFieldValue(o, field) == null) {
|
||||
nullColumnList.add(MyModelUtil.safeMapToColumnName(field.getName(), clazz));
|
||||
}
|
||||
}
|
||||
}
|
||||
return e;
|
||||
if (CollectionUtils.isNotEmpty(nullColumnList)) {
|
||||
for (String nullColumn : nullColumnList) {
|
||||
uw.set(nullColumn, null);
|
||||
}
|
||||
}
|
||||
return uw;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -1329,7 +1302,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
relationStruct.service = ApplicationContextHolder.getBean(
|
||||
StringUtils.uncapitalize(relationOneToOne.slaveServiceName()));
|
||||
} else {
|
||||
relationStruct.service = (BaseService<Object, Object>)
|
||||
relationStruct.service = (BaseService<Object, Serializable>)
|
||||
ApplicationContextHolder.getBean(relationOneToOne.slaveServiceClass());
|
||||
}
|
||||
relationOneToOneStructList.add(relationStruct);
|
||||
@@ -1345,7 +1318,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
relationStruct.service = ApplicationContextHolder.getBean(
|
||||
StringUtils.uncapitalize(relationOneToMany.slaveServiceName()));
|
||||
} else {
|
||||
relationStruct.service = (BaseService<Object, Object>)
|
||||
relationStruct.service = (BaseService<Object, Serializable>)
|
||||
ApplicationContextHolder.getBean(relationOneToMany.slaveServiceClass());
|
||||
}
|
||||
relationOneToManyStructList.add(relationStruct);
|
||||
@@ -1375,7 +1348,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
relationStruct.service = ApplicationContextHolder.getBean(
|
||||
StringUtils.uncapitalize(relationOneToManyAggregation.slaveServiceName()));
|
||||
} else {
|
||||
relationStruct.service = (BaseService<Object, Object>)
|
||||
relationStruct.service = (BaseService<Object, Serializable>)
|
||||
ApplicationContextHolder.getBean(relationOneToManyAggregation.slaveServiceClass());
|
||||
}
|
||||
relationOneToManyAggrStructList.add(relationStruct);
|
||||
@@ -1391,7 +1364,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
relationStruct.service = ApplicationContextHolder.getBean(
|
||||
StringUtils.uncapitalize(relationManyToManyAggregation.slaveServiceName()));
|
||||
} else {
|
||||
relationStruct.service = (BaseService<Object, Object>)
|
||||
relationStruct.service = (BaseService<Object, Serializable>)
|
||||
ApplicationContextHolder.getBean(relationManyToManyAggregation.slaveServiceClass());
|
||||
}
|
||||
relationManyToManyAggrStructList.add(relationStruct);
|
||||
@@ -1424,7 +1397,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
relationStruct.service = ApplicationContextHolder.getBean(
|
||||
StringUtils.uncapitalize(relationDict.slaveServiceName()));
|
||||
} else {
|
||||
relationStruct.service = (BaseService<Object, Object>)
|
||||
relationStruct.service = (BaseService<Object, Serializable>)
|
||||
ApplicationContextHolder.getBean(relationDict.slaveServiceClass());
|
||||
}
|
||||
relationDictStructList.add(relationStruct);
|
||||
@@ -1621,7 +1594,7 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
|
||||
private Field relationField;
|
||||
private Field masterIdField;
|
||||
private Field equalOneToOneRelationField;
|
||||
private BaseService<Object, Object> service;
|
||||
private BaseService<Object, Serializable> service;
|
||||
private BaseDaoMapper<Object> manyToManyMapper;
|
||||
private Map<Object, String> dictMap;
|
||||
private RelationDict relationDict;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.orange.demo.common.core.base.service;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -10,7 +11,7 @@ import java.util.List;
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
public interface IBaseDictService<M, K> extends IBaseService<M, K> {
|
||||
public interface IBaseDictService<M, K extends Serializable> extends IBaseService<M, K> {
|
||||
|
||||
/**
|
||||
* 重新加载数据库中所有当前表数据到系统内存。
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.orange.demo.common.core.base.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.orange.demo.common.core.object.MyRelationParam;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
@@ -12,15 +14,15 @@ import java.util.*;
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
public interface IBaseService<M, K> {
|
||||
public interface IBaseService<M, K extends Serializable> extends IService<M>{
|
||||
|
||||
/**
|
||||
* 基于主键Id删除数据。如果包含逻辑删除字段,则进行逻辑删除。
|
||||
* 根据过滤条件删除数据。
|
||||
*
|
||||
* @param id 主键Id值。
|
||||
* @return true删除成功,false数据不存在。
|
||||
* @param filter 过滤对象。
|
||||
* @return 删除数量。
|
||||
*/
|
||||
boolean removeById(K id);
|
||||
Integer removeBy(M filter);
|
||||
|
||||
/**
|
||||
* 判断指定字段的数据是否存在,且仅仅存在一条记录。
|
||||
@@ -40,14 +42,6 @@ public interface IBaseService<M, K> {
|
||||
*/
|
||||
boolean existId(K id);
|
||||
|
||||
/**
|
||||
* 获取主键Id关联的数据。
|
||||
*
|
||||
* @param id 主键Id。
|
||||
* @return 主键关联的数据,不存在返回null。
|
||||
*/
|
||||
M getById(K id);
|
||||
|
||||
/**
|
||||
* 返回符合 filterField = filterValue 条件的一条数据。
|
||||
*
|
||||
|
||||
@@ -11,15 +11,20 @@ public class DataSourceContextHolder {
|
||||
private static final ThreadLocal<Integer> CONTEXT_HOLDER = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
* 设置数据源类型
|
||||
* 设置数据源类型。
|
||||
*
|
||||
* @param type 数据源类型
|
||||
* @return 原有数据源类型,如果第一次设置则返回null。
|
||||
*/
|
||||
public static void setDataSourceType(Integer type) {
|
||||
public static Integer setDataSourceType(Integer type) {
|
||||
Integer datasourceType = CONTEXT_HOLDER.get();
|
||||
CONTEXT_HOLDER.set(type);
|
||||
return datasourceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前数据库操作执行线程的数据源类型,同时由动态数据源的路由函数调用。
|
||||
*
|
||||
* @return 数据源类型。
|
||||
*/
|
||||
public static Integer getDataSourceType() {
|
||||
@@ -27,10 +32,16 @@ public class DataSourceContextHolder {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除线程本地变量,以免内存泄漏
|
||||
* 清除线程本地变量,以免内存泄漏。
|
||||
|
||||
* @param originalType 原有的数据源类型,如果该值为null,则情况本地化变量。
|
||||
*/
|
||||
public static void clear() {
|
||||
CONTEXT_HOLDER.remove();
|
||||
public static void unset(Integer originalType) {
|
||||
if (originalType == null) {
|
||||
CONTEXT_HOLDER.remove();
|
||||
} else {
|
||||
CONTEXT_HOLDER.set(originalType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,11 +34,11 @@ public final class AggregationType {
|
||||
|
||||
private static final Map<Object, String> DICT_MAP = new HashMap<>(5);
|
||||
static {
|
||||
DICT_MAP.put(0, "累计总和");
|
||||
DICT_MAP.put(1, "数量总和");
|
||||
DICT_MAP.put(2, "平均值");
|
||||
DICT_MAP.put(3, "最小值");
|
||||
DICT_MAP.put(4, "最大值");
|
||||
DICT_MAP.put(SUM, "累计总和");
|
||||
DICT_MAP.put(COUNT, "数量总和");
|
||||
DICT_MAP.put(AVG, "平均值");
|
||||
DICT_MAP.put(MIN, "最小值");
|
||||
DICT_MAP.put(MAX, "最大值");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,8 +6,10 @@ import com.orange.demo.common.core.exception.InvalidClassFieldException;
|
||||
import com.orange.demo.common.core.exception.InvalidDataFieldException;
|
||||
import com.orange.demo.common.core.exception.InvalidDataModelException;
|
||||
import com.orange.demo.common.core.util.MyModelUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -227,6 +229,8 @@ public class MyOrderParam extends ArrayList<MyOrderParam.OrderInfo> {
|
||||
/**
|
||||
* 排序信息对象。
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public static class OrderInfo {
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -21,7 +22,6 @@ import java.util.List;
|
||||
@Slf4j
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MyWhereCriteria {
|
||||
|
||||
/**
|
||||
@@ -80,11 +80,26 @@ public class MyWhereCriteria {
|
||||
@JSONField(serialize = false)
|
||||
private Class<?> modelClazz;
|
||||
|
||||
/**
|
||||
* 数据库表名。
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* Java属性名称。
|
||||
*/
|
||||
private String fieldName;
|
||||
|
||||
/**
|
||||
* 数据表字段名。
|
||||
*/
|
||||
private String columnName;
|
||||
|
||||
/**
|
||||
* 数据表字段类型。
|
||||
*/
|
||||
private Integer columnType;
|
||||
|
||||
/**
|
||||
* 操作符类型,取值范围见上面的常量值。
|
||||
*/
|
||||
@@ -95,6 +110,13 @@ public class MyWhereCriteria {
|
||||
*/
|
||||
private Object value;
|
||||
|
||||
public MyWhereCriteria(Class<?> modelClazz, String fieldName, Integer operatorType, Object value) {
|
||||
this.modelClazz = modelClazz;
|
||||
this.fieldName = fieldName;
|
||||
this.operatorType = operatorType;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置条件值。
|
||||
*
|
||||
@@ -127,6 +149,29 @@ public class MyWhereCriteria {
|
||||
return doVerify();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置条件值,通过该构造方法设置时,通常是直接将表名、字段名、字段类型等赋值,无需在通过modelClazz进行推演。
|
||||
*
|
||||
* @param tableName 数据表名。
|
||||
* @param columnName 数据字段名。
|
||||
* @param columnType 数据字段类型。
|
||||
* @param operatorType 操作类型。具体值可参考当前对象的静态变量。
|
||||
* @param value 条件过滤值。
|
||||
*/
|
||||
public void setCriteria(
|
||||
String tableName, String columnName, String columnType, Integer operatorType, Object value) {
|
||||
this.tableName = tableName;
|
||||
this.columnName = columnName;
|
||||
this.columnType = MyModelUtil.NUMERIC_FIELD_TYPE;
|
||||
if (String.class.getSimpleName().equals(columnType)) {
|
||||
this.columnType = MyModelUtil.STRING_FIELD_TYPE;
|
||||
} else if (Date.class.getSimpleName().equals(columnType)) {
|
||||
this.columnType = MyModelUtil.DATE_FIELD_TYPE;
|
||||
}
|
||||
this.operatorType = operatorType;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在执行该函数之前,该对象的所有数据均已经赋值完毕。
|
||||
* 该函数主要验证操作符字段和条件值字段对应关系的合法性。
|
||||
@@ -182,13 +227,13 @@ public class MyWhereCriteria {
|
||||
case OPERATOR_NOT_EQUAL:
|
||||
return " != ";
|
||||
case OPERATOR_GE:
|
||||
return " >= ";
|
||||
return " >= ";
|
||||
case OPERATOR_GT:
|
||||
return " > ";
|
||||
return " > ";
|
||||
case OPERATOR_LE:
|
||||
return " <= ";
|
||||
return " <= ";
|
||||
case OPERATOR_LT:
|
||||
return " < ";
|
||||
return " < ";
|
||||
case OPERATOR_LIKE:
|
||||
return " LIKE ";
|
||||
case OPERATOR_NOT_NULL:
|
||||
@@ -222,18 +267,26 @@ public class MyWhereCriteria {
|
||||
* @return 组装后的SQL条件从句。
|
||||
*/
|
||||
public String makeCriteriaString(Class<?> modelClazz) {
|
||||
if (modelClazz == null) {
|
||||
throw new IllegalArgumentException("ModelClazz argument can't be NULL.");
|
||||
String tableName;
|
||||
String columnName;
|
||||
Integer columnType;
|
||||
if (modelClazz != null) {
|
||||
Tuple2<String, Integer> fieldInfo = MyModelUtil.mapToColumnInfo(fieldName, modelClazz);
|
||||
if (fieldInfo == null) {
|
||||
throw new InvalidDataFieldException(modelClazz.getSimpleName(), fieldName);
|
||||
}
|
||||
columnName = fieldInfo.getFirst();
|
||||
columnType = fieldInfo.getSecond();
|
||||
tableName = MyModelUtil.mapToTableName(modelClazz);
|
||||
if (tableName == null) {
|
||||
throw new InvalidDataModelException(modelClazz.getSimpleName());
|
||||
}
|
||||
} else {
|
||||
tableName = this.tableName;
|
||||
columnName = this.columnName;
|
||||
columnType = this.columnType;
|
||||
}
|
||||
Tuple2<String, Integer> fieldInfo = MyModelUtil.mapToColumnInfo(fieldName, modelClazz);
|
||||
if (fieldInfo == null) {
|
||||
throw new InvalidDataFieldException(modelClazz.getSimpleName(), fieldName);
|
||||
}
|
||||
String tableName = MyModelUtil.mapToTableName(modelClazz);
|
||||
if (tableName == null) {
|
||||
throw new InvalidDataModelException(modelClazz.getSimpleName());
|
||||
}
|
||||
return this.buildClauseString(tableName, fieldInfo.getFirst(), fieldInfo.getSecond());
|
||||
return this.buildClauseString(tableName, columnName, columnType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -135,7 +135,7 @@ public class ResponseResult<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数的errorCode和errorMessage创建新的错误应答对象。
|
||||
* 根据参数中出错的ResponseResult,创建新的错误应答对象。
|
||||
*
|
||||
* @param errorCause 导致错误原因的应答对象。
|
||||
* @return 返回创建的ResponseResult实例对象。
|
||||
@@ -144,6 +144,16 @@ public class ResponseResult<T> {
|
||||
return error(errorCause.errorCode, errorCause.getErrorMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据参数中出错的CallResult,创建新的错误应答对象。
|
||||
*
|
||||
* @param errorCause 导致错误原因的应答对象。
|
||||
* @return 返回创建的ResponseResult实例对象。
|
||||
*/
|
||||
public static <T> ResponseResult<T> errorFrom(CallResult errorCause) {
|
||||
return error(ErrorCodeEnum.DATA_VALIDATED_FAILED, errorCause.getErrorMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否成功。
|
||||
*
|
||||
|
||||
@@ -56,6 +56,10 @@ public class TokenData {
|
||||
* 仅当系统支持uaa时可用,否则可以直接忽略该字段。保留该字段是为了保持单体和微服务通用代码部分的兼容性。
|
||||
*/
|
||||
private String uaaAccessToken;
|
||||
/**
|
||||
* 数据库路由键(仅当水平分库时使用)。
|
||||
*/
|
||||
private Integer datasourceRouteKey;
|
||||
/**
|
||||
* 登录IP。
|
||||
*/
|
||||
|
||||
@@ -2,22 +2,19 @@ package com.orange.demo.common.core.util;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.orange.demo.common.core.exception.InvalidDataFieldException;
|
||||
import com.orange.demo.common.core.annotation.*;
|
||||
import com.orange.demo.common.core.exception.MyRuntimeException;
|
||||
import com.orange.demo.common.core.object.TokenData;
|
||||
import com.orange.demo.common.core.object.Tuple2;
|
||||
import com.orange.demo.common.core.upload.UploadStoreInfo;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import tk.mybatis.mapper.entity.Example;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -66,6 +63,24 @@ public class MyModelUtil {
|
||||
*/
|
||||
private static final Map<String, Tuple2<String, Integer>> CACHED_COLUMNINFO_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 将bean的数据列表转换为Map列表。
|
||||
*
|
||||
* @param dataList bean数据列表。
|
||||
* @param <T> bean对象类型。
|
||||
* @return 转换后的Map列表。
|
||||
*/
|
||||
public static <T> List<Map<String, Object>> beanToMapList(List<T> dataList) {
|
||||
if (CollectionUtils.isEmpty(dataList)) {
|
||||
return null;
|
||||
}
|
||||
List<Map<String, Object>> resultList = new LinkedList<>();
|
||||
for (T data : dataList) {
|
||||
resultList.add(BeanUtil.beanToMap(data));
|
||||
}
|
||||
return resultList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝源类型的集合数据到目标类型的集合中,其中源类型和目标类型中的对象字段类型完全相同。
|
||||
* NOTE: 该函数主要应用于框架中,Dto和Model之间的copy,特别针对一对一关联的深度copy。
|
||||
@@ -189,8 +204,20 @@ public class MyModelUtil {
|
||||
if (field == null) {
|
||||
return null;
|
||||
}
|
||||
Column c = field.getAnnotation(Column.class);
|
||||
String columnName = c == null ? fieldName : c.name();
|
||||
TableField c = field.getAnnotation(TableField.class);
|
||||
String columnName = null;
|
||||
if (c == null) {
|
||||
TableId id = field.getAnnotation(TableId.class);
|
||||
if (id != null) {
|
||||
columnName = id.value();
|
||||
}
|
||||
}
|
||||
if (columnName == null) {
|
||||
columnName = c == null ? CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName) : c.value();
|
||||
if (StringUtils.isBlank(columnName)) {
|
||||
columnName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, fieldName);
|
||||
}
|
||||
}
|
||||
// 这里缺省情况下都是按照整型去处理,因为他覆盖太多的类型了。
|
||||
// 如Integer/Long/Double/BigDecimal,可根据实际情况完善和扩充。
|
||||
String typeName = field.getType().getSimpleName();
|
||||
@@ -213,8 +240,8 @@ public class MyModelUtil {
|
||||
* @return Model对象对应的数据表名称。
|
||||
*/
|
||||
public static String mapToTableName(Class<?> modelClazz) {
|
||||
Table t = modelClazz.getAnnotation(Table.class);
|
||||
return t == null ? null : t.name();
|
||||
TableName t = modelClazz.getAnnotation(TableName.class);
|
||||
return t == null ? null : t.value();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,43 +601,6 @@ public class MyModelUtil {
|
||||
return isMap ? BeanUtil.beanToMap(model) : model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换过滤对象到与其等效的Example对象。
|
||||
*
|
||||
* @param filterModel 过滤对象。
|
||||
* @param modelClass 过滤对象的Class对象。
|
||||
* @param <T> 过滤对象类型。
|
||||
* @return 转换后的Example对象。
|
||||
*/
|
||||
public static <T> Example convertFilterModelToExample(T filterModel, Class<T> modelClass) {
|
||||
if (filterModel == null) {
|
||||
return null;
|
||||
}
|
||||
Example e = new Example(modelClass);
|
||||
Example.Criteria c = e.createCriteria();
|
||||
Field[] fields = ReflectUtil.getFields(modelClass);
|
||||
for (Field field : fields) {
|
||||
if (field.getAnnotation(Transient.class) == null) {
|
||||
int modifiers = field.getModifiers();
|
||||
// transient类型的字段不能作为查询条件
|
||||
if ((modifiers & 128) != 0 || Modifier.isStatic(modifiers)) {
|
||||
continue;
|
||||
}
|
||||
ReflectUtil.setAccessible(field);
|
||||
try {
|
||||
Object o = field.get(filterModel);
|
||||
if (o != null) {
|
||||
c.andEqualTo(field.getName(), field.get(filterModel));
|
||||
}
|
||||
} catch (IllegalAccessException ex) {
|
||||
log.error("Failed to call reflection code.", ex);
|
||||
throw new MyRuntimeException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传字段的存储信息。
|
||||
*
|
||||
|
||||
@@ -39,6 +39,9 @@ public class RedissonConfig {
|
||||
@Value("${redis.redisson.timeout}")
|
||||
private Integer timeout;
|
||||
|
||||
@Value("${redis.redisson.password:}")
|
||||
private String password;
|
||||
|
||||
@Value("${redis.redisson.pool.poolSize}")
|
||||
private Integer poolSize;
|
||||
|
||||
@@ -47,10 +50,14 @@ public class RedissonConfig {
|
||||
|
||||
@Bean
|
||||
public RedissonClient redissonClient() {
|
||||
if (StrUtil.isBlank(password)) {
|
||||
password = null;
|
||||
}
|
||||
Config config = new Config();
|
||||
if ("single".equals(mode)) {
|
||||
config.setLockWatchdogTimeout(lockWatchdogTimeout)
|
||||
.useSingleServer()
|
||||
.setPassword(password)
|
||||
.setAddress(address)
|
||||
.setConnectionPoolSize(poolSize)
|
||||
.setConnectionMinimumIdleSize(minIdle)
|
||||
@@ -59,6 +66,7 @@ public class RedissonConfig {
|
||||
String[] clusterAddresses = StrUtil.splitToArray(address, ',');
|
||||
config.setLockWatchdogTimeout(lockWatchdogTimeout)
|
||||
.useClusterServers()
|
||||
.setPassword(password)
|
||||
.addNodeAddress(clusterAddresses)
|
||||
.setConnectTimeout(timeout)
|
||||
.setMasterConnectionPoolSize(poolSize);
|
||||
@@ -66,6 +74,7 @@ public class RedissonConfig {
|
||||
String[] sentinelAddresses = StrUtil.splitToArray(address, ',');
|
||||
config.setLockWatchdogTimeout(lockWatchdogTimeout)
|
||||
.useSentinelServers()
|
||||
.setPassword(password)
|
||||
.setMasterName(masterName)
|
||||
.addSentinelAddress(sentinelAddresses)
|
||||
.setConnectTimeout(timeout)
|
||||
@@ -80,6 +89,7 @@ public class RedissonConfig {
|
||||
ArrayUtil.copy(masterSlaveAddresses, 1, slaveAddresses, 0, slaveAddresses.length);
|
||||
config.setLockWatchdogTimeout(lockWatchdogTimeout)
|
||||
.useMasterSlaveServers()
|
||||
.setPassword(password)
|
||||
.setMasterAddress(masterSlaveAddresses[0])
|
||||
.addSlaveAddress(slaveAddresses)
|
||||
.setConnectTimeout(timeout)
|
||||
|
||||
39
orange-demo-single-service/common/common-swagger/pom.xml
Normal file
39
orange-demo-single-service/common/common-swagger/pom.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>common</artifactId>
|
||||
<groupId>com.orange.demo</groupId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-swagger</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<name>common-swagger</name>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.plugin</groupId>
|
||||
<artifactId>spring-plugin-core</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.plugin</groupId>
|
||||
<artifactId>spring-plugin-metadata</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.orange.demo</groupId>
|
||||
<artifactId>common-core</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.orange.demo.common.swagger.config;
|
||||
|
||||
import com.orange.demo.common.core.annotation.MyRequestBody;
|
||||
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
|
||||
|
||||
/**
|
||||
* 自动加载bean的配置对象。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@EnableSwagger2WebMvc
|
||||
@EnableKnife4j
|
||||
@EnableConfigurationProperties(SwaggerProperties.class)
|
||||
@ConditionalOnProperty(prefix = "swagger", name = "enabled")
|
||||
public class SwaggerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public Docket upmsDocket(SwaggerProperties properties) {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.groupName("1. 用户权限分组接口")
|
||||
.ignoredParameterTypes(MyRequestBody.class)
|
||||
.apiInfo(apiInfo(properties))
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage(properties.getBasePackage() + ".upms.controller"))
|
||||
.paths(PathSelectors.any()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Docket bizDocket(SwaggerProperties properties) {
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.groupName("2. 业务应用分组接口")
|
||||
.ignoredParameterTypes(MyRequestBody.class)
|
||||
.apiInfo(apiInfo(properties))
|
||||
.select()
|
||||
.apis(RequestHandlerSelectors.basePackage(properties.getBasePackage() + ".app.controller"))
|
||||
.paths(PathSelectors.any()).build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo(SwaggerProperties properties) {
|
||||
return new ApiInfoBuilder()
|
||||
.title(properties.getTitle())
|
||||
.description(properties.getDescription())
|
||||
.version(properties.getVersion()).build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.orange.demo.common.swagger.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* 配置参数对象。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("swagger")
|
||||
public class SwaggerProperties {
|
||||
|
||||
/**
|
||||
* 是否开启Swagger。
|
||||
*/
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* Swagger解析的基础包路径。
|
||||
**/
|
||||
private String basePackage = "";
|
||||
|
||||
/**
|
||||
* ApiInfo中的标题。
|
||||
**/
|
||||
private String title = "";
|
||||
|
||||
/**
|
||||
* ApiInfo中的描述信息。
|
||||
**/
|
||||
private String description = "";
|
||||
|
||||
/**
|
||||
* ApiInfo中的版本信息。
|
||||
**/
|
||||
private String version = "";
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.orange.demo.common.swagger.plugin;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.orange.demo.common.core.annotation.MyRequestBody;
|
||||
import com.github.xiaoymin.knife4j.core.conf.Consts;
|
||||
import javassist.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import springfox.documentation.service.ResolvedMethodParameter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通过字节码方式动态创建接口参数封装对象。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@Slf4j
|
||||
public class ByteBuddyUtil {
|
||||
private static final ClassPool CLASS_POOL = ClassPool.getDefault();
|
||||
|
||||
public static Class<?> createDynamicModelClass(String name, List<ResolvedMethodParameter> parameters) {
|
||||
String clazzName = Consts.BASE_PACKAGE_PREFIX + name;
|
||||
try {
|
||||
CtClass tmp = CLASS_POOL.getCtClass(clazzName);
|
||||
if (tmp != null) {
|
||||
tmp.detach();
|
||||
}
|
||||
} catch (NotFoundException e) {
|
||||
// 需要吃掉这个异常。
|
||||
}
|
||||
CtClass ctClass = CLASS_POOL.makeClass(clazzName);
|
||||
try {
|
||||
int fieldCount = 0;
|
||||
for (ResolvedMethodParameter dynamicParameter : parameters) {
|
||||
// 因为在调用这个方法之前,这些参数都包含MyRequestBody注解。
|
||||
MyRequestBody myRequestBody =
|
||||
dynamicParameter.findAnnotation(MyRequestBody.class).orElse(null);
|
||||
Assert.notNull(myRequestBody);
|
||||
String fieldName = dynamicParameter.defaultName().isPresent()
|
||||
? dynamicParameter.defaultName().get() : "parameter";
|
||||
if (StringUtils.isNotBlank(myRequestBody.value())) {
|
||||
fieldName = myRequestBody.value();
|
||||
}
|
||||
ctClass.addField(createField(dynamicParameter, fieldName, ctClass));
|
||||
fieldCount++;
|
||||
}
|
||||
if (fieldCount > 0) {
|
||||
return ctClass.toClass();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static CtField createField(ResolvedMethodParameter parameter, String parameterName, CtClass ctClass)
|
||||
throws NotFoundException, CannotCompileException {
|
||||
CtField field = new CtField(getFieldType(parameter.getParameterType().getErasedType()), parameterName, ctClass);
|
||||
field.setModifiers(Modifier.PUBLIC);
|
||||
return field;
|
||||
}
|
||||
|
||||
private static CtClass getFieldType(Class<?> propetyType) {
|
||||
CtClass fieldType = null;
|
||||
try {
|
||||
if (!propetyType.isAssignableFrom(Void.class)) {
|
||||
fieldType = CLASS_POOL.get(propetyType.getName());
|
||||
} else {
|
||||
fieldType = CLASS_POOL.get(String.class.getName());
|
||||
}
|
||||
} catch (NotFoundException e) {
|
||||
// 抛异常
|
||||
ClassClassPath path = new ClassClassPath(propetyType);
|
||||
CLASS_POOL.insertClassPath(path);
|
||||
try {
|
||||
fieldType = CLASS_POOL.get(propetyType.getName());
|
||||
} catch (NotFoundException e1) {
|
||||
log.error(e1.getMessage(), e1);
|
||||
}
|
||||
}
|
||||
return fieldType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.orange.demo.common.swagger.plugin;
|
||||
|
||||
import com.orange.demo.common.core.annotation.MyRequestBody;
|
||||
import com.fasterxml.classmate.TypeResolver;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import springfox.documentation.service.ResolvedMethodParameter;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.OperationModelsProviderPlugin;
|
||||
import springfox.documentation.spi.service.contexts.RequestMappingContext;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 生成参数包装类的插件。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 200)
|
||||
@ConditionalOnProperty(prefix = "swagger", name = "enabled")
|
||||
public class DynamicBodyModelPlugin implements OperationModelsProviderPlugin {
|
||||
|
||||
private final TypeResolver typeResolver;
|
||||
|
||||
public DynamicBodyModelPlugin(TypeResolver typeResolver) {
|
||||
this.typeResolver = typeResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(RequestMappingContext context) {
|
||||
List<ResolvedMethodParameter> parameterTypes = context.getParameters();
|
||||
if (CollectionUtils.isEmpty(parameterTypes)) {
|
||||
return;
|
||||
}
|
||||
List<ResolvedMethodParameter> bodyParameter = parameterTypes.stream()
|
||||
.filter(p -> p.hasParameterAnnotation(MyRequestBody.class)).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(bodyParameter)) {
|
||||
return;
|
||||
}
|
||||
String groupName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, context.getGroupName());
|
||||
String clazzName = groupName + StringUtils.capitalize(context.getName());
|
||||
Class<?> clazz = ByteBuddyUtil.createDynamicModelClass(clazzName, bodyParameter);
|
||||
if (clazz != null) {
|
||||
context.operationModelsBuilder().addInputParam(typeResolver.resolve(clazz));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(DocumentationType delimiter) {
|
||||
// 支持2.0版本
|
||||
return delimiter == DocumentationType.SWAGGER_2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.orange.demo.common.swagger.plugin;
|
||||
|
||||
import com.orange.demo.common.core.annotation.MyRequestBody;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import springfox.documentation.builders.ParameterBuilder;
|
||||
import springfox.documentation.schema.ModelRef;
|
||||
import springfox.documentation.service.Parameter;
|
||||
import springfox.documentation.service.ResolvedMethodParameter;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.OperationBuilderPlugin;
|
||||
import springfox.documentation.spi.service.contexts.OperationContext;
|
||||
import springfox.documentation.spi.service.contexts.ParameterContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 构建操作接口参数对象的插件。
|
||||
*
|
||||
* @author Jerry
|
||||
* @date 2020-09-24
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 102)
|
||||
@ConditionalOnProperty(prefix = "swagger", name = "enabled")
|
||||
public class DynamicBodyParameterBuilder implements OperationBuilderPlugin {
|
||||
|
||||
@Override
|
||||
public void apply(OperationContext context) {
|
||||
List<ResolvedMethodParameter> methodParameters = context.getParameters();
|
||||
List<Parameter> parameters = new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(methodParameters)) {
|
||||
List<ResolvedMethodParameter> bodyParameter = methodParameters.stream()
|
||||
.filter(p -> p.hasParameterAnnotation(MyRequestBody.class)).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(bodyParameter)) {
|
||||
// 构造model
|
||||
String groupName = CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, context.getGroupName());
|
||||
String clazzName = groupName + StringUtils.capitalize(context.getName());
|
||||
ResolvedMethodParameter methodParameter = bodyParameter.get(0);
|
||||
ParameterContext parameterContext = new ParameterContext(methodParameter,
|
||||
new ParameterBuilder(),
|
||||
context.getDocumentationContext(),
|
||||
context.getGenericsNamingStrategy(),
|
||||
context);
|
||||
Parameter parameter = parameterContext.parameterBuilder()
|
||||
.parameterType("body").modelRef(new ModelRef(clazzName)).name(clazzName).build();
|
||||
parameters.add(parameter);
|
||||
}
|
||||
}
|
||||
context.operationBuilder().parameters(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(DocumentationType delimiter) {
|
||||
return delimiter == DocumentationType.SWAGGER_2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.orange.demo.common.swagger.config.SwaggerAutoConfiguration
|
||||
@@ -15,5 +15,6 @@
|
||||
<module>common-core</module>
|
||||
<module>common-redis</module>
|
||||
<module>common-sequence</module>
|
||||
<module>common-swagger</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
||||
Reference in New Issue
Block a user