feat:V3.1.1整合remi-comm到remi-cloud中

This commit is contained in:
dianjiu
2024-09-11 17:41:17 +08:00
parent e111c59a69
commit 28d62f6cf4
245 changed files with 20975 additions and 154 deletions

View File

@@ -0,0 +1,19 @@
package ai.remi.comm.jdbc.config;
import ai.remi.comm.jdbc.handler.CompanyTenantHandler;
import ai.remi.comm.jdbc.handler.DeptTenantHandler;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 自动配置类
*/
@Configuration
@Import({CompanyTenantHandler.class, DeptTenantHandler.class,TenantConfig.class})
public class AutoConfig {
}

View File

@@ -0,0 +1,30 @@
package ai.remi.comm.jdbc.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc DruidConfig 配置类
*/
@Configuration
public class DruidConfig {
/*
将自定义的 Druid数据源添加到容器中,不再让 Spring Boot 自动创建
绑定全局配置文件中的 druid 数据源属性到 com.alibaba.druid.pool.DruidDataSource从而让它们生效
@ConfigurationProperties(prefix = "spring.datasource"):作用就是将 全局配置文件中
前缀为 spring.datasource的属性值注入到 com.alibaba.druid.pool.DruidDataSource 的同名参数中
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druidDataSource() {
return new DruidDataSource();
}
}

View File

@@ -0,0 +1,27 @@
package ai.remi.comm.jdbc.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 字段填充配置类
*/
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "sql-intercept.field-fill")
public class FieldFillConfig {
private InterceptConfig createdByIntercept=new InterceptConfig();
private InterceptConfig updateByIntercept=new InterceptConfig();
private InterceptConfig createAtIntercept=new InterceptConfig();
private InterceptConfig updateAtIntercept=new InterceptConfig();
}

View File

@@ -0,0 +1,35 @@
package ai.remi.comm.jdbc.config;
import ai.remi.comm.jdbc.enums.InterceptTableStrategy;
import lombok.Data;
import java.util.HashSet;
import java.util.Set;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 拦截器配置类
*/
@Data
public class InterceptConfig {
/**
* 拦截表策略,默认拦截不包含这些表
*/
private InterceptTableStrategy interceptTableStrategy = InterceptTableStrategy.EXCLUDE;
/**
* 是否打开拦截配置
*/
private Boolean enable = false;
/**
* 不拦截或拦截的表
*/
private Set<String> tables = new HashSet<>();
/**
* 处理的字段
*/
private String column = "";
}

View File

