|
@@ -0,0 +1,127 @@
|
|
|
+package com.huashe.park.infrastructure.cfg.mybatis;
|
|
|
+
|
|
|
+import java.lang.reflect.Method;
|
|
|
+import java.sql.Connection;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Properties;
|
|
|
+
|
|
|
+import com.huashe.common.annotation.mybatis.Tenant;
|
|
|
+import org.apache.commons.lang3.ObjectUtils;
|
|
|
+import org.apache.ibatis.executor.statement.StatementHandler;
|
|
|
+import org.apache.ibatis.mapping.MappedStatement;
|
|
|
+import org.apache.ibatis.plugin.Interceptor;
|
|
|
+import org.apache.ibatis.plugin.Intercepts;
|
|
|
+import org.apache.ibatis.plugin.Invocation;
|
|
|
+import org.apache.ibatis.plugin.Plugin;
|
|
|
+import org.apache.ibatis.plugin.Signature;
|
|
|
+import org.apache.ibatis.reflection.MetaObject;
|
|
|
+import org.apache.ibatis.reflection.SystemMetaObject;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import com.huashe.park.common.animations.mybatis.MethodMetadataCache;
|
|
|
+
|
|
|
+import net.sf.jsqlparser.expression.Expression;
|
|
|
+import net.sf.jsqlparser.expression.JdbcParameter;
|
|
|
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
|
|
|
+import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
|
|
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
|
|
+import net.sf.jsqlparser.schema.Column;
|
|
|
+import net.sf.jsqlparser.statement.Statement;
|
|
|
+import net.sf.jsqlparser.statement.select.PlainSelect;
|
|
|
+import net.sf.jsqlparser.statement.select.Select;
|
|
|
+import net.sf.jsqlparser.statement.select.SelectBody;
|
|
|
+
|
|
|
+@Intercepts({
|
|
|
+ @Signature(type = StatementHandler.class, method = "prepare", args = {
|
|
|
+ Connection.class, Integer.class
|
|
|
+ })
|
|
|
+})
|
|
|
+@Component
|
|
|
+public class TenantSqlInterceptor extends BaseInterceptor implements Interceptor {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object intercept(Invocation invocation) throws Throwable {
|
|
|
+ StatementHandler handler = (StatementHandler) invocation.getTarget();
|
|
|
+ MetaObject metaObject = SystemMetaObject.forObject(handler);
|
|
|
+
|
|
|
+ // 获取原始 SQL 和 MappedStatement
|
|
|
+ String originalSql = (String) metaObject.getValue("delegate.boundSql.sql");
|
|
|
+ MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
|
|
|
+ Method mapperMethod = getMapperMethod(mappedStatement);
|
|
|
+ // 判断是否需要添加租户条件
|
|
|
+ if (ObjectUtils.isNotEmpty(mapperMethod)) {
|
|
|
+
|
|
|
+ // 解析 SQL 语法树
|
|
|
+ Statement stmt = CCJSqlParserUtil.parse(originalSql);
|
|
|
+ if (stmt instanceof Select) {
|
|
|
+ Select select = (Select) stmt;
|
|
|
+ SelectBody selectBody = select.getSelectBody();
|
|
|
+ if (selectBody instanceof PlainSelect) {
|
|
|
+ PlainSelect plainSelect = (PlainSelect) selectBody;
|
|
|
+ // 构建租户条件表达式(tenant_id = ?)
|
|
|
+ EqualsTo tenantCondition = new EqualsTo();
|
|
|
+ tenantCondition.setLeftExpression(handleColumn(mapperMethod));
|
|
|
+ tenantCondition.setRightExpression(new JdbcParameter());
|
|
|
+
|
|
|
+ // 修改 WHERE 条件
|
|
|
+ Expression where = plainSelect.getWhere();
|
|
|
+ if (where == null) {
|
|
|
+ // 没有 WHERE 子句时直接添加
|
|
|
+ plainSelect.setWhere(tenantCondition);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // 已有 WHERE 子句时用 AND 连接
|
|
|
+ AndExpression and = new AndExpression(where, tenantCondition);
|
|
|
+ plainSelect.setWhere(and);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成修改后的 SQL
|
|
|
+ String modifiedSql = plainSelect.toString();
|
|
|
+ metaObject.setValue("delegate.boundSql.sql", modifiedSql);
|
|
|
+
|
|
|
+ // 添加租户参数(动态绑定到 PreparedStatement)
|
|
|
+ List<Object> parameters = (List<Object>) metaObject.getValue("delegate.boundSql.parameterObject");
|
|
|
+
|
|
|
+ parameters.add(getLoginUser().getUser().getTenantId()); // 租户值
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return invocation.proceed();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Method getMapperMethod(MappedStatement mappedStatement) {
|
|
|
+ String methodId = mappedStatement.getId();
|
|
|
+ int lastDotIndex = methodId.lastIndexOf(".");
|
|
|
+ String methodName = methodId.substring(lastDotIndex + 1);
|
|
|
+ List<Method> annotatedMethods = MethodMetadataCache.getAnnotatedMethods(mappedStatement);
|
|
|
+ if (methodName.endsWith("_COUNT")) {
|
|
|
+ methodName = methodName.replaceFirst("_COUNT", "");
|
|
|
+ }
|
|
|
+ String finalMethodName = methodName;
|
|
|
+ return annotatedMethods.stream().filter(method -> method.getName().equals(finalMethodName)).findFirst()
|
|
|
+ .orElse(null);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Column handleColumn(Method mapperMethod) {
|
|
|
+ Tenant annotation = mapperMethod.getAnnotation(Tenant.class);
|
|
|
+ if (ObjectUtils.isNotEmpty(annotation)) {
|
|
|
+ if (ObjectUtils.isNotEmpty(annotation.tableAlias())) {
|
|
|
+ return new Column(String.format("%s.%s", annotation.tableAlias(), annotation.field()));
|
|
|
+ }
|
|
|
+ return new Column(annotation.field());
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ return new Column("tenant_id");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Object plugin(Object target) {
|
|
|
+ return Plugin.wrap(target, this);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void setProperties(Properties properties) {
|
|
|
+ }
|
|
|
+}
|