1. 初始化

This commit is contained in:
zhaowenyuan 2024-05-06 10:10:27 +08:00
commit b2c58c73b8
599 changed files with 59876 additions and 0 deletions

64
.gitignore vendored Normal file
View File

@ -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

204
Readme.md Normal file
View File

@ -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>
```

View File

@ -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

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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>-->
<!-- &lt;!&ndash; <redisAuth>${log.redis.auth}</redisAuth>&ndash;&gt;-->
<!-- </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>

24
chushang-centrale/pom.xml Normal file
View File

@ -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>

64
chushang-common/.gitignore vendored Normal file
View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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 "";
}

View File

@ -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 {};
}

View File

@ -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 {};
}

View File

@ -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 {};
}

View File

@ -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 {};
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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());
}
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -0,0 +1,9 @@
package com.chushang.common.canal.client.transfer;
public class MessageTransponders {
public static TransponderFactory defaultMessageTransponder() {
return new DefaultTransponderFactory();
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -0,0 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.chushang.common.canal.config.CanalConfig, \
com.chushang.common.canal.config.CanalClientAutoConfiguration

View File

@ -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>

View File

@ -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 "";
}

View File

@ -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());
};
}
}

View File

@ -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:";
}

View File

@ -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'
};
}

View File

@ -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 = "@";
}

View File

@ -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";
}

View File

@ -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_-]+)+$";
}

View File

@ -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";
}

View File

@ -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";
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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, ","));
}
}

View File

@ -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, ","));
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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), "");
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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));
}
}

View File

@ -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();
}
}

View File

@ -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是否为空 包含ListSetQueue
*
* @param coll 要判断的Collection
* @return true为空 false非空
*/
public static boolean isEmpty(Collection<?> coll)
{
return isNull(coll) || coll.isEmpty();
}
/**
* * 判断一个Collection是否非空包含ListSetQueue
*
* @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();
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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) };
}
}

View File

@ -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['\''] = "&#039;".toCharArray(); // 单引号
TEXT['"'] = "&#34;".toCharArray(); // 双引号
TEXT['&'] = "&#38;".toCharArray(); // &
TEXT['<'] = "&#60;".toCharArray(); // 小于号
TEXT['>'] = "&#62;".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();
}
}

View File

@ -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, "&amp;", result);
result = regexReplace(P_QUOTE, "&quot;", result);
result = regexReplace(P_LEFT_ARROW, "&lt;", result);
result = regexReplace(P_RIGHT_ARROW, "&gt;", 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, "&lt;$1", s);
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", 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, "&quot;", 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
: "&amp;" + 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));
}
}

View File

@ -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;
}
}

View File

@ -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 {};
}

View File

@ -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() {
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.chushang.common.core.util.SpringUtils, \
com.chushang.common.core.handler.GlobalExceptionHandler

View File

@ -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>

View File

@ -0,0 +1,8 @@
package com.chushang.common.ee;
import org.dromara.easyes.starter.register.EsMapperScan;
@EsMapperScan("com.chushang.**.mapper")
public class EasyEsConfig {
}

View File

@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.chushang.common.ee.EasyEsConfig

View File

@ -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>

View File

@ -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) {
}
}

View File

@ -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();
}
}

View File

@ -0,0 +1,8 @@
package com.chushang.common.excel.service;
import java.util.List;
public interface ExcelImporter<T> {
void save(List<T> data);
}

View File

@ -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