variable;
+
+}
diff --git a/remi-comm/remi-comm-email/src/main/java/ai/remi/comm/email/service/EmailService.java b/remi-comm/remi-comm-email/src/main/java/ai/remi/comm/email/service/EmailService.java
new file mode 100644
index 0000000..393cea3
--- /dev/null
+++ b/remi-comm/remi-comm-email/src/main/java/ai/remi/comm/email/service/EmailService.java
@@ -0,0 +1,227 @@
+package ai.remi.comm.email.service;
+
+import ai.remi.comm.email.config.EmailProperties;
+import ai.remi.comm.email.domain.Email;
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.mail.MailException;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.stereotype.Component;
+import org.springframework.ui.freemarker.FreeMarkerTemplateUtils;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.context.Context;
+
+import javax.annotation.Resource;
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import java.io.File;
+
+@Slf4j
+@Component
+public class EmailService {
+
+ /**
+ * 邮件配置
+ */
+ @Resource
+ private EmailProperties emailProperties;
+
+ /**
+ * 邮件执行
+ */
+ @Resource
+ private JavaMailSender mailSender;
+
+ /**
+ * thymeleaf
+ */
+ @Resource
+ private TemplateEngine templateEngine;
+
+ /**
+ * freemarker
+ */
+ @Resource
+ public Configuration configuration;
+
+
+ /**
+ * 发送文本邮件
+ */
+ public void sendTextMail(Email email) {
+ SimpleMailMessage message = new SimpleMailMessage();
+ try {
+ message.setFrom(emailProperties.getFromName());
+ message.setTo(email.getTo());
+ message.setCc(email.getCc());
+ message.setBcc(email.getBcc());
+ message.setSubject(email.getSubject());
+ message.setText(email.getContent());
+ mailSender.send(message);
+ log.info("发送基础邮件成功!");
+ } catch (MailException e) {
+ log.error("发送基础邮件异常:" + e);
+ }
+ }
+
+ /**
+ * 发送html邮件
+ */
+ public void sendHtmlMail(Email email) {
+ MimeMessage message = mailSender.createMimeMessage();
+ try {
+ //true表示需要创建一个multipart message
+ MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ helper.setFrom(emailProperties.getFromName());
+ helper.setTo(email.getTo());
+ helper.setSubject(email.getSubject());
+ helper.setText(email.getContent(), true);
+ // 检查附件数组是否为空
+ String[] filePaths = email.getFilePaths();
+ if (checkFilePaths(filePaths)) {
+ for (String filePath : filePaths) {
+ FileSystemResource file = new FileSystemResource(new File(filePath));
+ String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
+ helper.addAttachment(fileName, file);
+ }
+ }
+ mailSender.send(message);
+ log.info("发送html邮件成功");
+ } catch (MessagingException e) {
+ log.error("发送html邮件异常:" + e);
+ }
+ }
+
+ /**
+ * 发送正文是静态资源(图片)的邮件
+ */
+ public void sendInlineMail(Email email) {
+ MimeMessage message = mailSender.createMimeMessage();
+ try {
+ MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ helper.setFrom(emailProperties.getFromName());
+ helper.setTo(email.getTo());
+ helper.setSubject(email.getSubject());
+ helper.setText(email.getContent(), true);
+ // 嵌入静态资源
+ FileSystemResource res = new FileSystemResource(new File(email.getRscPath()));
+ helper.addInline(email.getRscId(), res);
+ // 检查附件数组是否为空
+ String[] filePaths = email.getFilePaths();
+ if (checkFilePaths(filePaths)) {
+ for (String filePath : filePaths) {
+ FileSystemResource file = new FileSystemResource(new File(filePath));
+ String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
+ helper.addAttachment(fileName, file);
+ }
+ }
+ mailSender.send(message);
+ log.info("发送嵌入图片的邮件成功!");
+ } catch (MessagingException e) {
+ log.error("发送嵌入图片的邮件异常:" + e);
+ }
+ }
+
+ /**
+ * 发送带附件的邮件
+ */
+ public void sendAttachmentsMail(Email email) {
+ MimeMessage message = mailSender.createMimeMessage();
+ try {
+ MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ helper.setFrom(emailProperties.getFromName());
+ helper.setTo(email.getTo());
+ helper.setSubject(email.getSubject());
+ helper.setText(email.getContent(), true);
+ // 检查附件数组是否为空
+ String[] filePaths = email.getFilePaths();
+ if (checkFilePaths(filePaths)) {
+ for (String filePath : filePaths) {
+ FileSystemResource file = new FileSystemResource(new File(filePath));
+ String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
+ helper.addAttachment(fileName, file);
+ }
+ }
+ mailSender.send(message);
+ log.info("发送带附件的邮件成功!");
+ } catch (MessagingException e) {
+ log.error("发送带附件的邮件异常:" + e);
+ }
+ }
+
+ public void sendThymeleafMail(Email email) {
+ MimeMessage message = mailSender.createMimeMessage();
+ try {
+ MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ helper.setFrom(emailProperties.getFromName());
+ helper.setTo(email.getTo());
+ helper.setSubject(email.getSubject());
+ helper.setText(email.getContent(), true);
+ // 设置邮件正文
+ Context context = new Context();
+ context.setVariables(email.getVariable());
+ String htmlContent = templateEngine.process(email.getTemplate(), context);
+ // 检查附件数组是否为空
+ String[] filePaths = email.getFilePaths();
+ if (checkFilePaths(filePaths)) {
+ for (String filePath : filePaths) {
+ FileSystemResource file = new FileSystemResource(new File(filePath));
+ String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
+ helper.addAttachment("瑞米平台:" + fileName, file);
+ }
+ }
+ // 第二个参数设置为true表示HTML格式
+ helper.setText(htmlContent, true);
+ mailSender.send(message);
+ log.info("发送Thymeleaf模板邮件成功!");
+ } catch (MessagingException e) {
+ log.error("发送Thymeleaf模板邮件异常:" + e);
+ }
+ }
+
+ public void sendFreemarkerMail(Email email) {
+ MimeMessage message = mailSender.createMimeMessage();
+ try {
+ MimeMessageHelper helper = new MimeMessageHelper(message, true);
+ helper.setFrom(emailProperties.getFromName());
+ helper.setTo(email.getTo());
+ helper.setSubject(email.getSubject());
+ helper.setText(email.getContent(), true);
+ // 设置邮件正文
+ Template template = configuration.getTemplate(email.getTemplate() + ".flt");
+ String htmlContent = FreeMarkerTemplateUtils.processTemplateIntoString(template, email.getVariable());
+ // 检查附件数组是否为空
+ String[] filePaths = email.getFilePaths();
+ if (checkFilePaths(filePaths)) {
+ for (String filePath : filePaths) {
+ FileSystemResource file = new FileSystemResource(new File(filePath));
+ String fileName = filePath.substring(filePath.lastIndexOf(File.separator));
+ helper.addAttachment("瑞米平台:" + fileName, file);
+ }
+ }
+ // 第二个参数设置为true表示HTML格式
+ helper.setText(htmlContent, true);
+ mailSender.send(message);
+ log.info("发送Freemarker模板邮件成功!");
+ } catch (Exception e) {
+ log.error("发送Freemarker模板邮件异常:" + e);
+ }
+ }
+
+ public static boolean checkFilePaths(String[] array) {
+ if (array == null) {
+ log.info("附件数组为null");
+ return false;
+ } else if (array.length == 0) {
+ log.info("附件数组为空");
+ return false;
+ } else {
+ log.info("附件数组不为空且不为null");
+ return true;
+ }
+ }
+}
diff --git a/remi-comm/remi-comm-email/src/main/resources/META-INF/spring.factories b/remi-comm/remi-comm-email/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..ce395ea
--- /dev/null
+++ b/remi-comm/remi-comm-email/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ ai.remi.comm.email.service.EmailService
\ No newline at end of file
diff --git a/remi-comm/remi-comm-email/src/main/resources/templates/freemarker.ftl b/remi-comm/remi-comm-email/src/main/resources/templates/freemarker.ftl
new file mode 100644
index 0000000..deb73b2
--- /dev/null
+++ b/remi-comm/remi-comm-email/src/main/resources/templates/freemarker.ftl
@@ -0,0 +1,11 @@
+
+
+
+
+ ${title}
+
+
+ ${greeting}
+ ${message}
+
+
\ No newline at end of file
diff --git a/remi-comm/remi-comm-email/src/main/resources/templates/thymeleaf.html b/remi-comm/remi-comm-email/src/main/resources/templates/thymeleaf.html
new file mode 100644
index 0000000..1a6eb12
--- /dev/null
+++ b/remi-comm/remi-comm-email/src/main/resources/templates/thymeleaf.html
@@ -0,0 +1,11 @@
+
+
+
+
+ Welcome
+
+
+ Hello,
+ Welcome to our site!
+
+
diff --git a/remi-comm/remi-comm-exception/README.md b/remi-comm/remi-comm-exception/README.md
new file mode 100644
index 0000000..4ffd5ad
--- /dev/null
+++ b/remi-comm/remi-comm-exception/README.md
@@ -0,0 +1,6 @@
+```yaml
+i18n:
+ messages:
+ basename: i18n/messages
+ encoding: utf-8
+```
\ No newline at end of file
diff --git a/remi-comm/remi-comm-exception/pom.xml b/remi-comm/remi-comm-exception/pom.xml
new file mode 100644
index 0000000..fdc23b1
--- /dev/null
+++ b/remi-comm/remi-comm-exception/pom.xml
@@ -0,0 +1,55 @@
+
+
+ 4.0.0
+ remi-comm-exception
+ remi-comm-exception
+ remi-comm-exception
+
+
+ ai.remi.top.cloud
+ remi-comm
+ 3.1.1
+
+
+
+
+
+ maven-releases
+ release
+ https://repository.njydsz.com/repository/maven-releases/
+
+
+ maven-snapshots
+ snapshot
+ https://repository.njydsz.com/repository/maven-snapshots/
+
+
+
+
+
+
+ ai.remi.top.cloud
+ remi-comm-domain
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ slf4j-nop
+
+
+
+
diff --git a/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/config/I18nConfig.java b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/config/I18nConfig.java
new file mode 100644
index 0000000..9088c21
--- /dev/null
+++ b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/config/I18nConfig.java
@@ -0,0 +1,40 @@
+package ai.remi.comm.exception.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.ReloadableResourceBundleMessageSource;
+import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class I18nConfig implements WebMvcConfigurer {
+
+ //@Value("${i18n.messages.basename:}")
+ //private String messagesBasename;
+
+ //@Value("${i18n.messages.encoding:}")
+ //private String messagesEncoding;
+
+ @Bean(name = "messageSource")
+ public ReloadableResourceBundleMessageSource messageSource() {
+ ReloadableResourceBundleMessageSource rbms = new ReloadableResourceBundleMessageSource();
+ //if (!StringUtils.isEmpty(messagesBasename)) {
+ // //设置path
+ // rbms.addBasenames(messagesBasename.split(","));
+ //}
+ //rbms.setDefaultEncoding(messagesEncoding);
+ rbms.setBasenames("classpath:i18n/messages");
+ rbms.setDefaultEncoding("UTF-8");
+ rbms.setUseCodeAsDefaultMessage(true);
+ rbms.setFallbackToSystemLocale(false);
+ return rbms;
+ }
+
+ @Bean
+ public LocalValidatorFactoryBean getValidator() {
+ LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
+ bean.setValidationMessageSource(messageSource());
+ return bean;
+ }
+}
+
diff --git a/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/custom/BusinessException.java b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/custom/BusinessException.java
new file mode 100644
index 0000000..440127c
--- /dev/null
+++ b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/custom/BusinessException.java
@@ -0,0 +1,89 @@
+package ai.remi.comm.exception.custom;
+
+import ai.remi.comm.core.constant.Constants;
+import ai.remi.comm.exception.util.MessageUtils;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * @author dianjiu【公众号 点九开源】
+ * @email dianjiuxyz@gmail.com
+ * @desc BusinessException
+ */
+@Data
+@NoArgsConstructor
+public class BusinessException extends RuntimeException {
+
+ @Getter
+ @Setter
+ private String code;
+ @Getter
+ @Setter
+ private String key;
+ @Getter
+ @Setter
+ private Object[] params;
+ @Getter
+ @Setter
+ private String message;
+
+ public BusinessException(String key) {
+ super(MessageUtils.getMessage(key));
+ this.code = Constants.ERROR;
+ this.message = MessageUtils.getMessage(key);
+ }
+
+ public BusinessException(String key, Object[] params) {
+ super(MessageUtils.getMessage(key, params));
+ this.code = Constants.ERROR;
+ this.message = MessageUtils.getMessage(key, params);
+ }
+
+ public BusinessException(String code, String key) {
+ super(MessageUtils.getMessage(key));
+ this.code = code;
+ this.message = MessageUtils.getMessage(key);
+ }
+
+ public BusinessException(String code, String key, Object[] params) {
+ super(MessageUtils.getMessage(key, params));
+ this.code = code;
+ this.message = MessageUtils.getMessage(key, params);
+ }
+
+ public BusinessException(Throwable cause) {
+ super(cause);
+ }
+
+ public BusinessException(String code, Throwable cause) {
+ super(cause);
+ this.code = code;
+ }
+
+ public BusinessException(String code, String key, Throwable cause) {
+ super(MessageUtils.getMessage(key), cause);
+ this.code = code;
+ this.message = MessageUtils.getMessage(key);
+ }
+
+ public BusinessException(String code, String key, Object[] params, Throwable cause) {
+ super(MessageUtils.getMessage(key, params), cause);
+ this.code = code;
+ this.message = MessageUtils.getMessage(key, params);
+ }
+
+ public BusinessException(String code, String key, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(MessageUtils.getMessage(key), cause, enableSuppression, writableStackTrace);
+ this.code = code;
+ this.message = MessageUtils.getMessage(key);
+ }
+
+ public BusinessException(String code, String key, Object[] params, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(MessageUtils.getMessage(key, params), cause, enableSuppression, writableStackTrace);
+ this.code = code;
+ this.message = MessageUtils.getMessage(key, params);
+ }
+}
+
diff --git a/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/enums/CommExceptionCode.java b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/enums/CommExceptionCode.java
new file mode 100644
index 0000000..952d93e
--- /dev/null
+++ b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/enums/CommExceptionCode.java
@@ -0,0 +1,52 @@
+package ai.remi.comm.exception.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author dianjiu【公众号 点九开源】
+ * @email dianjiuxyz@gmail.com
+ * @desc 错误码设计描述
+ *
+ * 面向日志的错误码定义共十三位(十位有意义,三位连接符),并且应该具有如下分类: 应用标识,表示错误属于哪个应用,三位数字。 功能域标识,表示错误属于应用中的哪个功能模块,三位数字。 错误类型,表示错误属于那种类型,一位字母。
+ * 错误编码,错误类型下的具体错误,三位数字。
+ *
+ * 面向外部传递的错误码共六位,并且有如下分类: 错误类型,表示错误来源,1位字母。 应用标识,表示错误属于哪个应用,2位数字。 错误编码,错误类型下的具体错误,3位数字。
+ *
+ * 错误类型: A-表示错误来源于用户 B-表示错误来源于系统 C-表示错误来源于第三方接口
+ */
+@Getter
+@AllArgsConstructor
+public enum CommExceptionCode implements ExceptionCode {
+
+ /**
+ * 面向外部传递的错误码共六位,并且有如下分类:
+ * 错误类型,表示错误来源,1位字母。
+ * 应用标识,表示错误属于哪个应用,2位数字。
+ * 错误编码,错误类型下的具体错误,3位数字。
+ *
+ * 错误类型: A-表示错误来源于用户 B-表示错误来源于系统 C-表示错误来源于第三方接口
+ */
+ /****************************客户端异常****************************/
+ BUSINESS_ERROR("A01001", "business.error"),
+ ILLEGAL_ARGUMENT("A01002", "illegal.argument"),
+ NOT_LOGGED_IN("A01003", "not.logged.in"),
+ /****************************系统异常****************************/
+ SYSTEM_ERROR("B01001", "system.error"),
+ ASSERT_ERROR("B01002", "assert.error"),
+ /****************************三方异常****************************/
+ OTHER_SYSTEM_ERROR("C01001", "other.system.error"),
+ CALL_FEIGN_ERROR("C01002", "call.feign.error"),
+ CALL_SQL_ERROR("C01003", "call.sql.error"),
+ CALL_REDIS_ERROR("C01004", "call.redis.error"),
+ CALL_MINIO_ERROR("C01005", "call.minio.error"),
+ CALL_NACOS_ERROR("C01006", "call.nacos.error"),
+ CALL_JOB_ERROR("C01007", "call.job.error"),
+ CALL_MQ_ERROR("C01008", "call.mq.error"),
+ CALL_ES_ERROR("C01009", "call.es.error");
+
+ private String code;
+
+ private String key;
+
+}
diff --git a/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/enums/ExceptionCode.java b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/enums/ExceptionCode.java
new file mode 100644
index 0000000..539b9c6
--- /dev/null
+++ b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/enums/ExceptionCode.java
@@ -0,0 +1,17 @@
+package ai.remi.comm.exception.enums;
+
+/**
+ * 异常code接口
+ */
+public interface ExceptionCode {
+ /**
+ * 异常code
+ * @return
+ */
+ String getCode();
+ /**
+ * 异常消息
+ * @return
+ */
+ String getKey();
+}
diff --git a/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/util/ExceptionUtils.java b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/util/ExceptionUtils.java
new file mode 100644
index 0000000..3c68f5d
--- /dev/null
+++ b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/util/ExceptionUtils.java
@@ -0,0 +1,24 @@
+package ai.remi.comm.exception.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * @author dianjiu【公众号 点九开源】
+ * @email dianjiuxyz@gmail.com
+ * @desc ExceptionUtils
+ */
+public class ExceptionUtils {
+
+
+ /**
+ * 获取exception的详细错误信息。
+ */
+ public static String getTrace(Throwable e) {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw, true));
+ return sw.toString();
+ }
+
+}
diff --git a/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/util/MessageUtils.java b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/util/MessageUtils.java
new file mode 100644
index 0000000..f32cce6
--- /dev/null
+++ b/remi-comm/remi-comm-exception/src/main/java/ai/remi/comm/exception/util/MessageUtils.java
@@ -0,0 +1,136 @@
+package ai.remi.comm.exception.util;
+
+import ai.remi.comm.core.spring.SpringBeans;
+import ai.remi.comm.util.auth.AuthInfoUtils;
+import ai.remi.comm.util.string.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.MessageSource;
+import org.springframework.stereotype.Component;
+
+import java.util.Locale;
+
+/**
+ * @author dianjiu【公众号 点九开源】
+ * @email dianjiuxyz@gmail.com
+ * @desc MessageUtils
+ */
+@Component
+public class MessageUtils {
+ private static final Logger logger = LoggerFactory.getLogger(MessageUtils.class);
+
+ /**
+ * 获得多语言内容,默认根据当前用户的Local获取
+ *
+ * @param key 多语言key
+ * @return 翻译后的值,如获取不到则返回key
+ */
+ public static String getMessage(String key) {
+ return getMessage(key, new Object[]{});
+ }
+
+
+ /**
+ * 获得多语言内容--带参数,默认根据当前用户的Local获取
+ *
+ * @param key 多语言key
+ * @param params 参数
+ * @return 翻译后的值,如获取不到则返回key
+ */
+ public static String getMessage(String key, Object[] params) {
+ Locale locale = getLocale();
+ if (params == null) {
+ params = new Object[]{};
+ }
+ return getMessage(locale, key, params);
+
+ }
+
+ /**
+ * 获取多语言内容--带默认值,默认根据当前用户的Local获取
+ *
+ * @param key 多语言key
+ * @param defaultMsg 默认返回值
+ * @return 翻译后的值,获取不到返回默认值
+ */
+ public static String getMessage(String key, String defaultMsg) {
+ return getMessage(key, new Object[]{}, defaultMsg);
+ }
+
+ /**
+ * 获得多语言内容--带参数--带默认值,默认根据当前用户的Local获取
+ *
+ * @param key 多语言key
+ * @param defaultMsg 默认返回值
+ * @param params 参数
+ * @return 翻译后的值,获取不到返回默认值
+ */
+ public static String getMessage(String key, Object[] params, String defaultMsg) {
+ Locale locale = getLocale();
+ return getMessage(locale, key, params, defaultMsg);
+ }
+
+ /**
+ * 不带参数
+ *
+ * @param locale 语言信息
+ * @param key 多语言key
+ * @return 翻译后的值
+ */
+ public static String getMessage(Locale locale, String key) {
+ return getMessage(locale, key, new Object[]{});
+ }
+
+
+ /**
+ * 获取多语言数据
+ *
+ * @param locale 语言信息
+ * @param key 多语言key
+ * @param params 参数
+ * @return 翻译后的值
+ */
+ public static String getMessage(Locale locale, String key, Object[] params) {
+ try {
+ MessageSource messageSource = (MessageSource) SpringBeans.getBean("messageSource");
+ return messageSource.getMessage(key, params, locale);
+ } catch (Exception e) {
+ logger.error("get locale msg error", e);
+ }
+ return key;
+ }
+
+ /**
+ * 获取多语言数据
+ *
+ * @param key 多语言key
+ * @param params 参数
+ * @param defaultMsg 默认返回值
+ * @param locale 语言信息
+ * @return 翻译后的值
+ */
+ public static String getMessage(Locale locale, String key, Object[] params, String defaultMsg) {
+ MessageSource messageSource = (MessageSource) SpringBeans.getBean("messageSource");
+ return messageSource.getMessage(key, params, defaultMsg, locale);
+ }
+
+ /**
+ * 获取当前语言
+ *
+ * @return
+ */
+ private static Locale getLocale() {
+ String userLanguage = AuthInfoUtils.getUserLanguage();
+ if (StringUtils.isBlank(userLanguage)) {
+ return Locale.CHINA;
+ }
+ switch (userLanguage.toLowerCase()) {
+ case "zh_cn":
+ case "zh-CN":
+ case "zh":
+ return Locale.CHINA;
+ default:
+ return Locale.US;
+ }
+ }
+}
diff --git a/remi-comm/remi-comm-exception/src/main/resources/META-INF/spring.factories b/remi-comm/remi-comm-exception/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..a18f148
--- /dev/null
+++ b/remi-comm/remi-comm-exception/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ai.remi.comm.exception.config.I18nConfig
\ No newline at end of file
diff --git a/remi-comm/remi-comm-feign/pom.xml b/remi-comm/remi-comm-feign/pom.xml
new file mode 100644
index 0000000..d3a0e00
--- /dev/null
+++ b/remi-comm/remi-comm-feign/pom.xml
@@ -0,0 +1,59 @@
+
+
+ 4.0.0
+ remi-comm-feign
+ remi-comm-feign
+ remi-comm-feign
+
+
+ ai.remi.top.cloud
+ remi-comm
+ 3.1.1
+
+
+
+
+
+ maven-releases
+ release
+ https://repository.njydsz.com/repository/maven-releases/
+
+
+ maven-snapshots
+ snapshot
+ https://repository.njydsz.com/repository/maven-snapshots/
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+ ai.remi.top.cloud
+ remi-comm-util
+
+
+
+ javax.servlet
+ javax.servlet-api
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ slf4j-simple
+
+
+ org.slf4j
+ slf4j-nop
+
+
+
diff --git a/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/annotation/EnableRemiFeignClients.java b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/annotation/EnableRemiFeignClients.java
new file mode 100644
index 0000000..91a93bf
--- /dev/null
+++ b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/annotation/EnableRemiFeignClients.java
@@ -0,0 +1,27 @@
+package ai.remi.comm.feign.annotation;
+
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+import java.lang.annotation.*;
+
+
+/**
+ * @author Remi
+ * @email remi@dianjiu.cc
+ * @desc 自定义feign注解,添加basePackages路径
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@EnableFeignClients
+public @interface EnableRemiFeignClients {
+ String[] value() default {};
+
+ String[] basePackages() default {"ai.remi"};
+
+ Class>[] basePackageClasses() default {};
+
+ Class>[] defaultConfiguration() default {};
+
+ Class>[] clients() default {};
+}
diff --git a/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/FeignAutoConfiguration.java b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/FeignAutoConfiguration.java
new file mode 100644
index 0000000..c044df8
--- /dev/null
+++ b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/FeignAutoConfiguration.java
@@ -0,0 +1,52 @@
+package ai.remi.comm.feign.aspect;
+
+import feign.Logger;
+import feign.RequestInterceptor;
+import feign.codec.ErrorDecoder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+/**
+ * @author Remi
+ * @email remi@dianjiu.cc
+ * @desc Feign 配置注册
+ */
+@Configuration
+public class FeignAutoConfiguration {
+
+ /**
+ * 自定义请求拦截器
+ * 传递用户信息请求头,防止丢失
+ * @return
+ */
+ @Bean
+ public RequestInterceptor requestInterceptor() {
+ return new FeignRequestInterceptor();
+ }
+
+ /**
+ * 设置Feign客户端日志级别
+ *
+ * 有四种日志记录级别可供选择:
+ *
+ * - NONE – 不记录日志,这是默认设置
+ * - BASIC – 仅记录请求方法、URL 和响应状态
+ * - HEADERS – 将基本信息与请求和响应标头一起记录
+ * - FULL – 记录请求和响应的主体、标头和元数据
+ */
+ @Bean
+ Logger.Level level() {
+ return Logger.Level.FULL;
+ }
+
+ /**
+ * Feign 的默认错误处理程序ErrorDecoder.default总是抛出FeignException。
+ * 种行为并不总是最有用的。因此,要自定义抛出的异常
+ * @return
+ */
+ @Bean
+ public ErrorDecoder errorDecoder() {
+ return new RemiFeignErrorDecoder();
+ }
+}
diff --git a/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/FeignRequestInterceptor.java b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/FeignRequestInterceptor.java
new file mode 100644
index 0000000..9e605b8
--- /dev/null
+++ b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/FeignRequestInterceptor.java
@@ -0,0 +1,72 @@
+package ai.remi.comm.feign.aspect;
+
+import ai.remi.comm.core.constant.HeaderConstant;
+import ai.remi.comm.core.enums.IdentityTypeEnum;
+import ai.remi.comm.core.enums.PermissionScopeEnum;
+import ai.remi.comm.util.auth.AuthInfoUtils;
+import ai.remi.comm.util.http.ServletUtils;
+import ai.remi.comm.util.ip.IpAddrUtils;
+import ai.remi.comm.util.string.StringUtils;
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * @author Remi
+ * @email remi@dianjiu.cc
+ * @desc Feign 请求拦截器
+ */
+@Component
+public class FeignRequestInterceptor implements RequestInterceptor {
+ private Logger log = LoggerFactory.getLogger(FeignRequestInterceptor.class);
+
+ @Override
+ public void apply(RequestTemplate requestTemplate) {
+ HttpServletRequest httpServletRequest = ServletUtils.getRequest();
+ Map headers = ServletUtils.getHeaders(httpServletRequest);
+ // 传递用户信息请求头,防止丢失
+ //String userId = headers.get(HeaderConstant.X_UNIQUE_ID);
+ if (StringUtils.isNotNull(httpServletRequest)) {
+ // 传递服务类型请求头,防止丢失
+ log.info("FeignHeaderRequestInterceptor X_SERVICE_TYPE:{}", AuthInfoUtils.getServiceTypeCode());
+ requestTemplate.header(HeaderConstant.X_SERVICE_TYPE, AuthInfoUtils.getServiceTypeCode());
+ // 传递部门租户请求头,防止丢失
+ requestTemplate.header(HeaderConstant.X_DEPT_TENANT_ID, AuthInfoUtils.getDeptTenantId());
+ // 传递公司租户请求头,防止丢失
+ requestTemplate.header(HeaderConstant.X_COMPANY_TENANT_ID, AuthInfoUtils.getCompanyTenantId());
+ // 传递用户信息请求头,防止丢失
+ requestTemplate.header(HeaderConstant.X_UNIQUE_ID, AuthInfoUtils.getUniqueId());
+ // 传递系统语言请求头,防止丢失
+ requestTemplate.header(HeaderConstant.X_USER_LANGUAGE, AuthInfoUtils.getUserLanguage());
+ // 传递用户设备请求头,防止丢失
+ requestTemplate.header(HeaderConstant.X_DISTINCT_ID, headers.get(HeaderConstant.X_DISTINCT_ID));
+ // 传递鉴权信息请求头,防止丢失
+ requestTemplate.header(HeaderConstant.X_ACCESS_TOKEN, headers.get(HeaderConstant.X_ACCESS_TOKEN));
+
+ // 判断用户类型是否为集团公司,是的话进一步判断是否为集团权限,是集团权限需要传递集团下属公司IDs
+ IdentityTypeEnum identityTypeEnum = AuthInfoUtils.getIdentityTypeEnum();
+ //集团公司的用户
+ if (identityTypeEnum == IdentityTypeEnum.COMPANY) {
+ //权限范围
+ String permissionScope = Optional.ofNullable(AuthInfoUtils.getPermissionScopeEnum())
+ .map(PermissionScopeEnum::getCode).orElse("");
+ requestTemplate.header(HeaderConstant.X_PERMISSION_SCOPE, permissionScope);
+ //当权限范围为集团时,为这个用户管理公司的id
+ requestTemplate.header(HeaderConstant.X_COMPANY_IDS, AuthInfoUtils.getHasPermissionCompanyIds());
+ //当权限范围为公司时,为这个用户管理部门的id
+ requestTemplate.header(HeaderConstant.X_COMPANY_IDS, AuthInfoUtils.getHasPermissionCompanyIds());
+ } else if (identityTypeEnum == IdentityTypeEnum.VISITOR) {
+ //访客体验的用户 do anything
+ }
+
+ // 传递请求来源请求头,防止丢失
+ requestTemplate.header("X-Forwarded-For", IpAddrUtils.getIpAddr(ServletUtils.getRequest()));
+ }
+ }
+}
\ No newline at end of file
diff --git a/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/RemiFeignErrorDecoder.java b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/RemiFeignErrorDecoder.java
new file mode 100644
index 0000000..3d22ea2
--- /dev/null
+++ b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/aspect/RemiFeignErrorDecoder.java
@@ -0,0 +1,19 @@
+package ai.remi.comm.feign.aspect;
+
+import ai.remi.comm.feign.exception.BadRequestException;
+import ai.remi.comm.feign.exception.NotFoundException;
+import ai.remi.comm.feign.exception.OpenFeignException;
+import feign.Response;
+import feign.codec.ErrorDecoder;
+
+public class RemiFeignErrorDecoder implements ErrorDecoder {
+ @Override
+ public Exception decode(String methodKey, Response response) {
+
+ return switch (response.status()) {
+ case 400 -> new BadRequestException();
+ case 404 -> new NotFoundException("Remi Feign ");
+ default -> new OpenFeignException("Remi Feign error");
+ };
+ }
+}
diff --git a/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/BadRequestException.java b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/BadRequestException.java
new file mode 100644
index 0000000..160eb37
--- /dev/null
+++ b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/BadRequestException.java
@@ -0,0 +1,17 @@
+package ai.remi.comm.feign.exception;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author Remi
+ * @email remi@dianjiu.cc
+ * @desc BadRequestException
+ */
+@Data
+@NoArgsConstructor
+public class BadRequestException extends RuntimeException {
+ public BadRequestException(String message) {
+ super(message);
+ }
+}
diff --git a/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/NotFoundException.java b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/NotFoundException.java
new file mode 100644
index 0000000..339b6fd
--- /dev/null
+++ b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/NotFoundException.java
@@ -0,0 +1,33 @@
+package ai.remi.comm.feign.exception;
+
+import ai.remi.comm.core.constant.Constants;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * @author Remi
+ * @email remi@dianjiu.cc
+ * @desc NotFoundException
+ */
+@Data
+@NoArgsConstructor
+public class NotFoundException extends Exception {
+ private static final long serialVersionUID = 1L;
+ @Getter
+ @Setter
+ private String code;
+ @Getter
+ @Setter
+ private String message;
+
+ public NotFoundException(String msg) {
+ super(msg);
+ this.code = Constants.ERROR;
+ }
+
+ public NotFoundException(String msg, Exception e) {
+ super(msg + " not found,because of" + e.getMessage());
+ }
+}
diff --git a/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/OpenFeignException.java b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/OpenFeignException.java
new file mode 100644
index 0000000..679fb29
--- /dev/null
+++ b/remi-comm/remi-comm-feign/src/main/java/ai/remi/comm/feign/exception/OpenFeignException.java
@@ -0,0 +1,63 @@
+package ai.remi.comm.feign.exception;
+
+import ai.remi.comm.core.constant.Constants;
+import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * @author Remi
+ * @email remi@dianjiu.cc
+ * @desc OpenFeignException
+ */
+@Data
+@NoArgsConstructor
+public class OpenFeignException extends RuntimeException {
+
+ @Getter
+ @Setter
+ private String code;
+ @Getter
+ @Setter
+ private String message;
+
+ @Getter
+ @Setter
+ private Object data;
+
+ public OpenFeignException(Throwable cause) {
+ super(cause);
+ }
+
+ public OpenFeignException(String message) {
+ super(message);
+ this.code = Constants.ERROR;
+ this.message = message;
+ }
+
+ public OpenFeignException(String code, String message) {
+ super(message);
+ this.code = code;
+ this.message = message;
+ }
+
+ public OpenFeignException(String code, Throwable cause) {
+ super(cause);
+ this.code = code;
+ this.message = cause.getMessage();
+ }
+
+ public OpenFeignException(String code, String message, Throwable cause) {
+ super(message, cause);
+ this.code = code;
+ this.message = message;
+ }
+
+ public OpenFeignException(String code, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ this.code = code;
+ this.message = message;
+ }
+}
+
diff --git a/remi-comm/remi-comm-jdbc/pom.xml b/remi-comm/remi-comm-jdbc/pom.xml
new file mode 100644
index 0000000..62bb34e
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/pom.xml
@@ -0,0 +1,90 @@
+
+
+ 4.0.0
+ remi-comm-jdbc
+ remi-comm-jdbc
+ remi-comm-jdbc
+
+
+ ai.remi.top.cloud
+ remi-comm
+ 3.1.1
+
+
+
+
+
+ maven-releases
+ release
+ https://repository.njydsz.com/repository/maven-releases/
+
+
+ maven-snapshots
+ snapshot
+ https://repository.njydsz.com/repository/maven-snapshots/
+
+
+
+
+
+
+ ai.remi.top.cloud
+ remi-comm-core
+
+
+
+ ai.remi.top.cloud
+ remi-comm-util
+
+
+
+ mybatis-plus-boot-starter
+ com.baomidou
+
+
+ mybatis-plus-annotation
+ com.baomidou
+
+
+ com.baomidou
+ mybatis-plus-extension
+
+
+
+ mybatis-spring-boot-starter
+ org.mybatis.spring.boot
+
+
+
+ druid-spring-boot-starter
+ com.alibaba
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+
+ mysql-connector-java
+ mysql
+ runtime
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.slf4j
+ slf4j-simple
+
+
+ org.slf4j
+ slf4j-nop
+
+
+
+
+
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/AutoConfig.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/AutoConfig.java
new file mode 100644
index 0000000..54685ce
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/AutoConfig.java
@@ -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 {
+
+
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/DruidConfig.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/DruidConfig.java
new file mode 100644
index 0000000..2f3f6b1
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/DruidConfig.java
@@ -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();
+ }
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/FieldFillConfig.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/FieldFillConfig.java
new file mode 100644
index 0000000..a022e9a
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/FieldFillConfig.java
@@ -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();
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/InterceptConfig.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/InterceptConfig.java
new file mode 100644
index 0000000..0546889
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/InterceptConfig.java
@@ -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 tables = new HashSet<>();
+
+ /**
+ * 处理的字段
+ */
+ private String column = "";
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/MybatisPlusConfig.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/MybatisPlusConfig.java
new file mode 100644
index 0000000..58c0c88
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/MybatisPlusConfig.java
@@ -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);
+ }
+
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/TenantConfig.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/TenantConfig.java
new file mode 100644
index 0000000..145434c
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/config/TenantConfig.java
@@ -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();
+
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/DataSourceType.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/DataSourceType.java
new file mode 100644
index 0000000..46c1730
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/DataSourceType.java
@@ -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
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/FieldFillStrategyEnum.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/FieldFillStrategyEnum.java
new file mode 100644
index 0000000..02355c9
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/FieldFillStrategyEnum.java
@@ -0,0 +1,22 @@
+package ai.remi.comm.jdbc.enums;
+
+/**
+ * @author dianjiu【公众号 点九开源】
+ * @email dianjiuxyz@gmail.com
+ * @desc 字段填充策略枚举
+ */
+public enum FieldFillStrategyEnum {
+
+ /**
+ * 插入时填充字段
+ */
+ INSERT,
+ /**
+ * 更新时填充字段
+ */
+ UPDATE,
+ /**
+ * 插入和更新时填充字段
+ */
+ INSERT_UPDATE
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/InterceptTableStrategy.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/InterceptTableStrategy.java
new file mode 100644
index 0000000..dba54db
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/enums/InterceptTableStrategy.java
@@ -0,0 +1,11 @@
+package ai.remi.comm.jdbc.enums;
+
+/**
+ * @author dianjiu【公众号 点九开源】
+ * @email dianjiuxyz@gmail.com
+ * @desc 拦截表策略
+ */
+public enum InterceptTableStrategy {
+ INCLUDE,
+ EXCLUDE;
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractFieldFillHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractFieldFillHandler.java
new file mode 100644
index 0000000..0ea3935
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractFieldFillHandler.java
@@ -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);
+ }
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractSqlHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractSqlHandler.java
new file mode 100644
index 0000000..8d2b903
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractSqlHandler.java
@@ -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 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();
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractTenantHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractTenantHandler.java
new file mode 100644
index 0000000..270985a
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/AbstractTenantHandler.java
@@ -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 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;
+ }
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CompanyTenantHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CompanyTenantHandler.java
new file mode 100644
index 0000000..983498f
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CompanyTenantHandler.java
@@ -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";
+ }
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CreatedAtHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CreatedAtHandler.java
new file mode 100644
index 0000000..fb8a076
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CreatedAtHandler.java
@@ -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";
+ }
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CreatedByHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CreatedByHandler.java
new file mode 100644
index 0000000..856abcf
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/CreatedByHandler.java
@@ -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";
+ }
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/DeptTenantHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/DeptTenantHandler.java
new file mode 100644
index 0000000..5f16198
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/DeptTenantHandler.java
@@ -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";
+ }
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/FieldFillHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/FieldFillHandler.java
new file mode 100644
index 0000000..5a81f0a
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/FieldFillHandler.java
@@ -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;
+ }
+
+
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/JsonTypeHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/JsonTypeHandler.java
new file mode 100644
index 0000000..afb7f86
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/JsonTypeHandler.java
@@ -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 extends BaseTypeHandler {
+
+ private static ObjectMapper objectMapper = new ObjectMapper();
+
+ private Class type;
+
+ public JsonTypeHandler(Class 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/ListTypeHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/ListTypeHandler.java
new file mode 100644
index 0000000..e29ef61
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/ListTypeHandler.java
@@ -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 {
+ public ListTypeHandler() {
+ super(List.class);
+ }
+}
diff --git a/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/MapTypeHandler.java b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/MapTypeHandler.java
new file mode 100644
index 0000000..1cece29
--- /dev/null
+++ b/remi-comm/remi-comm-jdbc/src/main/java/ai/remi/comm/jdbc/handler/MapTypeHandler.java
@@ -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