feat:V3.1.1整合remi-comm到remi-cloud中
This commit is contained in:
14
derby.log
14
derby.log
@@ -1,12 +1,12 @@
|
||||
----------------------------------------------------------------
|
||||
Sun Sep 01 21:38:15 CST 2024:
|
||||
Booting Derby version The Apache Software Foundation - Apache Derby - 10.14.2.0 - (1828579): instance a816c00e-0191-adce-b9d3-000003b04a40
|
||||
on database directory memory:D:\Codes\remi\remi-cloud\4f82a0bb-9d60-4d6f-b6a3-e6480d5e589c with class loader jdk.internal.loader.ClassLoaders$AppClassLoader@60e53b93
|
||||
Loaded from file:/D:/Maven/sdt-repository/org/apache/derby/derby/10.14.2.0/derby-10.14.2.0.jar
|
||||
Wed Sep 11 17:08:24 CST 2024:
|
||||
Booting Derby version The Apache Software Foundation - Apache Derby - 10.14.2.0 - (1828579): instance a816c00e-0191-e057-45b8-000002512970
|
||||
on database directory memory:D:\Codes\remi-top\remi-cloud\1ec76c96-9207-4b71-b497-ffec1d63b532 with class loader jdk.internal.loader.ClassLoaders$AppClassLoader@5e481248
|
||||
Loaded from file:/D:/Maven/remi-top-repo/org/apache/derby/derby/10.14.2.0/derby-10.14.2.0.jar
|
||||
java.vendor=Amazon.com Inc.
|
||||
java.runtime.version=17.0.11+9-LTS
|
||||
user.dir=D:\Codes\remi\remi-cloud
|
||||
os.name=Windows 11
|
||||
java.runtime.version=17.0.12+7-LTS
|
||||
user.dir=D:\Codes\remi-top\remi-cloud
|
||||
os.name=Windows 10
|
||||
os.arch=amd64
|
||||
os.version=10.0
|
||||
derby.system.home=null
|
||||
|
||||
155
pom.xml
155
pom.xml
@@ -4,9 +4,8 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-cloud</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.1.1</version>
|
||||
<packaging>pom</packaging>
|
||||
<url>https://github.com/remi/remi-cloud</url>
|
||||
<name>remi-cloud</name>
|
||||
<description>Remi Cloud</description>
|
||||
|
||||
@@ -19,7 +18,7 @@
|
||||
|
||||
<properties>
|
||||
<!--remi-->
|
||||
<remi.version>3.1.0</remi.version>
|
||||
<remi.version>3.1.1</remi.version>
|
||||
<!--jdk-->
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
@@ -31,71 +30,59 @@
|
||||
<spring-boot.version>2.7.18</spring-boot.version>
|
||||
<spring-cloud.version>2021.0.9</spring-cloud.version>
|
||||
<spring-cloud-alibaba.version>2021.0.6.0</spring-cloud-alibaba.version>
|
||||
<!--1.15-->
|
||||
<commons-codec.version>1.17.0</commons-codec.version>
|
||||
<!--3.2.2-->
|
||||
<commons-collections.version>3.2.2</commons-collections.version>
|
||||
<!--1.10.0-->
|
||||
<commons-text.version>1.10.0</commons-text.version>
|
||||
<!--2.11.0-->
|
||||
<!--commons-->
|
||||
<commons-text.version>1.12.0</commons-text.version>
|
||||
<commons-io.version>2.16.1</commons-io.version>
|
||||
<!--3.12.0-->
|
||||
<commons-lang3.version>3.14.0</commons-lang3.version>
|
||||
<!--1.0.4-->
|
||||
<commons-logging.version>1.3.1</commons-logging.version>
|
||||
<!--3.8.0-->
|
||||
<commons-net.version>3.10.0</commons-net.version>
|
||||
<!--1.7-->
|
||||
<commons-validator.version>1.9.0</commons-validator.version>
|
||||
<!--1.4-->
|
||||
<commons-lang3.version>3.17.0</commons-lang3.version>
|
||||
<commons-codec.version>1.17.1</commons-codec.version>
|
||||
<commons-logging.version>1.3.4</commons-logging.version>
|
||||
<commons-collections.version>3.2.2</commons-collections.version>
|
||||
<commons-net.version>3.11.1</commons-net.version>
|
||||
<commons-fileupload.version>1.5</commons-fileupload.version>
|
||||
<!--1.0.10-->
|
||||
<device.version>1.0.10</device.version>
|
||||
<!--2.1.3-->
|
||||
<dom4j.version>2.1.3</dom4j.version>
|
||||
<!--1.2.9-->
|
||||
<druid.version>1.2.9</druid.version>
|
||||
<!--3.1.1-->
|
||||
<easyexcel.version>3.1.1</easyexcel.version>
|
||||
<!--6.2.5.Final-->
|
||||
<hibernate-validator.version>6.2.5.Final</hibernate-validator.version>
|
||||
<!--1.21-->
|
||||
<bitwalker.version>1.21</bitwalker.version>
|
||||
<!--2.7.0-->
|
||||
<ip2region.version>2.7.0</ip2region.version>
|
||||
<!--5.8.4-->
|
||||
<!--hutool-->
|
||||
<hutool.version>5.8.4</hutool.version>
|
||||
<!--31.1-jre-->
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
<!--2.9.0-->
|
||||
<!--storage-->
|
||||
<tika.version>2.7.0</tika.version>
|
||||
<qiniu.version>7.13.1</qiniu.version>
|
||||
<aliyun-sdk-oss.version>3.10.2</aliyun-sdk-oss.version>
|
||||
<minio.version>8.5.12</minio.version>
|
||||
<awssdk.version>2.26.3</awssdk.version>
|
||||
<cos_api.version>5.6.213</cos_api.version>
|
||||
<!--easyexcel-->
|
||||
<easyexcel.version>4.0.2</easyexcel.version>
|
||||
<!--xxl-job-->
|
||||
<xxl-job.version>2.4.1</xxl-job.version>
|
||||
<!--user-agent-->
|
||||
<device.version>1.0.10</device.version>
|
||||
<bitwalker.version>1.21</bitwalker.version>
|
||||
<!--xml-dom-->
|
||||
<dom4j.version>2.1.3</dom4j.version>
|
||||
<!--validator-->
|
||||
<commons-validator.version>1.9.0</commons-validator.version>
|
||||
<hibernate-validator.version>8.0.1.Final</hibernate-validator.version>
|
||||
<!--ip2region-->
|
||||
<ip2region.version>2.7.0</ip2region.version>
|
||||
<!--json-->
|
||||
<gson.version>2.9.0</gson.version>
|
||||
<!--2.0.6-->
|
||||
<fastjson.version>2.0.6</fastjson.version>
|
||||
<!--2.13.3-->
|
||||
<jackson.version>2.13.3</jackson.version>
|
||||
<!--4.0.0-->
|
||||
<java-jwt.version>0.12.6</java-jwt.version>
|
||||
<!--4.0.1-->
|
||||
<!--java-extend-->
|
||||
<guava.version>33.3.0-jre</guava.version>
|
||||
<javax.version>4.0.1</javax.version>
|
||||
<!--2.3.1-->
|
||||
<jaxb-api.version>2.3.1</jaxb-api.version>
|
||||
<!--3.3.0-->
|
||||
<cglib.version>3.3.0</cglib.version>
|
||||
<!--4.5.0-->
|
||||
<!--knife4j-->
|
||||
<knife4j.version>4.5.0</knife4j.version>
|
||||
<!--2.6.0-->
|
||||
<springdoc.version>2.6.0</springdoc.version>
|
||||
<!--1.18.34-->
|
||||
<!--mapstruct-->
|
||||
<lombok.version>1.18.34</lombok.version>
|
||||
<!--1.5.2.Final-->
|
||||
<mapstruct.version>1.5.2.Final</mapstruct.version>
|
||||
<!--0.2.0-->
|
||||
<lombok-mapstruct.version>0.2.0</lombok-mapstruct.version>
|
||||
<!--3.5.2-->
|
||||
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
||||
<!--2.2.2-->
|
||||
<!--jdbc-->
|
||||
<druid.version>1.2.9</druid.version>
|
||||
<mybatis.version>2.2.2</mybatis.version>
|
||||
<!--rdb-sql-->
|
||||
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
||||
<!--jdbc rom-->
|
||||
<mysql.version>8.0.33</mysql.version>
|
||||
<ojdbc.version>19.7.0.0</ojdbc.version>
|
||||
<mssql-jdbc.version>7.4.1.jre8</mssql-jdbc.version>
|
||||
@@ -103,26 +90,20 @@
|
||||
<postgresql.version>42.6.0</postgresql.version>
|
||||
<h2.db.version>2.2.224</h2.db.version>
|
||||
<mongodb-driver-sync.version>4.10.2</mongodb-driver-sync.version>
|
||||
<!--4.5.13-->
|
||||
<!--http-->
|
||||
<httpclient.version>4.5.13</httpclient.version>
|
||||
<!--8.4.6 4.10.0-->
|
||||
<minio.version>8.5.12</minio.version>
|
||||
<!--4.9.3-->
|
||||
<okhttp3.version>4.12.0</okhttp3.version>
|
||||
<!--velocity-->
|
||||
<velocity.version>1.7</velocity.version>
|
||||
<!--2.4.1-->
|
||||
<xxl-job.version>2.4.1</xxl-job.version>
|
||||
<!--8.13.0-->
|
||||
<!--logger-->
|
||||
<trace.version>8.13.0</trace.version>
|
||||
<!--1.7.36-->
|
||||
<slf4j.version>1.7.32</slf4j.version>
|
||||
<!--logback-->
|
||||
<logback.version>1.3.14</logback.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<!--基础服务-->
|
||||
<module>remi-comm</module>
|
||||
<module>remi-nacos</module>
|
||||
<module>remi-sys</module>
|
||||
<module>remi-demo</module>
|
||||
@@ -160,82 +141,82 @@
|
||||
<!--内部服务依赖开始-->
|
||||
<!--remi-comm-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-core</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-minio</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-redis</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-jdbc</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-domain</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-exception</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-feign</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-job</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-log</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-swagger</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-thread</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-util</artifactId>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-auth</artifactId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-safe</artifactId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.comm</groupId>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-web</artifactId>
|
||||
<version>${remi.version}</version>
|
||||
</dependency>
|
||||
@@ -396,12 +377,12 @@
|
||||
<groupId>commons-fileupload</groupId>
|
||||
<version>${commons-fileupload.version}</version>
|
||||
</dependency>
|
||||
<!-- validator -->
|
||||
<dependency>
|
||||
<artifactId>commons-validator</artifactId>
|
||||
<groupId>commons-validator</groupId>
|
||||
<version>${commons-validator.version}</version>
|
||||
</dependency>
|
||||
<!-- hibernate-validator -->
|
||||
<dependency>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
@@ -565,22 +546,6 @@
|
||||
<artifactId>velocity</artifactId>
|
||||
<version>${velocity.version}</version>
|
||||
</dependency>
|
||||
<!-- jwt -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${java-jwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${java-jwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${java-jwt.version}</version>
|
||||
</dependency>
|
||||
<!-- jaxb-api -->
|
||||
<dependency>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
|
||||
60
remi-comm/README.md
Normal file
60
remi-comm/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">Remi Comm</h1>
|
||||
|
||||
## 平台简介
|
||||
|
||||
Remi Comm 即 Remi Dev Tools 启迪快速开发平台所依赖的公共依赖模块。
|
||||
|
||||
## 项目文档
|
||||
|
||||
https://remi.yuque.com/r/organizations/homepage
|
||||
|
||||
## 体验地址
|
||||
|
||||
https://remi.run
|
||||
|
||||
RemiAdmin/123456
|
||||
|
||||
## 项目源码
|
||||
|
||||
### Gitee
|
||||
|
||||
https://gitee.com/remi-top
|
||||
|
||||
### GitTok
|
||||
|
||||
https://gittok.com/remi-top
|
||||
|
||||
### Gitlab
|
||||
|
||||
https://gitlab.com/remi-top
|
||||
|
||||
### Github
|
||||
|
||||
https://github.com/remi-top
|
||||
|
||||
## 系统模块
|
||||
|
||||
~~~
|
||||
remi-comm
|
||||
├── remi-comm-auth // 权限控制
|
||||
├── remi-comm-core // 核心模块
|
||||
├── remi-comm-domain // 领域模型
|
||||
├── remi-comm-jdbc // 数据配置
|
||||
├── remi-comm-exception // 异常模块
|
||||
├── remi-comm-feign // 通信模块
|
||||
├── remi-comm-job // 任务模块
|
||||
├── remi-comm-minio // MinIo 文件存储
|
||||
├── remi-comm-activemq // ActiveMQ
|
||||
├── remi-comm-rabbitmq // RabbitMQ
|
||||
├── remi-comm-rocketmq // RocketMQ
|
||||
├── remi-comm-redis // 缓存服务
|
||||
├── remi-comm-util // 工具类
|
||||
├── remi-comm-thread // 线程池
|
||||
├── remi-comm-swagger // 系统接口
|
||||
├── remi-comm-netty // Netty
|
||||
├── remi-comm-safe // 安全控制
|
||||
├── remi-comm-web // Web
|
||||
├── remi-comm-webservice // WebService
|
||||
├── remi-comm-websocket // WebSocket
|
||||
├── pom.xml // 公共依赖
|
||||
~~~
|
||||
56
remi-comm/pom.xml
Normal file
56
remi-comm/pom.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<name>remi-comm</name>
|
||||
<packaging>pom</packaging>
|
||||
<description>瑞米平台-公共依赖</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-cloud</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<!--子模块管理-->
|
||||
<modules>
|
||||
<module>remi-comm-core</module>
|
||||
<module>remi-comm-util</module>
|
||||
<module>remi-comm-domain</module>
|
||||
<module>remi-comm-jdbc</module>
|
||||
<module>remi-comm-thread</module>
|
||||
<module>remi-comm-job</module>
|
||||
<module>remi-comm-log</module>
|
||||
<module>remi-comm-minio</module>
|
||||
<module>remi-comm-redis</module>
|
||||
<module>remi-comm-feign</module>
|
||||
<module>remi-comm-email</module>
|
||||
<module>remi-comm-exception</module>
|
||||
<module>remi-comm-auth</module>
|
||||
<module>remi-comm-safe</module>
|
||||
<module>remi-comm-swagger</module>
|
||||
<module>remi-comm-web</module>
|
||||
</modules>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
</project>
|
||||
59
remi-comm/remi-comm-auth/pom.xml
Normal file
59
remi-comm/remi-comm-auth/pom.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-auth</artifactId>
|
||||
<name>remi-comm-auth</name>
|
||||
<description>remi-comm-auth</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--公共依赖-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-exception</artifactId>
|
||||
</dependency>
|
||||
<!--spring-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<!--lombok-->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,34 @@
|
||||
package ai.remi.comm.auth.annotation;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author DianJiu 【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @date 2022-07-19
|
||||
* @desc 权限校验
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public @interface RbacPermission {
|
||||
|
||||
/**
|
||||
* 角色
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String roles() default "";
|
||||
|
||||
/**
|
||||
* 权限
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String permissions() default "";
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package ai.remi.comm.auth.aspect;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import ai.remi.comm.auth.annotation.RbacPermission;
|
||||
import ai.remi.comm.core.constant.Constants;
|
||||
import ai.remi.comm.core.constant.HeaderConstant;
|
||||
import ai.remi.comm.exception.custom.BusinessException;
|
||||
import ai.remi.comm.redis.service.RedisService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author DianJiu 【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @date 2022-07-19
|
||||
* @desc
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Order(10)
|
||||
@Slf4j
|
||||
public class RbacPermissionAspect {
|
||||
private static final Logger logger = LoggerFactory.getLogger(RbacPermissionAspect.class);
|
||||
|
||||
private final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>();
|
||||
|
||||
private final String ROLE_MENU = "tdcd-cloud-mps:role-menu:{}";
|
||||
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
|
||||
@Pointcut("@annotation(ai.remi.comm.auth.annotation.RbacPermission)")
|
||||
public void rbacPermissionPointCut() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 环绕通知
|
||||
* 方式一:传入注解,获取注解中的参数
|
||||
*
|
||||
* @Around("logRecordPointCut()&&@annotation(logRecord)") public void doAround(ProceedingJoinPoint joinPoint, LogRecord logRecord) {}
|
||||
* 方式二:通过切点获取注解实体
|
||||
*/
|
||||
@Around("rbacPermissionPointCut()")
|
||||
//@Around("rbacPermissionPointCut()&&@annotation(RbacPermission)")
|
||||
//public void doAround(ProceedingJoinPoint joinPoint, RbacPermission rbacPermission) {
|
||||
public Object doAround(ProceedingJoinPoint joinPoint) {
|
||||
// 接收到请求
|
||||
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
|
||||
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
|
||||
HttpServletRequest request = sra.getRequest();
|
||||
// 记录请求内容,threadInfo存储所有内容
|
||||
Map<String, Object> threadInfo = new HashMap<>();
|
||||
//该方法用于获取客户端发出请求时的完整URL,包括协议、服务器名、端口号、资源路径等信息,但不包括后面的查询参数部分。
|
||||
// 注意,getRequestRUL()方法返回的时StringBuffer类型,而不是String类型。
|
||||
threadInfo.put("url", request.getRequestURL());
|
||||
//该方法用于获取请求行中资源名称部分,即位于URL的主机和端口之后、参数部分之前的部分
|
||||
threadInfo.put("uri", request.getRequestURI());
|
||||
//该方法用于获取请求的协议名,例如http、https或ftp
|
||||
threadInfo.put("scheme", request.getScheme());
|
||||
//该方法用于获取HTTP请求消息中的请求方式(如GET、POST等)
|
||||
threadInfo.put("method", request.getMethod());
|
||||
logger.info("请求方式:{}", threadInfo.get("method"));
|
||||
//该方法用于获取请求客户端的IP地址,其格式类似于"192.168.0.1"
|
||||
threadInfo.put("ip", request.getRemoteAddr());
|
||||
logger.info("请求ip:{}", threadInfo.get("ip"));
|
||||
//该方法用于获取Content-Type头字段的值,结果为String类型
|
||||
threadInfo.put("contentType", request.getContentType());
|
||||
//包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号。
|
||||
threadInfo.put("userAgent", request.getHeader("User-Agent"));
|
||||
//threadInfo.put("businessNo", request.getHeader("businessNo"));
|
||||
threadLocal.set(threadInfo);
|
||||
//获取方法签名
|
||||
MethodSignature signature = getSignature(joinPoint);
|
||||
//获取注解对象
|
||||
RbacPermission rbacPermission = getAnnotation(joinPoint, RbacPermission.class);
|
||||
//获取注解方法
|
||||
Method method = getMethod(joinPoint);
|
||||
// 获取连接点所在类名
|
||||
String classname = joinPoint.getTarget().getClass().getSimpleName();
|
||||
// 获取连接点所在方法名称
|
||||
String methodName = method.getName();
|
||||
String functionName = String.format("【%s.%s()】", classname, methodName);
|
||||
threadInfo.put("functionName", functionName);
|
||||
logger.info("请求类方法:{}", threadInfo.get("functionName"));
|
||||
// 下面两个数组中,参数值和参数名的个数和位置是一一对应的。
|
||||
// 参数名
|
||||
String[] argNames = signature.getParameterNames();
|
||||
// 参数值
|
||||
Object[] args = joinPoint.getArgs();
|
||||
StringBuilder functionArgs = new StringBuilder();
|
||||
//不为空时便利组装
|
||||
if (Objects.nonNull(argNames) && argNames.length > 0 && args.length > 0) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
functionArgs.append(argNames[i] + ":" + args[i] + ";");
|
||||
}
|
||||
}
|
||||
threadInfo.put("functionArgs", functionArgs.toString());
|
||||
logger.info("请求参数:{}", threadInfo.get("functionArgs"));
|
||||
|
||||
// threadInfo.put("accessToken", request.getHeader("accessToken"));
|
||||
threadInfo.put("accessToken", request.getHeader(HeaderConstant.X_ACCESS_TOKEN));
|
||||
logger.info("鉴权令牌:{}", threadInfo.get("accessToken"));
|
||||
|
||||
// 权限校验
|
||||
if (method.isAnnotationPresent(RbacPermission.class)) {
|
||||
String roles = rbacPermission.roles();
|
||||
String permissions = rbacPermission.permissions();
|
||||
|
||||
// 根据鉴权令牌去redis查询到用户信息、角色信息、权限信息
|
||||
//Map map = redisService.hmget(RedisDemoEnum.ACCESS_TOKEN.getKey(String.valueOf(threadInfo.get("accessToken"))));
|
||||
Map map = redisService.hmget(String.valueOf(threadInfo.get("accessToken")));
|
||||
if (null == map || map.size() == 0) {
|
||||
throw new BusinessException(Constants.LOGIN_NO_PERMISSION, "accessToken expire! please login again!");
|
||||
}
|
||||
String roleCode = String.valueOf(map.get("roleCode"));
|
||||
List<String> roleCodes = Arrays.asList(roleCode.split(","));
|
||||
// 校验角色
|
||||
if (StringUtils.isNotBlank(roles)) {
|
||||
// 校验用户角色
|
||||
// JSONObject jsonObject = JSONObject.parseObject(String.valueOf(map.get("user")));
|
||||
// if (!Arrays.asList(roles.split(",")).contains(jsonObject.get("roleCode"))) {
|
||||
// throw new BusinessException(Constants.CODE_403, "当前角色权限不足");
|
||||
// }
|
||||
for (String authRoleCode : Arrays.asList(roles.split(","))) {
|
||||
if (!roleCodes.contains(authRoleCode)){
|
||||
throw new BusinessException(Constants.CODE_403, "the role no Permission!");
|
||||
}else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if (!Arrays.asList(roles.split(",")).contains(roleCode)) {
|
||||
// throw new BusinessException(Constants.CODE_403, "the role no Permission!");
|
||||
// }
|
||||
}
|
||||
// 校验权限
|
||||
if (StringUtils.isNotBlank(permissions)) {
|
||||
// 组装用户权限
|
||||
StringBuilder userPermissions = new StringBuilder();
|
||||
|
||||
// String menuString = JSONObject.toJSONString(map.get("menus"));
|
||||
// List<JSONObject> menus = JSONObject.parseArray(menuString);
|
||||
List<JSONObject> menus = new ArrayList<>();
|
||||
for (String code : roleCodes) {
|
||||
String data = null;
|
||||
try {
|
||||
data = redisService.getString(StrUtil.format(ROLE_MENU, code));
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户权限异常:{}", e.getMessage());
|
||||
throw new BusinessException(Constants.CODE_403, "get user Permission error!");
|
||||
}
|
||||
List<JSONObject> menuList = JSONObject.parseArray(data, JSONObject.class);
|
||||
menus.addAll(menuList);
|
||||
}
|
||||
|
||||
for (int i = 0; i < menus.size(); i++) {
|
||||
if ("2".equals(menus.get(i).get("menuType"))) {
|
||||
userPermissions.append(menus.get(i).get("menuPermission"));
|
||||
if (i != menus.size() - 1) {
|
||||
userPermissions.append(",");
|
||||
}
|
||||
}
|
||||
}
|
||||
// 校验用户权限
|
||||
if (!Arrays.asList(userPermissions.toString().split(",")).containsAll(Arrays.asList(permissions.split(",")))) {
|
||||
throw new BusinessException(Constants.CODE_403, "the user no Permission");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//执行到这里开始走进来的方法体(必须声明)
|
||||
Object returnValue;
|
||||
try {
|
||||
returnValue = joinPoint.proceed(args);
|
||||
} catch (Throwable throwable) {
|
||||
throwable.printStackTrace();
|
||||
throw new BusinessException(((BusinessException) throwable).getCode(), (throwable).getMessage());
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
|
||||
private MethodSignature getSignature(JoinPoint joinPoint) {
|
||||
//通过连接点获取方法签名
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
return signature;
|
||||
}
|
||||
|
||||
private Method getMethod(JoinPoint joinPoint) {
|
||||
//通过连接点获取方法签名
|
||||
MethodSignature signature = getSignature(joinPoint);
|
||||
//获取注解服务的方法
|
||||
Method method = signature.getMethod();
|
||||
return method;
|
||||
}
|
||||
|
||||
private <T extends Annotation> T getAnnotation(JoinPoint joinPoint, Class<T> annotationClass) {
|
||||
Method method = getMethod(joinPoint);
|
||||
//获取注解类
|
||||
T annotation = method.getAnnotation(annotationClass);
|
||||
return annotation;
|
||||
}
|
||||
|
||||
}
|
||||
83
remi-comm/remi-comm-core/README.md
Normal file
83
remi-comm/remi-comm-core/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
## 方式1: 实现ApplicationContextInitializer接口
|
||||
实现接口后,将此类注入到Spring容器中,有两种方式。当然也可以直接调用静态方法
|
||||
|
||||
第一种: 在此类上加`@Component`注解
|
||||
|
||||
第二种:在 `resources/META-INF/spring.factories`文件中添加以下配置: org.springframework.context.ApplicationContextInitializer=CustomerApplicationContextInitializer的路径
|
||||
|
||||
实现ApplicationContextInitializer的源码示例:
|
||||
```java
|
||||
|
||||
public class CustomApplicationContextInitializer implements ApplicationContextInitializer {
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
SpringBeanUtils.setApplicationContext(applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 方式2. 实现ApplicationListener接口
|
||||
实现接口后,将此类注入到Spring容器中,有两种方式。当然也可以直接调用静态方法
|
||||
|
||||
第一种: 在此类上加`@Component`注解
|
||||
|
||||
第二种:在 `resources/META-INF/spring.factories`文件中添加以下配置: org.springframework.context.ApplicationContextInitializer=CustomerApplicationListener的路径
|
||||
|
||||
实现ApplicationListener的源码示例:
|
||||
```java
|
||||
|
||||
public class CustomApplicationListener implements ApplicationListener<ApplicationContextEvent> {
|
||||
@Override
|
||||
public void onApplicationEvent(ApplicationContextEvent event) {
|
||||
SpringBeanUtils.setApplicationContext(event.getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
方式3. 在启动类main方法中设置
|
||||
```java
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableSwagger2
|
||||
public class Application {
|
||||
public static void main(String[] args) {
|
||||
ConfigurableApplicationContext applicationContext = SpringApplication.run(WangMikeSpringApplication.class, args);
|
||||
SpringBeanUtils.setApplicationContext(applicationContext);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
方式4. 实现ApplicationContextAware接口
|
||||
```java
|
||||
|
||||
@Component
|
||||
public class SpringBeanUtils implements ApplicationContextAware {
|
||||
private static ApplicationContext applicationContext;
|
||||
public void setApplicationContext(ApplicationContext applicationContext){
|
||||
SpringBeanUtils.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
public static ApplicationContext getApplicationContext(
|
||||
return applicationContext;
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
方式5. 直接通过@Resource注解注入
|
||||
```java
|
||||
|
||||
@Component
|
||||
@Order(value = 1)
|
||||
@Slf4j
|
||||
public class AppStartRunner implements org.springframework.boot.ApplicationRunner {
|
||||
protected static Logger logger = LoggerFactory.getLogger(AppStartRunner.class);
|
||||
|
||||
@Resource
|
||||
ApplicationContext applicationContext;
|
||||
}
|
||||
|
||||
```
|
||||
55
remi-comm/remi-comm-core/pom.xml
Normal file
55
remi-comm/remi-comm-core/pom.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-core</artifactId>
|
||||
<name>remi-comm-core</name>
|
||||
<description>remi-comm-core</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!-- spring -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<artifactId>guava</artifactId>
|
||||
<groupId>com.google.guava</groupId>
|
||||
</dependency>
|
||||
|
||||
<!-- 解析user-agent的包 -->
|
||||
<dependency>
|
||||
<artifactId>device-detector</artifactId>
|
||||
<groupId>io.github.mngsk</groupId>
|
||||
</dependency>
|
||||
|
||||
<!--lombok-->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,44 @@
|
||||
package ai.remi.comm.core.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc AuthFilterConfig
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "filter.config")
|
||||
public class AuthFilterConfig {
|
||||
|
||||
/**
|
||||
* 公共忽略的url,网关、公共web的AuthFilter 都会忽略---- 配置在application-dev.yaml
|
||||
*/
|
||||
private List<String> commonIgnoreUrl= new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 网关忽略的ur----配置在ebg-shop-gateway-dev.yaml
|
||||
*/
|
||||
private List<String> gatewayIgnoreUrl=new ArrayList<>();
|
||||
|
||||
/**
|
||||
*各个服务自定义忽略的url---配置在自己的服务
|
||||
*/
|
||||
private List<String> customIgnoreUrl= new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 是否验证权限----配置在idaas服务
|
||||
*/
|
||||
private Boolean verifyPermission=true;
|
||||
/**
|
||||
* 只验证token不验证 权限的
|
||||
*/
|
||||
private List<String> onlyVerifyToken=new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package ai.remi.comm.core.constant;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 流程审批常量信息
|
||||
*/
|
||||
public class ApprovalStatusConstants {
|
||||
|
||||
/**
|
||||
* 待审批
|
||||
*/
|
||||
public static final Integer DSP = 0;
|
||||
|
||||
/**
|
||||
* 审批中
|
||||
*/
|
||||
public static final Integer SPZ = 1;
|
||||
|
||||
/**
|
||||
* 审批完成
|
||||
*/
|
||||
public static final Integer SPWC = 2;
|
||||
|
||||
/**
|
||||
* 已撤销
|
||||
*/
|
||||
public static final Integer YCX = 3;
|
||||
|
||||
/**
|
||||
* 已驳回
|
||||
*/
|
||||
public static final Integer YBH = 4;
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package ai.remi.comm.core.constant;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 通用常量信息
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
/**
|
||||
* UTF-8 字符集
|
||||
*/
|
||||
public static final String UTF8 = "UTF-8";
|
||||
|
||||
/**
|
||||
* GBK 字符集
|
||||
*/
|
||||
public static final String GBK = "GBK";
|
||||
|
||||
/**
|
||||
* RMI 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_RMI = "rmi:";
|
||||
|
||||
/**
|
||||
* LDAP 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_LDAP = "ldap:";
|
||||
|
||||
/**
|
||||
* LDAPS 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_LDAPS = "ldaps:";
|
||||
|
||||
/**
|
||||
* http请求
|
||||
*/
|
||||
public static final String HTTP = "http://";
|
||||
|
||||
/**
|
||||
* https请求
|
||||
*/
|
||||
public static final String HTTPS = "https://";
|
||||
|
||||
/**
|
||||
* 成功标记
|
||||
*/
|
||||
public static final String SUCCESS = "000000";
|
||||
|
||||
/**
|
||||
* 失败标记
|
||||
*/
|
||||
public static final String ERROR = "111111";
|
||||
|
||||
/**
|
||||
* 登录成功状态
|
||||
*/
|
||||
public static final String LOGIN_SUCCESS_STATUS = "0";
|
||||
|
||||
/**
|
||||
* 登录失败状态
|
||||
*/
|
||||
public static final String LOGIN_FAIL_STATUS = "1";
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
public static final String LOGIN_SUCCESS = "Success";
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
public static final String LOGIN_FAIL = "Error";
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
public static final String LOGOUT = "Logout";
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
public static final String REGISTER = "Register";
|
||||
|
||||
/**
|
||||
* 当前记录起始索引
|
||||
*/
|
||||
public static final String PAGE_NUM = "pageNum";
|
||||
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
public static final String PAGE_SIZE = "pageSize";
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
public static final String ORDER_BY_COLUMN = "orderByColumn";
|
||||
|
||||
/**
|
||||
* 排序的方向 "desc" 或者 "asc".
|
||||
*/
|
||||
public static final String IS_ASC = "isAsc";
|
||||
|
||||
/**
|
||||
* 验证码 redis key
|
||||
*/
|
||||
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
|
||||
|
||||
/**
|
||||
* 验证码有效期(分钟)
|
||||
*/
|
||||
public static final long CAPTCHA_EXPIRATION = 2;
|
||||
|
||||
|
||||
/**
|
||||
* 参数管理 cache key
|
||||
*/
|
||||
public static final String SYS_CONFIG_KEY = "sys_config:";
|
||||
|
||||
/**
|
||||
* 字典管理 cache key
|
||||
*/
|
||||
public static final String SYS_DICT_KEY = "sys_dict:";
|
||||
|
||||
/**
|
||||
* 资源映射路径 前缀
|
||||
*/
|
||||
public static final String RESOURCE_PREFIX = "/profile";
|
||||
|
||||
/**
|
||||
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
|
||||
*/
|
||||
public static final String[] JOB_WHITELIST_STR = {"ai.remi"};
|
||||
|
||||
/**
|
||||
* 定时任务违规的字符
|
||||
*/
|
||||
public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
|
||||
"org.springframework", "org.apache", "ai.remi.comm.core.utils.file"};
|
||||
|
||||
/**
|
||||
* 系统市公司编号
|
||||
*/
|
||||
public static final String SYS_COMPANY_ID = "1667075454562709506";
|
||||
|
||||
/**
|
||||
* 登录无权限
|
||||
*/
|
||||
public static final String LOGIN_NO_PERMISSION = "401";
|
||||
|
||||
/**
|
||||
* 无权限访问资源
|
||||
*/
|
||||
public static final String CODE_401 = "401";
|
||||
|
||||
/**
|
||||
* 当前角色权限不足
|
||||
*/
|
||||
public static final String CODE_403 = "403";
|
||||
|
||||
/**
|
||||
* SRS校验数据错误码
|
||||
*/
|
||||
public static final String SRS_DATA_CHECK_ERROR = "A02001";
|
||||
|
||||
/**
|
||||
* 功能检验错误码
|
||||
*/
|
||||
public static final String COMM_CHECK_ERROR = "222222";
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package ai.remi.comm.core.constant;
|
||||
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 过滤器忽略常量
|
||||
*/
|
||||
public class FilterIgnoreConstant {
|
||||
/**
|
||||
* 默认全部忽略的url 一般用于跟服务无关的
|
||||
*/
|
||||
public static final Set<String> FILTER_COMMON_IGNORE_URL = Sets.newHashSet("/**/swagger**/**", "/**/webjars/**",
|
||||
"/**/v2/api-docs", "/**/v3/api-docs", "/**/error", "/**/doc.html", "/doc.html", "/**/favicon.ico");
|
||||
|
||||
/**
|
||||
* 全局拦截器忽略的服务
|
||||
*/
|
||||
public static final Set<String> AUTH_FILTER_IGNORE_SERVICE_NAME = Sets.newHashSet("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");
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package ai.remi.comm.core.constant;
|
||||
|
||||
import ai.remi.comm.core.enums.IdentityTypeEnum;
|
||||
import ai.remi.comm.core.enums.PermissionScopeEnum;
|
||||
import ai.remi.comm.core.enums.ServiceTypeEnum;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 全局请求头常量
|
||||
*/
|
||||
public class HeaderConstant {
|
||||
|
||||
/**
|
||||
* 部门租户id
|
||||
*/
|
||||
public static final String X_DEPT_TENANT_ID = "X-DEPT-TENANT-ID";
|
||||
|
||||
/**
|
||||
* 公司租户id
|
||||
*/
|
||||
public static final String X_COMPANY_TENANT_ID = "X-COMPANY-TENANT-ID";
|
||||
|
||||
/**
|
||||
* 当前登陆人唯一id
|
||||
*/
|
||||
public static final String X_UNIQUE_ID = "X-UNIQUE-ID";
|
||||
|
||||
/**
|
||||
* 用户系统语言
|
||||
*/
|
||||
public static final String X_USER_LANGUAGE = "X-USER-LANGUAGE";
|
||||
|
||||
/**
|
||||
* 用户设备唯一标识
|
||||
*/
|
||||
public static final String X_DISTINCT_ID = "X-DISTINCT-ID";
|
||||
|
||||
/**
|
||||
* 登录token
|
||||
*/
|
||||
public static final String X_ACCESS_TOKEN = "X-ACCESS-TOKEN";
|
||||
|
||||
/**
|
||||
* 服务类型
|
||||
*
|
||||
* @see ServiceTypeEnum
|
||||
*/
|
||||
public static final String X_SERVICE_TYPE = "X-SERVICE-TYPE";
|
||||
|
||||
/**
|
||||
* 身份类型 公司用户 访客用户 瑞米用户
|
||||
*
|
||||
* @see IdentityTypeEnum
|
||||
*/
|
||||
public static final String X_IDENTITY_TYPE = "X-IDENTITY-TYPE";
|
||||
|
||||
|
||||
/**
|
||||
* 此请求头为用户请求过来的为集团还是公司权限
|
||||
*
|
||||
* @see PermissionScopeEnum
|
||||
*/
|
||||
public static final String X_PERMISSION_SCOPE = "X-PERMISSION-SCOPE";
|
||||
|
||||
/**
|
||||
* 当权限类型为集团类型时,此请求头包含的为这个用户管理的所有公司id {@link PermissionScopeEnum}
|
||||
*/
|
||||
public static final String X_COMPANY_IDS = "X-COMPANY-IDS";
|
||||
|
||||
/**
|
||||
* 当权限类型为公司类型时,此请求头包含的为这个用户管理的所有部门id {@link PermissionScopeEnum}
|
||||
*/
|
||||
public static final String X_DEPT_IDS = "X-DEPT-IDS";
|
||||
|
||||
/**
|
||||
* 请求/响应头报文类型
|
||||
*/
|
||||
public static final String CONTENT_TYPE = "Content-Type";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package ai.remi.comm.core.constant;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc HTTP状态码
|
||||
*/
|
||||
public class HttpStatus {
|
||||
|
||||
/**
|
||||
* 操作成功
|
||||
*/
|
||||
public static final int SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 对象创建成功
|
||||
*/
|
||||
public static final int CREATED = 201;
|
||||
|
||||
/**
|
||||
* 请求已经被接受
|
||||
*/
|
||||
public static final int ACCEPTED = 202;
|
||||
|
||||
/**
|
||||
* 操作已经执行成功,但是没有返回数据
|
||||
*/
|
||||
public static final int NO_CONTENT = 204;
|
||||
|
||||
/**
|
||||
* 资源已被移除
|
||||
*/
|
||||
public static final int MOVED_PERM = 301;
|
||||
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
public static final int SEE_OTHER = 303;
|
||||
|
||||
/**
|
||||
* 资源没有被修改
|
||||
*/
|
||||
public static final int NOT_MODIFIED = 304;
|
||||
|
||||
/**
|
||||
* 参数列表错误(缺少,格式不匹配)
|
||||
*/
|
||||
public static final int BAD_REQUEST = 400;
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
public static final int UNAUTHORIZED = 401;
|
||||
|
||||
/**
|
||||
* 访问受限,授权过期
|
||||
*/
|
||||
public static final int FORBIDDEN = 403;
|
||||
|
||||
/**
|
||||
* 资源,服务未找到
|
||||
*/
|
||||
public static final int NOT_FOUND = 404;
|
||||
|
||||
/**
|
||||
* 不允许的http方法
|
||||
*/
|
||||
public static final int BAD_METHOD = 405;
|
||||
|
||||
/**
|
||||
* 资源冲突,或者资源被锁
|
||||
*/
|
||||
public static final int CONFLICT = 409;
|
||||
|
||||
/**
|
||||
* 不支持的数据,媒体类型
|
||||
*/
|
||||
public static final int UNSUPPORTED_TYPE = 415;
|
||||
|
||||
/**
|
||||
* 系统内部错误
|
||||
*/
|
||||
public static final int ERROR = 500;
|
||||
|
||||
/**
|
||||
* 接口未实现
|
||||
*/
|
||||
public static final int NOT_IMPLEMENTED = 501;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package ai.remi.comm.core.constant;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc OAuth2异常常量
|
||||
*/
|
||||
public class OAuth2Constants {
|
||||
|
||||
/**
|
||||
* 认证异常
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Error {
|
||||
USERNAME_NOT_FOUND("username_not_found", "用户名未找到", ""),
|
||||
BAD_CREDENTIALS("bad_credentials", "错误凭证", "AbstractUserDetailsAuthenticationProvider.badCredentials : Bad credentials"),
|
||||
USER_LOCKED("user_locked", "用户被锁", "AbstractUserDetailsAuthenticationProvider.locked : User account is locked"),
|
||||
USER_DISABLE("user_disable", "用户禁用", "AbstractUserDetailsAuthenticationProvider.disabled : User is disabled"),
|
||||
USER_EXPIRED("user_expired", "用户过期", "AbstractUserDetailsAuthenticationProvider.expired : User account has expired"),
|
||||
CREDENTIALS_EXPIRED("credentials_expired", "证书过期", "AbstractUserDetailsAuthenticationProvider.credentialsExpired : User credentials have expired"),
|
||||
INVALID_SCOPE("invalid_scope", "无效作用域", "AbstractAccessDecisionManager.accessDenied : invalid_scope"),
|
||||
SCOPE_IS_EMPTY("scope_is_empty", "scope 为空异常", ""),
|
||||
TOKEN_MISSING("token_missing", "令牌不存在", ""),
|
||||
UN_KNOW_LOGIN_ERROR("un_know_login_error", "未知的登录异常", ""),
|
||||
INVALID_BEARER_TOKEN("invalid_bearer_token", "不合法的Token", "");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
private final String msg;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package ai.remi.comm.core.constant;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc Token的Key通用常量
|
||||
*/
|
||||
public class TokenConstants {
|
||||
|
||||
/**
|
||||
* 令牌自定义标识
|
||||
*/
|
||||
public static final String AUTHENTICATION = "Authorization";
|
||||
|
||||
/**
|
||||
* 补充令牌自定义标识
|
||||
*/
|
||||
public static final String SUPPLY_AUTHORIZATION = "X-Access-Token";
|
||||
|
||||
/**
|
||||
* 令牌前缀
|
||||
*/
|
||||
public static final String PREFIX = "Remi";
|
||||
|
||||
/**
|
||||
* 回调地址
|
||||
*/
|
||||
public static final String REDIRECT_URL = "redirect_url";
|
||||
|
||||
/**
|
||||
* 令牌秘钥
|
||||
*/
|
||||
public final static String SECRET = "ce6e6cc15d0349b58f9d8c43e5f328ce7f329029434242c6aaaec5a06b930a4a4d73cf1a7e7b48eb80fad6a916ded78d614cc2e63779489d9a2aaaafd674c5515e708ba5869a4dcfa3e66799";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package ai.remi.comm.core.constant;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 用户常量信息
|
||||
*/
|
||||
public class UserConstants {
|
||||
|
||||
/**
|
||||
* 平台内系统用户的唯一标志
|
||||
*/
|
||||
public static final String SYS_USER = "SYS_USER";
|
||||
|
||||
/**
|
||||
* 正常状态
|
||||
*/
|
||||
public static final String NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 异常状态
|
||||
*/
|
||||
public static final String EXCEPTION = "1";
|
||||
|
||||
/**
|
||||
* 用户封禁状态
|
||||
*/
|
||||
public static final String USER_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 角色封禁状态
|
||||
*/
|
||||
public static final String ROLE_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 部门正常状态
|
||||
*/
|
||||
public static final String DEPT_NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 部门停用状态
|
||||
*/
|
||||
public static final String DEPT_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 字典正常状态
|
||||
*/
|
||||
public static final String DICT_NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 是否为系统默认(是)
|
||||
*/
|
||||
public static final String YES = "Y";
|
||||
|
||||
/**
|
||||
* 是否菜单外链(是)
|
||||
*/
|
||||
public static final String YES_FRAME = "0";
|
||||
|
||||
/**
|
||||
* 是否菜单外链(否)
|
||||
*/
|
||||
public static final String NO_FRAME = "1";
|
||||
|
||||
/**
|
||||
* 菜单类型(目录)
|
||||
*/
|
||||
public static final String TYPE_DIR = "M";
|
||||
|
||||
/**
|
||||
* 菜单类型(菜单)
|
||||
*/
|
||||
public static final String TYPE_MENU = "C";
|
||||
|
||||
/**
|
||||
* 菜单类型(按钮)
|
||||
*/
|
||||
public static final String TYPE_BUTTON = "F";
|
||||
|
||||
/**
|
||||
* Layout组件标识
|
||||
*/
|
||||
public final static String LAYOUT = "Layout";
|
||||
|
||||
/**
|
||||
* ParentView组件标识
|
||||
*/
|
||||
public final static String PARENT_VIEW = "ParentView";
|
||||
|
||||
/**
|
||||
* InnerLink组件标识
|
||||
*/
|
||||
public final static String INNER_LINK = "InnerLink";
|
||||
|
||||
/**
|
||||
* 校验返回结果码
|
||||
*/
|
||||
public final static String UNIQUE = "0";
|
||||
|
||||
public final static String NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 用户名长度限制
|
||||
*/
|
||||
public static final int USERNAME_MIN_LENGTH = 2;
|
||||
|
||||
public static final int USERNAME_MAX_LENGTH = 20;
|
||||
|
||||
/**
|
||||
* 密码长度限制
|
||||
*/
|
||||
public static final int PASSWORD_MIN_LENGTH = 5;
|
||||
|
||||
public static final int PASSWORD_MAX_LENGTH = 20;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package ai.remi.comm.core.enums;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 请求方式
|
||||
*/
|
||||
public enum HttpMethod {
|
||||
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
|
||||
|
||||
private static final Map<String, HttpMethod> mappings = new HashMap<>(16);
|
||||
|
||||
static {
|
||||
for (HttpMethod httpMethod : values()) {
|
||||
mappings.put(httpMethod.name(), httpMethod);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static HttpMethod resolve(@Nullable String method) {
|
||||
return (method != null ? mappings.get(method) : null);
|
||||
}
|
||||
|
||||
public boolean matches(String method) {
|
||||
return (this == resolve(method));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package ai.remi.comm.core.enums;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 身份类型枚举定义
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public enum IdentityTypeEnum {
|
||||
|
||||
STARTDIS("remi", "瑞米官方账号"),
|
||||
COMPANY("company", "集团公司账户"),
|
||||
VISITOR("visitor", "游客体验账号");
|
||||
|
||||
private String code;
|
||||
|
||||
private String desc;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package ai.remi.comm.core.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 权限范围枚举定义
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum PermissionScopeEnum {
|
||||
|
||||
GROUP("group","集团范围"),
|
||||
COMPANY("company","公司范围"),
|
||||
DEPT("dept","部门范围");
|
||||
|
||||
@JsonValue
|
||||
private String code;
|
||||
|
||||
private String desc;
|
||||
|
||||
@JsonCreator
|
||||
public static PermissionScopeEnum codeOf(String code) {
|
||||
return Arrays.stream(PermissionScopeEnum.values())
|
||||
.filter(each -> each.getCode().equals(code))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> {
|
||||
throw new IllegalArgumentException("非法参数");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package ai.remi.comm.core.enums;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 错误码设计描述
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public enum ServiceTypeEnum {
|
||||
|
||||
WEB_SERVICE("webService", "/web-api/**", "管理端服务"),
|
||||
APP_SERVICE("appService", "/app-api/**", "移动端服务");
|
||||
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 服务对应的路径前缀
|
||||
*/
|
||||
private String pathPrefix;
|
||||
|
||||
private String desc;
|
||||
|
||||
|
||||
public static ServiceTypeEnum codeOf(String code) {
|
||||
return Arrays.stream(values())
|
||||
.filter((each) -> each.getCode().equals(code)).findFirst()
|
||||
.orElseThrow(()->new RuntimeException("找不到服务"));
|
||||
}
|
||||
|
||||
public static ServiceTypeEnum pathPrefixOf(String pathPrefix){
|
||||
return Arrays.stream(values())
|
||||
.filter((each) -> each.getPathPrefix().equals(pathPrefix)).findFirst()
|
||||
.orElseThrow(()->new RuntimeException("找不到服务"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ai.remi.comm.core.enums;
|
||||
|
||||
public interface TypeEnum {
|
||||
/**
|
||||
* 类型编码
|
||||
*/
|
||||
Integer getCode();
|
||||
|
||||
/**
|
||||
* 类型描述
|
||||
*/
|
||||
String getDesc();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ai.remi.comm.core.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum YesNoEnum implements TypeEnum{
|
||||
|
||||
NO(0, "否"),
|
||||
|
||||
YES(1, "是");
|
||||
|
||||
@JsonValue
|
||||
private Integer code;
|
||||
|
||||
private String desc;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ai.remi.comm.core.excel;
|
||||
|
||||
public interface ExcelDynamicSelect {
|
||||
|
||||
/**
|
||||
* 获取动态生成的下拉框可选数据
|
||||
* @return 动态生成的下拉框可选数据
|
||||
*/
|
||||
String[] getSource();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ai.remi.comm.core.excel;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Documented
|
||||
@Target({ElementType.FIELD})//用此注解用在属性上。
|
||||
@Retention(RetentionPolicy.RUNTIME)//注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
|
||||
public @interface ExcelSelected {
|
||||
|
||||
/**
|
||||
* 固定下拉内容
|
||||
*/
|
||||
String[] source() default {};
|
||||
|
||||
/**
|
||||
* 动态下拉内容
|
||||
*/
|
||||
Class<? extends ExcelDynamicSelect>[] sourceClass() default {};
|
||||
|
||||
/**
|
||||
* 设置下拉框的起始行,默认为第二行
|
||||
*/
|
||||
int firstRow() default 1;
|
||||
|
||||
/**
|
||||
* 设置下拉框的结束行,默认为最后一行
|
||||
*/
|
||||
int lastRow() default 0x10000;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package ai.remi.comm.core.result;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc PagerBean
|
||||
*/
|
||||
@Data
|
||||
public class PagerBean<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 4804053559968742915L;
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
private Long total;
|
||||
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
protected Long pageNum;
|
||||
|
||||
/**
|
||||
* 页大小
|
||||
*/
|
||||
protected Long pageSize;
|
||||
|
||||
/**
|
||||
* 列表数据
|
||||
*/
|
||||
private List<?> records;
|
||||
|
||||
/**
|
||||
* 是否可导入
|
||||
*/
|
||||
private boolean if_import;
|
||||
public PagerBean() {
|
||||
}
|
||||
|
||||
public PagerBean(Long total, Long pageNum, Long pageSize, List<T> records) {
|
||||
this.total = total;
|
||||
this.pageNum = pageNum;
|
||||
this.pageSize = pageSize;
|
||||
this.records = records;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package ai.remi.comm.core.result;
|
||||
|
||||
import ai.remi.comm.core.constant.Constants;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc ResultBean
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
public class ResultBean<T> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8676131899637805509L;
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
public static final String SUCCESS = Constants.SUCCESS;
|
||||
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static final String ERROR = Constants.ERROR;
|
||||
|
||||
// 返回编码
|
||||
private String code;
|
||||
// 返回信息
|
||||
private String msg;
|
||||
// 返回数据封装
|
||||
@Builder.Default
|
||||
private T data = (T) "";
|
||||
|
||||
public ResultBean() {
|
||||
}
|
||||
|
||||
private static <T> ResultBean<T> restResult(String code, String msg, T data) {
|
||||
ResultBean<T> apiResult = new ResultBean<>();
|
||||
apiResult.setCode(code);
|
||||
apiResult.setData(data);
|
||||
apiResult.setMsg(msg);
|
||||
return apiResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功消息
|
||||
*
|
||||
* @return 成功消息
|
||||
*/
|
||||
public static <T> ResultBean<T> success() {
|
||||
return restResult(SUCCESS, null, null);
|
||||
}
|
||||
|
||||
public static <T> ResultBean<T> success(String msg) {
|
||||
return restResult(SUCCESS, msg, null);
|
||||
}
|
||||
|
||||
public static <T> ResultBean<T> success(T data) {
|
||||
return restResult(SUCCESS, null, data);
|
||||
}
|
||||
|
||||
public static <T> ResultBean<T> success(String msg, T data) {
|
||||
return restResult(SUCCESS, msg, data);
|
||||
}
|
||||
|
||||
public static <T> ResultBean<T> error() {
|
||||
return restResult(ERROR, null, null);
|
||||
}
|
||||
|
||||
public static <T> ResultBean<T> error(String msg) {
|
||||
return restResult(ERROR, msg, null);
|
||||
}
|
||||
|
||||
public static <T> ResultBean<T> error(T data) {
|
||||
return restResult(ERROR, null, data);
|
||||
}
|
||||
|
||||
public static <T> ResultBean<T> error(String msg, T data) {
|
||||
return restResult(ERROR, msg, data);
|
||||
}
|
||||
|
||||
public static <T> ResultBean<T> error(String code, String msg, T data) {
|
||||
return restResult(code, msg, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package ai.remi.comm.core.spring;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc SpringBeanUtils
|
||||
*/
|
||||
@Component
|
||||
public class SpringBeans implements ApplicationContextAware {
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
if (SpringBeans.applicationContext == null) {
|
||||
SpringBeans.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by dianjiu on 2022-06-23 ,contact lidianjiu@njydsz.com. 获取applicationContext.
|
||||
*/
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by dianjiu on 2022-06-23 ,contact lidianjiu@njydsz.com. 通过name获取 Bean.
|
||||
*/
|
||||
public static Object getBean(String name) {
|
||||
return getApplicationContext().getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by dianjiu on 2022-06-23 ,contact lidianjiu@njydsz.com. 通过class获取Bean.
|
||||
*/
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
return getApplicationContext().getBean(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by dianjiu on 2022-06-23 ,contact lidianjiu@njydsz.com. 通过name,以及Clazz返回指定的Bean.
|
||||
*/
|
||||
public static <T> T getBean(String name, Class<T> clazz) {
|
||||
return getApplicationContext().getBean(name, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据clazz类型获取spring容器中的对象
|
||||
*/
|
||||
public static <T> Map<String, T> getBeansOfType(Class<T> clazz) {
|
||||
return getApplicationContext().getBeansOfType(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据注解类从容器中获取对象
|
||||
*/
|
||||
public static <T> Map<String, Object> getBeansOfAnnotation(Class<? extends Annotation> clazz) {
|
||||
return getApplicationContext().getBeansWithAnnotation(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ai.remi.comm.core.spring;
|
||||
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 通过"server.port"作为name格式调用getPropertiesValue()方法
|
||||
*/
|
||||
@Component
|
||||
public class SpringProperties implements EnvironmentAware {
|
||||
|
||||
private static Environment env;
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
env = environment;
|
||||
}
|
||||
|
||||
public static String getString(String key) {
|
||||
return env.getProperty(key);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
ai.remi.comm.core.config.AuthFilterConfig,\
|
||||
ai.remi.comm.core.spring.SpringProperties,\
|
||||
ai.remi.comm.core.spring.SpringBeans
|
||||
|
||||
|
||||
87
remi-comm/remi-comm-domain/pom.xml
Normal file
87
remi-comm/remi-comm-domain/pom.xml
Normal file
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-domain</artifactId>
|
||||
<name>remi-comm-domain</name>
|
||||
<description>remi-comm-domain</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--remi-comm-core-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-core</artifactId>
|
||||
</dependency>
|
||||
<!--remi-comm-util-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-util</artifactId>
|
||||
</dependency>
|
||||
<!--mybatis-plus-->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-annotation</artifactId>
|
||||
</dependency>
|
||||
<!--jackson-core -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<!-- validator -->
|
||||
<dependency>
|
||||
<groupId>commons-validator</groupId>
|
||||
<artifactId>commons-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
<!--mapstruct-->
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
</dependency>
|
||||
<!--lombok-->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,84 @@
|
||||
package ai.remi.comm.domain.entity;
|
||||
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author dianjiu【】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc BaseEntity
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(type = IdType.ASSIGN_ID)
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 乐观锁
|
||||
*/
|
||||
@Version
|
||||
@TableField("revision")
|
||||
private Integer revision;
|
||||
|
||||
/**
|
||||
* 逻辑删除 (0正常 1删除)
|
||||
* value = "" 默认的原值,默认为 0;
|
||||
* delval = "" 删除后的值,默认为1;
|
||||
*/
|
||||
// @TableLogic(value="0",delval="1")
|
||||
@TableField("deleted")
|
||||
private Integer deleted;
|
||||
|
||||
/**
|
||||
* 部门租户ID
|
||||
*/
|
||||
@TableField("dept_tenant_id")
|
||||
private String deptTenantId;
|
||||
|
||||
/**
|
||||
* 公司租户ID
|
||||
*/
|
||||
@TableField("company_tenant_id")
|
||||
private String companyTenantId;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField("created_by")
|
||||
private String createdBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(value = "created_at", fill = FieldFill.INSERT)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@TableField("updated_by")
|
||||
private String updatedBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@TableField(value = "updated_at", fill = FieldFill.UPDATE)
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package ai.remi.comm.domain.entity;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc RootEntity
|
||||
*/
|
||||
public interface RootEntity extends Serializable {
|
||||
|
||||
public String getId();
|
||||
|
||||
public RootEntity setId(String id);
|
||||
|
||||
public Integer getRevision();
|
||||
|
||||
public RootEntity setRevision(Integer revision);
|
||||
|
||||
public Integer getDeleted();
|
||||
|
||||
public RootEntity setDeleted(Integer deleted);
|
||||
|
||||
public String getDeptTenantId();
|
||||
|
||||
public RootEntity setDeptTenantId(String deptTenantId);
|
||||
|
||||
public String getCompanyTenantId();
|
||||
|
||||
public RootEntity setCompanyTenantId(String companyTenantId);
|
||||
|
||||
public String getCreatedBy();
|
||||
|
||||
public RootEntity setCreatedBy(String createdBy);
|
||||
|
||||
public LocalDateTime getCreatedAt();
|
||||
|
||||
public RootEntity setCreatedAt(LocalDateTime createdAt);
|
||||
|
||||
public String getUpdatedBy();
|
||||
|
||||
public RootEntity setUpdatedBy(String updatedBy);
|
||||
|
||||
public LocalDateTime getUpdatedAt();
|
||||
|
||||
public RootEntity setUpdatedAt(LocalDateTime updatedAt);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package ai.remi.comm.domain.query;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc PageQuery
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuperBuilder(toBuilder = true)
|
||||
public class PageQuery implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 当前页
|
||||
*/
|
||||
@NotBlank(message = "pageNum当前页不能为空")
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 页大小
|
||||
*/
|
||||
@NotBlank(message = "pageSize页大小不能为空")
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 指定排序字段
|
||||
*/
|
||||
private String orderBy;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package ai.remi.comm.domain.tree;
|
||||
|
||||
import ai.remi.comm.util.collection.CollectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TreeBuild<T extends TreeNode> {
|
||||
/**
|
||||
* rootId
|
||||
*/
|
||||
public String rootId;
|
||||
/**
|
||||
* 保存参与构建树形的所有数据(通常数据库查询结果)
|
||||
*/
|
||||
public List<T> nodeList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
*
|
||||
* @param nodeList 将数据集合赋值给nodeList,即所有数据作为所有节点。
|
||||
*/
|
||||
public TreeBuild(String rootId, List<T> nodeList) {
|
||||
this.rootId = rootId;
|
||||
this.nodeList = nodeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需构建的所有根节点(顶级节点) "0"
|
||||
*
|
||||
* @return 所有根节点List集合
|
||||
*/
|
||||
public List<T> getRootNode() {
|
||||
// 保存所有根节点(所有根节点的数据)
|
||||
List<T> rootNodeList = new ArrayList<>();
|
||||
// treeNode:查询出的每一条数据(节点)
|
||||
for (T treeNode : nodeList) {
|
||||
// 判断当前节点是否为根节点,此处注意:若parentId类型是String,则要采用equals()方法判断。
|
||||
if (this.rootId.equals(treeNode.getParentId())) {
|
||||
// 是,添加
|
||||
rootNodeList.add(treeNode);
|
||||
}
|
||||
}
|
||||
return rootNodeList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据每一个顶级节点(根节点)进行构建树形结构
|
||||
*
|
||||
* @return 构建整棵树
|
||||
*/
|
||||
public List<T> buildTree() {
|
||||
// treeNodes:保存一个顶级节点所构建出来的完整树形
|
||||
List<T> treeNodes = new ArrayList<T>();
|
||||
// getRootNode():获取所有的根节点
|
||||
for (T treeRootNode : getRootNode()) {
|
||||
// 将顶级节点进行构建子树
|
||||
treeRootNode = buildChildTree(treeRootNode);
|
||||
// 完成一个顶级节点所构建的树形,增加进来
|
||||
treeNodes.add(treeRootNode);
|
||||
}
|
||||
// 这里对父节点排序
|
||||
CollectionUtils.sort(treeNodes, "sort");
|
||||
// for循环结束,即获取所有的根节点,树形构建结束,设置树结果
|
||||
return treeNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归-----构建子树形结构
|
||||
*
|
||||
* @param pNode 根节点(顶级节点)
|
||||
* @return 整棵树
|
||||
*/
|
||||
public T buildChildTree(T pNode) {
|
||||
List<T> childTree = new ArrayList<T>();
|
||||
// nodeList:所有节点集合(所有数据)
|
||||
for (T treeNode : nodeList) {
|
||||
// 判断当前节点的父节点ID是否等于根节点的ID,即当前节点为其下的子节点
|
||||
if (treeNode.getParentId().equals(pNode.getId())) {
|
||||
// 再递归进行判断当前节点的情况,调用自身方法
|
||||
childTree.add(buildChildTree(treeNode));
|
||||
}
|
||||
}
|
||||
// 这里对子节点排序
|
||||
CollectionUtils.sort(childTree, "sort");
|
||||
// for循环结束,即节点下没有任何节点,树形构建结束,设置树结果
|
||||
pNode.setChildren(childTree);
|
||||
return pNode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package ai.remi.comm.domain.tree;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc ResultBean
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class TreeNode<T extends TreeNode> implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 8676131899637805509L;
|
||||
|
||||
/**
|
||||
* 当前节点ID
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 父节点ID 顶级节点为0
|
||||
*/
|
||||
private String parentId;
|
||||
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
private List<T> children;
|
||||
}
|
||||
57
remi-comm/remi-comm-email/pom.xml
Normal file
57
remi-comm/remi-comm-email/pom.xml
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-email</artifactId>
|
||||
<name>remi-comm-email</name>
|
||||
<description>remi-comm-email</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--common-core-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-core</artifactId>
|
||||
</dependency>
|
||||
<!--springboot-mail整合包-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<!-- thymeleaf 模版 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<!-- freemarker 模版 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-freemarker</artifactId>
|
||||
</dependency>
|
||||
<!--slf4j-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,42 @@
|
||||
package ai.remi.comm.email.config;
|
||||
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.mail.javamail.JavaMailSenderImpl;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email startdis@njydsz.com
|
||||
* @desc MinioConfig
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(EmailProperties.class)
|
||||
public class EmailConfig {
|
||||
|
||||
@Resource
|
||||
private EmailProperties emailProperties;
|
||||
|
||||
|
||||
/**
|
||||
* 初始化 JavaMailSender
|
||||
*/
|
||||
@Bean
|
||||
public JavaMailSenderImpl javaMailSenderImpl() {
|
||||
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
|
||||
javaMailSender.setProtocol(emailProperties.getProtocol());
|
||||
javaMailSender.setHost(emailProperties.getSmtpHost());
|
||||
javaMailSender.setPort(Integer.parseInt(emailProperties.getSmtpPort()));
|
||||
javaMailSender.setDefaultEncoding(emailProperties.getEncoding());
|
||||
javaMailSender.setUsername(emailProperties.getFromMail());
|
||||
javaMailSender.setPassword(emailProperties.getPassword());
|
||||
// TODO 设置邮件参数
|
||||
Properties props = new Properties();
|
||||
// props.setProperty("mail.smtp.auth", emailProperties.get());
|
||||
javaMailSender.setJavaMailProperties(props);
|
||||
return javaMailSender;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package ai.remi.comm.email.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email startdis@njydsz.com
|
||||
* @desc Email配置类
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "remi-comm.email")
|
||||
public class EmailProperties {
|
||||
/**
|
||||
* 发送协议
|
||||
*/
|
||||
private String protocol;
|
||||
/**
|
||||
* 服务器地址
|
||||
*/
|
||||
private String smtpHost;
|
||||
/**
|
||||
* 服务器端口
|
||||
*/
|
||||
private String smtpPort;
|
||||
/**
|
||||
* 发件人名称
|
||||
*/
|
||||
private String fromName;
|
||||
/**
|
||||
* 发件人邮箱
|
||||
*/
|
||||
private String fromMail;
|
||||
/**
|
||||
* 发件人密码
|
||||
*/
|
||||
private String password;
|
||||
/**
|
||||
* 邮件编码
|
||||
*/
|
||||
private String encoding;
|
||||
|
||||
/**
|
||||
* SSL配置
|
||||
*/
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties("remi-comm.email.ssl")
|
||||
class sslConf {
|
||||
private String enabled;
|
||||
private String sslPort;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package ai.remi.comm.email.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Email {
|
||||
|
||||
/**
|
||||
* 收件人,多个收件人用“,”隔开
|
||||
*/
|
||||
private String to;
|
||||
/**
|
||||
* 抄送人,多个抄送人用“,”隔开
|
||||
*/
|
||||
private String cc;
|
||||
/**
|
||||
* 密送人,多个密送人用“,”隔开
|
||||
*/
|
||||
private String bcc;
|
||||
/**
|
||||
* 邮件主题
|
||||
*/
|
||||
private String subject;
|
||||
/**
|
||||
* 邮件内容
|
||||
*/
|
||||
private String content;
|
||||
/**
|
||||
* 图片资源路径
|
||||
*/
|
||||
private String rscPath;
|
||||
/**
|
||||
* 图片资源ID(CID)
|
||||
*/
|
||||
private String rscId;
|
||||
/**
|
||||
* 附件路径
|
||||
*/
|
||||
private String[] filePaths;
|
||||
/**
|
||||
* 邮件模板地址
|
||||
*/
|
||||
private String template;
|
||||
/**
|
||||
* 邮件模板变量
|
||||
*/
|
||||
private Map<String, Object> variable;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
ai.remi.comm.email.service.EmailService
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>${title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>${greeting}</h1>
|
||||
<p>${message}</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title th:text="${title}">Welcome</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 th:text="${greeting}">Hello,</h1>
|
||||
<p th:text="${message}">Welcome to our site!</p>
|
||||
</body>
|
||||
</html>
|
||||
6
remi-comm/remi-comm-exception/README.md
Normal file
6
remi-comm/remi-comm-exception/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
```yaml
|
||||
i18n:
|
||||
messages:
|
||||
basename: i18n/messages
|
||||
encoding: utf-8
|
||||
```
|
||||
55
remi-comm/remi-comm-exception/pom.xml
Normal file
55
remi-comm/remi-comm-exception/pom.xml
Normal file
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-exception</artifactId>
|
||||
<name>remi-comm-exception</name>
|
||||
<description>remi-comm-exception</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--remi-comm-domain-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-domain</artifactId>
|
||||
</dependency>
|
||||
<!--spring-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<!--slf4j-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package ai.remi.comm.exception.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 错误码设计描述
|
||||
* <p>
|
||||
* 面向日志的错误码定义共十三位(十位有意义,三位连接符),并且应该具有如下分类: 应用标识,表示错误属于哪个应用,三位数字。 功能域标识,表示错误属于应用中的哪个功能模块,三位数字。 错误类型,表示错误属于那种类型,一位字母。
|
||||
* 错误编码,错误类型下的具体错误,三位数字。
|
||||
* <p>
|
||||
* 面向外部传递的错误码共六位,并且有如下分类: 错误类型,表示错误来源,1位字母。 应用标识,表示错误属于哪个应用,2位数字。 错误编码,错误类型下的具体错误,3位数字。
|
||||
* <p>
|
||||
* 错误类型: A-表示错误来源于用户 B-表示错误来源于系统 C-表示错误来源于第三方接口
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum CommExceptionCode implements ExceptionCode {
|
||||
|
||||
/**
|
||||
* 面向外部传递的错误码共六位,并且有如下分类:
|
||||
* 错误类型,表示错误来源,1位字母。
|
||||
* 应用标识,表示错误属于哪个应用,2位数字。
|
||||
* 错误编码,错误类型下的具体错误,3位数字。
|
||||
* <p>
|
||||
* 错误类型: 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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ai.remi.comm.exception.enums;
|
||||
|
||||
/**
|
||||
* 异常code接口
|
||||
*/
|
||||
public interface ExceptionCode {
|
||||
/**
|
||||
* 异常code
|
||||
* @return
|
||||
*/
|
||||
String getCode();
|
||||
/**
|
||||
* 异常消息
|
||||
* @return
|
||||
*/
|
||||
String getKey();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
ai.remi.comm.exception.config.I18nConfig
|
||||
59
remi-comm/remi-comm-feign/pom.xml
Normal file
59
remi-comm/remi-comm-feign/pom.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-feign</artifactId>
|
||||
<name>remi-comm-feign</name>
|
||||
<description>remi-comm-feign</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--openfeign-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
<!--remi-comm-util-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-util</artifactId>
|
||||
</dependency>
|
||||
<!--servlet-api-->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
<!--slf4j-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -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 {};
|
||||
}
|
||||
@@ -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客户端日志级别
|
||||
* <p>
|
||||
* 有四种日志记录级别可供选择:
|
||||
* <p>
|
||||
* - 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();
|
||||
}
|
||||
}
|
||||
@@ -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<String, String> 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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
90
remi-comm/remi-comm-jdbc/pom.xml
Normal file
90
remi-comm/remi-comm-jdbc/pom.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-jdbc</artifactId>
|
||||
<name>remi-comm-jdbc</name>
|
||||
<description>remi-comm-jdbc</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--remi-comm-core-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-core</artifactId>
|
||||
</dependency>
|
||||
<!--remi-comm-util-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-util</artifactId>
|
||||
</dependency>
|
||||
<!-- mybatis-plus -->
|
||||
<dependency>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<groupId>com.baomidou</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>mybatis-plus-annotation</artifactId>
|
||||
<groupId>com.baomidou</groupId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-extension</artifactId>
|
||||
</dependency>
|
||||
<!--mybatis-->
|
||||
<dependency>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
</dependency>
|
||||
<!-- druid-->
|
||||
<dependency>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<groupId>com.alibaba</groupId>
|
||||
</dependency>
|
||||
<!--jdbc-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<!-- mysql -->
|
||||
<dependency>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<groupId>mysql</groupId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!--slf4j-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-nop</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -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 {
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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 = "";
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ai.remi.comm.jdbc.enums;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 字段填充策略枚举
|
||||
*/
|
||||
public enum FieldFillStrategyEnum {
|
||||
|
||||
/**
|
||||
* 插入时填充字段
|
||||
*/
|
||||
INSERT,
|
||||
/**
|
||||
* 更新时填充字段
|
||||
*/
|
||||
UPDATE,
|
||||
/**
|
||||
* 插入和更新时填充字段
|
||||
*/
|
||||
INSERT_UPDATE
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package ai.remi.comm.jdbc.enums;
|
||||
|
||||
/**
|
||||
* @author dianjiu【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @desc 拦截表策略
|
||||
*/
|
||||
public enum InterceptTableStrategy {
|
||||
INCLUDE,
|
||||
EXCLUDE;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
ai.remi.comm.jdbc.config.AutoConfig,\
|
||||
ai.remi.comm.jdbc.config.DruidConfig,\
|
||||
ai.remi.comm.jdbc.config.FieldFillConfig,\
|
||||
ai.remi.comm.jdbc.config.InterceptConfig,\
|
||||
ai.remi.comm.jdbc.config.MybatisPlusConfig,\
|
||||
ai.remi.comm.jdbc.config.TenantConfig
|
||||
|
||||
|
||||
51
remi-comm/remi-comm-job/pom.xml
Normal file
51
remi-comm/remi-comm-job/pom.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-job</artifactId>
|
||||
<name>remi-comm-job</name>
|
||||
<description>remi-comm-job</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--remi-comm-core-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-core</artifactId>
|
||||
</dependency>
|
||||
<!-- xxl-job 核心包-->
|
||||
<dependency>
|
||||
<groupId>com.xuxueli</groupId>
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<!--slf4j-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,60 @@
|
||||
package ai.remi.comm.job.config;
|
||||
|
||||
import ai.remi.comm.job.properties.XxlJobProperties;
|
||||
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 定时任务配置
|
||||
*
|
||||
* @author dianjiu
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(value = XxlJobProperties.class)
|
||||
@ConditionalOnProperty(value = "xxl-job.enabled", havingValue = "true")
|
||||
public class XxlJobConfiguration {
|
||||
|
||||
@Resource
|
||||
private XxlJobProperties xxlJobProperties;
|
||||
|
||||
/**
|
||||
* 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
|
||||
* <p>
|
||||
* 1、引入依赖:
|
||||
* <dependency>
|
||||
* <groupId>org.springframework.cloud</groupId>
|
||||
* <artifactId>spring-cloud-commons</artifactId>
|
||||
* <version>${version}</version>
|
||||
* </dependency>
|
||||
* <p>
|
||||
* 2、配置文件,或者容器启动变量
|
||||
* spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
|
||||
* <p>
|
||||
* 3、获取IP
|
||||
* String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
|
||||
*/
|
||||
@Bean
|
||||
public XxlJobSpringExecutor xxlJobExecutor() {
|
||||
log.info(">>>>>>>>>>> xxl-job config init start.");
|
||||
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
|
||||
xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdminAddresses());
|
||||
xxlJobSpringExecutor.setAppname(xxlJobProperties.getExecutor().getAppname());
|
||||
xxlJobSpringExecutor.setAddress(xxlJobProperties.getExecutor().getAddress());
|
||||
xxlJobSpringExecutor.setIp(xxlJobProperties.getExecutor().getIp());
|
||||
xxlJobSpringExecutor.setPort(xxlJobProperties.getExecutor().getPort());
|
||||
xxlJobSpringExecutor.setAccessToken(xxlJobProperties.getAccessToken());
|
||||
xxlJobSpringExecutor.setLogPath(xxlJobProperties.getExecutor().getLogPath());
|
||||
xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getExecutor().getLogRetentionDays());
|
||||
log.info(">>>>>>>>>>> xxl-job config init end.");
|
||||
return xxlJobSpringExecutor;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package ai.remi.comm.job.properties;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* xxl-job的配置属性
|
||||
*
|
||||
* @author dianjiu
|
||||
* 2021-12-28 09:40:38
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "xxl-job")
|
||||
public class XxlJobProperties {
|
||||
/**
|
||||
* 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册;
|
||||
* eg:http://127.0.0.1:8080/xxl-job-admin
|
||||
*/
|
||||
private String adminAddresses;
|
||||
/**
|
||||
* 执行器通讯TOKEN [选填]:非空时启用;
|
||||
*/
|
||||
private String accessToken;
|
||||
|
||||
private Boolean enabled;
|
||||
|
||||
private Executor executor;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Executor {
|
||||
/**
|
||||
* 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册
|
||||
* eg:remi-boot-job
|
||||
*/
|
||||
private String appname;
|
||||
/**
|
||||
* 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
|
||||
*/
|
||||
private String address;
|
||||
/**
|
||||
* 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
|
||||
*/
|
||||
private String ip;
|
||||
/**
|
||||
* 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
|
||||
*/
|
||||
private int port;
|
||||
/**
|
||||
* 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径;
|
||||
*/
|
||||
private String logPath;
|
||||
/**
|
||||
* 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能;
|
||||
*/
|
||||
private int logRetentionDays;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
ai.remi.comm.job.config.XxlJobConfiguration
|
||||
69
remi-comm/remi-comm-log/README.md
Normal file
69
remi-comm/remi-comm-log/README.md
Normal file
@@ -0,0 +1,69 @@
|
||||
# 工程简介
|
||||
瑞米平台公共的日志模块
|
||||
|
||||
## 操作日志
|
||||
主要面向用户的,要求简单易懂,反映出用户所做的动作。
|
||||
|
||||
### 核心功能
|
||||
- 要实现以下功能:
|
||||
|
||||
- 自定义spring boot starter
|
||||
- 定义日志注解
|
||||
- AOP拦截日志注解方法
|
||||
- 定义日志动态内容模板
|
||||
|
||||
- 模板中又需要实现:
|
||||
- 动态模板表达式解析:用强大的SpEL来解析表达式
|
||||
- 自定义函数:支持目标方法前置/后置的自定义函数
|
||||
|
||||
##
|
||||
|
||||
### SQL脚本
|
||||
```sql
|
||||
DROP TABLE IF EXISTS t_system_log;
|
||||
CREATE TABLE t_system_log
|
||||
(
|
||||
id VARCHAR(32) NOT NULL COMMENT '日志编号',
|
||||
user_id VARCHAR(32) COMMENT '用户ID',
|
||||
user_name VARCHAR(255) COMMENT '用户名称',
|
||||
app_code VARCHAR(255) COMMENT '应用编码',
|
||||
app_name VARCHAR(255) COMMENT '应用名称',
|
||||
business_no VARCHAR(255) COMMENT '业务流水号',
|
||||
business_type VARCHAR(255) COMMENT '业务类型(新增、修改、删除、查询、导入、导出、其他)',
|
||||
log_content LONGTEXT COMMENT '日志模板内容',
|
||||
log_extras LONGTEXT COMMENT '额外的日志内容',
|
||||
request_url VARCHAR(255) COMMENT '请求地址',
|
||||
request_ip VARCHAR(255) COMMENT '请求IP',
|
||||
request_type VARCHAR(255) COMMENT '请求类型(POST、PUT、OPTIONS、DELETE)',
|
||||
content_type VARCHAR(255) COMMENT '请求报文类型(map、json)',
|
||||
request_body LONGTEXT COMMENT '请求报文',
|
||||
response_body LONGTEXT COMMENT '响应报文',
|
||||
request_time DATETIME COMMENT '请求时间',
|
||||
response_time DATETIME COMMENT '响应时间',
|
||||
execute_time BIGINT COMMENT '请求耗时',
|
||||
trace_id VARCHAR(255) COMMENT '链路追踪ID',
|
||||
error_info LONGTEXT COMMENT '异常信息',
|
||||
user_agent VARCHAR(255) COMMENT '浏览器、操作系统、UI引擎标识',
|
||||
revision INTEGER(11) COMMENT '乐观锁',
|
||||
is_deleted INTEGER(1) COMMENT '逻辑删除;0-未删除 1-已删除',
|
||||
group_tenant_id VARCHAR(32) COMMENT '部门租户ID',
|
||||
company_tenant_id VARCHAR(32) COMMENT '公司租户ID',
|
||||
created_by VARCHAR(32) COMMENT '创建人',
|
||||
created_at DATETIME COMMENT '创建时间',
|
||||
updated_by VARCHAR(32) COMMENT '更新人',
|
||||
updated_at DATETIME COMMENT '更新时间',
|
||||
PRIMARY KEY (id)
|
||||
) COMMENT = '系统日志表';
|
||||
```
|
||||
## 系统日志
|
||||
主要用于开发者调试排查系统问题的,不要求固定格式和可读性。
|
||||
|
||||
###
|
||||
|
||||
## 延伸阅读
|
||||
https://tech.meituan.com/2021/09/16/operational-logbook.html
|
||||
|
||||
https://juejin.cn/post/7075184311397253128
|
||||
|
||||
http://mysql.taobao.org/monthly/2017/09/08/
|
||||
|
||||
69
remi-comm/remi-comm-log/pom.xml
Normal file
69
remi-comm/remi-comm-log/pom.xml
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>remi-comm-log</artifactId>
|
||||
<name>remi-comm-log</name>
|
||||
<description>remi-comm-log</description>
|
||||
|
||||
<parent>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm</artifactId>
|
||||
<version>3.1.1</version>
|
||||
</parent>
|
||||
|
||||
<!-- 打包上传到私服, 需要和配置文件中的server节点配合使用 -->
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>maven-releases</id>
|
||||
<name>release</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-releases/</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>maven-snapshots</id>
|
||||
<name>snapshot</name>
|
||||
<url>https://repository.njydsz.com/repository/maven-snapshots/</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
<dependencies>
|
||||
<!--spring-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<!--nacos-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<!--公共依赖-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-util</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-comm-redis</artifactId>
|
||||
</dependency>
|
||||
<!--项目依赖-->
|
||||
<dependency>
|
||||
<groupId>ai.remi.top.cloud</groupId>
|
||||
<artifactId>remi-sys-api</artifactId>
|
||||
</dependency>
|
||||
<!--lombok-->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,23 @@
|
||||
package ai.remi.comm.log.annotation;
|
||||
|
||||
import ai.remi.comm.log.config.LogRecordAutoConfigure;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author DianJiu 【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @date 2022-07-19
|
||||
* @desc 日志记录开启注解
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Order(Integer.MIN_VALUE)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import(LogRecordAutoConfigure.class)
|
||||
public @interface EnableLogRecord {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package ai.remi.comm.log.annotation;
|
||||
|
||||
|
||||
import ai.remi.comm.log.enums.BusinessType;
|
||||
import ai.remi.comm.log.enums.OperatorType;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* @author DianJiu 【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @date 2022-07-19
|
||||
* @desc 操作日志记录
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Order(Integer.MIN_VALUE)
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface LogRecord {
|
||||
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
String content() default "";
|
||||
|
||||
/**
|
||||
* 功能操作类型
|
||||
*/
|
||||
BusinessType businessType() default BusinessType.OTHER;
|
||||
|
||||
/**
|
||||
* 操作人类别
|
||||
*/
|
||||
OperatorType operatorType() default OperatorType.MANAGE;
|
||||
|
||||
/**
|
||||
* 是否保存请求的参数
|
||||
*/
|
||||
boolean isSaveRequestData() default true;
|
||||
|
||||
/**
|
||||
* 是否保存响应的参数
|
||||
*/
|
||||
boolean isSaveResponseData() default true;
|
||||
|
||||
/**
|
||||
* 是否保存异常的信息
|
||||
*/
|
||||
boolean isSaveErrorInfo() default true;
|
||||
|
||||
/**
|
||||
* 在方法成功执行后打点,记录方法的执行时间发送到指标系统,默认关闭
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean recordSuccessMetrics() default false;
|
||||
|
||||
/**
|
||||
* 在方法成功失败后打点,记录方法的执行时间发送到指标系统,默认关闭
|
||||
*/
|
||||
boolean recordFailMetrics() default false;
|
||||
|
||||
/**
|
||||
* 出现异常后忽略异常返回默认值,默认关闭
|
||||
*/
|
||||
boolean ignoreException() default false;
|
||||
|
||||
/**
|
||||
* 排除指定的请求参数
|
||||
*/
|
||||
public String[] excludeParamNames() default {};
|
||||
}
|
||||
@@ -0,0 +1,399 @@
|
||||
package ai.remi.comm.log.aspect;
|
||||
|
||||
import ai.remi.comm.core.spring.SpringProperties;
|
||||
import ai.remi.comm.log.annotation.LogRecord;
|
||||
import ai.remi.comm.log.domain.OperateLog;
|
||||
import ai.remi.comm.log.enums.BusinessStatus;
|
||||
import ai.remi.comm.log.filter.PropertyPreExcludeFilter;
|
||||
import ai.remi.comm.log.service.AsyncLogService;
|
||||
import ai.remi.comm.redis.service.RedisService;
|
||||
import ai.remi.comm.util.ip.IpAddrUtils;
|
||||
import ai.remi.comm.util.ip.IpInfoUtils;
|
||||
import ai.remi.comm.util.string.StringUtils;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import ai.remi.comm.log.enums.RedisKeyEnum;
|
||||
import eu.bitwalker.useragentutils.UserAgent;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.AfterReturning;
|
||||
import org.aspectj.lang.annotation.AfterThrowing;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.NamedThreadLocal;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 操作日志记录处理
|
||||
*/
|
||||
@Slf4j
|
||||
@Aspect
|
||||
public class LogRecordAspect {
|
||||
/**
|
||||
* 排除敏感属性字段
|
||||
*/
|
||||
public static final String[] EXCLUDE_PROPERTIES = {"password", "oldPassword", "newPassword", "confirmPassword"};
|
||||
|
||||
private final ThreadLocal<Map<String, Object>> INFO_THREADLOCAL = new ThreadLocal<Map<String, Object>>();
|
||||
|
||||
/**
|
||||
* 计算操作消耗时间
|
||||
*/
|
||||
private static final ThreadLocal<Long> TIME_THREADLOCAL = new NamedThreadLocal<>("Cost Time");
|
||||
|
||||
@Resource
|
||||
private AsyncLogService asyncLogService;
|
||||
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
|
||||
/**
|
||||
* 处理请求前执行
|
||||
*/
|
||||
@Before(value = "@annotation(logRecord)")
|
||||
public void boBefore(JoinPoint joinPoint, LogRecord logRecord) {
|
||||
TIME_THREADLOCAL.set(System.currentTimeMillis());
|
||||
//登出接口需要前置储存用户信息
|
||||
handleLog(joinPoint, logRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理完请求后执行
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
*/
|
||||
@AfterReturning(pointcut = "@annotation(logRecord)", returning = "jsonResult")
|
||||
public void doAfterReturning(JoinPoint joinPoint, LogRecord logRecord, Object jsonResult) {
|
||||
handleLog(joinPoint, logRecord, null, jsonResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截异常操作
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @param e 异常
|
||||
*/
|
||||
@AfterThrowing(value = "@annotation(logRecord)", throwing = "e")
|
||||
public void doAfterThrowing(JoinPoint joinPoint, LogRecord logRecord, Exception e) {
|
||||
handleLog(joinPoint, logRecord, e, null);
|
||||
}
|
||||
|
||||
protected void handleLog(final JoinPoint joinPoint, LogRecord logRecord) {
|
||||
// 传递线程信息
|
||||
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
RequestContextHolder.setRequestAttributes(requestAttributes, true);
|
||||
HttpServletRequest request = requestAttributes.getRequest();
|
||||
// 记录请求内容,threadInfo存储所有内容
|
||||
Map<String, Object> threadInfo = new HashMap<>();
|
||||
//该方法用于获取客户端发出请求时的完整URL,包括协议、服务器名、端口号、资源路径等信息,但不包括后面的查询参数部分。
|
||||
// 注意,getRequestRUL()方法返回的时StringBuffer类型,而不是String类型。
|
||||
threadInfo.put("url", request.getRequestURL());
|
||||
//该方法用于获取请求行中资源名称部分,即位于URL的主机和端口之后、参数部分之前的部分
|
||||
threadInfo.put("uri", request.getRequestURI());
|
||||
//该方法用于获取HTTP请求消息中的请求方式(如GET、POST等)
|
||||
threadInfo.put("httpMethod", request.getMethod());
|
||||
//该方法用于获取请求的协议名,例如http、https或ftp
|
||||
threadInfo.put("scheme", request.getScheme());
|
||||
//该方法用于获取请求客户端的IP地址,其格式类似于"192.168.0.1"
|
||||
//threadInfo.put("ip", request.getRemoteAddr());
|
||||
threadInfo.put("ip", IpAddrUtils.getIpAddr(request));
|
||||
//检查是否为内网IP
|
||||
String ipAddress = IpAddrUtils.internalIp(IpAddrUtils.getIpAddr(request)) ? "内网IP" : IpInfoUtils.getInfo(IpAddrUtils.getIpAddr(request)).getProvince() + "-" + IpInfoUtils.getInfo(IpAddrUtils.getIpAddr(request)).getCity();
|
||||
threadInfo.put("ipAddress", ipAddress);
|
||||
//该方法用于获取Content-Type头字段的值,结果为String类型
|
||||
threadInfo.put("contentType", request.getContentType());
|
||||
//包含了一个特征字符串,用来让网络协议的对端来识别发起请求的用户代理软件的应用类型、操作系统、软件开发商以及版本号。
|
||||
//UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
|
||||
UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
|
||||
threadInfo.put("platform", userAgent.getOperatingSystem().getName());
|
||||
threadInfo.put("browser", userAgent.getBrowser().getName());
|
||||
//从请求头获取操作人
|
||||
threadInfo.put("operator", request.getHeader("X-Unique-Id"));
|
||||
//从请求头获取部门租户
|
||||
threadInfo.put("deptTenantId", request.getHeader("X-Dept-Tenant-Id"));
|
||||
//从请求头获取公司租户
|
||||
threadInfo.put("companyTenantId", request.getHeader("X-Company-Tenant-Id"));
|
||||
//从请求头获取操作人
|
||||
String accessToken = request.getHeader("X-Access-Token");
|
||||
threadInfo.put("token", accessToken);
|
||||
threadInfo.put("userId", redisService.hget(RedisKeyEnum.USER_INFO.getKey(accessToken), "id"));
|
||||
threadInfo.put("userCode", redisService.hget(RedisKeyEnum.USER_INFO.getKey(accessToken), "userCode"));
|
||||
threadInfo.put("userName", redisService.hget(RedisKeyEnum.USER_INFO.getKey(accessToken), "userName"));
|
||||
threadInfo.put("userNameEn", redisService.hget(RedisKeyEnum.USER_INFO.getKey(accessToken), "userNameEn"));
|
||||
//从请求头获取系统语言
|
||||
threadInfo.put("language", request.getHeader("X-USER-LANGUAGE"));
|
||||
//从请求头获取业务流水号
|
||||
threadInfo.put("businessNo", request.getHeader("X-Business-No"));
|
||||
INFO_THREADLOCAL.set(threadInfo);
|
||||
}
|
||||
|
||||
protected void handleLog(final JoinPoint joinPoint, LogRecord logRecord, final Exception e, Object jsonResult) {
|
||||
try {
|
||||
// *========解析请求信息=========*//
|
||||
OperateLog operateLog = new OperateLog();
|
||||
|
||||
// *========获取注解信息=========*//
|
||||
//获取方法签名
|
||||
//MethodSignature signature = getSignature(joinPoint);
|
||||
//获取注解方法
|
||||
Method method = getMethod(joinPoint);
|
||||
//获取注解对象
|
||||
//LogRecord logRecord = getAnnotation(joinPoint, LogRecord.class);
|
||||
// 获取连接点所在类名
|
||||
String classname = joinPoint.getTarget().getClass().getSimpleName();
|
||||
// 获取连接点所在方法名称
|
||||
String methodName = method.getName();
|
||||
String name = String.format("%s.%s()", classname, methodName);
|
||||
// 保存请求方法
|
||||
operateLog.setRequestMethod(name);
|
||||
|
||||
// 处理设置注解上的参数
|
||||
getControllerMethodDescription(joinPoint, logRecord, operateLog, jsonResult);
|
||||
|
||||
// 是否需要保存request,参数和值
|
||||
if (logRecord.isSaveRequestData()) {
|
||||
// 获取参数的信息,传入到数据库中
|
||||
setRequestValue(joinPoint, operateLog, logRecord.excludeParamNames());
|
||||
}
|
||||
|
||||
// 是否需要保存response,参数和值
|
||||
if (logRecord.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {
|
||||
String result = JSON.toJSONString(jsonResult);
|
||||
// 对登录日志的特殊处理
|
||||
if (operateLog.getBusinessType()== 9) {
|
||||
Object data = JSON.parseObject(result).get("data");
|
||||
String accessToken = String.valueOf(JSON.parseObject(JSON.toJSONString(data)).get("accessToken"));
|
||||
setOperateUser(accessToken, operateLog);
|
||||
}
|
||||
operateLog.setStatus(String.valueOf(JSON.parseObject(result).get("code")));
|
||||
operateLog.setResponseBody(result);
|
||||
}
|
||||
|
||||
//请求的成功或失败
|
||||
if (e != null) {
|
||||
operateLog.setStatus(BusinessStatus.FAIL.getCode());
|
||||
// 是否需要保存errorMsg,参数和值
|
||||
if (logRecord.isSaveErrorInfo()) {
|
||||
operateLog.setStatus("111111");
|
||||
operateLog.setErrorInfo(StrUtil.sub(e.getMessage(), 0, 5000));
|
||||
}
|
||||
}
|
||||
|
||||
// 保存数据库
|
||||
//asyncLogService.saveOperateLog(operateLog);
|
||||
} catch (Exception exp) {
|
||||
// 记录本地异常日志
|
||||
log.error("异常信息:{}", exp.getMessage());
|
||||
exp.printStackTrace();
|
||||
} finally {
|
||||
TIME_THREADLOCAL.remove();
|
||||
INFO_THREADLOCAL.remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取注解中对方法的描述信息 用于Controller层注解
|
||||
*
|
||||
* @param logRecord 日志切面
|
||||
* @param operateLog 操作日志
|
||||
*/
|
||||
public void getControllerMethodDescription(JoinPoint joinPoint, LogRecord logRecord, OperateLog operateLog, Object jsonResult) throws Exception {
|
||||
Map<String, Object> objectMap = INFO_THREADLOCAL.get();
|
||||
// 业务模块
|
||||
operateLog.setAppId(SpringProperties.getString("log.record.appId"));
|
||||
operateLog.setAppCode(SpringProperties.getString("log.record.appCode"));
|
||||
// 操作方法名称或描述
|
||||
operateLog.setContent(logRecord.content());
|
||||
// 业务操作类型
|
||||
operateLog.setBusinessType(logRecord.businessType().getCode());
|
||||
// 操作人类别
|
||||
operateLog.setOperatorType(logRecord.operatorType().getCode());
|
||||
// 操作人IP
|
||||
operateLog.setUserIp(String.valueOf(objectMap.get("ip")));
|
||||
operateLog.setIpAddress(String.valueOf(objectMap.get("ipAddress")));
|
||||
// 操作人Id
|
||||
//operateLog.setUserId(Objects.nonNull(objectMap.get("operator")) ? String.valueOf(objectMap.get("operator")) : null);
|
||||
// 操作人信息
|
||||
operateLog.setUserId(Objects.nonNull(objectMap.get("userId")) ? String.valueOf(objectMap.get("userId")) : null);
|
||||
operateLog.setUserCode(Objects.nonNull(objectMap.get("userCode")) ? String.valueOf(objectMap.get("userCode")) : null);
|
||||
operateLog.setUserName(Objects.nonNull(objectMap.get("userName")) ? String.valueOf(objectMap.get("userName")) : null);
|
||||
operateLog.setUserNameEn(Objects.nonNull(objectMap.get("userNameEn")) ? String.valueOf(objectMap.get("userNameEn")) : null);
|
||||
// 用户系统语言
|
||||
operateLog.setUserLanguage(Objects.nonNull(objectMap.get("language")) ? String.valueOf(objectMap.get("language")) : null);
|
||||
// 请求地址
|
||||
operateLog.setRequestUrl(String.valueOf(objectMap.get("url")));
|
||||
// 请求类型
|
||||
operateLog.setRequestType(String.valueOf(objectMap.get("httpMethod")));
|
||||
// 请求报文类型
|
||||
operateLog.setContentType(Objects.nonNull(objectMap.get("contentType")) ? String.valueOf(objectMap.get("contentType")) : null);
|
||||
// 业务流水号
|
||||
operateLog.setBusinessNo(Objects.nonNull(objectMap.get("businessNo")) ? String.valueOf(objectMap.get("businessNo")) : null);
|
||||
// 链路追踪ID
|
||||
//logRecordDTO.setTraceId();
|
||||
// 浏览器、操作系统、UI引擎标识
|
||||
operateLog.setPlatform(String.valueOf(objectMap.get("platform")));
|
||||
operateLog.setBrowser(String.valueOf(objectMap.get("browser")));
|
||||
// 部门租户
|
||||
operateLog.setDeptTenantId(String.valueOf(objectMap.get("deptTenantId")));
|
||||
// 公司租户
|
||||
operateLog.setCompanyTenantId(String.valueOf(objectMap.get("companyTenantId")));
|
||||
// 设置消耗时间
|
||||
operateLog.setRequestTime(LocalDateTime.ofEpochSecond(TIME_THREADLOCAL.get() / 1000, 0, ZoneOffset.ofHours(8)));
|
||||
operateLog.setResponseTime(LocalDateTime.ofEpochSecond(System.currentTimeMillis() / 1000, 0, ZoneOffset.ofHours(8)));
|
||||
operateLog.setExecuteTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存操作人信息
|
||||
*
|
||||
* @param accessToken
|
||||
* @param operateLog
|
||||
*/
|
||||
private void setOperateUser(String accessToken, OperateLog operateLog) {
|
||||
if (StringUtils.isNotBlank(accessToken)) {
|
||||
String id = String.valueOf(redisService.hget(RedisKeyEnum.USER_INFO.getKey(accessToken), "id"));
|
||||
operateLog.setUserId(id);
|
||||
String userCode = String.valueOf(redisService.hget(RedisKeyEnum.USER_INFO.getKey(accessToken), "userCode"));
|
||||
operateLog.setUserCode(userCode);
|
||||
String userName = String.valueOf(redisService.hget(RedisKeyEnum.USER_INFO.getKey(accessToken), "userName"));
|
||||
operateLog.setUserName(userName);
|
||||
String userNameEn = String.valueOf(redisService.hget(RedisKeyEnum.USER_INFO.getKey(accessToken), "userNameEn"));
|
||||
operateLog.setUserNameEn(userNameEn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求的参数,放到log中
|
||||
*
|
||||
* @param operateLog 操作日志
|
||||
* @param excludeParamNames 排除指定的请求参数
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
private void setRequestValue(JoinPoint joinPoint, OperateLog operateLog, String[] excludeParamNames) throws Exception {
|
||||
// 获取RequestAttributes
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
// 从获取RequestAttributes中获取HttpServletRequest的信息
|
||||
HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
|
||||
// 获取请求类型
|
||||
// String requestType = operateLog.getRequestType();
|
||||
String requestType = request.getMethod();
|
||||
// 获取请求参数
|
||||
Map<String, String> paramsMap = converMap(request.getParameterMap());
|
||||
// 组装请求参数
|
||||
if (MapUtils.isEmpty(paramsMap) && (HttpMethod.PUT.name().equals(requestType) || HttpMethod.POST.name().equals(requestType))) {
|
||||
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
|
||||
operateLog.setRequestBody(params);
|
||||
} else {
|
||||
operateLog.setRequestBody(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求参数 转换
|
||||
*
|
||||
* @param paramMap request获取的参数数组
|
||||
*/
|
||||
public Map<String, String> converMap(Map<String, String[]> paramMap) {
|
||||
Map<String, String> returnMap = new HashMap<>();
|
||||
for (String key : paramMap.keySet()) {
|
||||
returnMap.put(key, paramMap.get(key)[0]);
|
||||
}
|
||||
return returnMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 参数拼装
|
||||
*/
|
||||
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {
|
||||
StringBuilder params = new StringBuilder();
|
||||
if (ArrayUtil.isNotEmpty(paramsArray)) {
|
||||
for (Object o : paramsArray) {
|
||||
if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
|
||||
try {
|
||||
String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames));
|
||||
params.append(jsonObj).append(StrUtil.SPACE);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return params.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略敏感属性
|
||||
*/
|
||||
public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) {
|
||||
return new PropertyPreExcludeFilter().addExcludes(ArrayUtil.addAll(EXCLUDE_PROPERTIES, excludeParamNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否需要过滤的对象
|
||||
*
|
||||
* @param o 对象信息。
|
||||
* @return 如果是需要过滤的对象,则返回true;否则返回false。
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean isFilterObject(final Object o) {
|
||||
Class<?> clazz = o.getClass();
|
||||
if (clazz.isArray()) {
|
||||
return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
|
||||
} else if (Collection.class.isAssignableFrom(clazz)) {
|
||||
Collection collection = (Collection) o;
|
||||
for (Object value : collection) {
|
||||
return value instanceof MultipartFile;
|
||||
}
|
||||
} else if (Map.class.isAssignableFrom(clazz)) {
|
||||
Map map = (Map) o;
|
||||
for (Object value : map.entrySet()) {
|
||||
Map.Entry entry = (Map.Entry) value;
|
||||
return entry.getValue() instanceof MultipartFile;
|
||||
}
|
||||
}
|
||||
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|
||||
|| o instanceof BindingResult;
|
||||
}
|
||||
|
||||
private MethodSignature getSignature(JoinPoint joinPoint) {
|
||||
//通过连接点获取方法签名
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
return signature;
|
||||
}
|
||||
|
||||
private Method getMethod(JoinPoint joinPoint) {
|
||||
//通过连接点获取方法签名
|
||||
MethodSignature signature = getSignature(joinPoint);
|
||||
//获取注解服务的方法
|
||||
Method method = signature.getMethod();
|
||||
return method;
|
||||
}
|
||||
|
||||
private <T extends Annotation> T getAnnotation(JoinPoint joinPoint, Class<T> annotationClass) {
|
||||
Method method = getMethod(joinPoint);
|
||||
//获取注解类
|
||||
T annotation = method.getAnnotation(annotationClass);
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package ai.remi.comm.log.config;
|
||||
|
||||
import ai.remi.comm.log.aspect.LogRecordAspect;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @author DianJiu 【公众号 点九开源】
|
||||
* @email dianjiuxyz@gmail.com
|
||||
* @date 2022-07-19
|
||||
* @desc
|
||||
*/
|
||||
@Slf4j
|
||||
@RefreshScope
|
||||
@Configuration
|
||||
public class LogRecordAutoConfigure {
|
||||
/**
|
||||
* 是否开启监控配置参数
|
||||
*/
|
||||
@Value("${remi-log.record.enabled}")
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* 日志监控的应用名称
|
||||
*/
|
||||
@Value("${remi-log.record.appName}")
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* 在方法成功执行后打点,记录方法的执行时间发送到指标系统,默认关闭
|
||||
* 在方法成功失败后打点,记录方法的执行时间发送到指标系统,默认关闭
|
||||
*/
|
||||
@Value("${remi-log.record.pushUrl}")
|
||||
private String pushUrl;
|
||||
|
||||
|
||||
/**
|
||||
* 根据运行环境决定是否开启日志记录监控
|
||||
*/
|
||||
@PostConstruct
|
||||
protected LogRecordAspect init() {
|
||||
if(!enabled){
|
||||
return null;
|
||||
}
|
||||
if (enabled && StringUtils.isBlank(appName)) {
|
||||
log.error("Please config the remi-log.record property in application.yml file to enabled log record, but appName is null.");
|
||||
throw new RuntimeException("Please config the remi-log.record property in application.yml file to config log record appName.");
|
||||
}
|
||||
return new LogRecordAspect();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user