@@ -0,0 +1,134 @@
package ai.remi.comm.jdbc.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import ai.remi.comm.jdbc.handler.*;
import ai.remi.comm.jdbc.interceptor.FieldFillInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc MybatisPlusConfig 配置类
*/
@Slf4j
@Configuration
@MapperScan(value = {"ai.remi.*.infra.mapper"})
public class MybatisPlusConfig {
@Resource
private TenantConfig tenantConfig;
@Resource
private FieldFillConfig fieldFillConfig;
/**
* 分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 多租户(部门)拦截器
Boolean deptTenantEnable = tenantConfig.getDeptTenant().getEnable();
if (deptTenantEnable){
TenantLineInnerInterceptor tenantSqlParser = new TenantLineInnerInterceptor();
tenantSqlParser.setTenantLineHandler(getDeptTenantInterceptor());
interceptor.addInnerInterceptor(tenantSqlParser);
log.info("----------------部门租户拦截器开启-----------------------");
}
// 多租户(公司)拦截器
Boolean companyTenantEnable = tenantConfig.getCompanyTenant().getEnable();
if (companyTenantEnable){
TenantLineInnerInterceptor dealerSqlParser = new TenantLineInnerInterceptor();
dealerSqlParser.setTenantLineHandler(getCompanyTenantInterceptor());
interceptor.addInnerInterceptor(dealerSqlParser);
log.info("----------------公司租户拦截器开启-----------------------");
}
//创建人填充
Boolean createByEnable = fieldFillConfig.getCreatedByIntercept().getEnable();
if (createByEnable){
FieldFillInterceptor createByInterceptor = new FieldFillInterceptor();
createByInterceptor.setFieldFillHandler(getCreatedByHandler());
interceptor.addInnerInterceptor(createByInterceptor);
log.info("----------------创建人填充器开启-----------------------");
}
//更新人填充
Boolean updateByEnable = fieldFillConfig.getUpdateByIntercept().getEnable();
if (updateByEnable){
FieldFillInterceptor updateByInterceptor = new FieldFillInterceptor();
updateByInterceptor.setFieldFillHandler(getUpdatedByHandler());
interceptor.addInnerInterceptor(updateByInterceptor);
log.info("----------------更新人填充器开启-----------------------");
}
//创建时间
Boolean createAtEnable = fieldFillConfig.getCreateAtIntercept().getEnable();
if (createAtEnable){
FieldFillInterceptor createAtInterceptor = new FieldFillInterceptor();
createAtInterceptor.setFieldFillHandler(getCreatedAtHandler());
interceptor.addInnerInterceptor(createAtInterceptor);
log.info("----------------创建时间填充器开启-----------------------");
}
//更新时间
Boolean updateAtEnable = fieldFillConfig.getUpdateAtIntercept().getEnable();
if (updateAtEnable){
FieldFillInterceptor updateAtInterceptor = new FieldFillInterceptor();
updateAtInterceptor.setFieldFillHandler(getUpdatedAtHandler());
interceptor.addInnerInterceptor(updateAtInterceptor);
log.info("----------------更新时间填充器开启-----------------------");
}
//自带分页拦截器
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.POSTGRE_SQL));
//自带乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
@Bean
public UpdatedByHandler getUpdatedByHandler(){
return new UpdatedByHandler(fieldFillConfig);
}
@Bean
public UpdatedAtHandler getUpdatedAtHandler(){
return new UpdatedAtHandler(fieldFillConfig);
}
@Bean
public CreatedByHandler getCreatedByHandler(){
return new CreatedByHandler(fieldFillConfig);
}
@Bean
public CreatedAtHandler getCreatedAtHandler(){
return new CreatedAtHandler(fieldFillConfig);
}
@Bean
public DeptTenantHandler getDeptTenantInterceptor() {
return new DeptTenantHandler(tenantConfig);
}
@Bean
public CompanyTenantHandler getCompanyTenantInterceptor() {
return new CompanyTenantHandler(tenantConfig);
}
}

View File

@@ -0,0 +1,27 @@
package ai.remi.comm.jdbc.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 租户配置类
*/
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "sql-intercept.tenant")
public class TenantConfig {
//不拦截的请求
private String anonUrlSet="";
private InterceptConfig deptTenant=new InterceptConfig();
private InterceptConfig companyTenant=new InterceptConfig();
}

View File

@@ -0,0 +1,20 @@
package ai.remi.comm.jdbc.enums;
/**
* 数据源
*
* @author DianJiu 【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @date 2022-07-19
*/
public enum DataSourceType {
/**
* 主库
*/
MASTER,
/**
* 从库
*/
SLAVE
}

View File

@@ -0,0 +1,22 @@
package ai.remi.comm.jdbc.enums;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 字段填充策略枚举
*/
public enum FieldFillStrategyEnum {
/**
* 插入时填充字段
*/
INSERT,
/**
* 更新时填充字段
*/
UPDATE,
/**
* 插入和更新时填充字段
*/
INSERT_UPDATE
}

View File

@@ -0,0 +1,11 @@
package ai.remi.comm.jdbc.enums;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 拦截表策略
*/
public enum InterceptTableStrategy {
INCLUDE,
EXCLUDE;
}

View File

@@ -0,0 +1,66 @@
package ai.remi.comm.jdbc.handler;
import ai.remi.comm.jdbc.config.InterceptConfig;
import ai.remi.comm.jdbc.enums.FieldFillStrategyEnum;
import net.sf.jsqlparser.expression.Expression;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 字段填充拦截器抽象类
*/
@SuppressWarnings("checkstyle:Indentation")
public abstract class AbstractFieldFillHandler extends AbstractSqlHandler implements FieldFillHandler {
private FieldFillStrategyEnum fieldFillStrategyEnum;
public AbstractFieldFillHandler(FieldFillStrategyEnum fieldFillStrategyEnum, InterceptConfig interceptConfig) {
super(interceptConfig);
this.fieldFillStrategyEnum = fieldFillStrategyEnum;
}
protected abstract Expression doGetFieldFillValue();
@Override
public Expression getFieldFillValue() {
return doGetFieldFillValue();
}
/**
* 优先级配置填充字段--》子类实现getDefaultFieldFillColumn
* 如果都为指定则抛出异常
*/
@Override
public String getFieldFillColumn() {
return handleColumn(interceptConfig.getColumn());
}
@Override
public boolean createIgnore(String tableName) {
if (FieldFillStrategyEnum.UPDATE==fieldFillStrategyEnum){
return true;
}
return defaultIgnoreStrategy(tableName,interceptConfig);
}
@Override
protected boolean customIgnore(){
return false;
}
@Override
public boolean updateIgnore(String tableName) {
if (FieldFillStrategyEnum.INSERT==fieldFillStrategyEnum){
return true;
}
return defaultIgnoreStrategy(tableName,interceptConfig);
}
}

View File

@@ -0,0 +1,70 @@
package ai.remi.comm.jdbc.handler;
import ai.remi.comm.jdbc.config.InterceptConfig;
import ai.remi.comm.jdbc.enums.InterceptTableStrategy;
import java.util.Set;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc SQL拦截器抽象类
*/
public abstract class AbstractSqlHandler {
protected InterceptConfig interceptConfig;
public AbstractSqlHandler(InterceptConfig interceptConfig) {
this.interceptConfig = interceptConfig;
}
protected boolean defaultIgnoreStrategy(String tableName, InterceptConfig interceptConfig) {
if (customIgnore()) {
return true;
}
InterceptTableStrategy interceptTableStrategy = interceptConfig.getInterceptTableStrategy();
Set<String> tables = interceptConfig.getTables();
if (InterceptTableStrategy.EXCLUDE.equals(interceptTableStrategy)) {
return tables.contains(tableName);
} else if (InterceptTableStrategy.INCLUDE.equals(interceptTableStrategy)) {
return !tables.contains(tableName);
}else {
throw new RuntimeException("未指定表拦截策略");
}
}
/**
* 如果传入的字段名为空串,则获取默认的字段
* @param column
* @return
*/
protected String handleColumn(String column) {
if (isNotBlank(column)){
return column;
}
column = getDefaultColumn();
if (isNotBlank(column)){
return column;
}
throw new RuntimeException(this.getClass()+"未指定填充字段,请检查");
}
public static boolean isNotBlank(String str) {
return str!=null&&str.trim().length() > 0;
}
/**
* 子类实现指定 字段名
* @return
*/
protected String getDefaultColumn(){
return "";
}
/**
* 子类需要实现自定义忽略策略
* @return
*/
protected abstract boolean customIgnore();
}

View File

@@ -0,0 +1,65 @@
package ai.remi.comm.jdbc.handler;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import ai.remi.comm.jdbc.config.InterceptConfig;
import ai.remi.comm.util.auth.TenantHolder;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import org.springframework.beans.factory.annotation.Value;
import java.util.List;
import java.util.Optional;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 租户拦截器抽象类
*/
@Slf4j
public abstract class AbstractTenantHandler extends AbstractSqlHandler implements TenantLineHandler {
@Value("${spring.application.name}")
private String applicationContextName;
private final static List<String> IGNORE_APP_NAME = List.of("remi-ids-web","remi-ams-web","remi-mps-web","remi-sso-web","remi-dfe-web","remi-wfe-web","remi-bie-web","remi-wpe-web","remi-job-web","remi-lce-web");
public AbstractTenantHandler(InterceptConfig interceptConfig) {
super(interceptConfig);
}
protected abstract String doGetTenantId();
@Override
public Expression getTenantId() {
return new StringValue(doGetTenantId());
}
@Override
public String getTenantIdColumn() {
return handleColumn(interceptConfig.getColumn());
}
@Override
public boolean ignoreTable(String tableName) {
return defaultIgnoreStrategy(tableName, interceptConfig);
}
@Override
protected boolean customIgnore(){
// 忽略的服务
if (IGNORE_APP_NAME.contains(applicationContextName)){
return true;
}
Boolean isAdmin=Optional.ofNullable(TenantHolder.isAdmin()).orElse(false);
// 是否是不拦截的请求
Boolean ignoredRequest = Optional.ofNullable(TenantHolder.getIgnoredRequest()).orElse(false);
// 拦截未打开、忽略的请求、ebg账号---》这些情况都忽略掉
return ignoredRequest || isAdmin;
}
}

