1. 初始化
This commit is contained in:
commit
b2c58c73b8
|
|
@ -0,0 +1,64 @@
|
||||||
|
### gradle ###
|
||||||
|
.gradle
|
||||||
|
/build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.settings/
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/*
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
rebel.xml
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
nbproject/private/
|
||||||
|
build/
|
||||||
|
nbbuild/
|
||||||
|
nbdist/
|
||||||
|
.nb-gradle/
|
||||||
|
|
||||||
|
### maven ###
|
||||||
|
target/
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
### vscode ###
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
### logs ###
|
||||||
|
/logs/
|
||||||
|
*.log
|
||||||
|
*.log.gz
|
||||||
|
|
||||||
|
### xxl-job log ###
|
||||||
|
/xxl-job/
|
||||||
|
|
||||||
|
|
||||||
|
### temp ignore ###
|
||||||
|
*.cache
|
||||||
|
*.diff
|
||||||
|
*.patch
|
||||||
|
*.tmp
|
||||||
|
*.java~
|
||||||
|
*.properties~
|
||||||
|
*.xml~
|
||||||
|
|
||||||
|
### system ignore ###
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
Servers
|
||||||
|
.metadata
|
||||||
|
.fastRequest
|
||||||
|
|
@ -0,0 +1,204 @@
|
||||||
|
### 模块说明
|
||||||
|
```txt
|
||||||
|
sanyicloud -->
|
||||||
|
├── centrale -- 中间件
|
||||||
|
├── canal -- canal
|
||||||
|
├── common -- 系统公共模块
|
||||||
|
├── common-canal -- 添加此依赖后就可以使用canal
|
||||||
|
├── common-core -- 公共工具类核心包
|
||||||
|
├── common-easy-es -- 针对easy-es 的相关配置依赖项 --> 未使用es
|
||||||
|
├── common-excel -- 公共工具之 表格导入导出
|
||||||
|
├── common-feign -- feign 扩展封装
|
||||||
|
├── common-job -- 定时任务扩展封装
|
||||||
|
├── common-log -- 日志扩展封装
|
||||||
|
├── common-mail -- 邮件 扩展封装
|
||||||
|
├── common-mongo -- mongoDB 扩展封装
|
||||||
|
├── common-mybatis -- orm (Object Relational Mapping) 框架, mybatis-plus 扩展封装
|
||||||
|
├── common-oss -- 文件存储 扩展封装
|
||||||
|
├── common-security -- 全局登录注册
|
||||||
|
├── -modules bi 平台相关
|
||||||
|
├── -module-auth 部署于阿里云测试和华为正式 --> 使用华为docker redis
|
||||||
|
├── -module-common bi平台相关公共类, 主要为 日志, xxl-job, security 等
|
||||||
|
├── -module-fast bi平台收入与支出拉取等 --> 使用华为云 docker mysql, redis
|
||||||
|
├── -module-gateway bi平台网关 --> 使用华为云 docker redis
|
||||||
|
├── -module-job bi平台定时任务 --> 使用华为云 docker mysql, redis
|
||||||
|
├── -module-manager bi平台项目管理 --> 使用阿里云 mysql 华为云 redis
|
||||||
|
├── -module-review bi平台评论管理 --> 使用阿里云 mysql redis
|
||||||
|
├── -module-system-v2 bi平台系统管理 --> 使用阿里云 mysql redis
|
||||||
|
├── -module-website bi平台招聘以及叁一官网 --> 使用华为云 docker mysql redis
|
||||||
|
└── -tp-data 拉取tp元数据 --> 使用阿里云 mongo mysql
|
||||||
|
└── visual 可视化相关
|
||||||
|
└── -sentinel -- 流量高可用 [5003] , 并进行了 nacos 持久化配置, 默认的 group 为 SENTINEL_GROUP
|
||||||
|
```
|
||||||
|
|
||||||
|
hadoop 安装教程 https://www.cnblogs.com/jhno1/p/15218656.html
|
||||||
|
mongo 单节点开启事务 https://www.jianshu.com/p/5a03b956ce1c
|
||||||
|
|
||||||
|
docker run --name canal \
|
||||||
|
-p 11111:11111 -d \
|
||||||
|
-v $PWD/conf:/home/admin/canal-server/conf \
|
||||||
|
-v $PWD/logs:/home/admin/canal-server/logs \
|
||||||
|
--net cloud_service \
|
||||||
|
canal/canal-server
|
||||||
|
|
||||||
|
docker run -d --name jenkins \
|
||||||
|
-p 9090:8080 -p 50000:50000 \
|
||||||
|
--restart=always \
|
||||||
|
-u root \
|
||||||
|
-v $PWD/attach:/root/attach \
|
||||||
|
-v $PWD/data:/var/jenkins_home \
|
||||||
|
jenkins/jenkins:jdk11
|
||||||
|
|
||||||
|
docker run --rm -e "ALIYUN_AK=LTAI5tG1noFiFSLDhpTM9Epm" -e "ALIYUN_SK=SEpCZJfZvRNgLqAUWgzmnfSqzPXChR" -e "EMAIL=antordragon@163.com" -v /data/cerbot/certbot-dns-aliyun/cert/:/etc/letsencrypt/ certbot obtain_cert -d "yoyogame.top" -d "*.yoyogame.top"
|
||||||
|
|
||||||
|
docker run --rm -v /data/cerbot/log/:/var/log/letsencrypt -v /data/cerbot/certbot-dns-aliyun/cert/:/etc/letsencrypt/ certbot renew_certs
|
||||||
|
|
||||||
|
```text
|
||||||
|
keytool -genkey -alias undertow -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -dname "CN=localhost, OU=localhost, O=localhost, L=Wuhan, ST=Hubei, C=CN"
|
||||||
|
输入密钥库口令:
|
||||||
|
再次输入新口令:
|
||||||
|
```
|
||||||
|
|
||||||
|
| Host | User | plugin | authentication_string |
|
||||||
|
+-----------+------------------+-----------------------+------------------------------------------------------------------------+
|
||||||
|
| localhost | mysql.infoschema | caching_sha2_password | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED |
|
||||||
|
| localhost | mysql.session | caching_sha2_password | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED |
|
||||||
|
| localhost | mysql.sys | caching_sha2_password | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED |
|
||||||
|
| localhost | root | caching_sha2_password | |
|
||||||
|
|
||||||
|
|
||||||
|
jvm 查看
|
||||||
|
```text
|
||||||
|
/root/home/docker/images/cloud2/amazon-jdk-11/bin/jstat -gcutil/-gc 3041(pid) 5000(ms) 20(打印次数) --打印gc
|
||||||
|
保存java进程内存占用情况的基准版本
|
||||||
|
/root/home/docker/images/cloud2/amazon-jdk-11/bin/jcmd 21309 VM.native_memory scale=MB baseline
|
||||||
|
与基准版本进行比较(若怀疑存在内存泄漏,可过段时间再执行观察)
|
||||||
|
/root/home/docker/images/cloud2/amazon-jdk-11/bin/jcmd 21309 VM.native_memory scale=MB summary.diff
|
||||||
|
堆中对象概览, 前50
|
||||||
|
/root/home/docker/images/cloud2/amazon-jdk-11/bin/jmap -histo 21309 | head -50
|
||||||
|
堆信息概览
|
||||||
|
/root/home/docker/images/cloud2/amazon-jdk-11/bin/jhsdb jmap --heap --pid 21309
|
||||||
|
查看jvm 启动参数
|
||||||
|
/root/home/docker/images/cloud2/amazon-jdk-11/bin/jinfo -flags 21309
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
-XX:CICompilerCount=3
|
||||||
|
-XX:CompressedClassSpaceSize=260046848
|
||||||
|
-XX:ConcGCThreads=1
|
||||||
|
-XX:G1ConcRefinementThreads=4
|
||||||
|
-XX:G1HeapRegionSize=1048576
|
||||||
|
-XX:GCDrainStackTargetSize=64
|
||||||
|
-XX:+HeapDumpOnOutOfMemoryError
|
||||||
|
-XX:HeapDumpPath=/root/home/docker/images/cloud2/fast
|
||||||
|
-XX:InitialHeapSize=2147483648
|
||||||
|
-XX:MarkStackSize=4194304
|
||||||
|
-XX:MaxDirectMemorySize=1073741824
|
||||||
|
-XX:MaxHeapSize=2147483648
|
||||||
|
-XX:MaxMetaspaceSize=268435456
|
||||||
|
-XX:MaxNewSize=1287651328
|
||||||
|
-XX:MinHeapDeltaBytes=1048576
|
||||||
|
-XX:NativeMemoryTracking=summary
|
||||||
|
-XX:NonNMethodCodeHeapSize=5830732
|
||||||
|
-XX:NonProfiledCodeHeapSize=122913754
|
||||||
|
-XX:+PrintGCDetails
|
||||||
|
-XX:ProfiledCodeHeapSize=122913754
|
||||||
|
-XX:ReservedCodeCacheSize=251658240
|
||||||
|
-XX:+SegmentedCodeCache
|
||||||
|
-XX:ThreadStackSize=256
|
||||||
|
-XX:+UseCompressedClassPointers
|
||||||
|
-XX:+UseCompressedOops
|
||||||
|
-XX:+UseFastUnorderedTimeStamps
|
||||||
|
-XX:+UseG1GC
|
||||||
|
|
||||||
|
### 只排除配置文件打包
|
||||||
|
```xml
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>**/application.yml</exclude>
|
||||||
|
<exclude>**/bootstrap.yml</exclude>
|
||||||
|
<exclude>**/logback-spring.xml</exclude>
|
||||||
|
<exclude>**/logback.xml</exclude>
|
||||||
|
</excludes>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addClasspath>true</addClasspath>
|
||||||
|
<!-- MANIFEST.MF 中 Class-Path 加入前缀 -->
|
||||||
|
<classpathPrefix>lib/</classpathPrefix>
|
||||||
|
<!-- jar包不包含唯一版本标识 -->
|
||||||
|
<useUniqueVersions>false</useUniqueVersions>
|
||||||
|
<!--指定入口类 -->
|
||||||
|
<mainClass>com.chushang.xxx</mainClass>
|
||||||
|
</manifest>
|
||||||
|
<manifestEntries>
|
||||||
|
<!--MANIFEST.MF 中 Class-Path 加入资源文件目录 -->
|
||||||
|
<Class-Path>./config/</Class-Path>
|
||||||
|
</manifestEntries>
|
||||||
|
</archive>
|
||||||
|
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<skipTests>true</skipTests>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<finalName>${project.build.finalName}</finalName>
|
||||||
|
<layers>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</layers>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>repackage</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<!-- 该插件的作用是用于复制指定的文件 -->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution> <!-- 复制配置文件 -->
|
||||||
|
<id>copy-resources</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<includes>
|
||||||
|
<include>application.yml</include>
|
||||||
|
<include>bootstrap.yml</include>
|
||||||
|
<include>logback-spring.xml</include>
|
||||||
|
<include>logback.xml</include>
|
||||||
|
</includes>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<nonFilteredFileExtensions>
|
||||||
|
</nonFilteredFileExtensions>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
<outputDirectory>${project.build.directory}/config</outputDirectory>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
<finalName>${project.artifactId}</finalName>
|
||||||
|
</build>
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
### gradle ###
|
||||||
|
.gradle
|
||||||
|
/build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.settings/
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/*
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
rebel.xml
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
nbproject/private/
|
||||||
|
build/
|
||||||
|
nbbuild/
|
||||||
|
nbdist/
|
||||||
|
.nb-gradle/
|
||||||
|
|
||||||
|
### maven ###
|
||||||
|
target/
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
### vscode ###
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
### logs ###
|
||||||
|
/logs/
|
||||||
|
*.log
|
||||||
|
*.log.gz
|
||||||
|
|
||||||
|
### xxl-job log ###
|
||||||
|
/xxl-job/
|
||||||
|
|
||||||
|
|
||||||
|
### temp ignore ###
|
||||||
|
*.cache
|
||||||
|
*.diff
|
||||||
|
*.patch
|
||||||
|
*.tmp
|
||||||
|
*.java~
|
||||||
|
*.properties~
|
||||||
|
*.xml~
|
||||||
|
|
||||||
|
### system ignore ###
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
Servers
|
||||||
|
.metadata
|
||||||
|
.fastRequest
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>chushang-centrale</artifactId>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>chushang-canal</artifactId>
|
||||||
|
<description>基于 canal 的数据同步,mysql -> redis
|
||||||
|
此处的仅供演示, 具体的应在对应的 项目中, 而非单独启动一个项目
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<!--注册中心客户端-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--配置中心客户端-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-canal</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.chushang.canal;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author by zhaowenyuan create 2022/8/15 16:14
|
||||||
|
*/
|
||||||
|
@EnableDiscoveryClient
|
||||||
|
@SpringBootApplication
|
||||||
|
public class SanyiCanalApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(SanyiCanalApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.chushang.canal.sync;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
import com.chushang.common.canal.event.CanalEventListener;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.redisson.api.RMap;
|
||||||
|
import org.redisson.api.RedissonClient;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author by zhaowenyuan create 2022/Ca6 15:45
|
||||||
|
* 项目 公共配置修改
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class SystemProjectConfigListen implements CanalEventListener {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
RedissonClient redissonClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 项目 公共配置 key 前缀 值
|
||||||
|
*/
|
||||||
|
private static final String PROJECT_CONFIG_KEY = "PROJECT-ID:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增会有
|
||||||
|
* @param rowData rowData
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onInsert(CanalEntry.RowData rowData) {
|
||||||
|
//
|
||||||
|
log.info("insert");
|
||||||
|
convert(rowData.getAfterColumnsList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpdate(CanalEntry.RowData rowData) {
|
||||||
|
//
|
||||||
|
log.info("update");
|
||||||
|
convert(rowData.getAfterColumnsList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 理论上 不会有删除
|
||||||
|
* @param rowData rowData
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onDelete(CanalEntry.RowData rowData) {
|
||||||
|
//
|
||||||
|
log.info("delete");
|
||||||
|
convert(rowData.getAfterColumnsList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前 实现对应的表名
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String tableName() {
|
||||||
|
return "sanyi_system_config";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String schemaName() {
|
||||||
|
return "sanyi_manager";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void convert(List<CanalEntry.Column> afterColumnsList){
|
||||||
|
if (CollectionUtil.isNotEmpty(afterColumnsList)){
|
||||||
|
Map<String, String> columnMap =
|
||||||
|
afterColumnsList.stream().collect(
|
||||||
|
Collectors.toMap(CanalEntry.Column::getName,
|
||||||
|
CanalEntry.Column::getValue,
|
||||||
|
(k1, k2) -> k2
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
log.info("{}", columnMap);
|
||||||
|
// 如果
|
||||||
|
if (CollectionUtil.isNotEmpty(columnMap)){
|
||||||
|
String config_key = columnMap.get("config_key");
|
||||||
|
// value 可能为空
|
||||||
|
String config_value = columnMap.get("config_value");
|
||||||
|
String del_state = columnMap.get("del_state");
|
||||||
|
// 任意一个为空, 则不进行继续
|
||||||
|
if (StrUtil.hasEmpty(config_key, del_state)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String[] split = config_key.split(":");
|
||||||
|
// 长度不为2时, 代表必定不是所需要的数据
|
||||||
|
if (split.length != 2){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String projectId = split[0];
|
||||||
|
String key = split[1];
|
||||||
|
// 0 时, 代表未删除 true 代表未删除
|
||||||
|
boolean flag = "0".equals(del_state);
|
||||||
|
updateConfig(projectId, key, flag ? config_value : null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateConfig(String projectId, String key, Object value){
|
||||||
|
RMap<String, Object> map = redissonClient.getMap(PROJECT_CONFIG_KEY + projectId);
|
||||||
|
if (map.containsKey(key)){
|
||||||
|
if (null == value){
|
||||||
|
map.remove(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
server:
|
||||||
|
port: 1234
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: @artifactId@
|
||||||
|
cloud:
|
||||||
|
nacos:
|
||||||
|
discovery:
|
||||||
|
server-addr: ${nacos.host}
|
||||||
|
namespace: ${nacos.namespace}
|
||||||
|
group: ${nacos.group}
|
||||||
|
metadata:
|
||||||
|
user:
|
||||||
|
name: sanyi
|
||||||
|
password: sanyiAdmin@.1
|
||||||
|
management:
|
||||||
|
context-path: ${server.servlet.context-path}/actuator
|
||||||
|
config:
|
||||||
|
namespace: ${spring.cloud.nacos.discovery.namespace}
|
||||||
|
group: ${spring.cloud.nacos.discovery.group}
|
||||||
|
server-addr: ${spring.cloud.nacos.discovery.server-addr}
|
||||||
|
file-extension: yml
|
||||||
|
# shared-configs:
|
||||||
|
# - dataId: application-admin-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||||
|
# group: COMMON_GROUP
|
||||||
|
# - dataId: application-log-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
||||||
|
# group: COMMON_GROUP
|
||||||
|
# refresh: true
|
||||||
|
profiles:
|
||||||
|
active: @profiles.active@
|
||||||
|
main:
|
||||||
|
allow-bean-definition-overriding: true
|
||||||
|
info:
|
||||||
|
version: @project.version@
|
||||||
|
canal:
|
||||||
|
client:
|
||||||
|
instances:
|
||||||
|
example:
|
||||||
|
host: 192.168.2.254
|
||||||
|
port: 11111
|
||||||
|
batchSize: 1024
|
||||||
|
# 过滤 dbName.tableName * 代表全部
|
||||||
|
filter: sanyi_manager.*
|
||||||
|
errorRetry: true
|
||||||
|
errorRetryTime: 60000
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<configuration debug="false" scan="false">
|
||||||
|
<include resource="org/springframework/boot/logging/logback/base.xml" />
|
||||||
|
<jmxConfigurator />
|
||||||
|
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||||
|
<!--用于动态修改 日志打印级别-->
|
||||||
|
<springProperty scope="context" name="log.level" source="log.level" />
|
||||||
|
|
||||||
|
<springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/>
|
||||||
|
<!--用于 plumelog 日志收集-->
|
||||||
|
<springProperty scope="context" name="plumelog.kafka.hosts" source="plumelog.kafka.hosts"/>
|
||||||
|
<springProperty scope="context" name="plumelog.redis.hosts" source="plumelog.redis.hosts" />
|
||||||
|
<springProperty scope="context" name="plumelog.redis.auth" source="plumelog.redis.auth" />
|
||||||
|
<springProperty scope="context" name="plumelog.redis.db" source="plumelog.redis.db" />
|
||||||
|
<property name="log.level" value="${log.level}" />
|
||||||
|
|
||||||
|
<property name="log.kafka.hosts" value="${plumelog.kafka.hosts}" />
|
||||||
|
<property name="log.redis.hosts" value="${plumelog.redis.hosts}" />
|
||||||
|
<property name="log.redis.auth" value="${plumelog.redis.auth}" />
|
||||||
|
<property name="log.redis.db" value="${plumelog.redis.db}" />
|
||||||
|
<property name="log.path" value="logs/${spring.application.name}"/>
|
||||||
|
|
||||||
|
<!-- 彩色日志格式 -->
|
||||||
|
<property name="CONSOLE_LOG_PATTERN"
|
||||||
|
value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
|
||||||
|
<!-- 彩色日志依赖的渲染类 -->
|
||||||
|
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
|
||||||
|
<conversionRule conversionWord="wex"
|
||||||
|
converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
|
||||||
|
<conversionRule conversionWord="wEx"
|
||||||
|
converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
|
||||||
|
|
||||||
|
<!-- Console log output -->
|
||||||
|
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
|
||||||
|
<!-- 设置字符集 -->
|
||||||
|
<charset>UTF-8</charset>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
<!-- Log file info output -->
|
||||||
|
<appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/info.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
<maxFileSize>500MB</maxFileSize>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
|
<level>info</level>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="debug" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/debug.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${log.path}/debug.%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
<maxFileSize>500MB</maxFileSize>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
|
<level>debug</level>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<appender name="error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
|
<file>${log.path}/error.log</file>
|
||||||
|
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||||
|
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}-%i.log.zip</fileNamePattern>
|
||||||
|
<maxHistory>5</maxHistory>
|
||||||
|
<maxFileSize>500MB</maxFileSize>
|
||||||
|
</rollingPolicy>
|
||||||
|
<encoder>
|
||||||
|
<pattern>%date [%thread] %-5level [%logger{50}] %file:%line - %msg%n</pattern>
|
||||||
|
</encoder>
|
||||||
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||||
|
<level>error</level>
|
||||||
|
</filter>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<!--nacos 心跳 INFO 屏蔽-->
|
||||||
|
<logger name="com.alibaba.nacos" level="OFF">
|
||||||
|
<appender-ref ref="error"/>
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<!-- 使用kafka启用下面配置 -->
|
||||||
|
<!-- <appender name="plumelog" class="com.plumelog.logback.appender.KafkaAppender">-->
|
||||||
|
<!-- <appName>${spring.application.name}</appName>-->
|
||||||
|
<!-- <kafkaHosts>${log.hosts}</kafkaHosts>-->
|
||||||
|
<!-- </appender>-->
|
||||||
|
|
||||||
|
<!--redis-->
|
||||||
|
<!-- <appender name="redisPlumelog" class="com.plumelog.logback.appender.RedisAppender">-->
|
||||||
|
<!-- <appName>${spring.application.name}</appName>-->
|
||||||
|
<!-- <redisHost>${log.redis.hosts}</redisHost>-->
|
||||||
|
<!-- <redisDb>${log.redis.db}</redisDb>-->
|
||||||
|
<!-- <!– <redisAuth>${log.redis.auth}</redisAuth>–>-->
|
||||||
|
<!-- </appender>-->
|
||||||
|
|
||||||
|
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
|
||||||
|
<root level="info">
|
||||||
|
<!-- <appender-ref ref="console"/>-->
|
||||||
|
<appender-ref ref="info"/>
|
||||||
|
<appender-ref ref="debug"/>
|
||||||
|
<appender-ref ref="error"/>
|
||||||
|
<!-- 输出plumelog -->
|
||||||
|
<!-- <appender-ref ref="redisPlumelog"/>-->
|
||||||
|
</root>
|
||||||
|
</configuration>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>chushangcloud</artifactId>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>chushang-centrale</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<description>叁一中间件部分 -- 可能仅仅只是针对部分表的</description>
|
||||||
|
<modules>
|
||||||
|
<module>chushang-canal</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
### gradle ###
|
||||||
|
.gradle
|
||||||
|
/build/
|
||||||
|
!gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
### STS ###
|
||||||
|
.settings/
|
||||||
|
.apt_generated
|
||||||
|
.classpath
|
||||||
|
.factorypath
|
||||||
|
.project
|
||||||
|
.settings
|
||||||
|
.springBeans
|
||||||
|
bin/
|
||||||
|
|
||||||
|
### IntelliJ IDEA ###
|
||||||
|
.idea/*
|
||||||
|
*.iws
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
rebel.xml
|
||||||
|
|
||||||
|
### NetBeans ###
|
||||||
|
nbproject/private/
|
||||||
|
build/
|
||||||
|
nbbuild/
|
||||||
|
nbdist/
|
||||||
|
.nb-gradle/
|
||||||
|
|
||||||
|
### maven ###
|
||||||
|
target/
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar
|
||||||
|
*.tar.gz
|
||||||
|
|
||||||
|
### vscode ###
|
||||||
|
.vscode
|
||||||
|
|
||||||
|
### logs ###
|
||||||
|
/logs/
|
||||||
|
*.log
|
||||||
|
*.log.gz
|
||||||
|
|
||||||
|
### xxl-job log ###
|
||||||
|
/xxl-job/
|
||||||
|
|
||||||
|
|
||||||
|
### temp ignore ###
|
||||||
|
*.cache
|
||||||
|
*.diff
|
||||||
|
*.patch
|
||||||
|
*.tmp
|
||||||
|
*.java~
|
||||||
|
*.properties~
|
||||||
|
*.xml~
|
||||||
|
|
||||||
|
### system ignore ###
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
Servers
|
||||||
|
.metadata
|
||||||
|
.fastRequest
|
||||||
|
|
@ -0,0 +1,93 @@
|
||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-bom</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<commonb.version>1.0.0</commonb.version>
|
||||||
|
</properties>
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-canal</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-core</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-easy-es</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-excel</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-feign</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-mail</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-mongo</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-mybatis</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-mybatis-join</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-oss</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-log</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-job</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-redis</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-security</artifactId>
|
||||||
|
<version>${commonb.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>chushang-common</artifactId>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>chushang-common-canal</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>canal.client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.otter</groupId>
|
||||||
|
<artifactId>canal.protocol</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package com.chushang.common.canal.annotation;
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* inject the present class to the spring context
|
||||||
|
* as a listener of the canal event
|
||||||
|
*
|
||||||
|
* @author chen.qian
|
||||||
|
* @date 2018/3/19
|
||||||
|
*/
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@Component
|
||||||
|
public @interface CanalEventListener {
|
||||||
|
|
||||||
|
@AliasFor(annotation = Component.class)
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.chushang.common.canal.annotation;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ListenPoint for delete
|
||||||
|
*
|
||||||
|
* @author chen.qian
|
||||||
|
* @date 2018/3/19
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@ListenPoint(eventType = CanalEntry.EventType.DELETE)
|
||||||
|
public @interface DeleteListenPoint {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal destination
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String destination() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* database schema which you are concentrate on
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String[] schema() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tables which you are concentrate on
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String[] table() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.chushang.common.canal.annotation;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ListenPoint for insert
|
||||||
|
*
|
||||||
|
* @author chen.qian
|
||||||
|
* @date 2018/3/19
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@ListenPoint(eventType = CanalEntry.EventType.INSERT)
|
||||||
|
public @interface InsertListenPoint {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal destination
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String destination() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* database schema which you are concentrate on
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String[] schema() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tables which you are concentrate on
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String[] table() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,48 @@
|
||||||
|
package com.chushang.common.canal.annotation;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* used to indicate that method(or methods) is(are) the candidate of the
|
||||||
|
* canal event distributor
|
||||||
|
*
|
||||||
|
* @author chen.qian
|
||||||
|
* @date 2018/3/19
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface ListenPoint {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal destination
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
String destination() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* database schema which you are concentrate on
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
String[] schema() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tables which you are concentrate on
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
String[] table() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal event type
|
||||||
|
* default for all
|
||||||
|
* @return canal event type
|
||||||
|
*/
|
||||||
|
CanalEntry.EventType[] eventType() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.chushang.common.canal.annotation;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ListenPoint for update
|
||||||
|
*
|
||||||
|
* @author chen.qian
|
||||||
|
* @date 2018/3/19
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
@ListenPoint(eventType = CanalEntry.EventType.UPDATE)
|
||||||
|
public @interface UpdateListenPoint {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal destination
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String destination() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* database schema which you are concentrate on
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String[] schema() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* tables which you are concentrate on
|
||||||
|
* default for all
|
||||||
|
* @return canal destination
|
||||||
|
*/
|
||||||
|
@AliasFor(annotation = ListenPoint.class)
|
||||||
|
String[] table() default {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,108 @@
|
||||||
|
package com.chushang.common.canal.client;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.CanalConnector;
|
||||||
|
import com.alibaba.otter.canal.client.CanalConnectors;
|
||||||
|
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
|
||||||
|
import com.chushang.common.canal.client.transfer.TransponderFactory;
|
||||||
|
import com.chushang.common.canal.config.CanalConfig;
|
||||||
|
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public abstract class AbstractCanalClient implements CanalClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* running flag
|
||||||
|
*/
|
||||||
|
private volatile boolean running;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* customer config
|
||||||
|
*/
|
||||||
|
private final CanalConfig canalConfig;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TransponderFactory
|
||||||
|
*/
|
||||||
|
protected final TransponderFactory factory;
|
||||||
|
|
||||||
|
AbstractCanalClient(CanalConfig canalConfig, TransponderFactory factory) {
|
||||||
|
Objects.requireNonNull(canalConfig, "canalConfig can not be null!");
|
||||||
|
Objects.requireNonNull(canalConfig, "transponderFactory can not be null!");
|
||||||
|
this.canalConfig = canalConfig;
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
Map<String, CanalConfig.Instance> instanceMap = getConfig();
|
||||||
|
for (Map.Entry<String, CanalConfig.Instance> instanceEntry : instanceMap.entrySet()) {
|
||||||
|
process(processInstanceEntry(instanceEntry), instanceEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To initialize the canal connector
|
||||||
|
* @param connector CanalConnector
|
||||||
|
* @param config config
|
||||||
|
*/
|
||||||
|
protected abstract void process(CanalConnector connector, Map.Entry<String, CanalConfig.Instance> config);
|
||||||
|
|
||||||
|
private CanalConnector processInstanceEntry(Map.Entry<String, CanalConfig.Instance> instanceEntry) {
|
||||||
|
CanalConfig.Instance instance = instanceEntry.getValue();
|
||||||
|
CanalConnector connector;
|
||||||
|
if (instance.isClusterEnabled()) {
|
||||||
|
List<SocketAddress> addresses = new ArrayList<>();
|
||||||
|
for (String s : instance.getZookeeperAddress()) {
|
||||||
|
String[] entry = s.split(":");
|
||||||
|
if (entry.length != 2)
|
||||||
|
throw new CanalClientException("error parsing zookeeper address:" + s);
|
||||||
|
addresses.add(new InetSocketAddress(entry[0], Integer.parseInt(entry[1])));
|
||||||
|
}
|
||||||
|
connector = CanalConnectors.newClusterConnector(addresses, instanceEntry.getKey(),
|
||||||
|
instance.getUserName(),
|
||||||
|
instance.getPassword());
|
||||||
|
} else {
|
||||||
|
connector = CanalConnectors.newSingleConnector(new InetSocketAddress(instance.getHost(), instance.getPort()),
|
||||||
|
instanceEntry.getKey(),
|
||||||
|
instance.getUserName(),
|
||||||
|
instance.getPassword());
|
||||||
|
}
|
||||||
|
return connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the config
|
||||||
|
*
|
||||||
|
* @return config
|
||||||
|
*/
|
||||||
|
protected Map<String, CanalConfig.Instance> getConfig() {
|
||||||
|
CanalConfig config = canalConfig;
|
||||||
|
Map<String, CanalConfig.Instance> instanceMap;
|
||||||
|
if (config != null && (instanceMap = config.getInstances()) != null && !instanceMap.isEmpty()) {
|
||||||
|
return config.getInstances();
|
||||||
|
} else {
|
||||||
|
throw new CanalClientException("can not get the configuration of canal client!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
setRunning(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setRunning(boolean running) {
|
||||||
|
this.running = running;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.chushang.common.canal.client;
|
||||||
|
|
||||||
|
public interface CanalClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open the canal client
|
||||||
|
* to get the config and connect to the canal server (1 : 1 or 1 : n)
|
||||||
|
* and then transfer the event to the special listener
|
||||||
|
* */
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop the client
|
||||||
|
*/
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is running
|
||||||
|
* @return yes or no
|
||||||
|
*/
|
||||||
|
boolean isRunning();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.chushang.common.canal.client;
|
||||||
|
|
||||||
|
import com.chushang.common.canal.annotation.ListenPoint;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class ListenerPoint {
|
||||||
|
private final Object target;
|
||||||
|
private final Map<Method, ListenPoint> invokeMap = new HashMap<>();
|
||||||
|
|
||||||
|
ListenerPoint(Object target, Method method, ListenPoint anno) {
|
||||||
|
this.target = target;
|
||||||
|
this.invokeMap.put(method, anno);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Method, ListenPoint> getInvokeMap() {
|
||||||
|
return invokeMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
package com.chushang.common.canal.client;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.CanalConnector;
|
||||||
|
import com.chushang.common.canal.annotation.CanalEventListener;
|
||||||
|
import com.chushang.common.canal.util.BeanUtil;
|
||||||
|
import com.chushang.common.canal.annotation.ListenPoint;
|
||||||
|
import com.chushang.common.canal.client.transfer.TransponderFactory;
|
||||||
|
import com.chushang.common.canal.config.CanalConfig;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.SynchronousQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class SimpleCanalClient extends AbstractCanalClient {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(SimpleCanalClient.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* executor
|
||||||
|
*/
|
||||||
|
private final ThreadPoolExecutor executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* listeners which are used by implementing the Interface
|
||||||
|
*/
|
||||||
|
private final List<com.chushang.common.canal.event.CanalEventListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* listeners which are used by annotation
|
||||||
|
*/
|
||||||
|
private final List<ListenerPoint> annoListeners = new ArrayList<>();
|
||||||
|
|
||||||
|
public SimpleCanalClient(CanalConfig canalConfig, TransponderFactory factory) {
|
||||||
|
super(canalConfig, factory);
|
||||||
|
executor = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
|
||||||
|
60L, TimeUnit.SECONDS,
|
||||||
|
new SynchronousQueue<>(), Executors.defaultThreadFactory());
|
||||||
|
initListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void process(CanalConnector connector, Map.Entry<String, CanalConfig.Instance> config) {
|
||||||
|
executor.submit(factory.newTransponder(connector, config, listeners, annoListeners));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
super.stop();
|
||||||
|
executor.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* init listeners
|
||||||
|
*/
|
||||||
|
private void initListeners() {
|
||||||
|
logger.info("{}: initializing the listeners....", Thread.currentThread().getName());
|
||||||
|
List<com.chushang.common.canal.event.CanalEventListener> list = BeanUtil.getBeansOfType(com.chushang.common.canal.event.CanalEventListener.class);
|
||||||
|
if (list != null) {
|
||||||
|
listeners.addAll(list);
|
||||||
|
}
|
||||||
|
Map<String, Object> listenerMap = BeanUtil.getBeansWithAnnotation(CanalEventListener.class);
|
||||||
|
if (listenerMap != null) {
|
||||||
|
for (Object target : listenerMap.values()) {
|
||||||
|
Method[] methods = target.getClass().getDeclaredMethods();
|
||||||
|
for (Method method : methods) {
|
||||||
|
ListenPoint l = AnnotationUtils.findAnnotation(method, ListenPoint.class);
|
||||||
|
if (l != null) {
|
||||||
|
annoListeners.add(new ListenerPoint(target, method, l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info("{}: initializing the listeners end.", Thread.currentThread().getName());
|
||||||
|
if (logger.isWarnEnabled() && listeners.isEmpty() && annoListeners.isEmpty()) {
|
||||||
|
logger.warn("{}: No listener found in context! ", Thread.currentThread().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
package com.chushang.common.canal.client.transfer;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.CanalConnector;
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
|
||||||
|
import com.chushang.common.canal.config.CanalConfig;
|
||||||
|
import com.chushang.common.canal.event.CanalEventListener;
|
||||||
|
import com.chushang.common.canal.annotation.ListenPoint;
|
||||||
|
import com.chushang.common.canal.client.ListenerPoint;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public abstract class AbstractBasicMessageTransponder extends AbstractMessageTransponder {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(AbstractBasicMessageTransponder.class);
|
||||||
|
|
||||||
|
public AbstractBasicMessageTransponder(CanalConnector connector, Map.Entry<String, CanalConfig.Instance> config, List<CanalEventListener> listeners, List<ListenerPoint> annoListeners) {
|
||||||
|
super(connector, config, listeners, annoListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void distributeEvent(List<CanalEntry.Entry> entryList) {
|
||||||
|
for (CanalEntry.Entry entry : entryList) {
|
||||||
|
//ignore the transaction operations
|
||||||
|
List<CanalEntry.EntryType> ignoreEntryTypes = getIgnoreEntryTypes();
|
||||||
|
if (ignoreEntryTypes != null
|
||||||
|
&& ignoreEntryTypes.stream().anyMatch(t -> entry.getEntryType() == t)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CanalEntry.RowChange rowChange;
|
||||||
|
try {
|
||||||
|
rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new CanalClientException("ERROR ## parser of event has an error , data:" + entry.toString(),
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
//ignore the ddl operation
|
||||||
|
if (rowChange.hasIsDdl() && rowChange.getIsDdl()) {
|
||||||
|
processDdl(rowChange);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CanalEntry.Header header = entry.getHeader();
|
||||||
|
String tableName = header.getTableName();
|
||||||
|
String schemaName = header.getSchemaName();
|
||||||
|
for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
|
||||||
|
//distribute to listener interfaces
|
||||||
|
distributeByImpl(rowChange.getEventType(),schemaName ,tableName, rowData);
|
||||||
|
//distribute to annotation listener interfaces
|
||||||
|
distributeByAnnotation(destination,
|
||||||
|
schemaName,
|
||||||
|
tableName,
|
||||||
|
rowChange.getEventType(),
|
||||||
|
rowData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* process the ddl event
|
||||||
|
* @param rowChange rowChange
|
||||||
|
*/
|
||||||
|
protected void processDdl(CanalEntry.RowChange rowChange) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* distribute to listener interfaces
|
||||||
|
*
|
||||||
|
* @param eventType eventType
|
||||||
|
* @param schemaName 指定数据库名称 --> 一般由filter 指定, 此处不应当判断
|
||||||
|
* @param tableName 指定数据表名
|
||||||
|
* @param rowData rowData
|
||||||
|
*/
|
||||||
|
protected void distributeByImpl(CanalEntry.EventType eventType, String schemaName, String tableName, CanalEntry.RowData rowData) {
|
||||||
|
logger.info("schemaName : {}, tableName : {}", schemaName, tableName);
|
||||||
|
if (listeners != null) {
|
||||||
|
for (CanalEventListener listener : listeners) {
|
||||||
|
if (tableName.equals(listener.tableName()) && schemaName.equals(listener.schemaName())){
|
||||||
|
listener.onEvent(eventType, rowData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* distribute to annotation listener interfaces
|
||||||
|
*
|
||||||
|
* @param destination destination
|
||||||
|
* @param schemaName schema
|
||||||
|
* @param tableName table name
|
||||||
|
* @param eventType event type
|
||||||
|
* @param rowData row data
|
||||||
|
*/
|
||||||
|
protected void distributeByAnnotation(String destination,
|
||||||
|
String schemaName,
|
||||||
|
String tableName,
|
||||||
|
CanalEntry.EventType eventType,
|
||||||
|
CanalEntry.RowData rowData) {
|
||||||
|
//invoke the listeners
|
||||||
|
annoListeners.forEach(point -> point
|
||||||
|
.getInvokeMap()
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(getAnnotationFilter(destination, schemaName, tableName, eventType))
|
||||||
|
.forEach(entry -> {
|
||||||
|
Method method = entry.getKey();
|
||||||
|
method.setAccessible(true);
|
||||||
|
try {
|
||||||
|
Object[] args = getInvokeArgs(method, eventType, rowData);
|
||||||
|
method.invoke(point.getTarget(), args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("{}: Error occurred when invoke the listener's interface! class:{}, method:{}",
|
||||||
|
Thread.currentThread().getName(),
|
||||||
|
point.getTarget().getClass().getName(), method.getName());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the filters predicate
|
||||||
|
*
|
||||||
|
* @param destination destination
|
||||||
|
* @param schemaName schema
|
||||||
|
* @param tableName table name
|
||||||
|
* @param eventType event type
|
||||||
|
* @return predicate
|
||||||
|
*/
|
||||||
|
protected abstract Predicate<Map.Entry<Method, ListenPoint>> getAnnotationFilter(String destination,
|
||||||
|
String schemaName,
|
||||||
|
String tableName,
|
||||||
|
CanalEntry.EventType eventType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the args
|
||||||
|
*
|
||||||
|
* @param method method
|
||||||
|
* @param eventType event type
|
||||||
|
* @param rowData row data
|
||||||
|
* @return args which will be used by invoking the annotation methods
|
||||||
|
*/
|
||||||
|
protected abstract Object[] getInvokeArgs(Method method, CanalEntry.EventType eventType,
|
||||||
|
CanalEntry.RowData rowData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the ignore eventType list
|
||||||
|
*
|
||||||
|
* @return eventType list
|
||||||
|
*/
|
||||||
|
protected List<CanalEntry.EntryType> getIgnoreEntryTypes() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
package com.chushang.common.canal.client.transfer;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.CanalConnector;
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
import com.alibaba.otter.canal.protocol.Message;
|
||||||
|
import com.alibaba.otter.canal.protocol.exception.CanalClientException;
|
||||||
|
import com.chushang.common.canal.client.ListenerPoint;
|
||||||
|
import com.chushang.common.canal.config.CanalConfig;
|
||||||
|
import com.chushang.common.canal.event.CanalEventListener;
|
||||||
|
import com.chushang.common.core.util.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public abstract class AbstractMessageTransponder extends MessageTransponder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal connector
|
||||||
|
*/
|
||||||
|
private final CanalConnector connector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* custom config
|
||||||
|
*/
|
||||||
|
protected final CanalConfig.Instance config;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* destination of canal server
|
||||||
|
*/
|
||||||
|
protected final String destination;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* listeners which are used by implementing the Interface
|
||||||
|
*/
|
||||||
|
protected final List<CanalEventListener> listeners = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* listeners which are used by annotation
|
||||||
|
*/
|
||||||
|
protected final List<ListenerPoint> annoListeners = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* running flag
|
||||||
|
*/
|
||||||
|
private volatile boolean running = true;
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AbstractMessageTransponder.class);
|
||||||
|
|
||||||
|
public AbstractMessageTransponder(CanalConnector connector,
|
||||||
|
Map.Entry<String, CanalConfig.Instance> config,
|
||||||
|
List<CanalEventListener> listeners,
|
||||||
|
List<ListenerPoint> annoListeners) {
|
||||||
|
Objects.requireNonNull(connector, "connector can not be null!");
|
||||||
|
Objects.requireNonNull(config, "config can not be null!");
|
||||||
|
this.connector = connector;
|
||||||
|
this.destination = config.getKey();
|
||||||
|
this.config = config.getValue();
|
||||||
|
if (listeners != null)
|
||||||
|
this.listeners.addAll(listeners);
|
||||||
|
if (annoListeners != null)
|
||||||
|
this.annoListeners.addAll(annoListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// 在 run 时 才进行连接
|
||||||
|
connect();
|
||||||
|
int errorCount = config.getRetryCount();
|
||||||
|
final long interval = config.getAcquireInterval();
|
||||||
|
final String threadName = Thread.currentThread().getName();
|
||||||
|
boolean interrupted = Thread.currentThread().isInterrupted();
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
Message message = connector.getWithoutAck(config.getBatchSize());
|
||||||
|
long batchId = message.getId();
|
||||||
|
int size = message.getEntries().size();
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("{}: Get message from canal server >>>>> size:{}", threadName, size);
|
||||||
|
}
|
||||||
|
//empty message
|
||||||
|
if (batchId == -1 || size == 0) {
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("{}: Empty message... sleep for {} millis", threadName, interval);
|
||||||
|
}
|
||||||
|
Thread.sleep(interval);
|
||||||
|
} else {
|
||||||
|
distributeEvent(message.getEntries());
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit ack
|
||||||
|
connector.ack(batchId);
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("{}: Ack message. batchId:{}", threadName, batchId);
|
||||||
|
}
|
||||||
|
} catch (CanalClientException e) {
|
||||||
|
errorCount--;
|
||||||
|
logger.error(threadName + ": Error occurred!! ", e);
|
||||||
|
try {
|
||||||
|
Thread.sleep(interval);
|
||||||
|
} catch (InterruptedException e1) {
|
||||||
|
errorCount = 0;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
errorCount = 0;
|
||||||
|
connector.rollback();
|
||||||
|
} finally {
|
||||||
|
// 重试次数 为0, 不在进行重试, 等待重新连接
|
||||||
|
if (errorCount <= 0) {
|
||||||
|
stop();
|
||||||
|
logger.info("{}: Topping the client.. ", Thread.currentThread().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while ((running && !interrupted));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void distributeEvent(List<CanalEntry.Entry> entryList);
|
||||||
|
|
||||||
|
private void connect() {
|
||||||
|
connector.connect();
|
||||||
|
// 此处 添加 过滤 ->
|
||||||
|
if (StringUtils.isNotEmpty(config.getFilter())) {
|
||||||
|
connector.subscribe(config.getFilter());
|
||||||
|
} else {
|
||||||
|
connector.subscribe();
|
||||||
|
}
|
||||||
|
connector.rollback();
|
||||||
|
|
||||||
|
logger.info("connector is connect");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* stop running
|
||||||
|
*/
|
||||||
|
void stop() {
|
||||||
|
// 此处应当就是失败了, 需要停止进行重试
|
||||||
|
logger.info("{}: client stopped. ", Thread.currentThread().getName());
|
||||||
|
running = false;
|
||||||
|
if (null == connector) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 停止时, 关闭连接
|
||||||
|
connector.disconnect();
|
||||||
|
// 说明失败重试
|
||||||
|
if (config.isErrorRetry()) {
|
||||||
|
logger.info("connector restart");
|
||||||
|
|
||||||
|
long errorRetryTime = config.getErrorRetryTime();
|
||||||
|
running = true;
|
||||||
|
// 延时执行
|
||||||
|
timer.schedule(this, errorRetryTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.chushang.common.canal.client.transfer;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.CanalConnector;
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
import com.chushang.common.canal.config.CanalConfig;
|
||||||
|
import com.chushang.common.canal.annotation.ListenPoint;
|
||||||
|
import com.chushang.common.canal.client.ListenerPoint;
|
||||||
|
import com.chushang.common.canal.event.CanalEventListener;
|
||||||
|
import com.chushang.common.core.util.StringUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class DefaultMessageTransponder extends AbstractBasicMessageTransponder {
|
||||||
|
|
||||||
|
public DefaultMessageTransponder(CanalConnector connector,
|
||||||
|
Map.Entry<String, CanalConfig.Instance> config,
|
||||||
|
List<CanalEventListener> listeners,
|
||||||
|
List<ListenerPoint> annoListeners) {
|
||||||
|
super(connector, config, listeners, annoListeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the filters predicate
|
||||||
|
*
|
||||||
|
* @param destination destination
|
||||||
|
* @param schemaName schema
|
||||||
|
* @param tableName table name
|
||||||
|
* @param eventType event type
|
||||||
|
* @return predicate
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected Predicate<Map.Entry<Method, ListenPoint>> getAnnotationFilter(String destination,
|
||||||
|
String schemaName,
|
||||||
|
String tableName,
|
||||||
|
CanalEntry.EventType eventType) {
|
||||||
|
Predicate<Map.Entry<Method, ListenPoint>> df = e -> StringUtils.isEmpty(e.getValue().destination())
|
||||||
|
|| e.getValue().destination().equals(destination);
|
||||||
|
|
||||||
|
Predicate<Map.Entry<Method, ListenPoint>> sf = e -> e.getValue().schema().length == 0
|
||||||
|
|| Arrays.asList(e.getValue().schema()).contains(schemaName);
|
||||||
|
|
||||||
|
Predicate<Map.Entry<Method, ListenPoint>> tf = e -> e.getValue().table().length == 0
|
||||||
|
|| Arrays.asList(e.getValue().table()).contains(tableName);
|
||||||
|
|
||||||
|
Predicate<Map.Entry<Method, ListenPoint>> ef = e -> (e.getValue().eventType().length == 0)
|
||||||
|
|| Arrays.stream(e.getValue().eventType()).anyMatch(ev -> ev == eventType);
|
||||||
|
return df.and(sf).and(tf).and(ef);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object[] getInvokeArgs(Method method, CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
|
||||||
|
return Arrays.stream(method.getParameterTypes())
|
||||||
|
.map(p -> p == CanalEntry.EventType.class
|
||||||
|
? eventType
|
||||||
|
: p == CanalEntry.RowData.class
|
||||||
|
? rowData : null)
|
||||||
|
.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<CanalEntry.EntryType> getIgnoreEntryTypes() {
|
||||||
|
return Arrays.asList(CanalEntry.EntryType.TRANSACTIONBEGIN, CanalEntry.EntryType.TRANSACTIONEND, CanalEntry.EntryType.HEARTBEAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.chushang.common.canal.client.transfer;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.CanalConnector;
|
||||||
|
import com.chushang.common.canal.config.CanalConfig;
|
||||||
|
import com.chushang.common.canal.client.ListenerPoint;
|
||||||
|
import com.chushang.common.canal.event.CanalEventListener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DefaultTransponderFactory implements TransponderFactory {
|
||||||
|
@Override
|
||||||
|
public MessageTransponder newTransponder(CanalConnector connector, Map.Entry<String, CanalConfig.Instance> config, List<CanalEventListener> listeners,
|
||||||
|
List<ListenerPoint> annoListeners) {
|
||||||
|
return new DefaultMessageTransponder(connector, config, listeners, annoListeners);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
package com.chushang.common.canal.client.transfer;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
public abstract class MessageTransponder extends TimerTask {
|
||||||
|
|
||||||
|
public static final Timer timer = new Timer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.chushang.common.canal.client.transfer;
|
||||||
|
|
||||||
|
public class MessageTransponders {
|
||||||
|
|
||||||
|
public static TransponderFactory defaultMessageTransponder() {
|
||||||
|
return new DefaultTransponderFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.chushang.common.canal.client.transfer;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.client.CanalConnector;
|
||||||
|
import com.chushang.common.canal.client.ListenerPoint;
|
||||||
|
import com.chushang.common.canal.config.CanalConfig;
|
||||||
|
import com.chushang.common.canal.event.CanalEventListener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface TransponderFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param connector connector
|
||||||
|
* @param config config
|
||||||
|
* @param listeners listeners
|
||||||
|
* @param annoListeners annoListeners
|
||||||
|
* @return MessageTransponder
|
||||||
|
*/
|
||||||
|
MessageTransponder newTransponder(CanalConnector connector, Map.Entry<String, CanalConfig.Instance> config, List<CanalEventListener> listeners,
|
||||||
|
List<ListenerPoint> annoListeners);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.chushang.common.canal.config;
|
||||||
|
|
||||||
|
|
||||||
|
import com.chushang.common.canal.client.SimpleCanalClient;
|
||||||
|
import com.chushang.common.canal.client.transfer.MessageTransponders;
|
||||||
|
import com.chushang.common.canal.util.BeanUtil;
|
||||||
|
import com.chushang.common.canal.client.CanalClient;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
|
||||||
|
|
||||||
|
public class CanalClientAutoConfiguration {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(CanalClientAutoConfiguration.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CanalConfig canalConfig;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public BeanUtil beanUtil() {
|
||||||
|
return new BeanUtil();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
private CanalClient canalClient() {
|
||||||
|
|
||||||
|
CanalClient canalClient =
|
||||||
|
new SimpleCanalClient(canalConfig, MessageTransponders.defaultMessageTransponder());
|
||||||
|
|
||||||
|
canalClient.start();
|
||||||
|
|
||||||
|
logger.info("Starting canal client....");
|
||||||
|
|
||||||
|
return canalClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,200 @@
|
||||||
|
package com.chushang.common.canal.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.core.annotation.Order;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
@RefreshScope
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "canal.client")
|
||||||
|
public class CanalConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* instance config
|
||||||
|
*/
|
||||||
|
private Map<String, Instance> instances = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public Map<String, Instance> getInstances() {
|
||||||
|
return instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInstances(Map<String, Instance> instances) {
|
||||||
|
this.instances = instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* instance config class
|
||||||
|
*/
|
||||||
|
public static class Instance {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is cluster-mod
|
||||||
|
*/
|
||||||
|
private boolean clusterEnabled;
|
||||||
|
/**
|
||||||
|
* zookeeper address
|
||||||
|
*/
|
||||||
|
private Set<String> zookeeperAddress = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal server host
|
||||||
|
*/
|
||||||
|
private String host = "127.0.0.1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal server port
|
||||||
|
*/
|
||||||
|
private int port = 10001;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal user name
|
||||||
|
*/
|
||||||
|
private String userName = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* canal password
|
||||||
|
*/
|
||||||
|
private String password = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* size when get messages from the canal server
|
||||||
|
*/
|
||||||
|
private int batchSize = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filter
|
||||||
|
*/
|
||||||
|
private String filter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retry count when error occurred
|
||||||
|
* 单次异常 的重试次数
|
||||||
|
*/
|
||||||
|
private int retryCount = 5;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败后重新启动 默认不进行失败重新连接
|
||||||
|
*/
|
||||||
|
private boolean errorRetry = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败后间隔多长时间进行重启
|
||||||
|
* 单位 ms
|
||||||
|
* 默认10分钟 一次进行重新连接
|
||||||
|
*/
|
||||||
|
private long errorRetryTime = 10 * 60 * 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* interval of the message-acquiring
|
||||||
|
*/
|
||||||
|
private long acquireInterval = 1000;
|
||||||
|
|
||||||
|
public Instance() {}
|
||||||
|
|
||||||
|
public boolean isClusterEnabled() {
|
||||||
|
return clusterEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClusterEnabled(boolean clusterEnabled) {
|
||||||
|
this.clusterEnabled = clusterEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getZookeeperAddress() {
|
||||||
|
return zookeeperAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setZookeeperAddress(Set<String> zookeeperAddress) {
|
||||||
|
this.zookeeperAddress = zookeeperAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(String host) {
|
||||||
|
this.host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPort(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserName() {
|
||||||
|
return userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserName(String userName) {
|
||||||
|
this.userName = userName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBatchSize() {
|
||||||
|
return batchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBatchSize(int batchSize) {
|
||||||
|
this.batchSize = batchSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilter() {
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFilter(String filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRetryCount() {
|
||||||
|
return retryCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRetryCount(int retryCount) {
|
||||||
|
this.retryCount = retryCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAcquireInterval() {
|
||||||
|
return acquireInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAcquireInterval(long acquireInterval) {
|
||||||
|
this.acquireInterval = acquireInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isErrorRetry() {
|
||||||
|
return errorRetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorRetry(boolean errorRetry) {
|
||||||
|
this.errorRetry = errorRetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getErrorRetryTime() {
|
||||||
|
return errorRetryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorRetryTime(long errorRetryTime) {
|
||||||
|
this.errorRetryTime = errorRetryTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.chushang.common.canal.event;
|
||||||
|
|
||||||
|
import com.alibaba.otter.canal.protocol.CanalEntry;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public interface CanalEventListener {
|
||||||
|
|
||||||
|
default void onEvent(CanalEntry.EventType eventType, CanalEntry.RowData rowData) {
|
||||||
|
Objects.requireNonNull(eventType);
|
||||||
|
switch (eventType) {
|
||||||
|
case INSERT:
|
||||||
|
onInsert(rowData);
|
||||||
|
break;
|
||||||
|
case UPDATE:
|
||||||
|
onUpdate(rowData);
|
||||||
|
break;
|
||||||
|
case DELETE:
|
||||||
|
onDelete(rowData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fired on insert event
|
||||||
|
*
|
||||||
|
* @param rowData rowData
|
||||||
|
*/
|
||||||
|
void onInsert(CanalEntry.RowData rowData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fired on update event
|
||||||
|
*
|
||||||
|
* @param rowData rowData
|
||||||
|
*/
|
||||||
|
void onUpdate(CanalEntry.RowData rowData);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fired on delete event
|
||||||
|
*
|
||||||
|
* @param rowData rowData
|
||||||
|
*/
|
||||||
|
void onDelete(CanalEntry.RowData rowData);
|
||||||
|
|
||||||
|
String tableName();
|
||||||
|
|
||||||
|
String schemaName();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package com.chushang.common.canal.util;
|
||||||
|
|
||||||
|
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.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class BeanUtil implements ApplicationContextAware {
|
||||||
|
|
||||||
|
private static ApplicationContext applicationContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
|
BeanUtil.applicationContext = applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T getBean(Class<T> clazz) {
|
||||||
|
T obj;
|
||||||
|
try {
|
||||||
|
obj = applicationContext.getBean(clazz);
|
||||||
|
} catch (Exception e) {
|
||||||
|
obj = null;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> getBeansOfType(Class<T> clazz) {
|
||||||
|
Map<String, T> map;
|
||||||
|
try {
|
||||||
|
map = applicationContext.getBeansOfType(clazz);
|
||||||
|
} catch (Exception e) {
|
||||||
|
map = null;
|
||||||
|
}
|
||||||
|
return map == null ? null : new ArrayList<>(map.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> anno) {
|
||||||
|
Map<String, Object> map;
|
||||||
|
try {
|
||||||
|
map = applicationContext.getBeansWithAnnotation(anno);
|
||||||
|
} catch (Exception e) {
|
||||||
|
map = null;
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
com.chushang.common.canal.config.CanalConfig, \
|
||||||
|
com.chushang.common.canal.config.CanalClientAutoConfiguration
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>chushang-common</artifactId>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>chushang-common-core</artifactId>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cn.hutool</groupId>
|
||||||
|
<artifactId>hutool-all</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-json</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- JWT -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- Transmittable ThreadLocal 处理父子线程无法共用 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>transmittable-thread-local</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.xnio</groupId>
|
||||||
|
<artifactId>xnio-api</artifactId>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<!-- SpringCloud Loadbalancer -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba.fastjson2</groupId>
|
||||||
|
<artifactId>fastjson2</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by FernFlower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
package com.chushang.common.core.cache;
|
||||||
|
|
||||||
|
import java.lang.annotation.Documented;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Documented
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
public @interface CacheNameSpase {
|
||||||
|
String value() default "";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.config;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||||
|
import com.chushang.common.core.jackson.JavaTimeModule;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@ConditionalOnClass(ObjectMapper.class)
|
||||||
|
@AutoConfigureBefore(JacksonAutoConfiguration.class)
|
||||||
|
public class JacksonConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public Jackson2ObjectMapperBuilderCustomizer customizer() {
|
||||||
|
return builder -> {
|
||||||
|
builder.locale(Locale.CHINA);
|
||||||
|
builder.timeZone(TimeZone.getTimeZone(ZoneId.systemDefault()));
|
||||||
|
builder.simpleDateFormat(DatePattern.NORM_DATETIME_PATTERN);
|
||||||
|
builder.serializerByType(Long.class, ToStringSerializer.instance);
|
||||||
|
builder.modules(new JavaTimeModule());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public interface CacheConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限缓存前缀
|
||||||
|
*/
|
||||||
|
String LOGIN_TOKEN_KEY = "login_tokens:";
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2022/2/15 18:09
|
||||||
|
*/
|
||||||
|
public interface CharConstants {
|
||||||
|
char[] chars = new char[]{
|
||||||
|
'2','3','4','5','6','7','8','9',
|
||||||
|
'a','b','c','d','e','f','g','h','j','k','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
|
||||||
|
'A','B','C','D','E','F','G','H','J','K','L','M','N','P','Q','R','S','T','U','V','W','X','Y','Z'
|
||||||
|
};
|
||||||
|
|
||||||
|
char[] numChars = new char[]{
|
||||||
|
'0','1','2','3','4','5','6','7','8','9'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
public interface CommonConstants {
|
||||||
|
|
||||||
|
String HEAD_TOKEN_KEY = "sanyi-token";
|
||||||
|
|
||||||
|
String ACCOUNT_SYMBOL = ":";
|
||||||
|
|
||||||
|
String SEMICOLON = ";";
|
||||||
|
|
||||||
|
String DOUBLE_QUOTATION_MARKS = "\"";
|
||||||
|
|
||||||
|
String SINGLE_QUOTATION_MARKS = "'";
|
||||||
|
|
||||||
|
String DOUBLE_SLASH = "\\";
|
||||||
|
|
||||||
|
String PERCENT_SIGN = "%";
|
||||||
|
|
||||||
|
String COMMA = ",";
|
||||||
|
|
||||||
|
String AT_SYMBOL = "@";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,145 @@
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用常量信息
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public interface Constants
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* UTF-8 字符集
|
||||||
|
*/
|
||||||
|
String UTF8 = "UTF-8";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GBK 字符集
|
||||||
|
*/
|
||||||
|
String GBK = "GBK";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RMI 远程方法调用
|
||||||
|
*/
|
||||||
|
String LOOKUP_RMI = "rmi:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAP 远程方法调用
|
||||||
|
*/
|
||||||
|
String LOOKUP_LDAP = "ldap:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LDAPS 远程方法调用
|
||||||
|
*/
|
||||||
|
String LOOKUP_LDAPS = "ldaps:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* http请求
|
||||||
|
*/
|
||||||
|
String HTTP = "http://";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https请求
|
||||||
|
*/
|
||||||
|
String HTTPS = "https://";
|
||||||
|
|
||||||
|
String WWW = "www.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功标记
|
||||||
|
*/
|
||||||
|
Integer SUCCESS = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败标记
|
||||||
|
*/
|
||||||
|
Integer FAIL = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录成功状态
|
||||||
|
*/
|
||||||
|
String LOGIN_SUCCESS_STATUS = "0";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录失败状态
|
||||||
|
*/
|
||||||
|
String LOGIN_FAIL_STATUS = "1";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录成功
|
||||||
|
*/
|
||||||
|
String LOGIN_SUCCESS = "Success";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销
|
||||||
|
*/
|
||||||
|
String LOGOUT = "Logout";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册
|
||||||
|
*/
|
||||||
|
String REGISTER = "Register";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录失败
|
||||||
|
*/
|
||||||
|
String LOGIN_FAIL = "Error";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前记录起始索引
|
||||||
|
*/
|
||||||
|
String PAGE_NUM = "pageNum";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页显示记录数
|
||||||
|
*/
|
||||||
|
String PAGE_SIZE = "pageSize";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序列
|
||||||
|
*/
|
||||||
|
String ORDER_BY_COLUMN = "orderByColumn";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 排序的方向 "desc" 或者 "asc".
|
||||||
|
*/
|
||||||
|
String IS_ASC = "isAsc";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码 redis key
|
||||||
|
*/
|
||||||
|
String CAPTCHA_CODE_KEY = "captcha_codes:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证码有效期(分钟)
|
||||||
|
*/
|
||||||
|
long CAPTCHA_EXPIRATION = 2;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数管理 cache key
|
||||||
|
*/
|
||||||
|
String SYS_CONFIG_KEY = "sys_config:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字典管理 cache key
|
||||||
|
*/
|
||||||
|
String SYS_DICT_KEY = "sys_dict:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资源映射路径 前缀
|
||||||
|
*/
|
||||||
|
String RESOURCE_PREFIX = "/profile";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
|
||||||
|
*/
|
||||||
|
String[] JOB_WHITELIST_STR = { "com.ruoyi" };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时任务违规的字符
|
||||||
|
*/
|
||||||
|
String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
|
||||||
|
"org.springframework", "org.apache", "com.ruoyi.common.core.utils.file" };
|
||||||
|
|
||||||
|
String SERIAL_VERSION_STR = "serialVersionUID";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author by zhaowenyuan create 2022/8/2 18:25
|
||||||
|
*/
|
||||||
|
public interface RegularConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱正则匹配
|
||||||
|
* 名称允许汉字、字母、数字,域名只允许英文域名
|
||||||
|
*/
|
||||||
|
String ZH_MAIL_REGULAR = "^[A-Za-z0-9\\u4e00-\\u9fa5]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
|
||||||
|
/**
|
||||||
|
* 只允许英文字母、数字、下划线、英文句号、以及中划线组成
|
||||||
|
*/
|
||||||
|
String EN_MAIL_REGULAR = "^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ruoyi
|
||||||
|
*/
|
||||||
|
public interface SecurityConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户ID字段
|
||||||
|
*/
|
||||||
|
String DETAILS_USER_ID = "user_id";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名字段
|
||||||
|
*/
|
||||||
|
String DETAILS_USERNAME = "username";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权信息字段
|
||||||
|
*/
|
||||||
|
String AUTHORIZATION_HEADER = "authorization";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求来源
|
||||||
|
*/
|
||||||
|
String FROM_SOURCE = "from-source";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部请求
|
||||||
|
*/
|
||||||
|
String INNER = "inner";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户标识
|
||||||
|
*/
|
||||||
|
String USER_KEY = "user_key";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录用户
|
||||||
|
*/
|
||||||
|
String LOGIN_USER = "login_user";
|
||||||
|
/**
|
||||||
|
* 角色权限
|
||||||
|
*/
|
||||||
|
String ROLE_PERMISSION = "role_permission";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
public interface ServiceNameConstants {
|
||||||
|
|
||||||
|
String SYSTEM_SERVICE_V2 = "system-service-v2";
|
||||||
|
|
||||||
|
String MANAGER_SERVICE = "manager-service";
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
public interface TokenConstants
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 令牌自定义标识
|
||||||
|
*/
|
||||||
|
String AUTHENTICATION = "Authorization";
|
||||||
|
/**
|
||||||
|
* 参数加密值
|
||||||
|
*/
|
||||||
|
String API_SIGN = "apiSign";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求时间戳
|
||||||
|
*/
|
||||||
|
String REQUEST_TIME = "requestTime";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 令牌前缀
|
||||||
|
*/
|
||||||
|
String PREFIX = "Bearer ";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 令牌秘钥
|
||||||
|
*/
|
||||||
|
String SECRET = "my_name_is_onn_secret";
|
||||||
|
|
||||||
|
// 1秒
|
||||||
|
Long MILLIS_SECOND = 1000L;
|
||||||
|
// 1分钟
|
||||||
|
Long MILLIS_MINUTE = 60L * MILLIS_SECOND;
|
||||||
|
// 1个小时
|
||||||
|
Long MILLIS_HOUR = 60L * MILLIS_MINUTE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,180 @@
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author by zhaowenyuan create 2022/7/26 19:01
|
||||||
|
*/
|
||||||
|
public interface UnityLanguageConstants {
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Afrikaans.
|
||||||
|
int Afrikaans = 0;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Arabic.
|
||||||
|
int Arabic = 1;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Basque.
|
||||||
|
int Basque = 2;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Belarusian.
|
||||||
|
int Belarusian = 3;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Bulgarian.
|
||||||
|
int Bulgarian = 4;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Catalan.
|
||||||
|
int Catalan = 5;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Chinese.
|
||||||
|
int Chinese = 6;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Czech.
|
||||||
|
int Czech = 7;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Danish.
|
||||||
|
int Danish = 8;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Dutch.
|
||||||
|
int Dutch = 9;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// English.
|
||||||
|
int English = 10;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Estonian.
|
||||||
|
int Estonian = 11;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Faroese.
|
||||||
|
int Faroese = 12;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Finnish.
|
||||||
|
int Finnish = 13;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// French.
|
||||||
|
int French = 14;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// German.
|
||||||
|
int German = 15;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Greek.
|
||||||
|
int Greek = 16;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Hebrew.
|
||||||
|
int Hebrew = 17;
|
||||||
|
int Hugarian = 18;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Hungarian.
|
||||||
|
int Hungarian = 18;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Icelandic.
|
||||||
|
int Icelandic = 19;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Indonesian.
|
||||||
|
int Indonesian = 20;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Italian.
|
||||||
|
int Italian = 21;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Japanese.
|
||||||
|
int Japanese = 22;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Korean.
|
||||||
|
int Korean = 23;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Latvian.
|
||||||
|
int Latvian = 24;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Lithuanian.
|
||||||
|
int Lithuanian = 25;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Norwegian.
|
||||||
|
int Norwegian = 26;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Polish.
|
||||||
|
int Polish = 27;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Portuguese.
|
||||||
|
int Portuguese = 28;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Romanian.
|
||||||
|
int Romanian = 29;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Russian.
|
||||||
|
int Russian = 30;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Serbo-Croatian.
|
||||||
|
int SerboCroatian = 31;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Slovak.
|
||||||
|
int Slovak = 32;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Slovenian.
|
||||||
|
int Slovenian = 33;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Spanish.
|
||||||
|
int Spanish = 34;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Swedish.
|
||||||
|
int Swedish = 35;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Thai.
|
||||||
|
int Thai = 36;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Turkish.
|
||||||
|
int Turkish = 37;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Ukrainian.
|
||||||
|
int Ukrainian = 38;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Vietnamese.
|
||||||
|
int Vietnamese = 39;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// ChineseSimplified.
|
||||||
|
int ChineseSimplified = 40;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// ChineseTraditional.
|
||||||
|
int ChineseTraditional = 41;
|
||||||
|
//
|
||||||
|
// 摘要:
|
||||||
|
// Unknown.
|
||||||
|
int Unknown = 42;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.chushang.common.core.constant;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户常量信息
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public interface UserConstants
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 平台内系统用户的唯一标志
|
||||||
|
*/
|
||||||
|
String SYS_USER = "SYS_USER";
|
||||||
|
|
||||||
|
/** 正常状态 */
|
||||||
|
String NORMAL = "0";
|
||||||
|
|
||||||
|
/** 异常状态 */
|
||||||
|
String EXCEPTION = "1";
|
||||||
|
|
||||||
|
/** 用户封禁状态 */
|
||||||
|
String USER_DISABLE = "1";
|
||||||
|
|
||||||
|
/** 角色封禁状态 */
|
||||||
|
String ROLE_DISABLE = "1";
|
||||||
|
|
||||||
|
/** 部门正常状态 */
|
||||||
|
String DEPT_NORMAL = "0";
|
||||||
|
|
||||||
|
/** 部门停用状态 */
|
||||||
|
String DEPT_DISABLE = "1";
|
||||||
|
|
||||||
|
/** 字典正常状态 */
|
||||||
|
String DICT_NORMAL = "0";
|
||||||
|
|
||||||
|
/** 是否为系统默认(是) */
|
||||||
|
String YES = "Y";
|
||||||
|
|
||||||
|
/** 是否菜单外链(是) */
|
||||||
|
String YES_FRAME = "0";
|
||||||
|
|
||||||
|
/** 是否菜单外链(否) */
|
||||||
|
String NO_FRAME = "1";
|
||||||
|
|
||||||
|
/** 菜单类型(目录) */
|
||||||
|
String TYPE_DIR = "M";
|
||||||
|
/** Layout组件标识 */
|
||||||
|
String LAYOUT = "Layout";
|
||||||
|
/** ParentView组件标识 */
|
||||||
|
String PARENT_VIEW = "ParentView";
|
||||||
|
|
||||||
|
/** InnerLink组件标识 */
|
||||||
|
String INNER_LINK = "InnerLink";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户名长度限制
|
||||||
|
*/
|
||||||
|
int USERNAME_MIN_LENGTH = 2;
|
||||||
|
|
||||||
|
int USERNAME_MAX_LENGTH = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码长度限制
|
||||||
|
*/
|
||||||
|
int PASSWORD_MIN_LENGTH = 5;
|
||||||
|
|
||||||
|
int PASSWORD_MAX_LENGTH = 20;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by FernFlower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
package com.chushang.common.core.enums;
|
||||||
|
|
||||||
|
public enum AppStartType {
|
||||||
|
main("MAIN", "内嵌方式启动"),
|
||||||
|
web_server("WEB_SERVER", "部署到容器方式启动");
|
||||||
|
|
||||||
|
public static final String START_FORMAT = "[{}]开始启动***{}***";
|
||||||
|
public static final String END_FORMAT = "[{}]***{}***启动完成";
|
||||||
|
public static final String END_ERROR_FORMAT = "[{}]***定时器***异常:{}";
|
||||||
|
private final String type;
|
||||||
|
private final String desc;
|
||||||
|
|
||||||
|
private AppStartType(String type, String desc) {
|
||||||
|
this.type = type;
|
||||||
|
this.desc = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String desc() {
|
||||||
|
return this.desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean eq(String type) {
|
||||||
|
return this.type().equals(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String type() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.exception;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lengleng
|
||||||
|
* @date 😴2018年06月22日16:21:57
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class CheckedException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public CheckedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckedException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckedException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CheckedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.chushang.common.core.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部认证异常
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class InnerAuthException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public InnerAuthException(String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.chushang.common.core.exception;
|
||||||
|
|
||||||
|
import com.chushang.common.core.exception.enums.ExceptionEnum;
|
||||||
|
import com.chushang.common.core.web.EnumUtils;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ResultException extends RuntimeException {
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
private String code = "500";
|
||||||
|
|
||||||
|
public ResultException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultException(String msg, Throwable e) {
|
||||||
|
super(msg, e);
|
||||||
|
this.msg = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultException(String msg, String code) {
|
||||||
|
super(msg);
|
||||||
|
this.msg = msg;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultException(EnumUtils.CodeEnum<String, String> codeEnum){
|
||||||
|
super(codeEnum.getMsg());
|
||||||
|
this.msg = codeEnum.getMsg();
|
||||||
|
this.code = codeEnum.getCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultException(String msg, String code, Throwable e) {
|
||||||
|
super(msg, e);
|
||||||
|
this.msg = msg;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
package com.chushang.common.core.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public final class ServiceException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误码
|
||||||
|
*/
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误提示
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误明细,内部调试错误
|
||||||
|
*
|
||||||
|
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
|
||||||
|
*/
|
||||||
|
private String detailMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 空构造方法,避免反序列化问题
|
||||||
|
*/
|
||||||
|
public ServiceException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException(String message)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException(String message, Integer code)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDetailMessage()
|
||||||
|
{
|
||||||
|
return detailMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage()
|
||||||
|
{
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCode()
|
||||||
|
{
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException setMessage(String message)
|
||||||
|
{
|
||||||
|
this.message = message;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServiceException setDetailMessage(String detailMessage)
|
||||||
|
{
|
||||||
|
this.detailMessage = detailMessage;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.chushang.common.core.exception.auth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未能通过的登录认证异常
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class NotLoginException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public NotLoginException(String message)
|
||||||
|
{
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.chushang.common.core.exception.auth;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未能通过的权限认证异常
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class NotPermissionException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public NotPermissionException(String permission)
|
||||||
|
{
|
||||||
|
super(permission);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotPermissionException(String[] permissions)
|
||||||
|
{
|
||||||
|
super(StringUtils.join(permissions, ","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.chushang.common.core.exception.auth;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 未能通过的角色认证异常
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class NotRoleException extends RuntimeException
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public NotRoleException(String role)
|
||||||
|
{
|
||||||
|
super(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotRoleException(String[] roles)
|
||||||
|
{
|
||||||
|
super(StringUtils.join(roles, ","));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.chushang.common.core.exception.enums;
|
||||||
|
|
||||||
|
import com.chushang.common.core.web.EnumUtils;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2022/1/6 18:00
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum ExceptionEnum implements EnumUtils.CodeEnum<Integer, String>{
|
||||||
|
RESOURCE_EXIST(3404,"资源不存在"),
|
||||||
|
APP_RESOURCE_EXIST(3404,"产品资源不存在"),
|
||||||
|
PLATFORM_RESOURCE_EXIST(3404,"平台资源不存在"),
|
||||||
|
CONVERT_ERROR(0,"json 转换错误"),
|
||||||
|
/**
|
||||||
|
* 数据权限不足
|
||||||
|
*/
|
||||||
|
INSUFFICIENT_DATA_PERMISSION(401,"权限不足, 请联系管理员进行数据授权"),
|
||||||
|
INSUFFICIENT_DATA_PERMISSION_NULL(401,"数据权限为空, 请联系管理员进行数据授权"),
|
||||||
|
;
|
||||||
|
|
||||||
|
private final Integer code;
|
||||||
|
|
||||||
|
private final String msg;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by FernFlower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
package com.chushang.common.core.exception.utils;
|
||||||
|
|
||||||
|
import com.chushang.common.core.exception.ResultException;
|
||||||
|
import com.chushang.common.core.web.EnumUtils;
|
||||||
|
|
||||||
|
public class AssertUtil {
|
||||||
|
public AssertUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void invalidate(boolean condition, EnumUtils.CodeEnum<String, String> msg) {
|
||||||
|
if (condition) {
|
||||||
|
throw new ResultException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void invalidate(boolean condition, String msg) {
|
||||||
|
if (condition) {
|
||||||
|
throw new ResultException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
package com.chushang.common.core.handler;
|
||||||
|
|
||||||
|
import cn.hutool.http.HttpStatus;
|
||||||
|
import com.chushang.common.core.exception.CheckedException;
|
||||||
|
import com.chushang.common.core.exception.InnerAuthException;
|
||||||
|
import com.chushang.common.core.exception.ResultException;
|
||||||
|
import com.chushang.common.core.exception.ServiceException;
|
||||||
|
import com.chushang.common.core.exception.auth.NotPermissionException;
|
||||||
|
import com.chushang.common.core.exception.auth.NotRoleException;
|
||||||
|
import com.chushang.common.core.util.StringUtils;
|
||||||
|
import com.chushang.common.core.web.AjaxResult;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.validation.BindException;
|
||||||
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全局异常处理器
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler
|
||||||
|
{
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限码异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(NotPermissionException.class)
|
||||||
|
public AjaxResult handleNotPermissionException(NotPermissionException e, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage());
|
||||||
|
return AjaxResult.error(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 角色权限异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(NotRoleException.class)
|
||||||
|
public AjaxResult handleNotRoleException(NotRoleException e, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage());
|
||||||
|
return AjaxResult.error(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求方式不支持
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||||
|
public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e,
|
||||||
|
HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业务异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(ServiceException.class)
|
||||||
|
public AjaxResult handleServiceException(ServiceException e)
|
||||||
|
{
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
Integer code = e.getCode();
|
||||||
|
return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拦截未知的运行时异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(RuntimeException.class)
|
||||||
|
public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',发生未知异常.", requestURI, e);
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(Exception.class)
|
||||||
|
public AjaxResult handleException(Exception e, HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',发生系统异常.", requestURI, e);
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义验证异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(BindException.class)
|
||||||
|
public AjaxResult handleBindException(BindException e)
|
||||||
|
{
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
String message = e.getAllErrors().get(0).getDefaultMessage();
|
||||||
|
return AjaxResult.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义验证异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
|
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
|
||||||
|
{
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
|
||||||
|
return AjaxResult.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内部认证异常
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(InnerAuthException.class)
|
||||||
|
public AjaxResult handleInnerAuthException(InnerAuthException e)
|
||||||
|
{
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.springframework.web.bind.annotation.ExceptionHandler(ResultException.class)
|
||||||
|
public AjaxResult handlerResultException(ResultException e, HttpServletRequest request){
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',发生系统异常.", requestURI, e);
|
||||||
|
return AjaxResult.error(e.getCode(), e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.springframework.web.bind.annotation.ExceptionHandler(CheckedException.class)
|
||||||
|
public AjaxResult handlerCheckedException(CheckedException e, HttpServletRequest request){
|
||||||
|
String requestURI = request.getRequestURI();
|
||||||
|
log.error("请求地址'{}',发生系统异常.", requestURI, e);
|
||||||
|
return AjaxResult.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.chushang.common.core.jackson;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JavaType;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class JacksonUtils {
|
||||||
|
|
||||||
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static String toJSONString(Object data) {
|
||||||
|
if (null == data) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return mapper.writeValueAsString(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static <T> T json2Bean(String jsonData, Class<T> beanType) {
|
||||||
|
if (null == jsonData) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return mapper.readValue(jsonData, beanType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static <T> List<T> json2List(String jsonData, Class<T> beanType) {
|
||||||
|
if (null == jsonData) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
JavaType javaType = mapper.getTypeFactory().constructParametricType(List.class, beanType);
|
||||||
|
return mapper.readValue(jsonData, javaType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static <K, V> Map<K, V> json2Map(String jsonData, Class<K> keyType, Class<V> valueType) {
|
||||||
|
if (null == jsonData) {
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
JavaType javaType = mapper.getTypeFactory().constructMapType(Map.class, keyType, valueType);
|
||||||
|
return mapper.readValue(jsonData, javaType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.chushang.common.core.jackson;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.PackageVersion;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.InstantSerializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
public class JavaTimeModule extends SimpleModule {
|
||||||
|
|
||||||
|
public JavaTimeModule() {
|
||||||
|
super(PackageVersion.VERSION);
|
||||||
|
|
||||||
|
// ======================= 时间序列化规则 ===============================
|
||||||
|
// yyyy-MM-dd HH:mm:ss
|
||||||
|
this.addSerializer(LocalDateTime.class,
|
||||||
|
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
|
// yyyy-MM-dd
|
||||||
|
this.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||||
|
// HH:mm:ss
|
||||||
|
this.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ISO_LOCAL_TIME));
|
||||||
|
// Instant 类型序列化
|
||||||
|
this.addSerializer(Instant.class, InstantSerializer.INSTANCE);
|
||||||
|
|
||||||
|
// ======================= 时间反序列化规则 ==============================
|
||||||
|
// yyyy-MM-dd HH:mm:ss
|
||||||
|
this.addDeserializer(LocalDateTime.class,
|
||||||
|
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
|
||||||
|
// yyyy-MM-dd
|
||||||
|
this.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||||
|
// HH:mm:ss
|
||||||
|
this.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ISO_LOCAL_TIME));
|
||||||
|
// Instant 反序列化
|
||||||
|
this.addDeserializer(Instant.class, InstantDeserializer.INSTANT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.chushang.common.core.text;
|
||||||
|
|
||||||
|
import com.chushang.common.core.util.StringUtils;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符集工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class CharsetKit
|
||||||
|
{
|
||||||
|
/** ISO-8859-1 */
|
||||||
|
public static final String ISO_8859_1 = "ISO-8859-1";
|
||||||
|
/** UTF-8 */
|
||||||
|
public static final String UTF_8 = "UTF-8";
|
||||||
|
/** GBK */
|
||||||
|
public static final String GBK = "GBK";
|
||||||
|
|
||||||
|
/** ISO-8859-1 */
|
||||||
|
public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
|
||||||
|
/** UTF-8 */
|
||||||
|
public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
|
||||||
|
/** GBK */
|
||||||
|
public static final Charset CHARSET_GBK = Charset.forName(GBK);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为Charset对象
|
||||||
|
*
|
||||||
|
* @param charset 字符集,为空则返回默认字符集
|
||||||
|
* @return Charset
|
||||||
|
*/
|
||||||
|
public static Charset charset(String charset)
|
||||||
|
{
|
||||||
|
return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换字符串的字符集编码
|
||||||
|
*
|
||||||
|
* @param source 字符串
|
||||||
|
* @param srcCharset 源字符集,默认ISO-8859-1
|
||||||
|
* @param destCharset 目标字符集,默认UTF-8
|
||||||
|
* @return 转换后的字符集
|
||||||
|
*/
|
||||||
|
public static String convert(String source, String srcCharset, String destCharset)
|
||||||
|
{
|
||||||
|
return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换字符串的字符集编码
|
||||||
|
*
|
||||||
|
* @param source 字符串
|
||||||
|
* @param srcCharset 源字符集,默认ISO-8859-1
|
||||||
|
* @param destCharset 目标字符集,默认UTF-8
|
||||||
|
* @return 转换后的字符集
|
||||||
|
*/
|
||||||
|
public static String convert(String source, Charset srcCharset, Charset destCharset)
|
||||||
|
{
|
||||||
|
if (null == srcCharset)
|
||||||
|
{
|
||||||
|
srcCharset = StandardCharsets.ISO_8859_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == destCharset)
|
||||||
|
{
|
||||||
|
destCharset = StandardCharsets.UTF_8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
|
||||||
|
{
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
return new String(source.getBytes(srcCharset), destCharset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 系统字符集编码
|
||||||
|
*/
|
||||||
|
public static String systemCharset()
|
||||||
|
{
|
||||||
|
return Charset.defaultCharset().name();
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,87 @@
|
||||||
|
package com.chushang.common.core.text;
|
||||||
|
|
||||||
|
import com.chushang.common.core.util.StringUtils;
|
||||||
|
|
||||||
|
public class StrFormatter
|
||||||
|
{
|
||||||
|
public static final String EMPTY_JSON = "{}";
|
||||||
|
public static final char C_BACKSLASH = '\\';
|
||||||
|
public static final char C_DELIM_START = '{';
|
||||||
|
public static final char C_DELIM_END = '}';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化字符串<br>
|
||||||
|
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||||
|
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||||
|
* 例:<br>
|
||||||
|
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||||
|
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||||
|
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||||
|
*
|
||||||
|
* @param strPattern 字符串模板
|
||||||
|
* @param argArray 参数列表
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String format(final String strPattern, final Object... argArray)
|
||||||
|
{
|
||||||
|
if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray))
|
||||||
|
{
|
||||||
|
return strPattern;
|
||||||
|
}
|
||||||
|
final int strPatternLength = strPattern.length();
|
||||||
|
|
||||||
|
// 初始化定义好的长度以获得更好的性能
|
||||||
|
StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
|
||||||
|
|
||||||
|
int handledPosition = 0;
|
||||||
|
int delimIndex;// 占位符所在位置
|
||||||
|
for (int argIndex = 0; argIndex < argArray.length; argIndex++)
|
||||||
|
{
|
||||||
|
delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
|
||||||
|
if (delimIndex == -1)
|
||||||
|
{
|
||||||
|
if (handledPosition == 0)
|
||||||
|
{
|
||||||
|
return strPattern;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
|
||||||
|
sbuf.append(strPattern, handledPosition, strPatternLength);
|
||||||
|
return sbuf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH)
|
||||||
|
{
|
||||||
|
if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH)
|
||||||
|
{
|
||||||
|
// 转义符之前还有一个转义符,占位符依旧有效
|
||||||
|
sbuf.append(strPattern, handledPosition, delimIndex - 1);
|
||||||
|
sbuf.append(Convert.utf8Str(argArray[argIndex]));
|
||||||
|
handledPosition = delimIndex + 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 占位符被转义
|
||||||
|
argIndex--;
|
||||||
|
sbuf.append(strPattern, handledPosition, delimIndex - 1);
|
||||||
|
sbuf.append(C_DELIM_START);
|
||||||
|
handledPosition = delimIndex + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 正常占位符
|
||||||
|
sbuf.append(strPattern, handledPosition, delimIndex);
|
||||||
|
sbuf.append(Convert.utf8Str(argArray[argIndex]));
|
||||||
|
handledPosition = delimIndex + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 加入最后一个占位符后所有的字符
|
||||||
|
sbuf.append(strPattern, handledPosition, strPattern.length());
|
||||||
|
|
||||||
|
return sbuf.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,319 @@
|
||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by FernFlower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
package com.chushang.common.core.text;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSONArray;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class ToolUtils {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ToolUtils.class);
|
||||||
|
|
||||||
|
public ToolUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isBlank(Object obj) {
|
||||||
|
if (obj != null && !"".equals(obj) && !StringUtils.isBlank(obj.toString())) {
|
||||||
|
if (obj instanceof JSONArray) {
|
||||||
|
return ((JSONArray) obj).isEmpty();
|
||||||
|
} else if (obj instanceof CharSequence) {
|
||||||
|
return ((CharSequence) obj).isEmpty();
|
||||||
|
} else if (obj instanceof Collection) {
|
||||||
|
return ((Collection<?>)obj).isEmpty();
|
||||||
|
} else if (obj instanceof Map) {
|
||||||
|
return ((Map<?, ?>)obj).isEmpty();
|
||||||
|
} else if (!(obj instanceof Object[] object)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (object.length == 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
boolean empty = true;
|
||||||
|
for (Object o : object) {
|
||||||
|
if (!isBlank(o)) {
|
||||||
|
empty = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dencodeURI(String beforeStr) {
|
||||||
|
return beforeStr != null ? URLDecoder.decode(beforeStr, StandardCharsets.UTF_8) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dencodeURI(String beforeStr, String charset) {
|
||||||
|
try {
|
||||||
|
return beforeStr != null ? URLDecoder.decode(beforeStr, charset) : null;
|
||||||
|
} catch (UnsupportedEncodingException var3) {
|
||||||
|
log.error("dencode Exception", var3);
|
||||||
|
var3.printStackTrace();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encodeString(String beforeStr, String charset) {
|
||||||
|
try {
|
||||||
|
return beforeStr != null ? new String(beforeStr.getBytes(StandardCharsets.ISO_8859_1), charset) : null;
|
||||||
|
} catch (UnsupportedEncodingException var3) {
|
||||||
|
log.error(" ISO-8859-1 to Byte convert to " + charset + " exception", var3);
|
||||||
|
var3.printStackTrace();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encodeURI(String beforeStr) {
|
||||||
|
return beforeStr != null ? URLEncoder.encode(beforeStr, StandardCharsets.UTF_8) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encodeURI(String beforeStr, String charset) {
|
||||||
|
try {
|
||||||
|
return beforeStr != null ? URLEncoder.encode(beforeStr, charset) : null;
|
||||||
|
} catch (UnsupportedEncodingException var3) {
|
||||||
|
log.error("URLEncoder encode by " + charset + " Exception", var3);
|
||||||
|
var3.printStackTrace();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCharacter(String str) {
|
||||||
|
boolean temp = false;
|
||||||
|
temp = str.matches("[\\u4e00-\\u9fa5]");
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isDate(String str) {
|
||||||
|
String eL = "^((\\d{2}(([02468][048])|([13579][26]))[\\-/\\s]?((((0?[13578])|(1[02]))[\\-/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-/\\s]?((0?[1-9])|([1-2][0-9])))))|(\\d{2}(([02468][1235679])|([13579][01345789]))[\\-/\\s]?((((0?[13578])|(1[02]))[\\-/\\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\\-/\\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\\-/\\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))";
|
||||||
|
Pattern pattern = Pattern.compile(eL);
|
||||||
|
return pattern.matcher(str).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(List<?> list) {
|
||||||
|
return isNull(list) || list.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(Object[] obj) {
|
||||||
|
return isNull(obj) || obj.length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isEmpty(String str) {
|
||||||
|
return StringUtils.isEmpty(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNull(Object object) {
|
||||||
|
return object == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLetter(String inputString) {
|
||||||
|
return inputString != null && inputString.matches("[a-zA-Z]+");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMail(String smail) {
|
||||||
|
Pattern pattern = Pattern.compile("[\\w.\\-]+@([\\w\\-]+\\.)+[\\w\\-]+", 2);
|
||||||
|
return pattern.matcher(smail).matches();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMobileNumber(String num) {
|
||||||
|
String regex = "^((13[0-9])|(14[5,79])|(15([0-3]|[5-9]))|(166)|(17[0,135678])|(18[0-9])|(19[0-9]))\\d{8}$";
|
||||||
|
if (num.length() != 11) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
Pattern p = Pattern.compile(regex);
|
||||||
|
Matcher m = p.matcher(num);
|
||||||
|
return m.matches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNotEmpty(String str) {
|
||||||
|
return str != null && !str.trim().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNumber(String inputString) {
|
||||||
|
return inputString != null && inputString.matches("[0-9]+");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double div(double v1, double v2, int scale) {
|
||||||
|
if (scale < 0) {
|
||||||
|
throw new IllegalArgumentException("The scale must be a positive integer or zero");
|
||||||
|
} else {
|
||||||
|
BigDecimal b1 = new BigDecimal(Double.toString(v1));
|
||||||
|
BigDecimal b2 = new BigDecimal(Double.toString(v2));
|
||||||
|
return b1.divide(b2, scale, 4).doubleValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isZipCodeNumber(String num) {
|
||||||
|
return Pattern.matches("\\d{6}", num);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String random(int n) {
|
||||||
|
StringBuilder s = new StringBuilder();
|
||||||
|
|
||||||
|
for(int i = 0; i < n; ++i) {
|
||||||
|
int a = (int)(Math.random() * 10.0);
|
||||||
|
s.append(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String randomChar(int n) {
|
||||||
|
StringBuilder str = new StringBuilder();
|
||||||
|
char[] cha = new char[52];
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 26; ++i) {
|
||||||
|
cha[i] = (char)(65 + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(i < 52) {
|
||||||
|
cha[i] = (char)(71 + i);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
|
||||||
|
for(i = 0; i < n; ++i) {
|
||||||
|
int rand = random.nextInt(52);
|
||||||
|
str.append(cha[rand]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String randomString(int n) {
|
||||||
|
String str = "";
|
||||||
|
char[] cha = new char[62];
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for(i = 0; i < 26; ++i) {
|
||||||
|
cha[i] = (char)(65 + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
while(i < 52) {
|
||||||
|
cha[i] = (char)(71 + i);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(i < 62) {
|
||||||
|
cha[i] = (char)(-4 + i);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
|
||||||
|
for(i = 0; i < n; ++i) {
|
||||||
|
int rand = random.nextInt(62);
|
||||||
|
str = str + cha[rand];
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Serializable[] split(String str, String expression) {
|
||||||
|
if (str != null && str != "") {
|
||||||
|
StringTokenizer a = new StringTokenizer(str, expression);
|
||||||
|
int i = 0;
|
||||||
|
Serializable[] strs = new Serializable[a.countTokens()];
|
||||||
|
if (a.countTokens() > 0) {
|
||||||
|
try {
|
||||||
|
while(a.hasMoreTokens()) {
|
||||||
|
strs[i] = a.nextToken();
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
} catch (ArrayIndexOutOfBoundsException var6) {
|
||||||
|
ArrayIndexOutOfBoundsException e = var6;
|
||||||
|
log.error("数组越界", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strs;
|
||||||
|
} else {
|
||||||
|
Serializable[] o = new Serializable[0];
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String arrayToStr(Serializable[] array, String insertString) {
|
||||||
|
String strs = "";
|
||||||
|
if (array != null) {
|
||||||
|
for(int i = 0; i < array.length; ++i) {
|
||||||
|
if (i < array.length - 1) {
|
||||||
|
strs = strs + array[i] + insertString;
|
||||||
|
} else {
|
||||||
|
strs = strs + array[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] strSplit(String str, String expression) {
|
||||||
|
if (str != null && str != "") {
|
||||||
|
StringTokenizer a = new StringTokenizer(str, expression);
|
||||||
|
int i = 0;
|
||||||
|
String[] strs = new String[a.countTokens()];
|
||||||
|
if (a.countTokens() > 0) {
|
||||||
|
try {
|
||||||
|
while(a.hasMoreTokens()) {
|
||||||
|
String token = a.nextToken();
|
||||||
|
if (!isEmpty(token) && !isEmpty(token = token.trim())) {
|
||||||
|
strs[i] = token;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ArrayIndexOutOfBoundsException var6) {
|
||||||
|
ArrayIndexOutOfBoundsException e = var6;
|
||||||
|
log.error("数组越界", e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return strs;
|
||||||
|
} else {
|
||||||
|
String[] o = new String[0];
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String unicodeEncoding(String str) {
|
||||||
|
StringBuffer unicodeBytes = new StringBuffer();
|
||||||
|
|
||||||
|
for(int byteIndex = 0; byteIndex < str.length(); ++byteIndex) {
|
||||||
|
String hexB = Integer.toHexString(str.charAt(byteIndex));
|
||||||
|
unicodeBytes.append("\\u");
|
||||||
|
if (hexB.length() <= 2) {
|
||||||
|
unicodeBytes.append("00");
|
||||||
|
}
|
||||||
|
|
||||||
|
unicodeBytes.append(hexB);
|
||||||
|
}
|
||||||
|
|
||||||
|
return unicodeBytes.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,196 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import com.chushang.common.core.constant.Constants;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.BridgeMethodResolver;
|
||||||
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||||
|
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类工具类
|
||||||
|
*
|
||||||
|
* @author L.cm
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@UtilityClass
|
||||||
|
public class ClassUtils extends org.springframework.util.ClassUtils {
|
||||||
|
|
||||||
|
private final ParameterNameDiscoverer PARAMETERNAMEDISCOVERER = new DefaultParameterNameDiscoverer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取方法参数信息
|
||||||
|
* @param constructor 构造器
|
||||||
|
* @param parameterIndex 参数序号
|
||||||
|
* @return {MethodParameter}
|
||||||
|
*/
|
||||||
|
public MethodParameter getMethodParameter(Constructor<?> constructor, int parameterIndex) {
|
||||||
|
MethodParameter methodParameter = new SynthesizingMethodParameter(constructor, parameterIndex);
|
||||||
|
methodParameter.initParameterNameDiscovery(PARAMETERNAMEDISCOVERER);
|
||||||
|
return methodParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取方法参数信息
|
||||||
|
* @param method 方法
|
||||||
|
* @param parameterIndex 参数序号
|
||||||
|
* @return {MethodParameter}
|
||||||
|
*/
|
||||||
|
public MethodParameter getMethodParameter(Method method, int parameterIndex) {
|
||||||
|
MethodParameter methodParameter = new SynthesizingMethodParameter(method, parameterIndex);
|
||||||
|
methodParameter.initParameterNameDiscovery(PARAMETERNAMEDISCOVERER);
|
||||||
|
return methodParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Annotation
|
||||||
|
* @param method Method
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @param <A> 泛型标记
|
||||||
|
* @return {Annotation}
|
||||||
|
*/
|
||||||
|
public <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
|
||||||
|
Class<?> targetClass = method.getDeclaringClass();
|
||||||
|
// The method may be on an interface, but we need attributes from the target
|
||||||
|
// class.
|
||||||
|
// If the target class is null, the method will be unchanged.
|
||||||
|
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||||
|
// If we are dealing with method with generic parameters, find the original
|
||||||
|
// method.
|
||||||
|
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||||
|
// 先找方法,再找方法上的类
|
||||||
|
A annotation = AnnotatedElementUtils.findMergedAnnotation(specificMethod, annotationType);
|
||||||
|
|
||||||
|
if (null != annotation) {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
|
||||||
|
return AnnotatedElementUtils.findMergedAnnotation(specificMethod.getDeclaringClass(), annotationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Annotation
|
||||||
|
* @param handlerMethod HandlerMethod
|
||||||
|
* @param annotationType 注解类
|
||||||
|
* @param <A> 泛型标记
|
||||||
|
* @return {Annotation}
|
||||||
|
*/
|
||||||
|
public <A extends Annotation> A getAnnotation(HandlerMethod handlerMethod, Class<A> annotationType) {
|
||||||
|
// 先找方法,再找方法上的类
|
||||||
|
A annotation = handlerMethod.getMethodAnnotation(annotationType);
|
||||||
|
if (null != annotation) {
|
||||||
|
return annotation;
|
||||||
|
}
|
||||||
|
// 获取类上面的Annotation,可能包含组合注解,故采用spring的工具类
|
||||||
|
Class<?> beanType = handlerMethod.getBeanType();
|
||||||
|
return AnnotatedElementUtils.findMergedAnnotation(beanType, annotationType);
|
||||||
|
}
|
||||||
|
public boolean objCheckNonNull(Object object)
|
||||||
|
{
|
||||||
|
if (null == object){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 默认忽略 序列化
|
||||||
|
return objCheckNonNull(object, Set.of(Constants.SERIAL_VERSION_STR));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 只要有一个 非null 值即为 true
|
||||||
|
*/
|
||||||
|
public boolean objCheckNonNull(Object object, Set<String> ignoreFieldNames)
|
||||||
|
{
|
||||||
|
Class<?> clazz = object.getClass();
|
||||||
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
|
boolean flag = false;
|
||||||
|
for(Field field : fields){
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object fieldValue = null;
|
||||||
|
try {
|
||||||
|
String fieldName = field.getName();
|
||||||
|
if (CollectionUtil.isNotEmpty(ignoreFieldNames)){
|
||||||
|
// 要求 fieldName 必须一致, 大小写不同, 视为不同
|
||||||
|
if (ignoreFieldNames.contains(fieldName))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fieldValue = field.get(object);
|
||||||
|
Type fieldType =field.getGenericType();
|
||||||
|
if (log.isDebugEnabled()){
|
||||||
|
log.debug("类名: {}, 属性类型: {},属性名: {}, 属性值: {}",clazz.getName(), fieldType, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
if(fieldValue != null){
|
||||||
|
flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean objCheckAllNotNull(Object object){
|
||||||
|
if (null == object) return false;
|
||||||
|
return objCheckAllNotNull(object, Set.of(Constants.SERIAL_VERSION_STR));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public boolean objCheckAllNotNull(Object object, Set<String> ignoreFieldNames){
|
||||||
|
// 默认其为true
|
||||||
|
Set<Boolean> flagSet = new HashSet<>();
|
||||||
|
flagSet.add(true);
|
||||||
|
Class<?> clazz = object.getClass();
|
||||||
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
|
for(Field field : fields){
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object fieldValue = null;
|
||||||
|
try {
|
||||||
|
String fieldName = field.getName();
|
||||||
|
if (CollectionUtil.isNotEmpty(ignoreFieldNames)){
|
||||||
|
// 要求 fieldName 必须一致, 大小写不同, 视为不同
|
||||||
|
if (ignoreFieldNames.contains(fieldName))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fieldValue = field.get(object);
|
||||||
|
Type fieldType =field.getGenericType();
|
||||||
|
if (log.isDebugEnabled()){
|
||||||
|
log.debug("类名: {}, 属性类型: {},属性名: {}, 属性值: {}",clazz.getName(), fieldType, fieldName, fieldValue);
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
flagSet.add(fieldValue != null);
|
||||||
|
}
|
||||||
|
// 为1 时, 代表没有 false --> 也就是没有 null 值, 否则就是存在null 值
|
||||||
|
return flagSet.size() == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2021/11/5 14:53
|
||||||
|
* 公共 请求 query
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CommonParam implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前页码
|
||||||
|
*/
|
||||||
|
private Integer page = 1;
|
||||||
|
/**
|
||||||
|
* 页面大小
|
||||||
|
*/
|
||||||
|
private Integer limit = 10;
|
||||||
|
/**
|
||||||
|
* 排序字段: 排序方式 -- 为 null 时, 没有排序
|
||||||
|
*/
|
||||||
|
private String sortStr;
|
||||||
|
/**
|
||||||
|
* 额外分组选项 --> 比如需要根据国家, 或者 pid 进行分组
|
||||||
|
*/
|
||||||
|
private String groupStr;
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, Object> sqlParam;
|
||||||
|
|
||||||
|
public Map<String, Object> getSqlParam()
|
||||||
|
{
|
||||||
|
if (sqlParam == null)
|
||||||
|
{
|
||||||
|
sqlParam = new HashMap<>();
|
||||||
|
}
|
||||||
|
return sqlParam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author by zhaowenyuan create 2022/7/18 10:14
|
||||||
|
* 并发测试
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class ConcurrencyUtilTest {
|
||||||
|
// 请求总数
|
||||||
|
public static int clientTotal = 10000000;
|
||||||
|
|
||||||
|
// 同时并发执行的线程数
|
||||||
|
public static int threadTotal = 1000;
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Map<String, Integer> map = new ConcurrentHashMap<>();
|
||||||
|
try {
|
||||||
|
insert(map);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
log.info("{}",map);
|
||||||
|
log.info("{}",map.values().stream().max(Integer::compareTo).orElse(0));
|
||||||
|
log.info("{}", map.values().stream().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void insert(Map<String, Integer> map) throws InterruptedException {
|
||||||
|
ExecutorService executorService = Executors.newFixedThreadPool(threadTotal);
|
||||||
|
//信号量,此处用于控制并发的线程数
|
||||||
|
final Semaphore semaphore = new Semaphore(threadTotal);
|
||||||
|
//闭锁,可实现计数器递减
|
||||||
|
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
|
||||||
|
for (int i = 0; i < clientTotal; i++) {
|
||||||
|
executorService.execute(() -> {
|
||||||
|
try {
|
||||||
|
//执行此方法用于获取执行许可,当总计未释放的许可数不超过200时,
|
||||||
|
//允许通行,否则线程阻塞等待,直到获取到许可。
|
||||||
|
semaphore.acquire();
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 放置具体的 待测试并发逻辑
|
||||||
|
// */
|
||||||
|
// String numId = IdUtils.getTimeId();
|
||||||
|
// map.merge(numId, 1, Integer::sum);
|
||||||
|
|
||||||
|
//释放许可
|
||||||
|
semaphore.release();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
countDownLatch.countDown();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//线程阻塞,直到闭锁值为0时,阻塞才释放,继续往下执行
|
||||||
|
countDownLatch.await();
|
||||||
|
executorService.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,327 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.*;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.time.temporal.WeekFields;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2021/10/26 14:52
|
||||||
|
* 时间工具类
|
||||||
|
*/
|
||||||
|
public class DateUtils {
|
||||||
|
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN);
|
||||||
|
public final static String DEFAULT_TIME_ZONE = "GMT";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间戳
|
||||||
|
* @author ant
|
||||||
|
* @return long
|
||||||
|
*/
|
||||||
|
public static long getZoneOfMillis(){
|
||||||
|
// 获取 Utc 的 localDateTime 并且将其转为时间戳
|
||||||
|
return getZoneOfMillis(LocalDateTime.now(ZoneOffset.UTC));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getZoneOfMillis(String dateTime){
|
||||||
|
Instant instant = Instant.parse(dateTime);
|
||||||
|
return instant.toEpochMilli();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getSecondAtStartOfDay(String date){
|
||||||
|
return getMillisAtStartOfDay(date) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getMillisAtStartOfDay(String date){
|
||||||
|
return getMillisAtStartOfDay(parseDate(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getSecondAtStartOfDay(LocalDate date){
|
||||||
|
return getMillisAtStartOfDay(date) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getMillisAtStartOfDay(LocalDate date){
|
||||||
|
return getZoneOfMillis(date.atStartOfDay());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getSecondAtEndOfDay(String date){
|
||||||
|
return getMillisAtEndOfDay(date) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getSecondAtEndOfDay(LocalDate date){
|
||||||
|
return getMillisAtEndOfDay(date) / 1000;
|
||||||
|
}
|
||||||
|
public static long getMillisAtEndOfDay(String date){
|
||||||
|
return getMillisAtEndOfDay(parseDate(date));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getMillisAtEndOfDay(LocalDate date){
|
||||||
|
return getZoneOfMillis(date.atTime(23,59,59));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getZoneOfMillis(LocalDateTime localDateTime){
|
||||||
|
return localDateTime.atZone(ZoneOffset.UTC).toInstant().toEpochMilli();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getZoneOfMillis(LocalDateTime localDateTime, ZoneOffset zoneOffset){
|
||||||
|
return localDateTime.atZone(zoneOffset).toInstant().toEpochMilli();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getMillisAtStartOfDay(LocalDate date, ZoneOffset zoneOffset){
|
||||||
|
return getZoneOfMillis(date.atStartOfDay(), zoneOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getZoneOfSecond() {
|
||||||
|
return getZoneOfMillis() / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getZoneOfSecond(LocalDateTime localDateTime) {
|
||||||
|
return getZoneOfMillis(localDateTime) / 1000;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取 utc+0 的时间字符串
|
||||||
|
*/
|
||||||
|
public static String getUtcTime(){
|
||||||
|
return format(ZonedDateTime.now(ZoneOffset.UTC));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化
|
||||||
|
*/
|
||||||
|
public static String format(ZonedDateTime zonedDateTime){
|
||||||
|
return format(zonedDateTime, dtf);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 格式化
|
||||||
|
*/
|
||||||
|
public static String format(ZonedDateTime zonedDateTime, DateTimeFormatter formatter){
|
||||||
|
return zonedDateTime.format(formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化
|
||||||
|
*/
|
||||||
|
public static String format(LocalDateTime localDateTime, DateTimeFormatter formatter){
|
||||||
|
return localDateTime.format(formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化
|
||||||
|
*/
|
||||||
|
public static String format(LocalDateTime localDateTime){
|
||||||
|
return format(localDateTime, dtf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** 时间格式(yyyy-MM-dd) */
|
||||||
|
public final static String DATE_PATTERN = "yyyy-MM-dd";
|
||||||
|
|
||||||
|
public static LocalDate toLocalDate(long secondTimestamp){
|
||||||
|
return Instant.ofEpochSecond(secondTimestamp).atZone(ZoneId.systemDefault()).toLocalDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDate parseDate(String date)
|
||||||
|
{
|
||||||
|
return parseDate(date,DATE_PATTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(LocalDate date){
|
||||||
|
return format(date,DATE_PATTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(LocalDate date,String pattern){
|
||||||
|
if (ObjectUtil.isNull(date)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
return date.format(dtf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDate parseDate(String date, String pattern)
|
||||||
|
{
|
||||||
|
if (StringUtils.isBlank(date)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return LocalDate.parse(date,DateTimeFormatter.ofPattern(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期格式化 日期格式为:yyyy-MM-dd
|
||||||
|
* @param date 日期
|
||||||
|
* @return 返回yyyy-MM-dd格式日期
|
||||||
|
*/
|
||||||
|
public static String format(Date date) {
|
||||||
|
return format(date, DATE_PATTERN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期格式化 日期格式为:yyyy-MM-dd
|
||||||
|
* @param date 日期
|
||||||
|
* @param pattern 格式,如:DatePattern.NORM_DATETIME_PATTERN
|
||||||
|
* @return 返回yyyy-MM-dd格式日期
|
||||||
|
*/
|
||||||
|
public static String format(Date date, String pattern) {
|
||||||
|
if(date != null){
|
||||||
|
SimpleDateFormat df = new SimpleDateFormat(pattern);
|
||||||
|
return df.format(date);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String format(ZonedDateTime zonedDateTime,String pattern){
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||||
|
return zonedDateTime.format(formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算日期的 区间
|
||||||
|
* @param startDateStr 开始时间
|
||||||
|
* @param endDateStr 结束时间
|
||||||
|
*/
|
||||||
|
public static Set<String> dateIntervalCalculation(String startDateStr, String endDateStr)
|
||||||
|
{
|
||||||
|
Clock clock = Clock.system(ZoneId.of(DateUtils.DEFAULT_TIME_ZONE));
|
||||||
|
// 开始日期
|
||||||
|
ZonedDateTime startDateTime = parseDate(startDateStr).atStartOfDay().atZone(clock.getZone());
|
||||||
|
// 结束日期
|
||||||
|
ZonedDateTime endDateTime = parseDate(endDateStr).atStartOfDay().atZone(clock.getZone());
|
||||||
|
Set<String> timeSet = new HashSet<>();
|
||||||
|
for (ZonedDateTime start = startDateTime; endDateTime.isAfter(start); start = start.plusDays(1L)){
|
||||||
|
timeSet.add(format(start, DateUtils.DATE_PATTERN));
|
||||||
|
}
|
||||||
|
// 最后需要添加一下 结束日期
|
||||||
|
timeSet.add(format(endDateTime, DateUtils.DATE_PATTERN));
|
||||||
|
return timeSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static Set<String> dateIntervalCalculationByMonth(String startDateStr, String endDateStr)
|
||||||
|
{
|
||||||
|
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DatePattern.NORM_MONTH_PATTERN);
|
||||||
|
Date startDate = simpleDateFormat.parse(startDateStr);
|
||||||
|
Date endDate = simpleDateFormat.parse(endDateStr);
|
||||||
|
Calendar min = Calendar.getInstance();
|
||||||
|
min.setTime(startDate);
|
||||||
|
Set<String> timeSet = new HashSet<>();
|
||||||
|
while (!min.getTime().after(endDate)){
|
||||||
|
timeSet.add(simpleDateFormat.format(min.getTime()));
|
||||||
|
min.add(Calendar.MONTH, 1);
|
||||||
|
}
|
||||||
|
return timeSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据传入的日期判断 日期属于年份的第几周
|
||||||
|
* 如果 week 小于10 , 则需要进行补0
|
||||||
|
* date 应该是具体的实际日期 -- 即不进行获取周一,周末
|
||||||
|
* @param date 某个指定日期
|
||||||
|
*/
|
||||||
|
public static String weekOfYear(LocalDate date)
|
||||||
|
{
|
||||||
|
WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY,4);
|
||||||
|
int weekYear = date.getYear();
|
||||||
|
int week = date.get(weekFields.weekOfWeekBasedYear());
|
||||||
|
if (week < 10)
|
||||||
|
return weekYear + "0" + week;
|
||||||
|
return weekYear + "" + week;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String weekOfYearName(LocalDate date)
|
||||||
|
{
|
||||||
|
WeekFields weekFields = WeekFields.of(DayOfWeek.MONDAY,4);
|
||||||
|
int weekYear = date.getYear();
|
||||||
|
int week = date.get(weekFields.weekOfWeekBasedYear());
|
||||||
|
return weekYear + "年第" + week + "周";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String quarterOfYear(String date){
|
||||||
|
String[] startSplit = date.split("-");
|
||||||
|
// 开始季度
|
||||||
|
int quarter = Integer.parseInt(startSplit[1]);
|
||||||
|
// 开始年份
|
||||||
|
int year = Integer.parseInt(startSplit[0]);
|
||||||
|
return year + "年第" + quarter + "季度";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String formatYearMonth(String date){
|
||||||
|
return parseDate(date).format(DateTimeFormatter.ofPattern("yyyy_MM"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatYearMonthDay(String date){
|
||||||
|
return parseDate(date).format(DateTimeFormatter.ofPattern("yyyy_MM_dd"));
|
||||||
|
}
|
||||||
|
public static String formatYearMonthDay(LocalDate date){
|
||||||
|
return date.format(DateTimeFormatter.ofPattern("yyyy_MM_dd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final static String ISO_DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||||
|
public final static String DATE_PATTERN_2 = "yyyyMMdd";
|
||||||
|
public static String isoFormat(LocalDateTime dateTime)
|
||||||
|
{
|
||||||
|
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(ISO_DATE_TIME_PATTERN);
|
||||||
|
return dateTime.format(dtf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Long getSecond(String offsetId) {
|
||||||
|
return LocalDateTime.now().toEpochSecond(ZoneOffset.of(offsetId));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Date getThisWeekThursday(String day){
|
||||||
|
return getWeekDate(day);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本周4
|
||||||
|
*
|
||||||
|
* @param day 日期
|
||||||
|
*/
|
||||||
|
private static Date getWeekDate(String day) {
|
||||||
|
Date date = stringToDate(day, DATE_PATTERN);
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(date);
|
||||||
|
// 获得当前日期是一个星期的第几天
|
||||||
|
int dayWeek = cal.get(Calendar.DAY_OF_WEEK);
|
||||||
|
if (1 == dayWeek) {
|
||||||
|
cal.add(Calendar.DAY_OF_MONTH, -1);
|
||||||
|
}
|
||||||
|
// 设置一个星期的第一天,按中国的习惯一个星期的第一天是星期一
|
||||||
|
cal.setFirstDayOfWeek(Calendar.MONDAY);
|
||||||
|
// 获得当前日期是一个星期的第几天
|
||||||
|
int week = cal.get(Calendar.DAY_OF_WEEK);
|
||||||
|
// 根据日历的规则,给当前日期减去星期几与一个星期第一天的差值
|
||||||
|
cal.add(Calendar.DATE, cal.getFirstDayOfWeek() - (week - 3));
|
||||||
|
return cal.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串转换成日期
|
||||||
|
* @param strDate 日期字符串
|
||||||
|
* @param pattern 日期的格式,如:DatePattern.NORM_DATETIME_PATTERN
|
||||||
|
*/
|
||||||
|
public static Date stringToDate(String strDate, String pattern) {
|
||||||
|
LocalDate localDate = parseDate(strDate, pattern);
|
||||||
|
ZoneId zone = ZoneId.systemDefault();
|
||||||
|
assert localDate != null;
|
||||||
|
Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
|
||||||
|
return Date.from(instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean judgeEquMonth(String startDateStr, String endDateStr) {
|
||||||
|
if (StringUtils.isEmpty(startDateStr) || StringUtils.isEmpty(endDateStr)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 均设置为1号, 进行比较
|
||||||
|
LocalDate startDate = parseDate(startDateStr).withDayOfMonth(1);
|
||||||
|
LocalDate endDate = parseDate(endDateStr).withDayOfMonth(1);
|
||||||
|
return startDate.equals(endDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,256 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
public class FileUtils extends FileUtil {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(FileUtils.class);
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static void createFile(InputStream inputStream, String filename) {
|
||||||
|
File tmp = new File(filename);
|
||||||
|
OutputStream os = Files.newOutputStream(tmp.toPath());
|
||||||
|
try (inputStream) {
|
||||||
|
int bytesRead;
|
||||||
|
byte[] buffer = new byte[8192];
|
||||||
|
while ((bytesRead = inputStream.read(buffer, 0, 8192)) != -1) {
|
||||||
|
os.write(buffer, 0, bytesRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final char[] Digit = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
|
||||||
|
public static void writeBytes(String filePath, OutputStream os) throws IOException {
|
||||||
|
FileInputStream fis = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
File file = new File(filePath);
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new FileNotFoundException(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
fis = new FileInputStream(file);
|
||||||
|
byte[] b = new byte[1024];
|
||||||
|
|
||||||
|
int length;
|
||||||
|
while ((length = fis.read(b)) > 0) {
|
||||||
|
os.write(b, 0, length);
|
||||||
|
}
|
||||||
|
} catch (IOException var9) {
|
||||||
|
IOException e = var9;
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (os != null) {
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fis != null) {
|
||||||
|
fis.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean deleteFile(String filePath) {
|
||||||
|
boolean flag = false;
|
||||||
|
File file = new File(filePath);
|
||||||
|
if (file.isFile() && file.exists()) {
|
||||||
|
file.delete();
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
|
||||||
|
String agent = request.getHeader("USER-AGENT");
|
||||||
|
String filename = fileName;
|
||||||
|
if (agent.contains("MSIE")) {
|
||||||
|
filename = URLEncoder.encode(filename, StandardCharsets.UTF_8);
|
||||||
|
filename = filename.replace("+", " ");
|
||||||
|
} else if (agent.contains("Firefox")) {
|
||||||
|
filename = new String(fileName.getBytes(), "ISO8859-1");
|
||||||
|
} else if (agent.contains("Chrome")) {
|
||||||
|
filename = URLEncoder.encode(filename, StandardCharsets.UTF_8);
|
||||||
|
} else {
|
||||||
|
filename = URLEncoder.encode(filename, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
|
||||||
|
String percentEncodedFileName = percentEncode(realFileName);
|
||||||
|
response.setHeader("Content-disposition", "attachment; filename=" + percentEncodedFileName + ";" + "filename*=" + "utf-8''" + percentEncodedFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String percentEncode(String s) throws UnsupportedEncodingException {
|
||||||
|
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8);
|
||||||
|
return encode.replaceAll("\\+", "%20");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileExtendName(byte[] photoByte) {
|
||||||
|
String strFileExtendName = "jpg";
|
||||||
|
if (photoByte[0] == 71 && photoByte[1] == 73 && photoByte[2] == 70 && photoByte[3] == 56 && (photoByte[4] == 55 || photoByte[4] == 57) && photoByte[5] == 97) {
|
||||||
|
strFileExtendName = "gif";
|
||||||
|
} else if (photoByte[6] == 74 && photoByte[7] == 70 && photoByte[8] == 73 && photoByte[9] == 70) {
|
||||||
|
strFileExtendName = "jpg";
|
||||||
|
} else if (photoByte[0] == 66 && photoByte[1] == 77) {
|
||||||
|
strFileExtendName = "bmp";
|
||||||
|
} else if (photoByte[1] == 80 && photoByte[2] == 78 && photoByte[3] == 71) {
|
||||||
|
strFileExtendName = "png";
|
||||||
|
}
|
||||||
|
|
||||||
|
return strFileExtendName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getName(String fileName) {
|
||||||
|
if (fileName == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
int lastUnixPos = fileName.lastIndexOf(47);
|
||||||
|
int lastWindowsPos = fileName.lastIndexOf(92);
|
||||||
|
int index = Math.max(lastUnixPos, lastWindowsPos);
|
||||||
|
return fileName.substring(index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNameNotSuffix(String fileName) {
|
||||||
|
String baseName = getName(fileName);
|
||||||
|
if (baseName != null && !baseName.trim().isEmpty()) {
|
||||||
|
int index = baseName.lastIndexOf(".");
|
||||||
|
return baseName.substring(0, index);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getExtension(String fileName) {
|
||||||
|
String baseName = getName(fileName);
|
||||||
|
if (baseName != null && !baseName.trim().isEmpty()) {
|
||||||
|
int index = baseName.lastIndexOf(".");
|
||||||
|
return baseName.substring(index + 1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMd5(File f) {
|
||||||
|
try (FileInputStream fis = new FileInputStream(f)){
|
||||||
|
return getMd5(fis);
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMd5(InputStream is) throws IOException, NoSuchAlgorithmException {
|
||||||
|
MessageDigest mdInst = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
while (is.read(buffer) != -1) {
|
||||||
|
mdInst.update(buffer);
|
||||||
|
}
|
||||||
|
return binaryToHexString(mdInst.digest());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String binaryToHexString(byte[] result) {
|
||||||
|
StringBuilder digestHexStr = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
char[] ob = new char[]{Digit[result[i] >>> 4 & 15], Digit[result[i] & 15]};
|
||||||
|
String s = new String(ob);
|
||||||
|
digestHexStr.append(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return digestHexStr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isText(File file) {
|
||||||
|
boolean isText = true;
|
||||||
|
try (FileInputStream fin = new FileInputStream(file)){
|
||||||
|
long len = file.length();
|
||||||
|
for (int j = 0; j < (int) len; ++j) {
|
||||||
|
int t = fin.read();
|
||||||
|
if (t < 32 && t != 9 && t != 10 && t != 13) {
|
||||||
|
isText = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
log.error("isText", e);
|
||||||
|
}
|
||||||
|
return isText;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileCharset(File sourceFile) {
|
||||||
|
String charset = "GBK";
|
||||||
|
byte[] first3Bytes = new byte[3];
|
||||||
|
String var7;
|
||||||
|
try (FileInputStream fis = new FileInputStream(sourceFile);
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(fis)){
|
||||||
|
boolean checked = false;
|
||||||
|
bis.mark(0);
|
||||||
|
int read = bis.read(first3Bytes, 0, 3);
|
||||||
|
if (read != -1) {
|
||||||
|
if (first3Bytes[0] == -1 && first3Bytes[1] == -2) {
|
||||||
|
charset = "UTF-16LE";
|
||||||
|
checked = true;
|
||||||
|
} else if (first3Bytes[0] == -2 && first3Bytes[1] == -1) {
|
||||||
|
charset = "UTF-16BE";
|
||||||
|
checked = true;
|
||||||
|
} else if (first3Bytes[0] == -17 && first3Bytes[1] == -69 && first3Bytes[2] == -65) {
|
||||||
|
charset = "UTF-8";
|
||||||
|
checked = true;
|
||||||
|
}
|
||||||
|
bis.reset();
|
||||||
|
if (!checked) {
|
||||||
|
label248:
|
||||||
|
do {
|
||||||
|
do {
|
||||||
|
if ((read = bis.read()) == -1 || read >= 240 || 128 <= read && read <= 191) {
|
||||||
|
break label248;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (192 <= read && read <= 223) {
|
||||||
|
read = bis.read();
|
||||||
|
continue label248;
|
||||||
|
}
|
||||||
|
} while(224 > read || read > 239);
|
||||||
|
|
||||||
|
read = bis.read();
|
||||||
|
if (128 <= read && read <= 191) {
|
||||||
|
read = bis.read();
|
||||||
|
if (128 <= read && read <= 191) {
|
||||||
|
charset = "UTF-8";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} while(128 <= read && read <= 191);
|
||||||
|
}
|
||||||
|
|
||||||
|
bis.close();
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
var7 = charset;
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return charset;
|
||||||
|
}
|
||||||
|
return var7;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
public final class HexByte
|
||||||
|
{
|
||||||
|
private HexByte()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将二进制转换成16进制
|
||||||
|
* @param buf
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String parseByte2HexStr(byte[] buf)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : buf) {
|
||||||
|
String hex = Integer.toHexString(b & 0xFF);
|
||||||
|
if (hex.length() == 1) {
|
||||||
|
hex = '0' + hex;
|
||||||
|
}
|
||||||
|
sb.append(hex.toUpperCase());
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将16进制转换为二进制
|
||||||
|
* @param hexStr
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static byte[] parseHexStr2Byte(String hexStr)
|
||||||
|
{
|
||||||
|
if (hexStr.length() < 1)
|
||||||
|
return null;
|
||||||
|
byte[] result = new byte[hexStr.length() / 2];
|
||||||
|
for (int i = 0; i < hexStr.length() / 2; i++)
|
||||||
|
{
|
||||||
|
int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
|
||||||
|
int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
|
||||||
|
result[i] = (byte) (high * 16 + low);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,321 @@
|
||||||
|
package com.chushang.common.core.util;//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by FernFlower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpRequest;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
|
||||||
|
public class IPUtils {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(IPUtils.class);
|
||||||
|
|
||||||
|
public IPUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String clientIp(HttpServletRequest request) {
|
||||||
|
String realIps = clientIps(request);
|
||||||
|
return realIps.contains(",") ? realIps.split(",")[0] : realIps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String clientIps(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String ip = request.getHeader("X-Forwarded-For");
|
||||||
|
log.debug("X-Forwarded-For ==>" + ip);
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = request.getHeader("X-Real-IP");
|
||||||
|
log.debug("X-Real-IP ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = request.getHeader("Proxy-Client-IP");
|
||||||
|
log.debug("Proxy-Client-IP ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||||
|
log.debug("WL-Proxy-Client-IP ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_CLIENT_IP");
|
||||||
|
log.debug("HTTP_CLIENT_IP ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
|
||||||
|
log.debug("HTTP_X_FORWARDED_FOR ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = request.getRemoteAddr();
|
||||||
|
log.debug("request.getRemoteAddr() ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("RealRemoteIP ==>" + ip);
|
||||||
|
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String clientIp(ServerHttpRequest request)
|
||||||
|
{
|
||||||
|
String realIps = clientIps(request);
|
||||||
|
return realIps.contains(",") ? realIps.split(",")[0] : realIps;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String clientIps(ServerHttpRequest request)
|
||||||
|
{
|
||||||
|
HttpHeaders headers = request.getHeaders();
|
||||||
|
String ip = headers.getFirst("X-Forwarded-For");
|
||||||
|
// 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
|
||||||
|
log.debug("X-Forwarded-For ==>" + ip);
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = headers.getFirst("X-Real-IP");
|
||||||
|
log.debug("X-Real-IP ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = headers.getFirst("Proxy-Client-IP");
|
||||||
|
log.debug("Proxy-Client-IP ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = headers.getFirst("WL-Proxy-Client-IP");
|
||||||
|
log.debug("WL-Proxy-Client-IP ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = headers.getFirst("HTTP_CLIENT_IP");
|
||||||
|
log.debug("HTTP_CLIENT_IP ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = headers.getFirst("HTTP_X_FORWARDED_FOR");
|
||||||
|
log.debug("HTTP_X_FORWARDED_FOR ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkIp(ip)) {
|
||||||
|
ip = Objects.requireNonNull(request.getRemoteAddress()).getAddress().getHostAddress();
|
||||||
|
log.debug("request.getRemoteAddr() ==>" + ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("RealRemoteIP ==>" + ip);
|
||||||
|
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkIp(String ip) {
|
||||||
|
return null == ip || ip.trim().isEmpty() || "unknown".equalsIgnoreCase(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isInvalid(String ip) {
|
||||||
|
if (null != ip && !ip.trim().isEmpty() && !"unknown".equalsIgnoreCase(ip) && ip.length() >= 7 && ip.length() <= 15) {
|
||||||
|
String rexp = "([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}";
|
||||||
|
Pattern pat = Pattern.compile(rexp);
|
||||||
|
Matcher mat = pat.matcher(ip);
|
||||||
|
return mat.find();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLocalHost() {
|
||||||
|
try {
|
||||||
|
return getLocalAddress().getHostAddress();
|
||||||
|
} catch (Exception var1) {
|
||||||
|
return "127.0.0.1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean internalIp(String ip) {
|
||||||
|
byte[] addr = textToNumericFormatV4(ip);
|
||||||
|
return internalIp(addr) || "127.0.0.1".equals(ip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean internalIp(byte[] addr) {
|
||||||
|
byte b0 = addr[0];
|
||||||
|
byte b1 = addr[1];
|
||||||
|
boolean SECTION_1 = true;
|
||||||
|
boolean SECTION_2 = true;
|
||||||
|
boolean SECTION_3 = true;
|
||||||
|
boolean SECTION_4 = true;
|
||||||
|
boolean SECTION_5 = true;
|
||||||
|
boolean SECTION_6 = true;
|
||||||
|
switch (b0) {
|
||||||
|
case -84:
|
||||||
|
if (b1 >= 16 && b1 <= 31) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case -64:
|
||||||
|
switch (b1) {
|
||||||
|
case -88:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
case 10:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] textToNumericFormatV4(String text) {
|
||||||
|
if (text.length() == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
byte[] bytes = new byte[4];
|
||||||
|
String[] elements = text.split("\\.", -1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
long l;
|
||||||
|
int i;
|
||||||
|
switch (elements.length) {
|
||||||
|
case 1:
|
||||||
|
l = Long.parseLong(elements[0]);
|
||||||
|
if (l < 0L || l > 4294967295L) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes[0] = (byte)((int)(l >> 24 & 255L));
|
||||||
|
bytes[1] = (byte)((int)((l & 16777215L) >> 16 & 255L));
|
||||||
|
bytes[2] = (byte)((int)((l & 65535L) >> 8 & 255L));
|
||||||
|
bytes[3] = (byte)((int)(l & 255L));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
l = (long)Integer.parseInt(elements[0]);
|
||||||
|
if (l >= 0L && l <= 255L) {
|
||||||
|
bytes[0] = (byte)((int)(l & 255L));
|
||||||
|
l = (long)Integer.parseInt(elements[1]);
|
||||||
|
if (l < 0L || l > 16777215L) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes[1] = (byte)((int)(l >> 16 & 255L));
|
||||||
|
bytes[2] = (byte)((int)((l & 65535L) >> 8 & 255L));
|
||||||
|
bytes[3] = (byte)((int)(l & 255L));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
case 3:
|
||||||
|
for(i = 0; i < 2; ++i) {
|
||||||
|
l = (long)Integer.parseInt(elements[i]);
|
||||||
|
if (l < 0L || l > 255L) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes[i] = (byte)((int)(l & 255L));
|
||||||
|
}
|
||||||
|
|
||||||
|
l = (long)Integer.parseInt(elements[2]);
|
||||||
|
if (l < 0L || l > 65535L) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes[2] = (byte)((int)(l >> 8 & 255L));
|
||||||
|
bytes[3] = (byte)((int)(l & 255L));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
for(i = 0; i < 4; ++i) {
|
||||||
|
l = (long)Integer.parseInt(elements[i]);
|
||||||
|
if (l < 0L || l > 255L) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes[i] = (byte)((int)(l & 255L));
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
} catch (NumberFormatException var6) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getLocalHostName() {
|
||||||
|
try {
|
||||||
|
return getLocalAddress().getHostName();
|
||||||
|
} catch (Exception var1) {
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static InetAddress getLocalAddress() {
|
||||||
|
InetAddress candidateAddress = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
|
||||||
|
label60:
|
||||||
|
while(true) {
|
||||||
|
while(true) {
|
||||||
|
if (!ifaces.hasMoreElements()) {
|
||||||
|
break label60;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkInterface iface = (NetworkInterface)ifaces.nextElement();
|
||||||
|
if (iface.isLoopback()) {
|
||||||
|
log.trace("NetworkInterface is Loopback:{} discard continue", JSON.toJSONString(iface));
|
||||||
|
} else if (iface.isVirtual()) {
|
||||||
|
log.trace("NetworkInterface is Virtual:{} discard continue", JSON.toJSONString(iface));
|
||||||
|
} else if (!iface.isUp()) {
|
||||||
|
log.trace("NetworkInterface is shutdown:{} discard continue", JSON.toJSONString(iface));
|
||||||
|
} else {
|
||||||
|
log.trace("NetworkInterface is normal:{} continue", JSON.toJSONString(iface));
|
||||||
|
Enumeration<InetAddress> inetAddrs = iface.getInetAddresses();
|
||||||
|
|
||||||
|
while(inetAddrs.hasMoreElements()) {
|
||||||
|
InetAddress inetAddr = (InetAddress)inetAddrs.nextElement();
|
||||||
|
if (inetAddr.isLoopbackAddress()) {
|
||||||
|
log.trace("InetAddress is Loopback:{} discard continue", JSON.toJSONString(inetAddr));
|
||||||
|
} else {
|
||||||
|
if (inetAddr.isSiteLocalAddress()) {
|
||||||
|
candidateAddress = inetAddr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.trace("Don't want to InetAddress:{}", JSON.toJSONString(inetAddr));
|
||||||
|
if (candidateAddress == null) {
|
||||||
|
candidateAddress = inetAddr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidateAddress != null) {
|
||||||
|
break label60;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidateAddress == null) {
|
||||||
|
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
|
||||||
|
if (jdkSuppliedAddress != null) {
|
||||||
|
candidateAddress = jdkSuppliedAddress;
|
||||||
|
log.warn("not found non-loopback InetAddress,use default scheme(InetAddress.getLocalHost()):{}", jdkSuppliedAddress);
|
||||||
|
} else {
|
||||||
|
log.warn("use default scheme(InetAddress.getLocalHost()) is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception var5) {
|
||||||
|
Exception e = var5;
|
||||||
|
log.error("Exception:{}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("IP:{},HostName:{}", candidateAddress.getHostAddress(), candidateAddress.getHostName());
|
||||||
|
return candidateAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.id.NanoId;
|
||||||
|
import cn.hutool.crypto.digest.MD5;
|
||||||
|
import com.chushang.common.core.constant.CharConstants;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2021/12/29 18:00
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@UtilityClass
|
||||||
|
public class IdUtils {
|
||||||
|
|
||||||
|
private final static String sign = "sanyi_cloud_sign";
|
||||||
|
|
||||||
|
private final static MD5 md5 = new MD5();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 19位 数值类型 id
|
||||||
|
* 字符串转数值类型 id
|
||||||
|
* @param str 设备 id
|
||||||
|
* @param sign 签名
|
||||||
|
* @return 根据设备码生成的 数值
|
||||||
|
*/
|
||||||
|
public synchronized static String strConvertNum(String str,String sign) {
|
||||||
|
BigInteger bigInteger = new BigInteger(1, md5.digest(str + ":" + sign));
|
||||||
|
return Math.abs(bigInteger.longValue()) + "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static String strConvertNum(String str) {
|
||||||
|
return strConvertNum(str, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized String getId(){
|
||||||
|
return getId(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized String getId(int count){
|
||||||
|
return getId(count, CharConstants.chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized String getId(int count, char[] chars){
|
||||||
|
return NanoId.randomNanoId(null, chars, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import com.chushang.common.core.constant.SecurityConstants;
|
||||||
|
import com.chushang.common.core.constant.TokenConstants;
|
||||||
|
import com.chushang.common.core.text.Convert;
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jwt工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class JwtUtils
|
||||||
|
{
|
||||||
|
public static String secret = TokenConstants.SECRET;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从数据声明生成令牌
|
||||||
|
*
|
||||||
|
* @param claims 数据声明
|
||||||
|
* @return 令牌
|
||||||
|
*/
|
||||||
|
public static String createToken(Map<String, Object> claims)
|
||||||
|
{
|
||||||
|
return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从令牌中获取数据声明
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 数据声明
|
||||||
|
*/
|
||||||
|
public static Claims parseToken(String token)
|
||||||
|
{
|
||||||
|
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据令牌获取用户标识
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 用户ID
|
||||||
|
*/
|
||||||
|
public static String getUserKey(String token)
|
||||||
|
{
|
||||||
|
Claims claims = parseToken(token);
|
||||||
|
return getValue(claims, SecurityConstants.USER_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据令牌获取用户标识
|
||||||
|
*
|
||||||
|
* @param claims 身份信息
|
||||||
|
* @return 用户ID
|
||||||
|
*/
|
||||||
|
public static String getUserKey(Claims claims)
|
||||||
|
{
|
||||||
|
return getValue(claims, SecurityConstants.USER_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据令牌获取用户ID
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 用户ID
|
||||||
|
*/
|
||||||
|
public static String getUserId(String token)
|
||||||
|
{
|
||||||
|
Claims claims = parseToken(token);
|
||||||
|
return getValue(claims, SecurityConstants.DETAILS_USER_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据身份信息获取用户ID
|
||||||
|
*
|
||||||
|
* @param claims 身份信息
|
||||||
|
* @return 用户ID
|
||||||
|
*/
|
||||||
|
public static String getUserId(Claims claims)
|
||||||
|
{
|
||||||
|
return getValue(claims, SecurityConstants.DETAILS_USER_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据令牌获取用户名
|
||||||
|
*
|
||||||
|
* @param token 令牌
|
||||||
|
* @return 用户名
|
||||||
|
*/
|
||||||
|
public static String getUserName(String token)
|
||||||
|
{
|
||||||
|
Claims claims = parseToken(token);
|
||||||
|
return getValue(claims, SecurityConstants.DETAILS_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据身份信息获取用户名
|
||||||
|
*
|
||||||
|
* @param claims 身份信息
|
||||||
|
* @return 用户名
|
||||||
|
*/
|
||||||
|
public static String getUserName(Claims claims)
|
||||||
|
{
|
||||||
|
return getValue(claims, SecurityConstants.DETAILS_USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据身份信息获取键值
|
||||||
|
*
|
||||||
|
* @param claims 身份信息
|
||||||
|
* @param key 键
|
||||||
|
* @return 值
|
||||||
|
*/
|
||||||
|
public static String getValue(Claims claims, String key)
|
||||||
|
{
|
||||||
|
return Convert.toStr(claims.get(key), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,127 @@
|
||||||
|
//
|
||||||
|
// Source code recreated from a .class file by IntelliJ IDEA
|
||||||
|
// (powered by FernFlower decompiler)
|
||||||
|
//
|
||||||
|
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.digest.MD5;
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
|
import com.alibaba.fastjson2.JSONReader;
|
||||||
|
import com.alibaba.fastjson2.JSONWriter;
|
||||||
|
import com.chushang.common.core.text.ToolUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class MD5Util extends MD5 {
|
||||||
|
|
||||||
|
public static String md5Sign(String jsonStr, String keyStr) throws Exception {
|
||||||
|
Map<String, Object> mapTemp = new HashMap<>();
|
||||||
|
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(JSON.parseObject(jsonStr), JSONWriter.Feature.MapSortField));
|
||||||
|
Set<Map.Entry<String, Object>> objs = jsonObject.entrySet();
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> stringObjectEntry : objs) {
|
||||||
|
if (!(stringObjectEntry.getKey().equals("sign") && !ToolUtils.isBlank(stringObjectEntry.getValue()))) {
|
||||||
|
mapTemp.put((stringObjectEntry).getKey(), stringObjectEntry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String strValue = genString(mapTemp, keyStr);
|
||||||
|
log.debug("signStr:" + strValue);
|
||||||
|
return calcValidateSign(strValue);
|
||||||
|
}
|
||||||
|
private static String genString(Map<String, Object> mapTmp, String keyStr) {
|
||||||
|
ArrayList<String> arrayTmp = new ArrayList<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> stringObjectEntry : mapTmp.entrySet()) {
|
||||||
|
String strValue = stringObjectEntry.getValue().toString();
|
||||||
|
if (!ToolUtils.isBlank(strValue)) {
|
||||||
|
arrayTmp.add((stringObjectEntry).getKey() + "=" + stringObjectEntry.getValue() + "&");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] strArray = arrayTmp.toArray(new String[0]);
|
||||||
|
Arrays.sort(strArray);
|
||||||
|
StringBuilder strBuf = new StringBuilder();
|
||||||
|
|
||||||
|
for (String s : strArray) {
|
||||||
|
strBuf.append(s);
|
||||||
|
}
|
||||||
|
strBuf.append("key=").append(keyStr);
|
||||||
|
return strBuf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String calcValidateSign(String strValue) throws UnsupportedEncodingException {
|
||||||
|
byte[] byteString = strValue.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
byte[] result;
|
||||||
|
try {
|
||||||
|
MessageDigest mdInst = MessageDigest.getInstance("MD5");
|
||||||
|
mdInst.update(byteString);
|
||||||
|
result = mdInst.digest();
|
||||||
|
} catch (NoSuchAlgorithmException var4) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return binaryToHexString(result).toUpperCase();
|
||||||
|
}
|
||||||
|
private static String binaryToHexString(byte[] result) {
|
||||||
|
StringBuilder digestHexStr = new StringBuilder();
|
||||||
|
|
||||||
|
for(int i = 0; i < 16; ++i) {
|
||||||
|
char[] Digit = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
|
||||||
|
char[] ob = new char[]{Digit[result[i] >>> 4 & 15], Digit[result[i] & 15]};
|
||||||
|
String s = new String(ob);
|
||||||
|
digestHexStr.append(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return digestHexStr.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMD5Encrypt(String strSrc) {
|
||||||
|
return encodeEncrypt(strSrc, "MD5");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encodeEncrypt(String strSrc, String encName) {
|
||||||
|
MessageDigest md;
|
||||||
|
String strDes;
|
||||||
|
byte[] bt = strSrc.getBytes();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (encName == null || encName.isEmpty()) {
|
||||||
|
encName = "MD5";
|
||||||
|
}
|
||||||
|
|
||||||
|
md = MessageDigest.getInstance(encName);
|
||||||
|
md.update(bt);
|
||||||
|
strDes = bytes2Hex(md.digest());
|
||||||
|
return strDes;
|
||||||
|
} catch (NoSuchAlgorithmException var6) {
|
||||||
|
log.error("Invalid algorithm.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static String bytes2Hex(byte[] bts) {
|
||||||
|
StringBuilder des = new StringBuilder();
|
||||||
|
String tmp;
|
||||||
|
for (byte bt : bts) {
|
||||||
|
tmp = Integer.toHexString(bt & 255);
|
||||||
|
if (tmp.length() == 1) {
|
||||||
|
des.append("0");
|
||||||
|
}
|
||||||
|
des.append(tmp);
|
||||||
|
}
|
||||||
|
return des.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,461 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
import java.util.jar.JarInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称空间实用工具
|
||||||
|
*/
|
||||||
|
public final class PackageUtil {
|
||||||
|
/**
|
||||||
|
* 类默认构造器
|
||||||
|
*/
|
||||||
|
private PackageUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列出指定接口的所有实现类
|
||||||
|
* 会将接口路径下的所有类都加载进去, 不值当
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static List<Class<?>> getAllInterfaceAchieveClass(Class<?> clazz) throws Throwable {
|
||||||
|
ArrayList<Class<?>> list = new ArrayList<>();
|
||||||
|
// 判断是否是接口
|
||||||
|
if (clazz.isInterface()) {
|
||||||
|
ArrayList<Class<?>> allClass = getAllClassByPath(clazz.getPackage().getName());
|
||||||
|
for (Class<?> aClass : allClass) {
|
||||||
|
// 排除抽象类
|
||||||
|
if (Modifier.isAbstract(aClass.getModifiers())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 判断是不是同一个接口
|
||||||
|
if (clazz.isAssignableFrom(aClass)) {
|
||||||
|
if (!clazz.equals(aClass)) {
|
||||||
|
list.add(aClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Class<?>> getAllInterfaceAchieveClassByPath(Class<?> clazz, String packageName) throws Throwable {
|
||||||
|
if (null == clazz){
|
||||||
|
throw new RuntimeException("clazz is null");
|
||||||
|
}
|
||||||
|
List<Class<?>> list = new ArrayList<>();
|
||||||
|
ArrayList<Class<?>> allClassByPath = getAllClassByPath(packageName);
|
||||||
|
if (clazz.isInterface()){
|
||||||
|
for (Class<?> aClass : allClassByPath) {
|
||||||
|
if (Modifier.isAbstract(aClass.getModifiers())){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (clazz.isAssignableFrom(aClass)){
|
||||||
|
if (!clazz.equals(aClass)){
|
||||||
|
list.add(aClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ArrayList<Class<?>> getAllClassByPath(String packageName) throws Throwable {
|
||||||
|
ArrayList<Class<?>> list = new ArrayList<>();
|
||||||
|
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
||||||
|
String path = packageName.replace('.', '/');
|
||||||
|
ArrayList<File> fileList = new ArrayList<>();
|
||||||
|
Enumeration<URL> enumeration = classLoader.getResources(path);
|
||||||
|
while (enumeration.hasMoreElements()) {
|
||||||
|
URL url = enumeration.nextElement();
|
||||||
|
fileList.add(new File(url.getFile()));
|
||||||
|
}
|
||||||
|
for (File file : fileList) {
|
||||||
|
list.addAll(findClass(file, packageName));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayList<Class<?>> findClass(File file, String packageName) throws ClassNotFoundException {
|
||||||
|
ArrayList<Class<?>> list = new ArrayList<>();
|
||||||
|
if (!file.exists()) {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
File[] files = file.listFiles();
|
||||||
|
assert files != null;
|
||||||
|
for (File file2 : files) {
|
||||||
|
if (file2.isDirectory()) {
|
||||||
|
assert !file2.getName().contains(".");
|
||||||
|
ArrayList<Class<?>> arrayList = findClass(file2, packageName + "." + file2.getName());
|
||||||
|
list.addAll(arrayList);
|
||||||
|
} else if (file2.getName().endsWith(".class")) {
|
||||||
|
// 保存的类文件不需要后缀.class
|
||||||
|
list.add(Class.forName(packageName + '.' + file2.getName().substring(0, file2.getName().length() - 6)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表指定包中的所有子类
|
||||||
|
*
|
||||||
|
* @param packageName 包名称
|
||||||
|
* @param recursive 是否递归查找
|
||||||
|
* @param superClazz 父类的类型
|
||||||
|
* @return 子类集合
|
||||||
|
*/
|
||||||
|
public static Set<Class<?>> listSubClazz(
|
||||||
|
String packageName,
|
||||||
|
boolean recursive,
|
||||||
|
Class<?> superClazz) {
|
||||||
|
if (superClazz == null) {
|
||||||
|
return Collections.emptySet();
|
||||||
|
} else {
|
||||||
|
return listClazz(packageName, recursive, superClazz::isAssignableFrom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表指定包中的所有类
|
||||||
|
*
|
||||||
|
* @param packageName 包名称
|
||||||
|
* @param recursive 是否递归查找?
|
||||||
|
* @param filter 过滤器
|
||||||
|
* @return 符合条件的类集合
|
||||||
|
*/
|
||||||
|
public static Set<Class<?>> listClazz(
|
||||||
|
String packageName, boolean recursive, IClazzFilter filter) {
|
||||||
|
|
||||||
|
if (packageName == null ||
|
||||||
|
packageName.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将点转换成斜杠
|
||||||
|
final String packagePath = packageName.replace('.', '/');
|
||||||
|
// 获取类加载器
|
||||||
|
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||||
|
|
||||||
|
// 结果集合
|
||||||
|
Set<Class<?>> resultSet = new HashSet<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取 URL 枚举
|
||||||
|
Enumeration<URL> urlEnum = cl.getResources(packagePath);
|
||||||
|
|
||||||
|
while (urlEnum.hasMoreElements()) {
|
||||||
|
// 获取当前 URL
|
||||||
|
URL currUrl = urlEnum.nextElement();
|
||||||
|
// 获取协议文本
|
||||||
|
final String protocol = currUrl.getProtocol();
|
||||||
|
// 定义临时集合
|
||||||
|
Set<Class<?>> tmpSet = null;
|
||||||
|
|
||||||
|
if ("FILE".equalsIgnoreCase(protocol)) {
|
||||||
|
// 从文件系统中加载类
|
||||||
|
tmpSet = listClazzFromDir(
|
||||||
|
new File(currUrl.getFile()), packageName, recursive, filter
|
||||||
|
);
|
||||||
|
} else if ("JAR".equalsIgnoreCase(protocol)) {
|
||||||
|
// 获取文件字符串
|
||||||
|
String fileStr = currUrl.getFile();
|
||||||
|
|
||||||
|
if (fileStr.startsWith("file:")) {
|
||||||
|
// 如果是以 "file:" 开头的,
|
||||||
|
// 则去除这个开头
|
||||||
|
fileStr = fileStr.substring(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileStr.lastIndexOf('!') > 0) {
|
||||||
|
// 如果有 '!' 字符,
|
||||||
|
// 则截断 '!' 字符之后的所有字符
|
||||||
|
fileStr = fileStr.substring(0, fileStr.lastIndexOf('!'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 JAR 文件中加载类
|
||||||
|
tmpSet = listClazzFromJar(
|
||||||
|
new File(fileStr), packageName, recursive, filter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmpSet != null) {
|
||||||
|
// 如果类集合不为空,
|
||||||
|
// 则添加到结果中
|
||||||
|
resultSet.addAll(tmpSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// 抛出异常!
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从目录中获取类列表
|
||||||
|
*
|
||||||
|
* @param dirFile 目录
|
||||||
|
* @param packageName 包名称
|
||||||
|
* @param recursive 是否递归查询子包
|
||||||
|
* @param filter 类过滤器
|
||||||
|
* @return 符合条件的类集合
|
||||||
|
*/
|
||||||
|
private static Set<Class<?>> listClazzFromDir(
|
||||||
|
final File dirFile, final String packageName, final boolean recursive, IClazzFilter filter) {
|
||||||
|
|
||||||
|
if (!dirFile.exists() ||
|
||||||
|
!dirFile.isDirectory()) {
|
||||||
|
// 如果参数对象为空,
|
||||||
|
// 则直接退出!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取子文件列表
|
||||||
|
File[] subFileArr = dirFile.listFiles();
|
||||||
|
|
||||||
|
if (subFileArr == null ||
|
||||||
|
subFileArr.length <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件队列, 将子文件列表添加到队列
|
||||||
|
Queue<File> fileQ = new LinkedList<>(Arrays.asList(subFileArr));
|
||||||
|
|
||||||
|
// 结果对象
|
||||||
|
Set<Class<?>> resultSet = new HashSet<>();
|
||||||
|
|
||||||
|
while (!fileQ.isEmpty()) {
|
||||||
|
// 从队列中获取文件
|
||||||
|
File currFile = fileQ.poll();
|
||||||
|
|
||||||
|
if (currFile.isDirectory() &&
|
||||||
|
recursive) {
|
||||||
|
// 如果当前文件是目录,
|
||||||
|
// 并且是执行递归操作时,
|
||||||
|
// 获取子文件列表
|
||||||
|
subFileArr = currFile.listFiles();
|
||||||
|
|
||||||
|
if (subFileArr != null &&
|
||||||
|
subFileArr.length > 0) {
|
||||||
|
// 添加文件到队列
|
||||||
|
fileQ.addAll(Arrays.asList(subFileArr));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currFile.isFile() ||
|
||||||
|
!currFile.getName().endsWith(".class")) {
|
||||||
|
// 如果当前文件不是文件,
|
||||||
|
// 或者文件名不是以 .class 结尾,
|
||||||
|
// 则直接跳过
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类名称
|
||||||
|
String clazzName;
|
||||||
|
|
||||||
|
// 设置类名称
|
||||||
|
clazzName = currFile.getAbsolutePath();
|
||||||
|
// 清除最后的 .class 结尾
|
||||||
|
clazzName = clazzName.substring(dirFile.getAbsolutePath().length(), clazzName.lastIndexOf('.'));
|
||||||
|
// 转换目录斜杠
|
||||||
|
clazzName = clazzName.replace('\\', '/');
|
||||||
|
// 清除开头的 /
|
||||||
|
clazzName = trimLeft(clazzName, "/");
|
||||||
|
// 将所有的 / 修改为 .
|
||||||
|
clazzName = join(clazzName.split("/"), ".");
|
||||||
|
// 包名 + 类名
|
||||||
|
clazzName = packageName + "." + clazzName;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 加载类定义
|
||||||
|
Class<?> clazzObj = Class.forName(clazzName);
|
||||||
|
|
||||||
|
if (null != filter &&
|
||||||
|
!filter.accept(clazzObj)) {
|
||||||
|
// 如果过滤器不为空,
|
||||||
|
// 且过滤器不接受当前类,
|
||||||
|
// 则直接跳过!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加类定义到集合
|
||||||
|
resultSet.add(clazzObj);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// 抛出异常
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 .jar 文件中获取类列表
|
||||||
|
*
|
||||||
|
* @param jarFilePath .jar 文件路径
|
||||||
|
* @param recursive 是否递归查询子包
|
||||||
|
* @param filter 类过滤器
|
||||||
|
* @return 符合条件的类集合
|
||||||
|
*/
|
||||||
|
private static Set<Class<?>> listClazzFromJar(
|
||||||
|
final File jarFilePath, final String packageName, final boolean recursive, IClazzFilter filter) {
|
||||||
|
|
||||||
|
if (jarFilePath == null ||
|
||||||
|
jarFilePath.isDirectory()) {
|
||||||
|
// 如果参数对象为空,
|
||||||
|
// 则直接退出!
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 结果对象
|
||||||
|
Set<Class<?>> resultSet = new HashSet<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建 .jar 文件读入流
|
||||||
|
JarInputStream jarIn = new JarInputStream(new FileInputStream(jarFilePath));
|
||||||
|
// 进入点
|
||||||
|
JarEntry entry;
|
||||||
|
|
||||||
|
while ((entry = jarIn.getNextJarEntry()) != null) {
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取进入点名称
|
||||||
|
String entryName = entry.getName();
|
||||||
|
|
||||||
|
if (!entryName.endsWith(".class")) {
|
||||||
|
// 如果不是以 .class 结尾,
|
||||||
|
// 则说明不是 JAVA 类文件, 直接跳过!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recursive) {
|
||||||
|
//
|
||||||
|
// 如果没有开启递归模式,
|
||||||
|
// 那么就需要判断当前 .class 文件是否在指定目录下?
|
||||||
|
//
|
||||||
|
// 获取目录名称
|
||||||
|
String tmpStr = entryName.substring(0, entryName.lastIndexOf('/'));
|
||||||
|
// 将目录中的 "/" 全部替换成 "."
|
||||||
|
tmpStr = join(tmpStr.split("/"), ".");
|
||||||
|
|
||||||
|
if (!packageName.equals(tmpStr)) {
|
||||||
|
// 如果包名和目录名不相等,
|
||||||
|
// 则直接跳过!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String clazzName;
|
||||||
|
|
||||||
|
// 清除最后的 .class 结尾
|
||||||
|
clazzName = entryName.substring(0, entryName.lastIndexOf('.'));
|
||||||
|
// 将所有的 / 修改为 .
|
||||||
|
clazzName = join(clazzName.split("/"), ".");
|
||||||
|
|
||||||
|
// 加载类定义
|
||||||
|
Class<?> clazzObj = Class.forName(clazzName);
|
||||||
|
|
||||||
|
if (null != filter &&
|
||||||
|
!filter.accept(clazzObj)) {
|
||||||
|
// 如果过滤器不为空,
|
||||||
|
// 且过滤器不接受当前类,
|
||||||
|
// 则直接跳过!
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加类定义到集合
|
||||||
|
resultSet.add(clazzObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭 jar 输入流
|
||||||
|
jarIn.close();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// 抛出异常
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类名称过滤器
|
||||||
|
*
|
||||||
|
* @author hjj2019
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
private interface IClazzFilter {
|
||||||
|
/**
|
||||||
|
* 是否接受当前类?
|
||||||
|
*
|
||||||
|
* @param clazz 被筛选的类
|
||||||
|
* @return 是否符合条件
|
||||||
|
*/
|
||||||
|
boolean accept(Class<?> clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用连接符连接字符串数组
|
||||||
|
*
|
||||||
|
* @param strArr 字符串数组
|
||||||
|
* @param conn 连接符
|
||||||
|
* @return 连接后的字符串
|
||||||
|
*/
|
||||||
|
private static String join(String[] strArr, String conn) {
|
||||||
|
if (null == strArr ||
|
||||||
|
strArr.length <= 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < strArr.length; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
// 添加连接符
|
||||||
|
sb.append(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加字符串
|
||||||
|
sb.append(strArr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除源字符串左边的字符串
|
||||||
|
*
|
||||||
|
* @param src 原字符串
|
||||||
|
* @param trimStr 需要被清除的字符串
|
||||||
|
* @return 清除后的字符串
|
||||||
|
*/
|
||||||
|
private static String trimLeft(String src, String trimStr) {
|
||||||
|
if (null == src ||
|
||||||
|
src.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == trimStr ||
|
||||||
|
trimStr.isEmpty()) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.equals(trimStr)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
while (src.startsWith(trimStr)) {
|
||||||
|
src = src.substring(trimStr.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2021/12/30 18:26
|
||||||
|
* HttpServletRequest 获取工具类
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
public class RequestUtils {
|
||||||
|
public String ReadAsChars(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
BufferedReader br = null;
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
br = request.getReader();
|
||||||
|
String str;
|
||||||
|
while ((str = br.readLine()) != null)
|
||||||
|
{
|
||||||
|
sb.append(str);
|
||||||
|
}
|
||||||
|
br.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (null != br)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
br.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,264 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.chushang.common.core.text.Convert;
|
||||||
|
import com.chushang.common.core.web.Result;
|
||||||
|
import org.springframework.core.io.buffer.DataBuffer;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class ServletUtils
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* 获取String参数
|
||||||
|
*/
|
||||||
|
public static String getParameter(String name)
|
||||||
|
{
|
||||||
|
return getRequest().getParameter(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取String参数
|
||||||
|
*/
|
||||||
|
public static String getParameter(String name, String defaultValue)
|
||||||
|
{
|
||||||
|
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Integer参数
|
||||||
|
*/
|
||||||
|
public static Integer getParameterToInt(String name)
|
||||||
|
{
|
||||||
|
return Convert.toInt(getRequest().getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Integer参数
|
||||||
|
*/
|
||||||
|
public static Integer getParameterToInt(String name, Integer defaultValue)
|
||||||
|
{
|
||||||
|
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Boolean参数
|
||||||
|
*/
|
||||||
|
public static Boolean getParameterToBool(String name)
|
||||||
|
{
|
||||||
|
return Convert.toBool(getRequest().getParameter(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取Boolean参数
|
||||||
|
*/
|
||||||
|
public static Boolean getParameterToBool(String name, Boolean defaultValue)
|
||||||
|
{
|
||||||
|
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://blog.csdn.net/nlx1450161741/article/details/108266393 异步操作导致异步线程获取不到主线程的request信息
|
||||||
|
*/
|
||||||
|
public static HttpServletRequest getRequest(){
|
||||||
|
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RequestAttributes getRequestAttributes(){
|
||||||
|
return RequestContextHolder.getRequestAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取session
|
||||||
|
*/
|
||||||
|
public static HttpSession getSession()
|
||||||
|
{
|
||||||
|
return getRequest().getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getHeader(HttpServletRequest request, String name)
|
||||||
|
{
|
||||||
|
String value = request.getHeader(name);
|
||||||
|
if (StringUtils.isEmpty(value))
|
||||||
|
{
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
return urlDecode(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> getHeaders(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
Map<String, String> map = new LinkedHashMap<>();
|
||||||
|
Enumeration<String> enumeration = request.getHeaderNames();
|
||||||
|
if (enumeration != null)
|
||||||
|
{
|
||||||
|
while (enumeration.hasMoreElements())
|
||||||
|
{
|
||||||
|
String key = enumeration.nextElement();
|
||||||
|
String value = request.getHeader(key);
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字符串渲染到客户端
|
||||||
|
*
|
||||||
|
* @param response 渲染对象
|
||||||
|
* @param string 待渲染的字符串
|
||||||
|
*/
|
||||||
|
public static void renderString(HttpServletResponse response, String string)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
response.setStatus(200);
|
||||||
|
response.setContentType("application/json");
|
||||||
|
response.setCharacterEncoding("utf-8");
|
||||||
|
response.getWriter().print(string);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否是Ajax异步请求
|
||||||
|
*/
|
||||||
|
public static boolean isAjaxRequest(HttpServletRequest request)
|
||||||
|
{
|
||||||
|
String accept = request.getHeader("accept");
|
||||||
|
if (accept != null && accept.contains("application/json"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||||
|
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String uri = request.getRequestURI();
|
||||||
|
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
String ajax = request.getParameter("__ajax");
|
||||||
|
return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容编码
|
||||||
|
*
|
||||||
|
* @param str 内容
|
||||||
|
* @return 编码后的内容
|
||||||
|
*/
|
||||||
|
public static String urlEncode(String str)
|
||||||
|
{
|
||||||
|
return URLEncoder.encode(str, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容解码
|
||||||
|
*
|
||||||
|
* @param str 内容
|
||||||
|
* @return 解码后的内容
|
||||||
|
*/
|
||||||
|
public static String urlDecode(String str)
|
||||||
|
{
|
||||||
|
return URLDecoder.decode(str, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置webflux模型响应
|
||||||
|
*
|
||||||
|
* @param response ServerHttpResponse
|
||||||
|
* @param value 响应内容
|
||||||
|
* @return Mono<Void>
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value)
|
||||||
|
{
|
||||||
|
return webFluxResponseWriter(response, HttpStatus.OK, value, Result.FAIL_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置webflux模型响应
|
||||||
|
*
|
||||||
|
* @param response ServerHttpResponse
|
||||||
|
* @param code 响应状态码
|
||||||
|
* @param value 响应内容
|
||||||
|
* @return Mono<Void>
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, Object value, int code)
|
||||||
|
{
|
||||||
|
return webFluxResponseWriter(response, HttpStatus.OK, value, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置webflux模型响应
|
||||||
|
*
|
||||||
|
* @param response ServerHttpResponse
|
||||||
|
* @param status http状态码
|
||||||
|
* @param code 响应状态码
|
||||||
|
* @param value 响应内容
|
||||||
|
* @return Mono<Void>
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status, Object value, int code)
|
||||||
|
{
|
||||||
|
return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态码 进行设置
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, HttpStatus status)
|
||||||
|
{
|
||||||
|
return webFluxResponseWriter(response, HttpStatus.OK, status.getReasonPhrase(), status.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置webflux模型响应
|
||||||
|
*
|
||||||
|
* @param response ServerHttpResponse
|
||||||
|
* @param contentType content-type
|
||||||
|
* @param status http状态码
|
||||||
|
* @param code 响应状态码
|
||||||
|
* @param value 响应内容
|
||||||
|
* @return Mono<Void>
|
||||||
|
*/
|
||||||
|
public static Mono<Void> webFluxResponseWriter(ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code)
|
||||||
|
{
|
||||||
|
response.setStatusCode(status);
|
||||||
|
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
|
||||||
|
Result<?> result = Result.failed(code, value + "");
|
||||||
|
DataBuffer dataBuffer = response.bufferFactory().wrap(JSONUtil.toJsonStr(result).getBytes());
|
||||||
|
return response.writeWith(Mono.just(dataBuffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import org.springframework.aop.framework.AopContext;
|
||||||
|
import org.springframework.beans.BeansException;
|
||||||
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||||
|
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* spring工具类 方便在非spring管理环境中获取bean
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public final class SpringUtils implements BeanFactoryPostProcessor
|
||||||
|
{
|
||||||
|
/** Spring应用上下文环境 */
|
||||||
|
private static ConfigurableListableBeanFactory beanFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException
|
||||||
|
{
|
||||||
|
SpringUtils.beanFactory = beanFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取对象
|
||||||
|
* @return Object 一个以所给名字注册的bean的实例
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T getBean(String name) throws BeansException
|
||||||
|
{
|
||||||
|
return (T) beanFactory.getBean(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类型为requiredType的对象
|
||||||
|
*/
|
||||||
|
public static <T> T getBean(Class<T> clz) throws BeansException
|
||||||
|
{
|
||||||
|
return (T) beanFactory.getBean(clz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
|
||||||
|
*/
|
||||||
|
public static boolean containsBean(String name)
|
||||||
|
{
|
||||||
|
return beanFactory.containsBean(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException
|
||||||
|
{
|
||||||
|
return beanFactory.isSingleton(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Class 注册对象的类型
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException
|
||||||
|
{
|
||||||
|
return beanFactory.getType(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
|
||||||
|
*/
|
||||||
|
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException
|
||||||
|
{
|
||||||
|
return beanFactory.getAliases(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取aop代理对象
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T getAopProxy(T invoker)
|
||||||
|
{
|
||||||
|
return (T) AopContext.currentProxy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,537 @@
|
||||||
|
package com.chushang.common.core.util;
|
||||||
|
|
||||||
|
import com.chushang.common.core.text.StrFormatter;
|
||||||
|
import com.chushang.common.core.constant.Constants;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class StringUtils extends org.apache.commons.lang3.StringUtils
|
||||||
|
{
|
||||||
|
/** 空字符串 */
|
||||||
|
private static final String NULLSTR = "";
|
||||||
|
|
||||||
|
/** 下划线 */
|
||||||
|
private static final char SEPARATOR = '_';
|
||||||
|
|
||||||
|
private static final Pattern NUMBER_PATTERN = Pattern.compile("-?[0-9]+(\\\\.[0-9]+)?");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取参数不为空值
|
||||||
|
*
|
||||||
|
* @param value defaultValue 要判断的value
|
||||||
|
* @return value 返回值
|
||||||
|
*/
|
||||||
|
public static <T> T nvl(T value, T defaultValue)
|
||||||
|
{
|
||||||
|
return value != null ? value : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个Collection是否为空, 包含List,Set,Queue
|
||||||
|
*
|
||||||
|
* @param coll 要判断的Collection
|
||||||
|
* @return true:为空 false:非空
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(Collection<?> coll)
|
||||||
|
{
|
||||||
|
return isNull(coll) || coll.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个Collection是否非空,包含List,Set,Queue
|
||||||
|
*
|
||||||
|
* @param coll 要判断的Collection
|
||||||
|
* @return true:非空 false:空
|
||||||
|
*/
|
||||||
|
public static boolean isNotEmpty(Collection<?> coll)
|
||||||
|
{
|
||||||
|
return !isEmpty(coll);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个对象数组是否为空
|
||||||
|
*
|
||||||
|
* @param objects 要判断的对象数组
|
||||||
|
** @return true:为空 false:非空
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(Object[] objects)
|
||||||
|
{
|
||||||
|
return isNull(objects) || (objects.length == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个对象数组是否非空
|
||||||
|
*
|
||||||
|
* @param objects 要判断的对象数组
|
||||||
|
* @return true:非空 false:空
|
||||||
|
*/
|
||||||
|
public static boolean isNotEmpty(Object[] objects)
|
||||||
|
{
|
||||||
|
return !isEmpty(objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个Map是否为空
|
||||||
|
*
|
||||||
|
* @param map 要判断的Map
|
||||||
|
* @return true:为空 false:非空
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(Map<?, ?> map)
|
||||||
|
{
|
||||||
|
return isNull(map) || map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个Map是否为空
|
||||||
|
*
|
||||||
|
* @param map 要判断的Map
|
||||||
|
* @return true:非空 false:空
|
||||||
|
*/
|
||||||
|
public static boolean isNotEmpty(Map<?, ?> map)
|
||||||
|
{
|
||||||
|
return !isEmpty(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个字符串是否为空串
|
||||||
|
*
|
||||||
|
* @param str String
|
||||||
|
* @return true:为空 false:非空
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(String str)
|
||||||
|
{
|
||||||
|
return isNull(str) || NULLSTR.equals(str.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个字符串是否为非空串
|
||||||
|
*
|
||||||
|
* @param str String
|
||||||
|
* @return true:非空串 false:空串
|
||||||
|
*/
|
||||||
|
public static boolean isNotEmpty(String str)
|
||||||
|
{
|
||||||
|
return !isEmpty(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个对象是否为空
|
||||||
|
*
|
||||||
|
* @param object Object
|
||||||
|
* @return true:为空 false:非空
|
||||||
|
*/
|
||||||
|
public static boolean isNull(Object object)
|
||||||
|
{
|
||||||
|
return object == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个对象是否非空
|
||||||
|
*
|
||||||
|
* @param object Object
|
||||||
|
* @return true:非空 false:空
|
||||||
|
*/
|
||||||
|
public static boolean isNotNull(Object object)
|
||||||
|
{
|
||||||
|
return !isNull(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * 判断一个对象是否是数组类型(Java基本型别的数组)
|
||||||
|
*
|
||||||
|
* @param object 对象
|
||||||
|
* @return true:是数组 false:不是数组
|
||||||
|
*/
|
||||||
|
public static boolean isArray(Object object)
|
||||||
|
{
|
||||||
|
return isNotNull(object) && object.getClass().isArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 去空格
|
||||||
|
*/
|
||||||
|
public static String trim(String str)
|
||||||
|
{
|
||||||
|
return (str == null ? "" : str.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截取字符串
|
||||||
|
*
|
||||||
|
* @param str 字符串
|
||||||
|
* @param start 开始
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String substring(final String str, int start)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
return NULLSTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < 0)
|
||||||
|
{
|
||||||
|
start = str.length() + start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < 0)
|
||||||
|
{
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
if (start > str.length())
|
||||||
|
{
|
||||||
|
return NULLSTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.substring(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截取字符串
|
||||||
|
*
|
||||||
|
* @param str 字符串
|
||||||
|
* @param start 开始
|
||||||
|
* @param end 结束
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static String substring(final String str, int start, int end)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
return NULLSTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end < 0)
|
||||||
|
{
|
||||||
|
end = str.length() + end;
|
||||||
|
}
|
||||||
|
if (start < 0)
|
||||||
|
{
|
||||||
|
start = str.length() + start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end > str.length())
|
||||||
|
{
|
||||||
|
end = str.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start > end)
|
||||||
|
{
|
||||||
|
return NULLSTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start < 0)
|
||||||
|
{
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
if (end < 0)
|
||||||
|
{
|
||||||
|
end = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.substring(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为空,并且不是空白字符
|
||||||
|
*
|
||||||
|
* @param str 要判断的value
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static boolean hasText(String str)
|
||||||
|
{
|
||||||
|
return (str != null && !str.isEmpty() && containsText(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean containsText(CharSequence str)
|
||||||
|
{
|
||||||
|
int strLen = str.length();
|
||||||
|
for (int i = 0; i < strLen; i++)
|
||||||
|
{
|
||||||
|
if (!Character.isWhitespace(str.charAt(i)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化文本, {} 表示占位符<br>
|
||||||
|
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||||
|
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||||
|
* 例:<br>
|
||||||
|
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||||
|
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||||
|
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||||
|
*
|
||||||
|
* @param template 文本模板,被替换的部分用 {} 表示
|
||||||
|
* @param params 参数值
|
||||||
|
* @return 格式化后的文本
|
||||||
|
*/
|
||||||
|
public static String format(String template, Object... params)
|
||||||
|
{
|
||||||
|
if (isEmpty(params) || isEmpty(template))
|
||||||
|
{
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
return StrFormatter.format(template, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为http(s)://开头
|
||||||
|
*
|
||||||
|
* @param link 链接
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
public static boolean isHttp(String link)
|
||||||
|
{
|
||||||
|
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 驼峰转下划线命名
|
||||||
|
*/
|
||||||
|
public static String toUnderScoreCase(String str)
|
||||||
|
{
|
||||||
|
if (str == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
// 前置字符是否大写
|
||||||
|
boolean preCharIsUpperCase = true;
|
||||||
|
// 当前字符是否大写
|
||||||
|
boolean curreCharIsUpperCase = true;
|
||||||
|
// 下一字符是否大写
|
||||||
|
boolean nexteCharIsUpperCase = true;
|
||||||
|
for (int i = 0; i < str.length(); i++)
|
||||||
|
{
|
||||||
|
char c = str.charAt(i);
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
preCharIsUpperCase = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
curreCharIsUpperCase = Character.isUpperCase(c);
|
||||||
|
|
||||||
|
if (i < (str.length() - 1))
|
||||||
|
{
|
||||||
|
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase)
|
||||||
|
{
|
||||||
|
sb.append(SEPARATOR);
|
||||||
|
}
|
||||||
|
else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase)
|
||||||
|
{
|
||||||
|
sb.append(SEPARATOR);
|
||||||
|
}
|
||||||
|
sb.append(Character.toLowerCase(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否包含字符串
|
||||||
|
*
|
||||||
|
* @param str 验证字符串
|
||||||
|
* @param strs 字符串组
|
||||||
|
* @return 包含返回true
|
||||||
|
*/
|
||||||
|
public static boolean inStringIgnoreCase(String str, String... strs)
|
||||||
|
{
|
||||||
|
if (str != null && strs != null)
|
||||||
|
{
|
||||||
|
for (String s : strs)
|
||||||
|
{
|
||||||
|
if (str.equalsIgnoreCase(trim(s)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
|
||||||
|
*
|
||||||
|
* @param name 转换前的下划线大写方式命名的字符串
|
||||||
|
* @return 转换后的驼峰式命名的字符串
|
||||||
|
*/
|
||||||
|
public static String convertToCamelCase(String name)
|
||||||
|
{
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
// 快速检查
|
||||||
|
if (name == null || name.isEmpty())
|
||||||
|
{
|
||||||
|
// 没必要转换
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
else if (!name.contains("_"))
|
||||||
|
{
|
||||||
|
// 不含下划线,仅将首字母大写
|
||||||
|
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||||
|
}
|
||||||
|
// 用下划线将原始字符串分割
|
||||||
|
String[] camels = name.split("_");
|
||||||
|
for (String camel : camels)
|
||||||
|
{
|
||||||
|
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
||||||
|
if (camel.isEmpty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 首字母大写
|
||||||
|
result.append(camel.substring(0, 1).toUpperCase());
|
||||||
|
result.append(camel.substring(1).toLowerCase());
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 驼峰式命名法 例如:user_name->userName
|
||||||
|
*/
|
||||||
|
public static String toCamelCase(String s)
|
||||||
|
{
|
||||||
|
if (s == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
s = s.toLowerCase();
|
||||||
|
StringBuilder sb = new StringBuilder(s.length());
|
||||||
|
boolean upperCase = false;
|
||||||
|
for (int i = 0; i < s.length(); i++)
|
||||||
|
{
|
||||||
|
char c = s.charAt(i);
|
||||||
|
|
||||||
|
if (c == SEPARATOR)
|
||||||
|
{
|
||||||
|
upperCase = true;
|
||||||
|
}
|
||||||
|
else if (upperCase)
|
||||||
|
{
|
||||||
|
sb.append(Character.toUpperCase(c));
|
||||||
|
upperCase = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
|
||||||
|
*
|
||||||
|
* @param str 指定字符串
|
||||||
|
* @param strs 需要检查的字符串数组
|
||||||
|
* @return 是否匹配
|
||||||
|
*/
|
||||||
|
public static boolean matches(String str, List<String> strs)
|
||||||
|
{
|
||||||
|
if (isEmpty(str) || isEmpty(strs))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (String pattern : strs)
|
||||||
|
{
|
||||||
|
if (isMatch(pattern, str))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断url是否与规则配置:
|
||||||
|
* ? 表示单个字符;
|
||||||
|
* * 表示一层路径内的任意字符串,不可跨层级;
|
||||||
|
* ** 表示任意层路径;
|
||||||
|
*
|
||||||
|
* @param pattern 匹配规则
|
||||||
|
* @param url 需要匹配的url
|
||||||
|
*/
|
||||||
|
public static boolean isMatch(String pattern, String url)
|
||||||
|
{
|
||||||
|
AntPathMatcher matcher = new AntPathMatcher();
|
||||||
|
return matcher.match(pattern, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T cast(Object obj)
|
||||||
|
{
|
||||||
|
return (T) obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||||
|
*
|
||||||
|
* @param num 数字对象
|
||||||
|
* @param size 字符串指定长度
|
||||||
|
* @return 返回数字的字符串格式,该字符串为指定长度。
|
||||||
|
*/
|
||||||
|
public static String padl(final Number num, final int size)
|
||||||
|
{
|
||||||
|
return padl(num.toString(), size, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
||||||
|
*
|
||||||
|
* @param s 原始字符串
|
||||||
|
* @param size 字符串指定长度
|
||||||
|
* @param c 用于补齐的字符
|
||||||
|
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
|
||||||
|
*/
|
||||||
|
public static String padl(final String s, final int size, final char c)
|
||||||
|
{
|
||||||
|
final StringBuilder sb = new StringBuilder(size);
|
||||||
|
if (s != null)
|
||||||
|
{
|
||||||
|
final int len = s.length();
|
||||||
|
if (s.length() <= size)
|
||||||
|
{
|
||||||
|
sb.append(String.valueOf(c).repeat(size - len));
|
||||||
|
sb.append(s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return s.substring(len - size, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.append(String.valueOf(c).repeat(Math.max(0, size)));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否为数值类型的字符串
|
||||||
|
*/
|
||||||
|
public static boolean isInteger(String str) {
|
||||||
|
if (isEmpty(str)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return NUMBER_PATTERN.matcher(str).matches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,181 @@
|
||||||
|
package com.chushang.common.core.web;
|
||||||
|
|
||||||
|
import cn.hutool.http.HttpStatus;
|
||||||
|
import com.chushang.common.core.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作消息提醒
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class AjaxResult extends HashMap<String, Object>
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 状态码 */
|
||||||
|
public static final String CODE_TAG = "code";
|
||||||
|
|
||||||
|
/** 返回内容 */
|
||||||
|
public static final String MSG_TAG = "msg";
|
||||||
|
|
||||||
|
/** 数据对象 */
|
||||||
|
public static final String DATA_TAG = "data";
|
||||||
|
|
||||||
|
public static final String SUCCESS = "success";
|
||||||
|
|
||||||
|
public static final String FAIL = "Server internal error";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
|
||||||
|
*/
|
||||||
|
public AjaxResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化一个新创建的 AjaxResult 对象
|
||||||
|
*
|
||||||
|
* @param code 状态码
|
||||||
|
* @param msg 返回内容
|
||||||
|
*/
|
||||||
|
public AjaxResult(int code, String msg)
|
||||||
|
{
|
||||||
|
super.put(CODE_TAG, code);
|
||||||
|
super.put(MSG_TAG, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化一个新创建的 AjaxResult 对象
|
||||||
|
*
|
||||||
|
* @param code 状态码
|
||||||
|
* @param msg 返回内容
|
||||||
|
* @param data 数据对象
|
||||||
|
*/
|
||||||
|
public AjaxResult(int code, String msg, Object data)
|
||||||
|
{
|
||||||
|
super.put(CODE_TAG, code);
|
||||||
|
super.put(MSG_TAG, msg);
|
||||||
|
if (StringUtils.isNotNull(data))
|
||||||
|
{
|
||||||
|
super.put(DATA_TAG, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 方便链式调用
|
||||||
|
*/
|
||||||
|
// @Override
|
||||||
|
// public AjaxResult put(String key, Object value)
|
||||||
|
// {
|
||||||
|
// super.put(key, value);
|
||||||
|
// return this;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public AjaxResult add(String key, Object value){
|
||||||
|
super.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AjaxResult addAll(Map<String, Object> map){
|
||||||
|
super.putAll(map);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static AjaxResult Builder(){
|
||||||
|
return new AjaxResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成功消息
|
||||||
|
*
|
||||||
|
* @return 成功消息
|
||||||
|
*/
|
||||||
|
public static AjaxResult success()
|
||||||
|
{
|
||||||
|
return AjaxResult.success(SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成功数据
|
||||||
|
*
|
||||||
|
* @return 成功消息
|
||||||
|
*/
|
||||||
|
public static AjaxResult success(Object data)
|
||||||
|
{
|
||||||
|
return AjaxResult.success(SUCCESS, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成功消息
|
||||||
|
* @param data 返回内容
|
||||||
|
*/
|
||||||
|
public static AjaxResult success(String data)
|
||||||
|
{
|
||||||
|
return AjaxResult.success(SUCCESS, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回成功消息
|
||||||
|
*
|
||||||
|
* @param msg 返回内容
|
||||||
|
* @param data 数据对象
|
||||||
|
* @return 成功消息
|
||||||
|
*/
|
||||||
|
public static AjaxResult success(String msg, Object data)
|
||||||
|
{
|
||||||
|
return new AjaxResult(HttpStatus.HTTP_OK, msg, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回错误消息
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static AjaxResult error()
|
||||||
|
{
|
||||||
|
return AjaxResult.error(FAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回错误消息
|
||||||
|
*
|
||||||
|
* @param msg 返回内容
|
||||||
|
* @return 警告消息
|
||||||
|
*/
|
||||||
|
public static AjaxResult error(String msg)
|
||||||
|
{
|
||||||
|
return AjaxResult.error(msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回错误消息
|
||||||
|
*
|
||||||
|
* @param msg 返回内容
|
||||||
|
* @param data 数据对象
|
||||||
|
* @return 警告消息
|
||||||
|
*/
|
||||||
|
public static AjaxResult error(String msg, Object data)
|
||||||
|
{
|
||||||
|
return new AjaxResult(HttpStatus.HTTP_INTERNAL_ERROR, msg, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回错误消息
|
||||||
|
*
|
||||||
|
* @param code 状态码
|
||||||
|
* @param msg 返回内容
|
||||||
|
* @return 警告消息
|
||||||
|
*/
|
||||||
|
public static AjaxResult error(int code, String msg)
|
||||||
|
{
|
||||||
|
return new AjaxResult(code, msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess(){
|
||||||
|
return HttpStatus.HTTP_OK == (int)this.get(CODE_TAG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.chushang.common.core.web;
|
||||||
|
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2022/1/10 14:28
|
||||||
|
* 枚举获取帮助类 , 要求 enum 类必须 实现 CodeEnum, 包含有 Code , 并且 Code 唯一
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
public class EnumUtils {
|
||||||
|
public static <T extends CodeEnum<?, ?>> T getByCode(Serializable code, Class<T> tClass) {
|
||||||
|
if (null == code){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (T each : tClass.getEnumConstants()) {
|
||||||
|
if (code.equals(each.getCode())) {
|
||||||
|
return each;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface CodeEnum<C,S extends Serializable> {
|
||||||
|
C getCode();
|
||||||
|
S getMsg();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.web;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应信息主体
|
||||||
|
*
|
||||||
|
* @author lengleng
|
||||||
|
*/
|
||||||
|
@ToString
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class Result<T> implements Serializable {
|
||||||
|
public static final int SUCCESS_CODE = 200;
|
||||||
|
public static final String SUCCESS_MSG = "success";
|
||||||
|
public static final String FAIL_MSG = "fail";
|
||||||
|
public static final int FAIL_CODE = 500;
|
||||||
|
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
public static Result<Map<String, Object>> putAll(Map<String, Object> params) {
|
||||||
|
return Result.responseData()
|
||||||
|
.addParams(params)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Result<Map<String, Object>> put(String key, Object data) {
|
||||||
|
return Result.responseData()
|
||||||
|
.addParam(key, data)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> ok() {
|
||||||
|
return restResult(null, SUCCESS_CODE, SUCCESS_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> ok(T data) {
|
||||||
|
return restResult(data, SUCCESS_CODE, SUCCESS_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> ok(T data, String msg) {
|
||||||
|
return restResult(data, SUCCESS_CODE, SUCCESS_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> failed() {
|
||||||
|
return restResult(null, FAIL_CODE, FAIL_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> failed(String msg) {
|
||||||
|
return restResult(null, FAIL_CODE, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> failedFeign(){
|
||||||
|
return restResult(null, FAIL_CODE, "请稍后再试");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> failed(T data) {
|
||||||
|
return restResult(data, FAIL_CODE, FAIL_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> failed(T data, String msg) {
|
||||||
|
return restResult(data, FAIL_CODE, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> failed(int code, String msg) {
|
||||||
|
return restResult(null, code, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> Result<T> restResult(T data, int code, String msg) {
|
||||||
|
Result<T> apiResult = new Result<>();
|
||||||
|
apiResult.setCode(code);
|
||||||
|
apiResult.setData(data);
|
||||||
|
apiResult.setMsg(msg);
|
||||||
|
return apiResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResultMap responseData(){
|
||||||
|
return new ResultMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ResultMap {
|
||||||
|
private final Map<String, Object> resultMap = new HashMap<>();
|
||||||
|
|
||||||
|
public ResultMap addParam(String key, Object value){
|
||||||
|
this.resultMap.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultMap addParams(Map<String, Object> params) {
|
||||||
|
this.resultMap.putAll(params);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<Map<String, Object>> build(){
|
||||||
|
return Result.ok(this.resultMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.chushang.common.core.web;
|
||||||
|
|
||||||
|
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
|
||||||
|
import cn.hutool.crypto.symmetric.SymmetricCrypto;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2021/12/30 11:42
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@UtilityClass
|
||||||
|
public class TokenUtils {
|
||||||
|
private final String TOKEN_SIGN = "sane_cloud_token";
|
||||||
|
|
||||||
|
public String decryptStr(String sanyiToken){
|
||||||
|
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.AES, TOKEN_SIGN.getBytes(StandardCharsets.UTF_8));
|
||||||
|
try {
|
||||||
|
return aes.decryptStr(sanyiToken);
|
||||||
|
}catch (Exception ignored){
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decryptStr(String sanyiToken, String signType) {
|
||||||
|
// SymmetricAlgorithm.valueof() 必定不为空, 并且不会异常
|
||||||
|
try {
|
||||||
|
SymmetricCrypto aes = new SymmetricCrypto(SymmetricAlgorithm.valueOf(signType), TOKEN_SIGN.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return aes.decryptStr(sanyiToken);
|
||||||
|
}catch (Exception ignored){
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
package com.chushang.common.core.web;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2022/2/22 09:38
|
||||||
|
*/
|
||||||
|
public class ValidList<E> implements List<E>, Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Valid
|
||||||
|
@Size(min = 1, message = "集合不能为空")
|
||||||
|
@NotNull(message = "集合不能为null")
|
||||||
|
private final List<E> list = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return list.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return list.contains(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return list.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return list.toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(T[] a) {
|
||||||
|
return list.toArray(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(E e) {
|
||||||
|
return list.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
return list.remove(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> c) {
|
||||||
|
return list.containsAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends E> c) {
|
||||||
|
return list.addAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int index, Collection<? extends E> c) {
|
||||||
|
return list.addAll(index,c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> c) {
|
||||||
|
return list.removeAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> c) {
|
||||||
|
return list.retainAll(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E get(int index) {
|
||||||
|
return list.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E set(int index, E element) {
|
||||||
|
return list.set(index,element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, E element) {
|
||||||
|
list.add(index, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public E remove(int index) {
|
||||||
|
return list.remove(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int indexOf(Object o) {
|
||||||
|
return list.indexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lastIndexOf(Object o) {
|
||||||
|
return list.lastIndexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator<E> listIterator() {
|
||||||
|
return list.listIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator<E> listIterator(int index) {
|
||||||
|
return list.listIterator(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<E> subList(int fromIndex, int toIndex) {
|
||||||
|
return list.subList(fromIndex,toIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 pig4cloud Authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.web;
|
||||||
|
|
||||||
|
import cn.hutool.core.codec.Base64;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.chushang.common.core.util.ClassUtils;
|
||||||
|
import com.chushang.common.core.exception.CheckedException;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseBody;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
import org.springframework.web.method.HandlerMethod;
|
||||||
|
|
||||||
|
import javax.servlet.http.Cookie;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Miscellaneous utilities for web applications.
|
||||||
|
*
|
||||||
|
* @author L.cm
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@UtilityClass
|
||||||
|
public class WebUtils extends org.springframework.web.util.WebUtils {
|
||||||
|
|
||||||
|
private final String BASIC_ = "Basic ";
|
||||||
|
|
||||||
|
private final String UNKNOWN = "unknown";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否ajax请求 spring ajax 返回含有 ResponseBody 或者 RestController注解
|
||||||
|
* @param handlerMethod HandlerMethod
|
||||||
|
* @return 是否ajax请求
|
||||||
|
*/
|
||||||
|
public boolean isBody(HandlerMethod handlerMethod) {
|
||||||
|
ResponseBody responseBody = ClassUtils.getAnnotation(handlerMethod, ResponseBody.class);
|
||||||
|
return responseBody != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取cookie
|
||||||
|
* @param name cookie name
|
||||||
|
* @return cookie value
|
||||||
|
*/
|
||||||
|
public String getCookieVal(String name) {
|
||||||
|
HttpServletRequest request = WebUtils.getRequest();
|
||||||
|
Assert.notNull(request, "request from RequestContextHolder is null");
|
||||||
|
return getCookieVal(request, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取cookie
|
||||||
|
* @param request HttpServletRequest
|
||||||
|
* @param name cookie name
|
||||||
|
* @return cookie value
|
||||||
|
*/
|
||||||
|
public String getCookieVal(HttpServletRequest request, String name) {
|
||||||
|
Cookie cookie = getCookie(request, name);
|
||||||
|
return cookie != null ? cookie.getValue() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除 某个指定的cookie
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
* @param key cookie key
|
||||||
|
*/
|
||||||
|
public void removeCookie(HttpServletResponse response, String key) {
|
||||||
|
setCookie(response, key, null, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置cookie
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
* @param name cookie name
|
||||||
|
* @param value cookie value
|
||||||
|
* @param maxAgeInSeconds maxage
|
||||||
|
*/
|
||||||
|
public void setCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds) {
|
||||||
|
Cookie cookie = new Cookie(name, value);
|
||||||
|
cookie.setPath("/");
|
||||||
|
cookie.setMaxAge(maxAgeInSeconds);
|
||||||
|
cookie.setHttpOnly(true);
|
||||||
|
response.addCookie(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 获取 HttpServletRequest
|
||||||
|
// * @return {HttpServletRequest}
|
||||||
|
// */
|
||||||
|
// public Optional<HttpServletRequest> getRequest() {
|
||||||
|
// return Optional
|
||||||
|
// .ofNullable(((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 HttpServletRequest
|
||||||
|
* @return {HttpServletRequest}
|
||||||
|
*/
|
||||||
|
public HttpServletRequest getRequest() {
|
||||||
|
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取 HttpServletResponse
|
||||||
|
* @return {HttpServletResponse}
|
||||||
|
*/
|
||||||
|
public HttpServletResponse getResponse() {
|
||||||
|
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回json
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
* @param result 结果对象
|
||||||
|
*/
|
||||||
|
public void renderJson(HttpServletResponse response, Object result) {
|
||||||
|
renderJson(response, result, MediaType.APPLICATION_JSON_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回json
|
||||||
|
* @param response HttpServletResponse
|
||||||
|
* @param result 结果对象
|
||||||
|
* @param contentType contentType
|
||||||
|
*/
|
||||||
|
public void renderJson(HttpServletResponse response, Object result, String contentType) {
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType(contentType);
|
||||||
|
try (PrintWriter out = response.getWriter()) {
|
||||||
|
out.append(JSONUtil.toJsonStr(result));
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从request 获取CLIENT_ID
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public String getClientId(ServerHttpRequest request) {
|
||||||
|
String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||||
|
|
||||||
|
return splitClient(header)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public String getClientId(HttpServletRequest request) {
|
||||||
|
String header = WebUtils.getRequest().getHeader("Authorization");
|
||||||
|
|
||||||
|
return splitClient(header)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static String[] splitClient(String header) throws UnsupportedEncodingException {
|
||||||
|
if (header == null || !header.startsWith(BASIC_)) {
|
||||||
|
throw new CheckedException("请求头中client信息为空");
|
||||||
|
}
|
||||||
|
byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
|
||||||
|
byte[] decoded;
|
||||||
|
try {
|
||||||
|
decoded = Base64.decode(base64Token);
|
||||||
|
}
|
||||||
|
catch (IllegalArgumentException e) {
|
||||||
|
throw new CheckedException("Failed to decode basic authentication token");
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = new String(decoded, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
int delim = token.indexOf(":");
|
||||||
|
|
||||||
|
if (delim == -1) {
|
||||||
|
throw new CheckedException("Invalid basic authentication token");
|
||||||
|
}
|
||||||
|
return new String[] { token.substring(0, delim), token.substring(delim + 1) };
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
package com.chushang.common.core.xss;
|
||||||
|
|
||||||
|
import com.chushang.common.core.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义和反转义工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class EscapeUtil
|
||||||
|
{
|
||||||
|
public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
|
||||||
|
|
||||||
|
private static final char[][] TEXT = new char[64][];
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
TEXT[i] = new char[] { (char) i };
|
||||||
|
}
|
||||||
|
|
||||||
|
// special HTML characters
|
||||||
|
TEXT['\''] = "'".toCharArray(); // 单引号
|
||||||
|
TEXT['"'] = """.toCharArray(); // 双引号
|
||||||
|
TEXT['&'] = "&".toCharArray(); // &符
|
||||||
|
TEXT['<'] = "<".toCharArray(); // 小于号
|
||||||
|
TEXT['>'] = ">".toCharArray(); // 大于号
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义文本中的HTML字符为安全的字符
|
||||||
|
*
|
||||||
|
* @param text 被转义的文本
|
||||||
|
* @return 转义后的文本
|
||||||
|
*/
|
||||||
|
public static String escape(String text)
|
||||||
|
{
|
||||||
|
return encode(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 还原被转义的HTML特殊字符
|
||||||
|
*
|
||||||
|
* @param content 包含转义符的HTML内容
|
||||||
|
* @return 转换后的字符串
|
||||||
|
*/
|
||||||
|
public static String unescape(String content)
|
||||||
|
{
|
||||||
|
return decode(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有HTML标签,但是不删除标签内的内容
|
||||||
|
*
|
||||||
|
* @param content 文本
|
||||||
|
* @return 清除标签后的文本
|
||||||
|
*/
|
||||||
|
public static String clean(String content)
|
||||||
|
{
|
||||||
|
return new HTMLFilter().filter(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape编码
|
||||||
|
*
|
||||||
|
* @param text 被编码的文本
|
||||||
|
* @return 编码后的字符
|
||||||
|
*/
|
||||||
|
private static String encode(String text)
|
||||||
|
{
|
||||||
|
if (StringUtils.isEmpty(text))
|
||||||
|
{
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuilder tmp = new StringBuilder(text.length() * 6);
|
||||||
|
char c;
|
||||||
|
for (int i = 0; i < text.length(); i++)
|
||||||
|
{
|
||||||
|
c = text.charAt(i);
|
||||||
|
if (c < 256)
|
||||||
|
{
|
||||||
|
tmp.append("%");
|
||||||
|
if (c < 16)
|
||||||
|
{
|
||||||
|
tmp.append("0");
|
||||||
|
}
|
||||||
|
tmp.append(Integer.toString(c, 16));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmp.append("%u");
|
||||||
|
if (c <= 0xfff)
|
||||||
|
{
|
||||||
|
// issue#I49JU8@Gitee
|
||||||
|
tmp.append("0");
|
||||||
|
}
|
||||||
|
tmp.append(Integer.toString(c, 16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmp.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escape解码
|
||||||
|
*
|
||||||
|
* @param content 被转义的内容
|
||||||
|
* @return 解码后的字符串
|
||||||
|
*/
|
||||||
|
public static String decode(String content)
|
||||||
|
{
|
||||||
|
if (StringUtils.isEmpty(content))
|
||||||
|
{
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder tmp = new StringBuilder(content.length());
|
||||||
|
int lastPos = 0, pos = 0;
|
||||||
|
char ch;
|
||||||
|
while (lastPos < content.length())
|
||||||
|
{
|
||||||
|
pos = content.indexOf("%", lastPos);
|
||||||
|
if (pos == lastPos)
|
||||||
|
{
|
||||||
|
if (content.charAt(pos + 1) == 'u')
|
||||||
|
{
|
||||||
|
ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
|
||||||
|
tmp.append(ch);
|
||||||
|
lastPos = pos + 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
|
||||||
|
tmp.append(ch);
|
||||||
|
lastPos = pos + 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pos == -1)
|
||||||
|
{
|
||||||
|
tmp.append(content.substring(lastPos));
|
||||||
|
lastPos = content.length();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tmp.append(content, lastPos, pos);
|
||||||
|
lastPos = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tmp.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,531 @@
|
||||||
|
package com.chushang.common.core.xss;
|
||||||
|
|
||||||
|
import com.chushang.common.core.constant.CommonConstants;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* HTML filtering utility for protecting against XSS (Cross Site Scripting).
|
||||||
|
*
|
||||||
|
* This code is licensed LGPLv3
|
||||||
|
*
|
||||||
|
* This code is a Java port of the original work in PHP by Cal Hendersen.
|
||||||
|
* http://code.iamcal.com/php/lib_filter/
|
||||||
|
*
|
||||||
|
* The trickiest part of the translation was handling the differences in regex handling
|
||||||
|
* between PHP and Java. These resources were helpful in the process:
|
||||||
|
*
|
||||||
|
* http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html
|
||||||
|
* http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php
|
||||||
|
* http://www.regular-expressions.info/modifiers.html
|
||||||
|
*
|
||||||
|
* A note on naming conventions: instance variables are prefixed with a "v"; global
|
||||||
|
* constants are in all caps.
|
||||||
|
*
|
||||||
|
* Sample use:
|
||||||
|
* String input = ...
|
||||||
|
* String clean = new HTMLFilter().filter( input );
|
||||||
|
*
|
||||||
|
* The class is not thread safe. Create a new instance if in doubt.
|
||||||
|
*
|
||||||
|
* If you find bugs or have suggestions on improvement (especially regarding
|
||||||
|
* performance), please contact us. The latest version of this
|
||||||
|
* source, and our contact details, can be found at http://xss-html-filter.sf.net
|
||||||
|
*
|
||||||
|
* @author Joseph O'Connell
|
||||||
|
* @author Cal Hendersen
|
||||||
|
* @author Michael Semb Wever
|
||||||
|
*/
|
||||||
|
public final class HTMLFilter {
|
||||||
|
|
||||||
|
/** regex flag union representing /si modifiers in php **/
|
||||||
|
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
|
||||||
|
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
|
||||||
|
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
|
||||||
|
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
|
||||||
|
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
|
||||||
|
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
|
||||||
|
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
|
||||||
|
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
|
||||||
|
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
|
||||||
|
private static final Pattern P_END_ARROW = Pattern.compile("^>");
|
||||||
|
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||||
|
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||||
|
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||||
|
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||||
|
private static final Pattern P_AMP = Pattern.compile("&");
|
||||||
|
private static final Pattern P_QUOTE = Pattern.compile("<");
|
||||||
|
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
|
||||||
|
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
|
||||||
|
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
|
||||||
|
|
||||||
|
// @xxx could grow large... maybe use sesat's ReferenceMap
|
||||||
|
private static final ConcurrentMap<String,Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
|
||||||
|
private static final ConcurrentMap<String,Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/** set of allowed html elements, along with allowed attributes for each element **/
|
||||||
|
private final Map<String, List<String>> vAllowed;
|
||||||
|
/** counts of open tags for each (allowable) html element **/
|
||||||
|
private final Map<String, Integer> vTagCounts = new HashMap<>();
|
||||||
|
|
||||||
|
/** html elements which must always be self-closing (e.g. "<img />") **/
|
||||||
|
private final String[] vSelfClosingTags;
|
||||||
|
/** html elements which must always have separate opening and closing tags (e.g. "<b></b>") **/
|
||||||
|
private final String[] vNeedClosingTags;
|
||||||
|
/** set of disallowed html elements **/
|
||||||
|
private final String[] vDisallowed;
|
||||||
|
/** attributes which should be checked for valid protocols **/
|
||||||
|
private final String[] vProtocolAtts;
|
||||||
|
/** allowed protocols **/
|
||||||
|
private final String[] vAllowedProtocols;
|
||||||
|
/** tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />") **/
|
||||||
|
private final String[] vRemoveBlanks;
|
||||||
|
/** entities allowed within html markup **/
|
||||||
|
private final String[] vAllowedEntities;
|
||||||
|
/** flag determining whether comments are allowed in input String. */
|
||||||
|
private final boolean stripComment;
|
||||||
|
private final boolean encodeQuotes;
|
||||||
|
private boolean vDebug = false;
|
||||||
|
/**
|
||||||
|
* flag determining whether to try to make tags when presented with "unbalanced"
|
||||||
|
* angle brackets (e.g. "<b text </b>" becomes "<b> text </b>"). If set to false,
|
||||||
|
* unbalanced angle brackets will be html escaped.
|
||||||
|
*/
|
||||||
|
private final boolean alwaysMakeTags;
|
||||||
|
|
||||||
|
/** Default constructor.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public HTMLFilter() {
|
||||||
|
vAllowed = new HashMap<>();
|
||||||
|
|
||||||
|
final ArrayList<String> a_atts = new ArrayList<>();
|
||||||
|
a_atts.add("href");
|
||||||
|
a_atts.add("target");
|
||||||
|
vAllowed.put("a", a_atts);
|
||||||
|
|
||||||
|
final ArrayList<String> img_atts = new ArrayList<>();
|
||||||
|
img_atts.add("src");
|
||||||
|
img_atts.add("width");
|
||||||
|
img_atts.add("height");
|
||||||
|
img_atts.add("alt");
|
||||||
|
vAllowed.put("img", img_atts);
|
||||||
|
|
||||||
|
final ArrayList<String> no_atts = new ArrayList<>();
|
||||||
|
vAllowed.put("b", no_atts);
|
||||||
|
vAllowed.put("strong", no_atts);
|
||||||
|
vAllowed.put("i", no_atts);
|
||||||
|
vAllowed.put("em", no_atts);
|
||||||
|
|
||||||
|
vSelfClosingTags = new String[]{"img"};
|
||||||
|
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
|
||||||
|
vDisallowed = new String[]{};
|
||||||
|
vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
|
||||||
|
vProtocolAtts = new String[]{"src", "href"};
|
||||||
|
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
|
||||||
|
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
|
||||||
|
stripComment = true;
|
||||||
|
encodeQuotes = true;
|
||||||
|
alwaysMakeTags = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set debug flag to true. Otherwise use default settings. See the default constructor.
|
||||||
|
*
|
||||||
|
* @param debug turn debug on with a true argument
|
||||||
|
*/
|
||||||
|
public HTMLFilter(final boolean debug) {
|
||||||
|
this();
|
||||||
|
vDebug = debug;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Map-parameter configurable constructor.
|
||||||
|
*
|
||||||
|
* @param conf map containing configuration. keys match field names.
|
||||||
|
*/
|
||||||
|
public HTMLFilter(final Map<String,Object> conf) {
|
||||||
|
|
||||||
|
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
|
||||||
|
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
|
||||||
|
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
|
||||||
|
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
|
||||||
|
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
|
||||||
|
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
|
||||||
|
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
|
||||||
|
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
|
||||||
|
|
||||||
|
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
|
||||||
|
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
|
||||||
|
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
|
||||||
|
vDisallowed = (String[]) conf.get("vDisallowed");
|
||||||
|
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
|
||||||
|
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
|
||||||
|
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
|
||||||
|
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
|
||||||
|
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
|
||||||
|
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
|
||||||
|
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
vTagCounts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void debug(final String msg) {
|
||||||
|
if (vDebug) {
|
||||||
|
Logger.getAnonymousLogger().info(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
// my versions of some PHP library functions
|
||||||
|
public static String chr(final int decimal) {
|
||||||
|
return String.valueOf((char) decimal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String htmlSpecialChars(final String s) {
|
||||||
|
String result = s;
|
||||||
|
result = regexReplace(P_AMP, "&", result);
|
||||||
|
result = regexReplace(P_QUOTE, """, result);
|
||||||
|
result = regexReplace(P_LEFT_ARROW, "<", result);
|
||||||
|
result = regexReplace(P_RIGHT_ARROW, ">", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* given a user submitted input String, filter out any invalid or restricted
|
||||||
|
* html.
|
||||||
|
*
|
||||||
|
* @param input text (i.e. submitted by a user) than may contain html
|
||||||
|
* @return "clean" version of input, with only valid, whitelisted html elements allowed
|
||||||
|
*/
|
||||||
|
public String filter(final String input) {
|
||||||
|
reset();
|
||||||
|
String s = input;
|
||||||
|
|
||||||
|
debug("************************************************");
|
||||||
|
debug(" INPUT: " + input);
|
||||||
|
|
||||||
|
s = escapeComments(s);
|
||||||
|
debug(" escapeComments: " + s);
|
||||||
|
|
||||||
|
s = balanceHTML(s);
|
||||||
|
debug(" balanceHTML: " + s);
|
||||||
|
|
||||||
|
s = checkTags(s);
|
||||||
|
debug(" checkTags: " + s);
|
||||||
|
|
||||||
|
s = processRemoveBlanks(s);
|
||||||
|
debug("processRemoveBlanks: " + s);
|
||||||
|
|
||||||
|
s = validateEntities(s);
|
||||||
|
debug(" validateEntites: " + s);
|
||||||
|
|
||||||
|
debug("************************************************\n\n");
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAlwaysMakeTags(){
|
||||||
|
return alwaysMakeTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStripComments(){
|
||||||
|
return stripComment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String escapeComments(final String s) {
|
||||||
|
final Matcher m = P_COMMENTS.matcher(s);
|
||||||
|
final StringBuffer buf = new StringBuffer();
|
||||||
|
if (m.find()) {
|
||||||
|
final String match = m.group(1); //(.*?)
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String balanceHTML(String s) {
|
||||||
|
if (alwaysMakeTags) {
|
||||||
|
//
|
||||||
|
// try and form html
|
||||||
|
//
|
||||||
|
s = regexReplace(P_END_ARROW, "", s);
|
||||||
|
s = regexReplace(P_BODY_TO_END, "<$1>", s);
|
||||||
|
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// escape stray brackets
|
||||||
|
//
|
||||||
|
s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
|
||||||
|
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
|
||||||
|
|
||||||
|
//
|
||||||
|
// the last regexp causes '<>' entities to appear
|
||||||
|
// (we need to do a lookahead assertion so that the last bracket can
|
||||||
|
// be used in the next pass of the regexp)
|
||||||
|
//
|
||||||
|
s = regexReplace(P_BOTH_ARROWS, "", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkTags(String s) {
|
||||||
|
Matcher m = P_TAGS.matcher(s);
|
||||||
|
|
||||||
|
final StringBuffer buf = new StringBuffer();
|
||||||
|
while (m.find()) {
|
||||||
|
String replaceStr = m.group(1);
|
||||||
|
replaceStr = processTag(replaceStr);
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
|
||||||
|
// these get tallied in processTag
|
||||||
|
// (remember to reset before subsequent calls to filter method)
|
||||||
|
StringBuilder sBuilder = new StringBuilder(buf.toString());
|
||||||
|
for (String key : vTagCounts.keySet()) {
|
||||||
|
for (int ii = 0; ii < vTagCounts.get(key); ii++) {
|
||||||
|
sBuilder.append("</").append(key).append(">");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = sBuilder.toString();
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processRemoveBlanks(final String s) {
|
||||||
|
String result = s;
|
||||||
|
for (String tag : vRemoveBlanks) {
|
||||||
|
if(!P_REMOVE_PAIR_BLANKS.containsKey(tag)){
|
||||||
|
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
|
||||||
|
}
|
||||||
|
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
|
||||||
|
if(!P_REMOVE_SELF_BLANKS.containsKey(tag)){
|
||||||
|
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
|
||||||
|
}
|
||||||
|
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) {
|
||||||
|
Matcher m = regex_pattern.matcher(s);
|
||||||
|
return m.replaceAll(replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processTag(final String s) {
|
||||||
|
// ending tags
|
||||||
|
Matcher m = P_END_TAG.matcher(s);
|
||||||
|
if (m.find()) {
|
||||||
|
final String name = m.group(1).toLowerCase();
|
||||||
|
if (allowed(name)) {
|
||||||
|
if (!inArray(name, vSelfClosingTags)) {
|
||||||
|
if (vTagCounts.containsKey(name)) {
|
||||||
|
vTagCounts.put(name, vTagCounts.get(name) - 1);
|
||||||
|
return "</" + name + ">";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// starting tags
|
||||||
|
m = P_START_TAG.matcher(s);
|
||||||
|
if (m.find()) {
|
||||||
|
final String name = m.group(1).toLowerCase();
|
||||||
|
final String body = m.group(2);
|
||||||
|
String ending = m.group(3);
|
||||||
|
|
||||||
|
//debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
|
||||||
|
if (allowed(name)) {
|
||||||
|
StringBuilder params = new StringBuilder();
|
||||||
|
|
||||||
|
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
|
||||||
|
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
|
||||||
|
final List<String> paramNames = new ArrayList<>();
|
||||||
|
final List<String> paramValues = new ArrayList<>();
|
||||||
|
while (m2.find()) {
|
||||||
|
paramNames.add(m2.group(1)); //([a-z0-9]+)
|
||||||
|
paramValues.add(m2.group(3)); //(.*?)
|
||||||
|
}
|
||||||
|
while (m3.find()) {
|
||||||
|
paramNames.add(m3.group(1)); //([a-z0-9]+)
|
||||||
|
paramValues.add(m3.group(3)); //([^\"\\s']+)
|
||||||
|
}
|
||||||
|
|
||||||
|
String paramName, paramValue;
|
||||||
|
for (int ii = 0; ii < paramNames.size(); ii++) {
|
||||||
|
paramName = paramNames.get(ii).toLowerCase();
|
||||||
|
paramValue = paramValues.get(ii);
|
||||||
|
|
||||||
|
// debug( "paramName='" + paramName + "'" );
|
||||||
|
// debug( "paramValue='" + paramValue + "'" );
|
||||||
|
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
|
||||||
|
|
||||||
|
if (allowedAttribute(name, paramName)) {
|
||||||
|
if (inArray(paramName, vProtocolAtts)) {
|
||||||
|
paramValue = processParamProtocol(paramValue);
|
||||||
|
}
|
||||||
|
params.append(" ").append(paramName).append("=\"").append(paramValue).append("\"");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inArray(name, vSelfClosingTags)) {
|
||||||
|
ending = " /";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inArray(name, vNeedClosingTags)) {
|
||||||
|
ending = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ending == null || ending.length() < 1) {
|
||||||
|
if (vTagCounts.containsKey(name)) {
|
||||||
|
vTagCounts.put(name, vTagCounts.get(name) + 1);
|
||||||
|
} else {
|
||||||
|
vTagCounts.put(name, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ending = " /";
|
||||||
|
}
|
||||||
|
return "<" + name + params + ending + ">";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// comments
|
||||||
|
m = P_COMMENT.matcher(s);
|
||||||
|
if (!stripComment && m.find()) {
|
||||||
|
return "<" + m.group() + ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String processParamProtocol(String s) {
|
||||||
|
s = decodeEntities(s);
|
||||||
|
final Matcher m = P_PROTOCOL.matcher(s);
|
||||||
|
if (m.find()) {
|
||||||
|
final String protocol = m.group(1);
|
||||||
|
if (!inArray(protocol, vAllowedProtocols)) {
|
||||||
|
// bad protocol, turn into local anchor link instead
|
||||||
|
s = "#" + s.substring(protocol.length() + 1);
|
||||||
|
if (s.startsWith("#//")) {
|
||||||
|
s = "#" + s.substring(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String decodeEntities(String s) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
Matcher m = P_ENTITY.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String match = m.group(1);
|
||||||
|
final int decimal = Integer.decode(match);
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
s = buf.toString();
|
||||||
|
|
||||||
|
buf = new StringBuffer();
|
||||||
|
m = P_ENTITY_UNICODE.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String match = m.group(1);
|
||||||
|
final int decimal = Integer.valueOf(match, 16);
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
s = buf.toString();
|
||||||
|
|
||||||
|
buf = new StringBuffer();
|
||||||
|
m = P_ENCODE.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String match = m.group(1);
|
||||||
|
final int decimal = Integer.valueOf(match, 16);
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
s = buf.toString();
|
||||||
|
|
||||||
|
s = validateEntities(s);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String validateEntities(final String s) {
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
|
||||||
|
// validate entities throughout the string
|
||||||
|
Matcher m = P_VALID_ENTITIES.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String one = m.group(1); //([^&;]*)
|
||||||
|
final String two = m.group(2); //(?=(;|&|$))
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
|
||||||
|
return encodeQuotes(buf.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String encodeQuotes(final String s){
|
||||||
|
if(encodeQuotes){
|
||||||
|
StringBuffer buf = new StringBuffer();
|
||||||
|
Matcher m = P_VALID_QUOTES.matcher(s);
|
||||||
|
while (m.find()) {
|
||||||
|
final String one = m.group(1); //(>|^)
|
||||||
|
final String two = m.group(2); //([^<]+?)
|
||||||
|
final String three = m.group(3); //(<|$)
|
||||||
|
m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, """, two) + three));
|
||||||
|
}
|
||||||
|
m.appendTail(buf);
|
||||||
|
return buf.toString();
|
||||||
|
}else{
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkEntity(final String preamble, final String term) {
|
||||||
|
|
||||||
|
return CommonConstants.SEMICOLON.equals(term) && isValidEntity(preamble)
|
||||||
|
? '&' + preamble
|
||||||
|
: "&" + preamble;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidEntity(final String entity) {
|
||||||
|
return inArray(entity, vAllowedEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean inArray(final String s, final String[] array) {
|
||||||
|
for (String item : array) {
|
||||||
|
if (item != null && item.equals(s)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean allowed(final String name) {
|
||||||
|
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean allowedAttribute(final String name, final String paramName) {
|
||||||
|
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016-2019 人人开源 All rights reserved.
|
||||||
|
*
|
||||||
|
* https://www.renren.io
|
||||||
|
*
|
||||||
|
* 版权所有,侵权必究!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.xss;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import com.chushang.common.core.constant.CommonConstants;
|
||||||
|
import com.chushang.common.core.exception.ResultException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL过滤
|
||||||
|
*
|
||||||
|
* @author Mark sunlightcs@gmail.com
|
||||||
|
*/
|
||||||
|
public class SQLFilter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQL注入过滤
|
||||||
|
* @param str 待验证的字符串
|
||||||
|
*/
|
||||||
|
public static String sqlInject(String str){
|
||||||
|
if(StringUtils.isBlank(str)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
//去掉'|"|;|\字符
|
||||||
|
str = StringUtils.replace(str, CommonConstants.SINGLE_QUOTATION_MARKS, "");
|
||||||
|
str = StringUtils.replace(str, CommonConstants.DOUBLE_QUOTATION_MARKS, "");
|
||||||
|
str = StringUtils.replace(str, CommonConstants.SEMICOLON, "");
|
||||||
|
str = StringUtils.replace(str, CommonConstants.DOUBLE_SLASH, "");
|
||||||
|
|
||||||
|
//转换成小写
|
||||||
|
str = str.toLowerCase();
|
||||||
|
|
||||||
|
//非法字符
|
||||||
|
String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"};
|
||||||
|
|
||||||
|
//判断是否包含非法字符
|
||||||
|
for(String keyword : keywords){
|
||||||
|
if(str.contains(keyword)){
|
||||||
|
throw new ResultException("包含非法字符");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.chushang.common.core.xss;
|
||||||
|
|
||||||
|
import javax.validation.Constraint;
|
||||||
|
import javax.validation.Payload;
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义xss校验注解
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
|
||||||
|
@Constraint(validatedBy = { XssValidator.class })
|
||||||
|
public @interface Xss
|
||||||
|
{
|
||||||
|
String message()
|
||||||
|
|
||||||
|
default "不允许任何脚本运行";
|
||||||
|
|
||||||
|
Class<?>[] groups() default {};
|
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016-2019 人人开源 All rights reserved.
|
||||||
|
*
|
||||||
|
* https://www.renren.io
|
||||||
|
*
|
||||||
|
* 版权所有,侵权必究!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.xss;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XSS过滤
|
||||||
|
*
|
||||||
|
* @author Mark sunlightcs@gmail.com
|
||||||
|
*/
|
||||||
|
public class XssFilter implements Filter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig config) throws ServletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
|
||||||
|
(HttpServletRequest) request);
|
||||||
|
|
||||||
|
chain.doFilter(xssRequest, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,149 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016-2019 人人开源 All rights reserved.
|
||||||
|
*
|
||||||
|
* https://www.renren.io
|
||||||
|
*
|
||||||
|
* 版权所有,侵权必究!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.chushang.common.core.xss;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.util.IOUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
|
import javax.servlet.ReadListener;
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletRequestWrapper;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XSS过滤处理
|
||||||
|
*
|
||||||
|
* @author Mark sunlightcs@gmail.com
|
||||||
|
*/
|
||||||
|
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
//没被包装过的HttpServletRequest(特殊场景,需要自己过滤)
|
||||||
|
HttpServletRequest orgRequest;
|
||||||
|
//html过滤
|
||||||
|
private final static HTMLFilter htmlFilter = new HTMLFilter();
|
||||||
|
|
||||||
|
public XssHttpServletRequestWrapper(HttpServletRequest request) {
|
||||||
|
super(request);
|
||||||
|
orgRequest = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServletInputStream getInputStream() throws IOException {
|
||||||
|
//非json类型,直接返回
|
||||||
|
if(!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))){
|
||||||
|
return super.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
//为空,直接返回
|
||||||
|
String json = IOUtils.toString(super.getInputStream(),Charset.defaultCharset());
|
||||||
|
if (StringUtils.isBlank(json)) {
|
||||||
|
return super.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
//xss过滤
|
||||||
|
json = xssEncode(json);
|
||||||
|
final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8));
|
||||||
|
return new ServletInputStream() {
|
||||||
|
@Override
|
||||||
|
public boolean isFinished() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setReadListener(ReadListener readListener) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return bis.read();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getParameter(String name) {
|
||||||
|
String value = super.getParameter(xssEncode(name));
|
||||||
|
if (StringUtils.isNotBlank(value)) {
|
||||||
|
value = xssEncode(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getParameterValues(String name) {
|
||||||
|
String[] parameters = super.getParameterValues(name);
|
||||||
|
if (parameters == null || parameters.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < parameters.length; i++) {
|
||||||
|
parameters[i] = xssEncode(parameters[i]);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String,String[]> getParameterMap() {
|
||||||
|
Map<String,String[]> map = new LinkedHashMap<>();
|
||||||
|
Map<String,String[]> parameters = super.getParameterMap();
|
||||||
|
for (String key : parameters.keySet()) {
|
||||||
|
String[] values = parameters.get(key);
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
values[i] = xssEncode(values[i]);
|
||||||
|
}
|
||||||
|
map.put(key, values);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String name) {
|
||||||
|
String value = super.getHeader(xssEncode(name));
|
||||||
|
if (StringUtils.isNotBlank(value)) {
|
||||||
|
value = xssEncode(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String xssEncode(String input) {
|
||||||
|
return htmlFilter.filter(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最原始的request
|
||||||
|
*/
|
||||||
|
public HttpServletRequest getOrgRequest() {
|
||||||
|
return orgRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最原始的request
|
||||||
|
*/
|
||||||
|
public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
|
||||||
|
if (request instanceof XssHttpServletRequestWrapper) {
|
||||||
|
return ((XssHttpServletRequestWrapper) request).getOrgRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.chushang.common.core.xss;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义xss校验注解实现
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class XssValidator implements ConstraintValidator<Xss, String>
|
||||||
|
{
|
||||||
|
private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext)
|
||||||
|
{
|
||||||
|
if (StringUtils.isBlank(value))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !containsHtml(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean containsHtml(String value)
|
||||||
|
{
|
||||||
|
Pattern pattern = Pattern.compile(HTML_PATTERN);
|
||||||
|
Matcher matcher = pattern.matcher(value);
|
||||||
|
return matcher.matches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
com.chushang.common.core.util.SpringUtils, \
|
||||||
|
com.chushang.common.core.handler.GlobalExceptionHandler
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>chushang-common</artifactId>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>chushang-common-easy-es</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- es 相关 -> 拉取的评论数据, 均放置到es 中, 此项目进行es 拉取, manager 项目进行es 的获取-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.dromara.easy-es</groupId>
|
||||||
|
<artifactId>easy-es-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.elasticsearch.client</groupId>
|
||||||
|
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.elasticsearch</groupId>
|
||||||
|
<artifactId>elasticsearch</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.chushang.common.ee;
|
||||||
|
|
||||||
|
|
||||||
|
import org.dromara.easyes.starter.register.EsMapperScan;
|
||||||
|
|
||||||
|
@EsMapperScan("com.chushang.**.mapper")
|
||||||
|
public class EasyEsConfig {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
com.chushang.common.ee.EasyEsConfig
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<parent>
|
||||||
|
<artifactId>chushang-common</artifactId>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>chushang-common-excel</artifactId>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.chushang</groupId>
|
||||||
|
<artifactId>chushang-common-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>easyexcel</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.chushang.common.excel.listener;
|
||||||
|
|
||||||
|
import com.alibaba.excel.context.AnalysisContext;
|
||||||
|
import com.alibaba.excel.event.AnalysisEventListener;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class DataListener<T> extends AnalysisEventListener<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存的数据列表
|
||||||
|
*/
|
||||||
|
private final List<T> dataList = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(T data, AnalysisContext analysisContext) {
|
||||||
|
dataList.add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
package com.chushang.common.excel.listener;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import com.alibaba.excel.context.AnalysisContext;
|
||||||
|
import com.alibaba.excel.event.AnalysisEventListener;
|
||||||
|
import com.chushang.common.excel.service.ExcelImporter;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.beans.PropertyDescriptor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Slf4j
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class ImportListener<T> extends AnalysisEventListener<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认每隔3000条存储数据库
|
||||||
|
*/
|
||||||
|
private int batchCount = 3000;
|
||||||
|
/**
|
||||||
|
* 缓存的数据列表
|
||||||
|
*/
|
||||||
|
private List<T> list = new ArrayList<>();
|
||||||
|
/**
|
||||||
|
* 数据导入类
|
||||||
|
*/
|
||||||
|
private final ExcelImporter<T> importer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(T data, AnalysisContext analysisContext) {
|
||||||
|
log.info("read size {}", list.size());
|
||||||
|
if (isNotNull(data)) list.add(data);
|
||||||
|
// 达到BATCH_COUNT,则调用importer方法入库,防止数据几万条数据在内存,容易OOM
|
||||||
|
if (list.size() >= batchCount) {
|
||||||
|
// 调用importer方法
|
||||||
|
importer.save(list);
|
||||||
|
// 存储完成清理list
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
private boolean isNotNull(T data){
|
||||||
|
Class<?> clazz = data.getClass();
|
||||||
|
Field[] declaredFields = data.getClass().getDeclaredFields();
|
||||||
|
List<Object> collect = new ArrayList<>();
|
||||||
|
for (Field df : declaredFields) {
|
||||||
|
PropertyDescriptor pd = new PropertyDescriptor(df.getName(), clazz);
|
||||||
|
Object value = pd.getReadMethod().invoke(data);
|
||||||
|
collect.add(value);
|
||||||
|
}
|
||||||
|
return !CollectionUtil.hasNull(collect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
||||||
|
// 调用importer方法
|
||||||
|
importer.save(list);
|
||||||
|
// 存储完成清理list
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.chushang.common.excel.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ExcelImporter<T> {
|
||||||
|
void save(List<T> data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,177 @@
|
||||||
|
package com.chushang.common.excel.utils;
|
||||||
|
|
||||||
|
import com.alibaba.excel.EasyExcel;
|
||||||
|
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
|
||||||
|
import com.alibaba.excel.read.listener.ReadListener;
|
||||||
|
import com.chushang.common.core.exception.ResultException;
|
||||||
|
import com.chushang.common.core.util.StringUtils;
|
||||||
|
import com.chushang.common.excel.listener.DataListener;
|
||||||
|
import com.chushang.common.excel.listener.ImportListener;
|
||||||
|
import com.chushang.common.excel.service.ExcelImporter;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* by zhaowenyuan create 2021/12/27 10:49
|
||||||
|
* 表格工具
|
||||||
|
*/
|
||||||
|
public class ExcelUtils {
|
||||||
|
/**
|
||||||
|
* 导出文件时为Writer生成OutputStream.
|
||||||
|
*
|
||||||
|
* @param response response
|
||||||
|
* @return ""
|
||||||
|
*/
|
||||||
|
public static OutputStream getOutputStream(HttpServletResponse response){
|
||||||
|
try {
|
||||||
|
response.setContentType("application/vnd.ms-excel; charset=utf-8");
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType("application/octet-stream;charset=UTF-8");
|
||||||
|
response.setHeader("Content-Disposition", "attachment; filename=filename.xlsx");
|
||||||
|
response.setHeader("Pragma", "public");
|
||||||
|
response.setHeader("Cache-Control", "no-store");
|
||||||
|
response.addHeader("Cache-Control", "max-age=0");
|
||||||
|
return response.getOutputStream();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ResultException("导出excel表格失败!", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static <T> void exportList(HttpServletResponse response, Class<T> clazz, List<T> list, String sheetName){
|
||||||
|
OutputStream outputStream = getOutputStream(response);
|
||||||
|
EasyExcel.write(outputStream, clazz)
|
||||||
|
.sheet(sheetName)
|
||||||
|
.doWrite(list);
|
||||||
|
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取excel的所有sheet数据
|
||||||
|
*
|
||||||
|
* @param excel excel文件
|
||||||
|
* @return List<Object>
|
||||||
|
*/
|
||||||
|
public static <T> List<T> read(MultipartFile excel, Class<T> clazz) {
|
||||||
|
DataListener<T> dataListener = new DataListener<>();
|
||||||
|
ExcelReaderBuilder builder = getReaderBuilder(excel, dataListener, clazz);
|
||||||
|
if (builder == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
builder.doReadAll();
|
||||||
|
return dataListener.getDataList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取excel的指定sheet数据
|
||||||
|
*
|
||||||
|
* @param excel excel文件
|
||||||
|
* @param sheetNo sheet序号(从0开始)
|
||||||
|
* @return List<Object>
|
||||||
|
*/
|
||||||
|
public static <T> List<T> read(MultipartFile excel, int sheetNo, Class<T> clazz) {
|
||||||
|
return read(excel, sheetNo, 1, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取excel的指定sheet数据
|
||||||
|
*
|
||||||
|
* @param excel excel文件
|
||||||
|
* @param sheetNo sheet序号(从0开始)
|
||||||
|
* @param headRowNumber 表头行数
|
||||||
|
* @return List<Object>
|
||||||
|
*/
|
||||||
|
public static <T> List<T> read(MultipartFile excel, int sheetNo, int headRowNumber, Class<T> clazz) {
|
||||||
|
DataListener<T> dataListener = new DataListener<>();
|
||||||
|
ExcelReaderBuilder builder = getReaderBuilder(excel, dataListener, clazz);
|
||||||
|
if (builder == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
builder.sheet(sheetNo).headRowNumber(headRowNumber).doRead();
|
||||||
|
return dataListener.getDataList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> read(InputStream inputStream, Class<T> clazz){
|
||||||
|
DataListener<T> dataListener = new DataListener<>();
|
||||||
|
ExcelReaderBuilder builder = getReaderBuilder(inputStream, dataListener, clazz);
|
||||||
|
builder.doReadAll();
|
||||||
|
return dataListener.getDataList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取并导入数据
|
||||||
|
*
|
||||||
|
* @param excel excel文件
|
||||||
|
* @param importer 导入逻辑类
|
||||||
|
* @param <T> 泛型
|
||||||
|
*/
|
||||||
|
public static <T> void save(MultipartFile excel, ExcelImporter<T> importer, Class<T> clazz) {
|
||||||
|
ImportListener<T> importListener = new ImportListener<>(importer);
|
||||||
|
ExcelReaderBuilder builder = getReaderBuilder(excel, importListener, clazz);
|
||||||
|
if (builder != null) {
|
||||||
|
builder.doReadAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static <T> void save(List<T> rowList, ExcelImporter<T> importer) {
|
||||||
|
importer.save(rowList);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 导出excel
|
||||||
|
*
|
||||||
|
* @param response 响应类
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param sheetName sheet名
|
||||||
|
* @param dataList 数据列表
|
||||||
|
* @param clazz class类
|
||||||
|
* @param <T> 泛型
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
public static <T> void exportList(HttpServletResponse response, String fileName, String sheetName, List<T> dataList, Class<T> clazz) {
|
||||||
|
response.setContentType("application/vnd.ms-excel");
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
|
||||||
|
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
|
||||||
|
exportList(response, clazz, dataList, sheetName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ExcelReaderBuilder getReaderBuilder(InputStream inputStream, ReadListener<T> readListener, Class<T> clazz) {
|
||||||
|
return EasyExcel.read(inputStream, clazz, readListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取构建类
|
||||||
|
*
|
||||||
|
* @param excel excel文件
|
||||||
|
* @param readListener excel监听类
|
||||||
|
* @return ExcelReaderBuilder
|
||||||
|
*/
|
||||||
|
public static <T> ExcelReaderBuilder getReaderBuilder(MultipartFile excel, ReadListener<T> readListener, Class<T> clazz) {
|
||||||
|
String filename = excel.getOriginalFilename();
|
||||||
|
if (StringUtils.isEmpty(filename)) {
|
||||||
|
throw new RuntimeException("请上传文件!");
|
||||||
|
}
|
||||||
|
if ((!StringUtils.endsWithIgnoreCase(filename, ".xls")
|
||||||
|
&& !StringUtils.endsWithIgnoreCase(filename, ".xlsx"))) {
|
||||||
|
throw new RuntimeException("请上传正确的excel文件!");
|
||||||
|
}
|
||||||
|
InputStream inputStream;
|
||||||
|
try {
|
||||||
|
inputStream = new BufferedInputStream(excel.getInputStream());
|
||||||
|
return getReaderBuilder(inputStream, readListener, clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue