joins = plainSelect.getJoins();
+ if (joins != null && joins.size() > 0) {
+ joins.forEach(j -> {
+ processJoin(j);
+ processFromItem(j.getRightItem());
+ });
+ }
+ }
+
+ /**
+ * 处理where条件内的子查询
+ *
+ * 支持如下: 1. in 2. = 3. > 4. < 5. >= 6. <= 7. <> 8. EXISTS 9. NOT EXISTS
+ *
+ * 前提条件: 1. 子查询必须放在小括号中 2. 子查询一般放在比较操作符的右边
+ * @param where where 条件
+ */
+ protected void processWhereSubSelect(Expression where) {
+ if (where == null) {
+ return;
+ }
+ if (where instanceof FromItem) {
+ processFromItem((FromItem) where);
+ return;
+ }
+ if (where.toString().indexOf("SELECT") > 0) {
+ // 有子查询
+ if (where instanceof BinaryExpression) {
+ // 比较符号 , and , or , 等等
+ BinaryExpression expression = (BinaryExpression) where;
+ processWhereSubSelect(expression.getLeftExpression());
+ processWhereSubSelect(expression.getRightExpression());
+ }
+ else if (where instanceof InExpression) {
+ // in
+ InExpression expression = (InExpression) where;
+ ItemsList itemsList = expression.getRightItemsList();
+ if (itemsList instanceof SubSelect) {
+ processSelectBody(((SubSelect) itemsList).getSelectBody());
+ }
+ }
+ else if (where instanceof ExistsExpression) {
+ // exists
+ ExistsExpression expression = (ExistsExpression) where;
+ processWhereSubSelect(expression.getRightExpression());
+ }
+ else if (where instanceof NotExpression) {
+ // not exists
+ NotExpression expression = (NotExpression) where;
+ processWhereSubSelect(expression.getExpression());
+ }
+ else if (where instanceof Parenthesis) {
+ Parenthesis expression = (Parenthesis) where;
+ processWhereSubSelect(expression.getExpression());
+ }
+ }
+ }
+
+ /**
+ * 处理子查询等
+ */
+ protected void processFromItem(FromItem fromItem) {
+ if (fromItem instanceof SubJoin) {
+ SubJoin subJoin = (SubJoin) fromItem;
+ if (subJoin.getJoinList() != null) {
+ subJoin.getJoinList().forEach(this::processJoin);
+ }
+ if (subJoin.getLeft() != null) {
+ processFromItem(subJoin.getLeft());
+ }
+ }
+ else if (fromItem instanceof SubSelect) {
+ SubSelect subSelect = (SubSelect) fromItem;
+ if (subSelect.getSelectBody() != null) {
+ processSelectBody(subSelect.getSelectBody());
+ }
+ }
+ else if (fromItem instanceof ValuesList) {
+ log.debug("Perform a subquery, if you do not give us feedback");
+ }
+ else if (fromItem instanceof LateralSubSelect) {
+ LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem;
+ if (lateralSubSelect.getSubSelect() != null) {
+ SubSelect subSelect = lateralSubSelect.getSubSelect();
+ if (subSelect.getSelectBody() != null) {
+ processSelectBody(subSelect.getSelectBody());
+ }
+ }
+ }
+ }
+
+ /**
+ * 处理联接语句
+ */
+ protected void processJoin(Join join) {
+ if (join.getRightItem() instanceof Table) {
+ Table fromTable = (Table) join.getRightItem();
+ join.setOnExpression(injectExpression(join.getOnExpression(), fromTable));
+ }
+ }
+
+ /**
+ * 根据 DataScope ,将数据过滤的表达式注入原本的 where/or 条件
+ * @param currentExpression Expression where/or
+ * @param table 表信息
+ * @return 修改后的 where/or 条件
+ */
+ private Expression injectExpression(Expression currentExpression, Table table) {
+ // TODO 重写 dataPermissionHandler
+ // TODO 当用户检索到 dataScope 所属字段时,需要判断该值是否在 scope 中,然后将其合并
+ String tableName = table.getName();
+ Expression dataFilterExpression = dataPermissionHandler.getDataScopes().stream()
+ .filter(x -> x.getTableNames().contains(tableName))
+ .map(x -> x.getExpression(tableName, table.getAlias())).filter(Objects::nonNull)
+ .reduce(AndExpression::new).orElse(null);
+
+ if (currentExpression == null) {
+ return dataFilterExpression;
+ }
+ if (dataFilterExpression == null) {
+ return currentExpression;
+ }
+ if (currentExpression instanceof OrExpression) {
+ return new AndExpression(new Parenthesis(currentExpression), dataFilterExpression);
+ }
+ else {
+ return new AndExpression(currentExpression, dataFilterExpression);
+ }
+ }
+
+ /**
+ * 根据当前表是否有别名,动态对字段名前添加表别名 eg. 表名: table_1 as t 原始字段:column1 返回: t.column1
+ * @param table 表信息
+ * @param column 字段名
+ * @return 原始字段名,或者添加了表别名的字段名
+ */
+ protected Column getAliasColumn(Table table, String column) {
+ StringBuilder columnBuilder = new StringBuilder();
+ if (table.getAlias() != null) {
+ columnBuilder.append(table.getAlias().getName()).append(".");
+ }
+ columnBuilder.append(column);
+ return new Column(columnBuilder.toString());
+ }
+
+}
diff --git a/ballcat-starters/ballcat-spring-boot-starter-datascope/src/main/java/com/hccake/ballcat/common/datascope/util/PluginUtils.java b/ballcat-starters/ballcat-spring-boot-starter-datascope/src/main/java/com/hccake/ballcat/common/datascope/util/PluginUtils.java
new file mode 100644
index 00000000..7bf9871d
--- /dev/null
+++ b/ballcat-starters/ballcat-spring-boot-starter-datascope/src/main/java/com/hccake/ballcat/common/datascope/util/PluginUtils.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2011-2020, baomidou (jobob@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.hccake.ballcat.common.datascope.util;
+
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.executor.parameter.ParameterHandler;
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.SystemMetaObject;
+import org.apache.ibatis.session.Configuration;
+
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 插件工具类
+ *
+ * @author TaoYu , hubin
+ * @since 2017-06-20
+ */
+public abstract class PluginUtils {
+
+ public static final String DELEGATE_BOUNDSQL_SQL = "delegate.boundSql.sql";
+
+ /**
+ * 获得真正的处理对象,可能多层代理.
+ */
+ @SuppressWarnings("unchecked")
+ public static T realTarget(Object target) {
+ if (Proxy.isProxyClass(target.getClass())) {
+ MetaObject metaObject = SystemMetaObject.forObject(target);
+ return realTarget(metaObject.getValue("h.target"));
+ }
+ return (T) target;
+ }
+
+ /**
+ * 给 BoundSql 设置 additionalParameters
+ * @param boundSql BoundSql
+ * @param additionalParameters additionalParameters
+ */
+ public static void setAdditionalParameter(BoundSql boundSql, Map additionalParameters) {
+ additionalParameters.forEach(boundSql::setAdditionalParameter);
+ }
+
+ public static MPBoundSql mpBoundSql(BoundSql boundSql) {
+ return new MPBoundSql(boundSql);
+ }
+
+ public static MPStatementHandler mpStatementHandler(StatementHandler statementHandler) {
+ statementHandler = realTarget(statementHandler);
+ MetaObject object = SystemMetaObject.forObject(statementHandler);
+ return new MPStatementHandler(SystemMetaObject.forObject(object.getValue("delegate")));
+ }
+
+ /**
+ * {@link org.apache.ibatis.executor.statement.BaseStatementHandler}
+ */
+ public static class MPStatementHandler {
+
+ private final MetaObject statementHandler;
+
+ MPStatementHandler(MetaObject statementHandler) {
+ this.statementHandler = statementHandler;
+ }
+
+ public ParameterHandler parameterHandler() {
+ return get("parameterHandler");
+ }
+
+ public MappedStatement mappedStatement() {
+ return get("mappedStatement");
+ }
+
+ public Executor executor() {
+ return get("executor");
+ }
+
+ public MPBoundSql mPBoundSql() {
+ return new MPBoundSql(boundSql());
+ }
+
+ public BoundSql boundSql() {
+ return get("boundSql");
+ }
+
+ public Configuration configuration() {
+ return get("configuration");
+ }
+
+ @SuppressWarnings("unchecked")
+ private T get(String property) {
+ return (T) statementHandler.getValue(property);
+ }
+
+ }
+
+ /**
+ * {@link BoundSql}
+ */
+ public static class MPBoundSql {
+
+ private final MetaObject boundSql;
+
+ private final BoundSql delegate;
+
+ MPBoundSql(BoundSql boundSql) {
+ this.delegate = boundSql;
+ this.boundSql = SystemMetaObject.forObject(boundSql);
+ }
+
+ public String sql() {
+ return delegate.getSql();
+ }
+
+ public void sql(String sql) {
+ boundSql.setValue("sql", sql);
+ }
+
+ public List parameterMappings() {
+ List parameterMappings = delegate.getParameterMappings();
+ return new ArrayList<>(parameterMappings);
+ }
+
+ public void parameterMappings(List parameterMappings) {
+ boundSql.setValue("parameterMappings", Collections.unmodifiableList(parameterMappings));
+ }
+
+ public Object parameterObject() {
+ return get("parameterObject");
+ }
+
+ public Map additionalParameters() {
+ return get("additionalParameters");
+ }
+
+ @SuppressWarnings("unchecked")
+ private T get(String property) {
+ return (T) boundSql.getValue(property);
+ }
+
+ }
+
+}
diff --git a/ballcat-starters/ballcat-spring-boot-starter-datascope/src/test/java/com/hccake/ballcat/common/datascope/test/SqlParseTest.java b/ballcat-starters/ballcat-spring-boot-starter-datascope/src/test/java/com/hccake/ballcat/common/datascope/test/SqlParseTest.java
new file mode 100644
index 00000000..92c88968
--- /dev/null
+++ b/ballcat-starters/ballcat-spring-boot-starter-datascope/src/test/java/com/hccake/ballcat/common/datascope/test/SqlParseTest.java
@@ -0,0 +1,64 @@
+package com.hccake.ballcat.common.datascope.test;
+
+import com.hccake.ballcat.common.datascope.DataScope;
+import com.hccake.ballcat.common.datascope.DataScopeHolder;
+import com.hccake.ballcat.common.datascope.handler.DataPermissionHandler;
+import com.hccake.ballcat.common.datascope.processor.DataScopeSqlProcessor;
+import net.sf.jsqlparser.expression.Alias;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.expression.operators.relational.InExpression;
+import net.sf.jsqlparser.schema.Column;
+import org.junit.Test;
+
+import java.util.*;
+
+/**
+ * @author Hccake 2020/9/28
+ * @version 1.0
+ */
+public class SqlParseTest {
+
+ @Test
+ public void test() {
+ DataScope dataScope = new DataScope() {
+ final String columnId = "order_id";
+
+ @Override
+ public String getResource() {
+ return "order";
+ }
+
+ @Override
+ public Collection getTableNames() {
+ Set tableNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+ tableNames.addAll(Arrays.asList("t_order", "t_order_info"));
+ return tableNames;
+ }
+
+ @Override
+ public Expression getExpression(String tableName, Alias tableAlias) {
+ Column column = new Column(tableAlias == null ? columnId : tableAlias.getName() + "." + columnId);
+ ExpressionList expressionList = new ExpressionList();
+ expressionList.setExpressions(Arrays.asList(new StringValue("1"), new StringValue("2")));
+ return new InExpression(column, expressionList);
+ }
+ };
+
+ DataPermissionHandler dataPermissionHandler = new DataPermissionHandler();
+ List list = new ArrayList<>();
+ list.add(dataScope);
+ dataPermissionHandler.setDataScopes(list);
+
+ DataScopeSqlProcessor dataScopeSqlProcessor = new DataScopeSqlProcessor(dataPermissionHandler);
+
+ DataScopeHolder.putDataScope("order", dataScope);
+ String sql = "select o.order_id,o.order_name,oi.order_price "
+ + "from t_ORDER o left join t_order_info oi on o.order_id = oi.order_id "
+ + "where oi.order_price > 100";
+
+ dataScopeSqlProcessor.parserSingle(sql, null);
+ }
+
+}
diff --git a/ballcat-starters/pom.xml b/ballcat-starters/pom.xml
index 6d437e77..2913f846 100644
--- a/ballcat-starters/pom.xml
+++ b/ballcat-starters/pom.xml
@@ -13,15 +13,16 @@
pom
- ballcat-spring-boot-starter-log
- ballcat-spring-boot-starter-job
- ballcat-spring-boot-starter-swagger
- ballcat-spring-boot-starter-storage
- ballcat-spring-boot-starter-mail
- ballcat-spring-boot-starter-easyexcel
- ballcat-spring-boot-starter-redis
+ ballcat-spring-boot-starter-datascope
ballcat-spring-boot-starter-dingtalk
- ballcat-spring-boot-starter-kafka
+ ballcat-spring-boot-starter-easyexcel
+ ballcat-spring-boot-starter-job
+ ballcat-spring-boot-starter-kafka
+ ballcat-spring-boot-starter-log
+ ballcat-spring-boot-starter-mail
+ ballcat-spring-boot-starter-redis
+ ballcat-spring-boot-starter-storage
+ ballcat-spring-boot-starter-swagger
\ No newline at end of file