View File

@@ -0,0 +1,32 @@
package ai.remi.comm.jdbc.handler;
import ai.remi.comm.jdbc.config.TenantConfig;
import ai.remi.comm.util.auth.AuthInfoUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.Optional;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 部门租户ID字段填充拦截器
*/
@Slf4j
public class CompanyTenantHandler extends AbstractTenantHandler {
public CompanyTenantHandler(TenantConfig tenantConfig) {
super(tenantConfig.getCompanyTenant());
}
@Override
protected String doGetTenantId() {
return Optional.ofNullable(AuthInfoUtils.getCompanyTenantId()).orElse("");
}
@Override
protected String getDefaultColumn() {
return "company_tenant_id";
}
}

View File

@@ -0,0 +1,35 @@
package ai.remi.comm.jdbc.handler;
import ai.remi.comm.jdbc.config.FieldFillConfig;
import ai.remi.comm.jdbc.enums.FieldFillStrategyEnum;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.TimestampValue;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 创建时间字段填充拦截器
*/
public class CreatedAtHandler extends AbstractFieldFillHandler {
public CreatedAtHandler(FieldFillConfig fieldFillConfig) {
super(FieldFillStrategyEnum.INSERT, fieldFillConfig.getUpdateAtIntercept());
}
@Override
protected Expression doGetFieldFillValue() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = format.format(new Date());
return new TimestampValue(date);
}
@Override
protected String getDefaultColumn() {
return "created_at";
}
}

View File

@@ -0,0 +1,38 @@
package ai.remi.comm.jdbc.handler;
import ai.remi.comm.jdbc.config.FieldFillConfig;
import ai.remi.comm.jdbc.enums.FieldFillStrategyEnum;
import ai.remi.comm.util.auth.AuthInfo;
import ai.remi.comm.util.auth.AuthInfoUtils;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import java.util.Optional;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 创建人字段填充拦截器
*/
public class CreatedByHandler extends AbstractFieldFillHandler {
public CreatedByHandler(FieldFillConfig fieldFillConfig) {
super(FieldFillStrategyEnum.INSERT, fieldFillConfig.getCreatedByIntercept());
}
@Override
protected Expression doGetFieldFillValue() {
AuthInfo authInfo = AuthInfoUtils.getAuthInfo();
String uniqueId = Optional.ofNullable(authInfo)
.map(AuthInfo::getUniqueId).orElse("系统创建");
return new StringValue(uniqueId);
}
@Override
protected String getDefaultColumn() {
return "created_by";
}
}

View File

@@ -0,0 +1,31 @@
package ai.remi.comm.jdbc.handler;
import ai.remi.comm.jdbc.config.TenantConfig;
import ai.remi.comm.util.auth.AuthInfoUtils;
import lombok.extern.slf4j.Slf4j;
import java.util.Optional;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 部门租户ID字段填充拦截器
*/
@Slf4j
public class DeptTenantHandler extends AbstractTenantHandler {
public DeptTenantHandler(TenantConfig tenantConfig) {
super(tenantConfig.getDeptTenant());
}
@Override
protected String doGetTenantId() {
return Optional.ofNullable(AuthInfoUtils.getDeptTenantId()).orElse("");
}
@Override
protected String getDefaultColumn() {
return "dept_tenant_id";
}
}

View File

@@ -0,0 +1,43 @@
package ai.remi.comm.jdbc.handler;
import net.sf.jsqlparser.expression.Expression;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 字段填充拦截器
*/
public interface FieldFillHandler {
/**
* 获取自动填充值表达式
*/
Expression getFieldFillValue();
/**
* 获取自动填充的字段名
*/
String getFieldFillColumn();
/**
* 在创建的时候根据表名判断是否忽略拼接字段
*
* @param tableName 表名
* @return 是否忽略, true:表示忽略,false:需要解析并拼接
*/
default boolean createIgnore(String tableName){
return false;
}
/**
* 在更新的时候根据表名判断是否忽略拼接字段
*
* @param tableName 表名
* @return 是否忽略, true:表示忽略,false:需要解析并拼接
*/
default boolean updateIgnore(String tableName) {
return false;
}
}

