docs 补充项目注释
This commit is contained in:
+53
@@ -37,17 +37,36 @@ import java.util.function.Supplier;
|
||||
@Getter
|
||||
public class MPJSqlInjector extends DefaultSqlInjector {
|
||||
|
||||
/**
|
||||
* 原始 SQL 注入器,用于兼容项目自定义注入逻辑。
|
||||
*/
|
||||
private AbstractSqlInjector sqlInjector;
|
||||
|
||||
/**
|
||||
* 构造 MPJ SQL 注入器。
|
||||
*/
|
||||
public MPJSqlInjector() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造带原始注入器的 MPJ SQL 注入器。
|
||||
*
|
||||
* @param sqlInjector 原始 SQL 注入器
|
||||
*/
|
||||
public MPJSqlInjector(ISqlInjector sqlInjector) {
|
||||
if (Objects.nonNull(sqlInjector) && sqlInjector instanceof AbstractSqlInjector) {
|
||||
this.sqlInjector = (AbstractSqlInjector) sqlInjector;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Mapper 可用的注入方法列表。
|
||||
*
|
||||
* @param configuration MyBatis 配置
|
||||
* @param mapperClass Mapper 类型
|
||||
* @param tableInfo 表信息
|
||||
* @return 注入方法列表
|
||||
*/
|
||||
@Override
|
||||
public List<AbstractMethod> getMethodList(Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
|
||||
if (!isJoinMapper(mapperClass)) {
|
||||
@@ -62,6 +81,12 @@ public class MPJSqlInjector extends DefaultSqlInjector {
|
||||
return methodFilter(super.getMethodList(configuration, mapperClass, tableInfo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤并追加 MPJ 需要的 SQL 注入方法。
|
||||
*
|
||||
* @param list 原始注入方法列表
|
||||
* @return 过滤后的注入方法列表
|
||||
*/
|
||||
private List<AbstractMethod> methodFilter(List<AbstractMethod> list) {
|
||||
String packageStr = SelectList.class.getPackage().getName();
|
||||
List<String> methodList = Arrays.asList(
|
||||
@@ -81,6 +106,11 @@ public class MPJSqlInjector extends DefaultSqlInjector {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 MPJ 联表操作注入方法。
|
||||
*
|
||||
* @return 联表操作注入方法列表
|
||||
*/
|
||||
private List<AbstractMethod> getJoinMethod() {
|
||||
List<AbstractMethod> list = new ArrayList<>();
|
||||
if (VersionUtils.compare(VersionUtils.getVersion(), "3.5.0") >= 0) {
|
||||
@@ -103,6 +133,11 @@ public class MPJSqlInjector extends DefaultSqlInjector {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 MPJ 覆盖 MyBatis-Plus 默认 Wrapper 的注入方法。
|
||||
*
|
||||
* @return Wrapper 注入方法列表
|
||||
*/
|
||||
private List<AbstractMethod> getWrapperMethod() {
|
||||
List<AbstractMethod> list = new ArrayList<>();
|
||||
list.add(new com.github.yulichang.method.mp.Delete());
|
||||
@@ -117,6 +152,12 @@ public class MPJSqlInjector extends DefaultSqlInjector {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将新增注入方法追加到原始列表中,已存在同名方法时不重复追加。
|
||||
*
|
||||
* @param source 原始方法列表
|
||||
* @param addList 待追加方法列表
|
||||
*/
|
||||
private void addAll(List<AbstractMethod> source, List<AbstractMethod> addList) {
|
||||
for (AbstractMethod method : addList) {
|
||||
if (source.stream().noneMatch(m -> m.getClass().getSimpleName().equals(method.getClass().getSimpleName()))) {
|
||||
@@ -125,6 +166,12 @@ public class MPJSqlInjector extends DefaultSqlInjector {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 Mapper 注入信息,并为 JoinMapper 注册 MPJ 表映射缓存。
|
||||
*
|
||||
* @param builderAssistant Mapper 构建助手
|
||||
* @param mapperClass Mapper 类型
|
||||
*/
|
||||
@Override
|
||||
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
|
||||
super.inspectInject(builderAssistant, mapperClass);
|
||||
@@ -172,6 +219,12 @@ public class MPJSqlInjector extends DefaultSqlInjector {
|
||||
return target == null ? null : (Class<?>) target.getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 Mapper 是否继承 MPJ JoinMapper。
|
||||
*
|
||||
* @param mapperClass Mapper 类型
|
||||
* @return true 是 JoinMapper false 不是 JoinMapper
|
||||
*/
|
||||
private boolean isJoinMapper(Class<?> mapperClass) {
|
||||
return JoinMapper.class.isAssignableFrom(mapperClass);
|
||||
}
|
||||
|
||||
+17
@@ -15,6 +15,13 @@ import java.lang.reflect.Proxy;
|
||||
*/
|
||||
public class DataPermissionAdvice implements MethodInterceptor {
|
||||
|
||||
/**
|
||||
* 拦截带有数据权限注解的方法调用,设置当前线程的数据权限上下文。
|
||||
*
|
||||
* @param invocation 方法调用上下文
|
||||
* @return 代理方法执行结果
|
||||
* @throws Throwable 代理方法执行异常
|
||||
*/
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
Object target = invocation.getThis();
|
||||
@@ -32,6 +39,10 @@ public class DataPermissionAdvice implements MethodInterceptor {
|
||||
|
||||
/**
|
||||
* 获取数据权限注解
|
||||
*
|
||||
* @param target 目标对象
|
||||
* @param method 当前执行方法
|
||||
* @return 数据权限注解,未配置时返回 null
|
||||
*/
|
||||
private DataPermission getDataPermissionAnnotation(Object target, Method method) {
|
||||
DataPermission dataPermission = method.getAnnotation(DataPermission.class);
|
||||
@@ -48,6 +59,12 @@ public class DataPermissionAdvice implements MethodInterceptor {
|
||||
return targetClass.getAnnotation(DataPermission.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 JDK 动态代理接口上获取数据权限注解。
|
||||
*
|
||||
* @param targetClass 代理类
|
||||
* @return 数据权限注解,未配置时返回 null
|
||||
*/
|
||||
private DataPermission getProxyClassDataPermission(Class<?> targetClass) {
|
||||
for (Class<?> interfaceClass : targetClass.getInterfaces()) {
|
||||
DataPermission dataPermission = interfaceClass.getAnnotation(DataPermission.class);
|
||||
|
||||
+13
@@ -13,6 +13,13 @@ import java.lang.reflect.Proxy;
|
||||
*/
|
||||
public class DataPermissionPointcut extends StaticMethodMatcherPointcut {
|
||||
|
||||
/**
|
||||
* 判断当前方法或目标类型是否命中数据权限切点。
|
||||
*
|
||||
* @param method 当前执行方法
|
||||
* @param targetClass 目标类型
|
||||
* @return true 命中数据权限切点 false 未命中
|
||||
*/
|
||||
@Override
|
||||
public boolean matches(Method method, Class<?> targetClass) {
|
||||
// 优先匹配方法
|
||||
@@ -26,6 +33,12 @@ public class DataPermissionPointcut extends StaticMethodMatcherPointcut {
|
||||
return targetClassRef.isAnnotationPresent(DataPermission.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析真实目标类型,兼容 MyBatis Mapper 的 JDK 动态代理类。
|
||||
*
|
||||
* @param targetClass Spring AOP 传入的目标类型
|
||||
* @return 真实目标类型或可匹配数据权限注解的接口类型
|
||||
*/
|
||||
private Class<?> resolveTargetClass(Class<?> targetClass) {
|
||||
if (!Proxy.isProxyClass(targetClass)) {
|
||||
return targetClass;
|
||||
|
||||
+20
@@ -12,19 +12,39 @@ import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
@SuppressWarnings("all")
|
||||
public class DataPermissionPointcutAdvisor extends AbstractPointcutAdvisor {
|
||||
|
||||
/**
|
||||
* 数据权限通知逻辑。
|
||||
*/
|
||||
private final Advice advice;
|
||||
|
||||
/**
|
||||
* 数据权限切点匹配器。
|
||||
*/
|
||||
private final Pointcut pointcut;
|
||||
|
||||
/**
|
||||
* 构造数据权限切面定义。
|
||||
*/
|
||||
public DataPermissionPointcutAdvisor() {
|
||||
this.advice = new DataPermissionAdvice();
|
||||
this.pointcut = new DataPermissionPointcut();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据权限切点。
|
||||
*
|
||||
* @return 数据权限切点
|
||||
*/
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据权限通知。
|
||||
*
|
||||
* @return 数据权限通知
|
||||
*/
|
||||
@Override
|
||||
public Advice getAdvice() {
|
||||
return this.advice;
|
||||
|
||||
+5
@@ -9,6 +9,8 @@ import java.util.Set;
|
||||
/**
|
||||
* 当前请求的数据权限访问上下文
|
||||
*
|
||||
* @param perms 当前请求接口权限标识集合
|
||||
* @param roleKeys 当前请求角色标识集合
|
||||
* @author Lion Li
|
||||
*/
|
||||
public record DataPermissionAccess(Set<String> perms, Set<String> roleKeys) implements Serializable {
|
||||
@@ -16,6 +18,9 @@ public record DataPermissionAccess(Set<String> perms, Set<String> roleKeys) impl
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 空访问上下文,表示不按接口权限或角色约束数据权限角色。
|
||||
*/
|
||||
public static final DataPermissionAccess EMPTY = new DataPermissionAccess(Set.of(), Set.of());
|
||||
|
||||
/**
|
||||
|
||||
+12
@@ -33,9 +33,21 @@ import java.util.function.Function;
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface BaseMapperPlus<T, V> extends BaseMapper<T> {
|
||||
|
||||
/**
|
||||
* Mapper 日志对象。
|
||||
*/
|
||||
Log log = LogFactory.getLog(BaseMapperPlus.class);
|
||||
|
||||
/**
|
||||
* Mapper 泛型类型缓存,避免重复解析实体与 VO 类型。
|
||||
*/
|
||||
ClassValue<Class<?>[]> TYPE_ARGUMENT_CACHE = new ClassValue<>() {
|
||||
/**
|
||||
* 解析指定 Mapper 类型的实体与 VO 泛型。
|
||||
*
|
||||
* @param type Mapper 类型
|
||||
* @return 泛型类型数组
|
||||
*/
|
||||
@Override
|
||||
protected Class<?>[] computeValue(Class<?> type) {
|
||||
return GenericTypeUtils.resolveTypeArguments(type, BaseMapperPlus.class);
|
||||
|
||||
+11
@@ -40,8 +40,19 @@ public class LambdaCrudChainWrapper<T, V> extends AbstractLambdaWrapper<T, Lambd
|
||||
Update<LambdaCrudChainWrapper<T, V>, SFunction<T, ?>>,
|
||||
LambdaQueryCondition<T, LambdaCrudChainWrapper<T, V>> {
|
||||
|
||||
/**
|
||||
* 当前链式操作绑定的 Mapper。
|
||||
*/
|
||||
private final BaseMapperPlus<T, V> crudMapper;
|
||||
|
||||
/**
|
||||
* 更新 SET 片段集合。
|
||||
*/
|
||||
private final List<String> sqlSet;
|
||||
|
||||
/**
|
||||
* 查询字段 SQL 片段。
|
||||
*/
|
||||
private SharedString sqlSelect = new SharedString();
|
||||
|
||||
/**
|
||||
|
||||
+5
-2
@@ -59,7 +59,10 @@ public class PageQuery implements Serializable {
|
||||
public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* 构建分页对象
|
||||
* 构建分页对象。
|
||||
*
|
||||
* @param <T> 分页记录类型
|
||||
* @return MyBatis-Plus 分页对象
|
||||
*/
|
||||
public <T> Page<T> build() {
|
||||
Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM);
|
||||
@@ -77,7 +80,7 @@ public class PageQuery implements Serializable {
|
||||
|
||||
/**
|
||||
* 构建排序
|
||||
*
|
||||
* <p>
|
||||
* 支持的用法如下:
|
||||
* {isAsc:"asc",orderByColumn:"id"} order by id asc
|
||||
* {isAsc:"asc",orderByColumn:"id,createTime"} order by id asc,create_time asc
|
||||
|
||||
+16
@@ -11,8 +11,16 @@ import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
*/
|
||||
class AggregateLambdaQueryWrapper<T> extends LambdaQueryWrapper<T> {
|
||||
|
||||
/**
|
||||
* 追加后的聚合查询字段 SQL。
|
||||
*/
|
||||
private String aggregateSqlSelect;
|
||||
|
||||
/**
|
||||
* 构造聚合查询包装器。
|
||||
*
|
||||
* @param entityClass 实体类型
|
||||
*/
|
||||
AggregateLambdaQueryWrapper(Class<T> entityClass) {
|
||||
super(entityClass);
|
||||
}
|
||||
@@ -75,6 +83,11 @@ class AggregateLambdaQueryWrapper<T> extends LambdaQueryWrapper<T> {
|
||||
return columnToString(column);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最终查询字段 SQL。
|
||||
*
|
||||
* @return 查询字段 SQL
|
||||
*/
|
||||
@Override
|
||||
public String getSqlSelect() {
|
||||
if (aggregateSqlSelect != null) {
|
||||
@@ -83,6 +96,9 @@ class AggregateLambdaQueryWrapper<T> extends LambdaQueryWrapper<T> {
|
||||
return super.getSqlSelect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空查询条件与聚合查询字段。
|
||||
*/
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
|
||||
+6
@@ -16,8 +16,14 @@ import java.util.regex.Pattern;
|
||||
*/
|
||||
public final class AggregateSelectUtils {
|
||||
|
||||
/**
|
||||
* 查询别名合法性匹配规则。
|
||||
*/
|
||||
private static final Pattern ALIAS_PATTERN = Pattern.compile("[A-Za-z_][A-Za-z0-9_]*");
|
||||
|
||||
/**
|
||||
* 工具类不允许实例化。
|
||||
*/
|
||||
private AggregateSelectUtils() {
|
||||
}
|
||||
|
||||
|
||||
+16
@@ -916,12 +916,28 @@ public final class LambdaJoinQueryBuilder<T> {
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建使用占位参数模式的子查询。
|
||||
*
|
||||
* @param entityClass 子查询实体类型
|
||||
* @param consumer 子查询构造逻辑
|
||||
* @param <Q> 子查询实体类型
|
||||
* @return 子查询构造器
|
||||
*/
|
||||
private <Q> SubQuery<Q> buildPlaceholderSubQuery(Class<Q> entityClass, Consumer<SubQuery<Q>> consumer) {
|
||||
SubQuery<Q> subQuery = SubQuery.ofPlaceholders(entityClass);
|
||||
consumer.accept(subQuery);
|
||||
return subQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析带表别名的数据库列名。
|
||||
*
|
||||
* @param alias 表别名
|
||||
* @param column 字段引用
|
||||
* @param <S> 字段所属实体类型
|
||||
* @return 表别名限定列名
|
||||
*/
|
||||
private <S> String qualifiedColumn(String alias, SFunction<S, ?> column) {
|
||||
return AggregateSelectUtils.checkAlias(alias) + StringPool.DOT + ColumnCache.getMapField(LambdaUtils.getEntityClass(column))
|
||||
.get(LambdaUtils.getName(column)).getColumn();
|
||||
|
||||
+3
@@ -9,6 +9,9 @@ import com.github.yulichang.toolkit.JoinWrappers;
|
||||
*/
|
||||
public final class QueryBuilder {
|
||||
|
||||
/**
|
||||
* 工具入口类不允许实例化。
|
||||
*/
|
||||
private QueryBuilder() {
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -32,8 +32,16 @@ public enum SqlAggregateFunction {
|
||||
*/
|
||||
COUNT("COUNT");
|
||||
|
||||
/**
|
||||
* 聚合函数名称。
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* 构造 SQL 聚合函数。
|
||||
*
|
||||
* @param name 聚合函数名称
|
||||
*/
|
||||
SqlAggregateFunction(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
+93
@@ -51,18 +51,58 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public final class SubQuery<T> {
|
||||
|
||||
/**
|
||||
* 子查询实体类型。
|
||||
*/
|
||||
private final Class<T> entityClass;
|
||||
|
||||
/**
|
||||
* 外层查询传入的 SQL 参数格式化器。
|
||||
*/
|
||||
private final SqlParamFormatter paramFormatter;
|
||||
|
||||
/**
|
||||
* 是否使用 {@code {0}} 形式的占位参数模式。
|
||||
*/
|
||||
private final boolean placeholderParamMode;
|
||||
|
||||
/**
|
||||
* 子查询 SELECT 字段集合。
|
||||
*/
|
||||
private final List<String> selects = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 子查询 WHERE 条件集合。
|
||||
*/
|
||||
private final List<String> conditions = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 占位参数模式下收集的参数值集合。
|
||||
*/
|
||||
private final List<Object> params = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 是否追加逻辑删除条件。
|
||||
*/
|
||||
private boolean withLogicDelete = true;
|
||||
|
||||
/**
|
||||
* 构造子查询。
|
||||
*
|
||||
* @param entityClass 子查询实体类型
|
||||
* @param paramFormatter SQL 参数格式化器
|
||||
*/
|
||||
private SubQuery(Class<T> entityClass, SqlParamFormatter paramFormatter) {
|
||||
this(entityClass, paramFormatter, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造子查询。
|
||||
*
|
||||
* @param entityClass 子查询实体类型
|
||||
* @param paramFormatter SQL 参数格式化器
|
||||
* @param placeholderParamMode 是否使用占位参数模式
|
||||
*/
|
||||
private SubQuery(Class<T> entityClass, SqlParamFormatter paramFormatter, boolean placeholderParamMode) {
|
||||
this.entityClass = entityClass;
|
||||
this.paramFormatter = paramFormatter;
|
||||
@@ -381,16 +421,37 @@ public final class SubQuery<T> {
|
||||
return params.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加聚合查询字段。
|
||||
*
|
||||
* @param function 聚合函数
|
||||
* @param column 聚合字段
|
||||
* @return 当前子查询构造器
|
||||
*/
|
||||
private SubQuery<T> selectAggregate(SqlAggregateFunction function, SFunction<T, ?> column) {
|
||||
selects.add(function.format(columnName(column)));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加普通比较条件。
|
||||
*
|
||||
* @param column 条件字段
|
||||
* @param operator 比较操作符
|
||||
* @param value 条件值
|
||||
* @return 当前子查询构造器
|
||||
*/
|
||||
private SubQuery<T> condition(SFunction<T, ?> column, String operator, Object value) {
|
||||
conditions.add(columnName(column) + StringPool.SPACE + operator + StringPool.SPACE + formatParam(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化 SQL 参数。
|
||||
*
|
||||
* @param value 参数值
|
||||
* @return SQL 参数占位符
|
||||
*/
|
||||
private String formatParam(Object value) {
|
||||
if (placeholderParamMode) {
|
||||
params.add(value);
|
||||
@@ -400,10 +461,20 @@ public final class SubQuery<T> {
|
||||
return paramFormatter.format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子查询实体表名。
|
||||
*
|
||||
* @return 表名
|
||||
*/
|
||||
private String tableName() {
|
||||
return tableInfo().getTableName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建最终 WHERE 条件集合。
|
||||
*
|
||||
* @return WHERE 条件集合
|
||||
*/
|
||||
private List<String> buildWhereConditions() {
|
||||
List<String> whereConditions = new ArrayList<>();
|
||||
String logicDeleteSql = logicDeleteSql();
|
||||
@@ -414,6 +485,11 @@ public final class SubQuery<T> {
|
||||
return whereConditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取逻辑删除 SQL 条件。
|
||||
*
|
||||
* @return 逻辑删除 SQL 条件,禁用时返回空字符串
|
||||
*/
|
||||
private String logicDeleteSql() {
|
||||
if (!withLogicDelete) {
|
||||
return StringPool.EMPTY;
|
||||
@@ -421,12 +497,23 @@ public final class SubQuery<T> {
|
||||
return tableInfo().getLogicDeleteSql(false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子查询实体对应的 MyBatis-Plus 表信息。
|
||||
*
|
||||
* @return 表信息
|
||||
*/
|
||||
private TableInfo tableInfo() {
|
||||
TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
|
||||
Assert.notNull(tableInfo, "无法获取实体表信息: %s", entityClass.getName());
|
||||
return tableInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取外层查询字段的表名限定列名。
|
||||
*
|
||||
* @param column 外层查询字段
|
||||
* @return 表名限定列名
|
||||
*/
|
||||
private String qualifiedColumnName(SFunction<?, ?> column) {
|
||||
Class<?> columnEntityClass = LambdaUtils.extract(column).getInstantiatedClass();
|
||||
TableInfo tableInfo = TableInfoHelper.getTableInfo(columnEntityClass);
|
||||
@@ -434,6 +521,12 @@ public final class SubQuery<T> {
|
||||
return tableInfo.getTableName() + StringPool.DOT + columnName(column);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Lambda 字段引用解析数据库列名。
|
||||
*
|
||||
* @param column 字段引用
|
||||
* @return 数据库列名
|
||||
*/
|
||||
private static String columnName(SFunction<?, ?> column) {
|
||||
LambdaMeta meta = LambdaUtils.extract(column);
|
||||
String fieldName = PropertyNamer.methodToProperty(meta.getImplMethodName());
|
||||
|
||||
+3
@@ -55,6 +55,9 @@ public enum DataScopeType {
|
||||
*/
|
||||
DEPT_AND_CHILD_OR_SELF("6", " #{#deptName} IN ( #{@sdss.getDeptAndChild( #user.deptId )} ) OR #{#userName} = #{#user.userId} ", " 1 = 0 ");
|
||||
|
||||
/**
|
||||
* 数据权限类型编码。
|
||||
*/
|
||||
private final String code;
|
||||
|
||||
/**
|
||||
|
||||
+93
@@ -51,7 +51,12 @@ public class PlusDataPermissionHandler {
|
||||
* spel 解析器
|
||||
*/
|
||||
private final ExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
/**
|
||||
* SpEL 模板解析上下文。
|
||||
*/
|
||||
private final ParserContext parserContext = new TemplateParserContext();
|
||||
|
||||
/**
|
||||
* bean解析器 用于处理 spel 表达式中对 bean 的调用
|
||||
*/
|
||||
@@ -189,6 +194,11 @@ public class PlusDataPermissionHandler {
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前请求已解析的数据权限访问控制对象。
|
||||
*
|
||||
* @return 数据权限访问控制对象
|
||||
*/
|
||||
private DataPermissionAccess currentAccess() {
|
||||
DataPermissionAccess access = DataPermissionHelper.getAccess();
|
||||
if (access != null) {
|
||||
@@ -199,6 +209,13 @@ public class PlusDataPermissionHandler {
|
||||
return resolvedAccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前接口权限约束筛选参与数据权限计算的角色。
|
||||
*
|
||||
* @param user 当前登录用户
|
||||
* @param access 当前接口访问约束
|
||||
* @return 参与数据权限计算的角色集合
|
||||
*/
|
||||
private List<RoleDTO> scopeRoles(LoginUser user, DataPermissionAccess access) {
|
||||
List<RoleDTO> roles = user.getRoles();
|
||||
if (!access.constrained()) {
|
||||
@@ -232,6 +249,11 @@ public class PlusDataPermissionHandler {
|
||||
return new ArrayList<>(roleMap.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从当前请求处理器上解析接口权限和角色约束。
|
||||
*
|
||||
* @return 数据权限访问控制对象
|
||||
*/
|
||||
private DataPermissionAccess resolveAccess() {
|
||||
HttpServletRequest request = ServletUtils.getRequest();
|
||||
if (request == null) {
|
||||
@@ -258,6 +280,14 @@ public class PlusDataPermissionHandler {
|
||||
return new DataPermissionAccess(Set.copyOf(perms), Set.copyOf(roleKeys));
|
||||
}
|
||||
|
||||
/**
|
||||
* 优先从方法、再从类上查找指定注解。
|
||||
*
|
||||
* @param handlerMethod 当前请求处理方法
|
||||
* @param annotationType 注解类型
|
||||
* @param <A> 注解类型
|
||||
* @return 注解对象,未配置时返回 null
|
||||
*/
|
||||
private <A extends Annotation> A findAnnotation(HandlerMethod handlerMethod, Class<A> annotationType) {
|
||||
A annotation = AnnotationUtil.getAnnotation(handlerMethod.getMethod(), annotationType);
|
||||
if (annotation != null) {
|
||||
@@ -266,6 +296,12 @@ public class PlusDataPermissionHandler {
|
||||
return AnnotationUtil.getAnnotation(handlerMethod.getBeanType(), annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将注解中的字符串数组转换为去空后的集合。
|
||||
*
|
||||
* @param values 注解值数组
|
||||
* @return 字符串集合
|
||||
*/
|
||||
private Set<String> toSet(String[] values) {
|
||||
if (values == null || values.length == 0) {
|
||||
return Set.of();
|
||||
@@ -299,8 +335,17 @@ public class PlusDataPermissionHandler {
|
||||
@AllArgsConstructor
|
||||
private static class NullSafeStandardEvaluationContext extends StandardEvaluationContext {
|
||||
|
||||
/**
|
||||
* 变量值为空时返回的默认值。
|
||||
*/
|
||||
private final Object defaultValue;
|
||||
|
||||
/**
|
||||
* 查找 SpEL 变量,变量为空时返回默认值。
|
||||
*
|
||||
* @param name 变量名
|
||||
* @return 变量值或默认值
|
||||
*/
|
||||
@Override
|
||||
public Object lookupVariable(String name) {
|
||||
Object obj = super.lookupVariable(name);
|
||||
@@ -319,19 +364,49 @@ public class PlusDataPermissionHandler {
|
||||
@AllArgsConstructor
|
||||
private static class NullSafePropertyAccessor implements PropertyAccessor {
|
||||
|
||||
/**
|
||||
* 原始属性访问器。
|
||||
*/
|
||||
private final PropertyAccessor delegate;
|
||||
|
||||
/**
|
||||
* 属性值为空时返回的默认值。
|
||||
*/
|
||||
private final Object defaultValue;
|
||||
|
||||
/**
|
||||
* 获取当前访问器支持的目标类型。
|
||||
*
|
||||
* @return 目标类型数组
|
||||
*/
|
||||
@Override
|
||||
public Class<?>[] getSpecificTargetClasses() {
|
||||
return delegate.getSpecificTargetClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定属性是否可读。
|
||||
*
|
||||
* @param context 表达式上下文
|
||||
* @param target 目标对象
|
||||
* @param name 属性名
|
||||
* @return true 可读 false 不可读
|
||||
* @throws AccessException 属性访问异常
|
||||
*/
|
||||
@Override
|
||||
public boolean canRead(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
return delegate.canRead(context, target, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取属性值,属性值为空时返回默认值。
|
||||
*
|
||||
* @param context 表达式上下文
|
||||
* @param target 目标对象
|
||||
* @param name 属性名
|
||||
* @return 属性值
|
||||
* @throws AccessException 属性访问异常
|
||||
*/
|
||||
@Override
|
||||
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
TypedValue value = delegate.read(context, target, name);
|
||||
@@ -342,11 +417,29 @@ public class PlusDataPermissionHandler {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定属性是否可写。
|
||||
*
|
||||
* @param context 表达式上下文
|
||||
* @param target 目标对象
|
||||
* @param name 属性名
|
||||
* @return true 可写 false 不可写
|
||||
* @throws AccessException 属性访问异常
|
||||
*/
|
||||
@Override
|
||||
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException {
|
||||
return delegate.canWrite(context, target, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入属性值。
|
||||
*
|
||||
* @param context 表达式上下文
|
||||
* @param target 目标对象
|
||||
* @param name 属性名
|
||||
* @param newValue 新属性值
|
||||
* @throws AccessException 属性访问异常
|
||||
*/
|
||||
@Override
|
||||
public void write(EvaluationContext context, Object target, String name, Object newValue) throws AccessException {
|
||||
delegate.write(context, target, name, newValue);
|
||||
|
||||
+6
@@ -15,6 +15,12 @@ import org.dromara.common.core.utils.reflect.ReflectUtils;
|
||||
*/
|
||||
public class PlusPostInitTableInfoHandler implements PostInitTableInfoHandler {
|
||||
|
||||
/**
|
||||
* 表信息初始化后统一调整逻辑删除开关。
|
||||
*
|
||||
* @param tableInfo 表信息
|
||||
* @param configuration MyBatis 配置
|
||||
*/
|
||||
@Override
|
||||
public void postTableInfo(TableInfo tableInfo, Configuration configuration) {
|
||||
String flag = SpringUtils.getProperty("mybatis-plus.enableLogicDelete", "true");
|
||||
|
||||
+7
@@ -27,7 +27,14 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class DataBaseHelper {
|
||||
|
||||
/**
|
||||
* 动态数据源路由对象。
|
||||
*/
|
||||
private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);
|
||||
|
||||
/**
|
||||
* 数据源对应数据库类型缓存。
|
||||
*/
|
||||
private static final Map<String, DataBaseType> DB_TYPE_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
|
||||
+10
@@ -22,9 +22,19 @@ import java.util.function.Supplier;
|
||||
@SuppressWarnings("unchecked")
|
||||
public class DataPermissionHelper {
|
||||
|
||||
/**
|
||||
* Sa-Token Storage 中保存数据权限上下文的键。
|
||||
*/
|
||||
private static final String DATA_PERMISSION_KEY = "data:permission";
|
||||
|
||||
/**
|
||||
* 数据权限访问控制对象在上下文中的键。
|
||||
*/
|
||||
private static final String ACCESS_KEY = "data:permission:access";
|
||||
|
||||
/**
|
||||
* 当前线程正在执行的 Mapper 数据权限注解缓存。
|
||||
*/
|
||||
private static final ThreadLocal<DataPermission> PERMISSION_CACHE = new ThreadLocal<>();
|
||||
|
||||
/**
|
||||
|
||||
+14
@@ -17,6 +17,9 @@ import java.util.Deque;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
final class DataPermissionIgnoreContext {
|
||||
|
||||
/**
|
||||
* 数据权限忽略状态栈,用于支持嵌套忽略并恢复进入前状态。
|
||||
*/
|
||||
private static final ThreadLocal<Deque<Boolean>> DATA_PERMISSION_STACK = ThreadLocal.withInitial(ArrayDeque::new);
|
||||
|
||||
/**
|
||||
@@ -53,6 +56,11 @@ final class DataPermissionIgnoreContext {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 MyBatis-Plus 当前线程中的拦截器忽略策略。
|
||||
*
|
||||
* @return 当前忽略策略,未设置时返回 null
|
||||
*/
|
||||
private static IgnoreStrategy getIgnoreStrategy() {
|
||||
Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL"));
|
||||
if (ignoreStrategyLocal instanceof ThreadLocal<?> ignoreStrategyThreadLocal
|
||||
@@ -62,6 +70,12 @@ final class DataPermissionIgnoreContext {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前忽略策略是否只忽略了数据权限插件。
|
||||
*
|
||||
* @param ignoreStrategy 忽略策略
|
||||
* @return true 仅忽略数据权限 false 还忽略了其他插件能力
|
||||
*/
|
||||
private static boolean isOnlyDataPermissionIgnored(IgnoreStrategy ignoreStrategy) {
|
||||
return !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName())
|
||||
&& !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack())
|
||||
|
||||
+3
-1
@@ -35,6 +35,9 @@ import java.util.List;
|
||||
@Slf4j
|
||||
public class PlusDataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor {
|
||||
|
||||
/**
|
||||
* 数据权限 SQL 处理器。
|
||||
*/
|
||||
private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler();
|
||||
|
||||
/**
|
||||
@@ -169,4 +172,3 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto
|
||||
return handler.getSqlSegment(table, where, whereSegment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -14,6 +14,9 @@ import org.dromara.common.core.utils.SpringUtils;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class IdGeneratorUtil {
|
||||
|
||||
/**
|
||||
* MyBatis-Plus 主键生成器。
|
||||
*/
|
||||
private static final IdentifierGenerator GENERATOR = SpringUtils.getBean(IdentifierGenerator.class);
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user