View File

@@ -0,0 +1,70 @@
package ai.remi.comm.jdbc.handler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc Json类型转换器
*/
public class JsonTypeHandler<T> extends BaseTypeHandler<T> {
private static ObjectMapper objectMapper = new ObjectMapper();
private Class<T> type;
public JsonTypeHandler(Class<T> type) {
if (type == null) {
throw new NullPointerException("Type argument cannot be null");
}
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, toJsonString(parameter));
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
return parse(rs.getString(columnName));
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return parse(rs.getString(columnIndex));
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return parse(cs.getString(columnIndex));
}
private String toJsonString(Object parameter) {
try {
return objectMapper.writeValueAsString(parameter);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private T parse(String json) {
try {
return objectMapper.readValue(json, type);
} catch (JsonProcessingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,14 @@
package ai.remi.comm.jdbc.handler;
import java.util.List;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc List类型转换器
*/
public class ListTypeHandler extends JsonTypeHandler<List> {
public ListTypeHandler() {
super(List.class);
}
}

View File

@@ -0,0 +1,14 @@
package ai.remi.comm.jdbc.handler;
import java.util.Map;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc Map类型转换器
*/
public class MapTypeHandler extends JsonTypeHandler<Map> {
public MapTypeHandler() {
super(Map.class);
}
}

View File

@@ -0,0 +1,32 @@
package ai.remi.comm.jdbc.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc MyMetaObjectHandler 配置类
*/
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill...");
this.setFieldValByName("createdAt", LocalDateTime.now(), metaObject);
//this.setFieldValByName("createdBy", AuthInfoUtils.getUniqueId(), metaObject);
this.setFieldValByName("updatedAt", LocalDateTime.now(), metaObject);
//this.setFieldValByName("updatedBy", AuthInfoUtils.getUniqueId(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill...");
this.setFieldValByName("updatedAt", LocalDateTime.now(), metaObject);
}
}

View File

@@ -0,0 +1,35 @@
package ai.remi.comm.jdbc.handler;
import ai.remi.comm.jdbc.config.FieldFillConfig;
import ai.remi.comm.jdbc.enums.FieldFillStrategyEnum;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.TimestampValue;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 更新时间字段填充拦截器
*/
public class UpdatedAtHandler extends AbstractFieldFillHandler {
public UpdatedAtHandler(FieldFillConfig fieldFillConfig) {
super(FieldFillStrategyEnum.INSERT_UPDATE, fieldFillConfig.getUpdateAtIntercept());
}
@Override
protected Expression doGetFieldFillValue() {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = format.format(new Date());
return new TimestampValue(date);
}
@Override
protected String getDefaultColumn() {
return "updated_at";
}
}

View File

@@ -0,0 +1,37 @@
package ai.remi.comm.jdbc.handler;
import ai.remi.comm.jdbc.config.FieldFillConfig;
import ai.remi.comm.jdbc.enums.FieldFillStrategyEnum;
import ai.remi.comm.util.auth.AuthInfo;
import ai.remi.comm.util.auth.AuthInfoUtils;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import java.util.Optional;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 更新人字段填充拦截器
*/
public class UpdatedByHandler extends AbstractFieldFillHandler {
public UpdatedByHandler(FieldFillConfig fieldFillConfig) {
super(FieldFillStrategyEnum.INSERT_UPDATE, fieldFillConfig.getUpdateByIntercept());
}
@Override
protected Expression doGetFieldFillValue() {
AuthInfo authInfo = AuthInfoUtils.getAuthInfo();
String uniqueId = Optional.ofNullable(authInfo)
.map(AuthInfo::getUniqueId).orElse("系统创建");
return new StringValue(uniqueId);
}
@Override
protected String getDefaultColumn() {
return "updated_by";
}
}

View File

@@ -0,0 +1,430 @@
package ai.remi.comm.jdbc.interceptor;
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;
import ai.remi.comm.jdbc.handler.FieldFillHandler;
import lombok.*;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import java.sql.Connection;
import java.util.List;
import java.util.Properties;
/**
* @author dianjiu【公众号 点九开源】
* @email dianjiuxyz@gmail.com
* @desc 字段填充拦截器
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class FieldFillInterceptor extends JsqlParserSupport implements InnerInterceptor {
private FieldFillHandler fieldFillHandler;
/**
* 拦截器处理前
*
* @param sh
* @param connection
* @param transactionTimeout
*/
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
SqlCommandType sct = ms.getSqlCommandType();
if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) {
return;
}
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(parserMulti(mpBs.sql(), null));
}
}
/**
* 查询进程拦截器
*
* @param select
* @param index
* @param sql
* @param obj
*/
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
}
/**
* 删除进程拦截器
*
* @param delete
* @param index
* @param sql
* @param obj
*/
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
}
/**
* 查询属性进程拦截器
*
* @param selectBody
*/
protected void processSelectBody(SelectBody selectBody) {
if (selectBody == null) {
return;
}
if (selectBody instanceof PlainSelect) {
processPlainSelect((PlainSelect) selectBody);
} else if (selectBody instanceof WithItem) {
WithItem withItem = (WithItem) selectBody;
processSelectBody(withItem.getSubSelect().getSelectBody());
} else {
SetOperationList operationList = (SetOperationList) selectBody;
List<SelectBody> selectBodys = operationList.getSelects();
if (CollectionUtils.isNotEmpty(selectBodys)) {
selectBodys.forEach(this::processSelectBody);
}
}
}
/**
* 新增进程拦截器
*
* @param insert
* @param index
* @param sql
* @param obj
*/
@Override
protected void processInsert(Insert insert, int index, String sql, Object obj) {
if (fieldFillHandler.createIgnore(insert.getTable().getName())) {
// 过滤退出执行
return;
}
//获取所有字段
List<Column> columns = insert.getColumns();
if (CollectionUtils.isEmpty(columns)) {
// 针对没有任何列的insert 不处理
return;
}
//获取操作人字段
String FieldFillColumn = fieldFillHandler.getFieldFillColumn();
if (columns.stream().map(Column::getColumnName).anyMatch(i -> i.equals(FieldFillColumn))) {
// 针对已给出操作人列的insert 不处理 防止重复字段
return;
}
columns.add(new Column(fieldFillHandler.getFieldFillColumn()));
//获取重复表达式 列表
List<Expression> duplicateUpdateColumns = insert.getDuplicateUpdateExpressionList();
if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(new StringValue(fieldFillHandler.getFieldFillColumn()));
equalsTo.setRightExpression(fieldFillHandler.getFieldFillValue());
duplicateUpdateColumns.add(equalsTo);
}
Select select = insert.getSelect();
if (select != null) {
this.processInsertSelect(select.getSelectBody());
} else if (insert.getItemsList() != null) {
ItemsList itemsList = insert.getItemsList();
if (itemsList instanceof MultiExpressionList) {
// 支持批量操作
((MultiExpressionList) itemsList).getExprList()
.forEach(el -> el.getExpressions().add(fieldFillHandler.getFieldFillValue()));
} else {
((ExpressionList) itemsList).getExpressions().add(fieldFillHandler.getFieldFillValue());
}
} else {
throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");
}
}
/**
* update 语句处理
*/
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
final Table table = update.getTable();
if (fieldFillHandler.updateIgnore(table.getName())) {
// 过滤退出执行
return;
}
List<Column> columns = update.getColumns();
if (CollectionUtils.isEmpty(columns)) {
// 针对没有任何列的 update 不处理
return;
}
String FieldFillColumn = fieldFillHandler.getFieldFillColumn();
if (columns.stream().map(Column::getColumnName).anyMatch(i -> i.equals(FieldFillColumn))) {
// 针对已给出操作人列的 update 不处理 防止重复字段
return;
}
//这里出问题了,已修复,使用addUpdateSet()实现动态添加更新人和更新时间
//columns.add(new Column(fieldFillHandler.getFieldFillColumn()));
//update.getExpressions().add(fieldFillHandler.getFieldFillValue());
update.addUpdateSet(new Column(fieldFillHandler.getFieldFillColumn()), fieldFillHandler.getFieldFillValue());
}
/**
* 处理 insert into select
* <p>
* 进入这里表示需要 insert 的表启用了操作人插件,则 select 的表都启动了
*
* @param selectBody SelectBody
*/
protected void processInsertSelect(SelectBody selectBody) {
PlainSelect plainSelect = (PlainSelect) selectBody;
FromItem fromItem = plainSelect.getFromItem();
if (fromItem instanceof Table) {
processPlainSelect(plainSelect);
appendSelectItem(plainSelect.getSelectItems());
} else if (fromItem instanceof SubSelect) {
SubSelect subSelect = (SubSelect) fromItem;
appendSelectItem(plainSelect.getSelectItems());
processInsertSelect(subSelect.getSelectBody());
}
}
/**
* 追加 SelectItem
*
* @param selectItems SelectItem
*/
protected void appendSelectItem(List<SelectItem> selectItems) {
if (CollectionUtils.isEmpty(selectItems)) {
return;
}
if (selectItems.size() == 1) {
SelectItem item = selectItems.get(0);
if (item instanceof AllColumns || item instanceof AllTableColumns) {
return;
}
}
selectItems.add(new SelectExpressionItem(new Column(fieldFillHandler.getFieldFillColumn())));
}
/**
* 处理 PlainSelect
*/
protected void processPlainSelect(PlainSelect plainSelect) {
FromItem fromItem = plainSelect.getFromItem();
Expression where = plainSelect.getWhere();
processWhereSubSelect(where);
if (fromItem instanceof Table) {
Table fromTable = (Table) fromItem;
if (!fieldFillHandler.createIgnore(fromTable.getName())) {
plainSelect.setWhere(builderExpression(where, fromTable));
}
} else {
processFromItem(fromItem);
}
List<SelectItem> selectItems = plainSelect.getSelectItems();
if (CollectionUtils.isNotEmpty(selectItems)) {
selectItems.forEach(this::processSelectItem);
}
List<Join> joins = plainSelect.getJoins();
if (CollectionUtils.isNotEmpty(joins)) {
joins.forEach(j -> {
processJoin(j);
processFromItem(j.getRightItem());
});
}
}
/**
* 处理where条件内的子查询
* <p>
* 支持如下:
* 1. in
* 2. =
* 3. >
* 4. <
* 5. >=
* 6. <=
* 7. <>
* 8. EXISTS
* 9. NOT EXISTS
* <p>
* 前提条件:
* 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 processSelectItem(SelectItem selectItem) {
if (selectItem instanceof SelectExpressionItem) {
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem;
if (selectExpressionItem.getExpression() instanceof SubSelect) {
processSelectBody(((SubSelect) selectExpressionItem.getExpression()).getSelectBody());
} else if (selectExpressionItem.getExpression() instanceof Function) {
processFunction((Function) selectExpressionItem.getExpression());
}
}
}
/**
* 处理函数
* <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>
*
* @param function
*/
protected void processFunction(Function function) {
ExpressionList parameters = function.getParameters();
if (parameters != null) {
parameters.getExpressions().forEach(expression -> {
if (expression instanceof SubSelect) {
processSelectBody(((SubSelect) expression).getSelectBody());
} else if (expression instanceof Function) {
processFunction((Function) expression);
}
});
}
}
/**
* 处理子查询等
*/
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) {
logger.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();
if (fieldFillHandler.createIgnore(fromTable.getName())) {
// 过滤退出执行
return;
}
join.setOnExpression(builderExpression(join.getOnExpression(), fromTable));
}
}
/**
* 处理条件
*/
protected Expression builderExpression(Expression currentExpression, Table table) {
EqualsTo equalsTo = new EqualsTo();
equalsTo.setLeftExpression(this.getAliasColumn(table));
equalsTo.setRightExpression(fieldFillHandler.getFieldFillValue());
if (currentExpression == null) {
return equalsTo;
}
if (currentExpression instanceof OrExpression) {
return new AndExpression(new Parenthesis(currentExpression), equalsTo);
} else {
return new AndExpression(currentExpression, equalsTo);
}
}
/**
* 字段别名设置
*
* @param table 表对象
* @return 字段
*/
protected Column getAliasColumn(Table table) {
StringBuilder column = new StringBuilder();
if (table.getAlias() != null) {
column.append(table.getAlias().getName()).append(StringPool.DOT);
}
column.append(fieldFillHandler.getFieldFillColumn());
return new Column(column.toString());
}
@Override
public void setProperties(Properties properties) {
PropertyMapper.newInstance(properties).whenNotBlank("fieldFillHandler", ClassUtils::newInstance, this::setFieldFillHandler);
}
}