Compare commits
No commits in common. "4ed336b17ab4e2cdbccf650c299ab4beebc267fb" and "9cba8615efcd390626ee088d667a5f09c2fd6094" have entirely different histories.
4ed336b17a
...
9cba8615ef
384
ReadMe.md
|
@ -1,384 +0,0 @@
|
||||||
# Spring模板
|
|
||||||
|
|
||||||
每个服务下都有`Dockerfile`文件,几乎是写好的模板,如果要添加在这基础上即可。
|
|
||||||
|
|
||||||
- 基础包有
|
|
||||||
- 邮件发送
|
|
||||||
- WebSocket
|
|
||||||
- Minio
|
|
||||||
- Redis
|
|
||||||
- rabbitMq
|
|
||||||
- velocity
|
|
||||||
- IP地址查询
|
|
||||||
- knife4j
|
|
||||||
- 数据库多源配置
|
|
||||||
|
|
||||||
|
|
||||||
## 基础配置
|
|
||||||
|
|
||||||
### 配置文件详情
|
|
||||||
|
|
||||||
### 打包命令
|
|
||||||
|
|
||||||
命令解释:清理之前内容,打包,使用生产环境,跳过测试
|
|
||||||
|
|
||||||
```shell
|
|
||||||
mvn clean package -Pprod -DskipTests
|
|
||||||
```
|
|
||||||
|
|
||||||
#### SpringBoot配置文件
|
|
||||||
|
|
||||||
在开发中需要使用到开发环境、上线需要生产环境,在环境中设置`@profiles.active@`可以根据不同环境切换
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
spring:
|
|
||||||
profiles:
|
|
||||||
active: @profiles.active@
|
|
||||||
application:
|
|
||||||
name: service-admin
|
|
||||||
```
|
|
||||||
|
|
||||||
只需要在IDE中勾选相关环境即可
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
> 注意!!!
|
|
||||||
>
|
|
||||||
> 因为Java每次启动都需要生成target,有缓存在里面,很有可能明明选择了配置但是没有生效的情况。
|
|
||||||
>
|
|
||||||
> 解决办法就是,每次改变环境执行`mvn clean`或者点击IDE中`mvn clean`
|
|
||||||
>
|
|
||||||
> 
|
|
||||||
|
|
||||||
### Dockerfile配置
|
|
||||||
|
|
||||||
如果需要访问宿主机文件目录,这个是Docker内部地址
|
|
||||||
|
|
||||||
```dockerfile
|
|
||||||
# 程序内部挂在目录
|
|
||||||
VOLUME /home/server/uploads
|
|
||||||
```
|
|
||||||
|
|
||||||
#### IDE中配置
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 整体返回响应
|
|
||||||
|
|
||||||
整体返回响应如下
|
|
||||||
|
|
||||||
```java
|
|
||||||
// 状态码
|
|
||||||
private Integer code;
|
|
||||||
// 返回消息
|
|
||||||
private String message;
|
|
||||||
// 返回数据
|
|
||||||
private T data;
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
和分页返回
|
|
||||||
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 封装分页查询结果
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
@NoArgsConstructor
|
|
||||||
@Builder
|
|
||||||
public class ResultPage<T> implements Serializable {
|
|
||||||
// 当前页
|
|
||||||
private Integer pageNo;
|
|
||||||
// 每页记录数
|
|
||||||
private Integer pageSize;
|
|
||||||
// 总记录数
|
|
||||||
private long total;
|
|
||||||
// 当前页数据集合
|
|
||||||
private List<T> list;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
以及常用的枚举状态码(展示部分)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 多数据库源配置
|
|
||||||
|
|
||||||
开发中有时会使用到多个数据库源,这个配置也是来自MybatisPlus官方推荐的库
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<!-- 多数据库源插件 -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.baomidou</groupId>
|
|
||||||
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
|
|
||||||
<version>4.3.1</version>
|
|
||||||
</dependency>
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 配置简介
|
|
||||||
|
|
||||||
如果不需要多数据库,移除包之后将注释的部分放开,删除`dynamic`节点以下内容
|
|
||||||
|
|
||||||
```java
|
|
||||||
datasource:
|
|
||||||
# type: com.zaxxer.hikari.HikariDataSource
|
|
||||||
# driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
# url: jdbc:mysql://${bunny.datasource.host}:${bunny.datasource.port}/${bunny.datasource.sqlData}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
|
|
||||||
# username: ${bunny.datasource.username}
|
|
||||||
# password: ${bunny.datasource.password}
|
|
||||||
dynamic:
|
|
||||||
primary: master #设置默认的数据源或者数据源组,默认值即为master
|
|
||||||
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
|
|
||||||
grace-destroy: false #是否优雅关闭数据源,默认为false,设置为true时,关闭数据源时如果数据源中还存在活跃连接,至多等待10s后强制关闭
|
|
||||||
datasource:
|
|
||||||
master:
|
|
||||||
type: com.zaxxer.hikari.HikariDataSource
|
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
|
||||||
url: jdbc:mysql://${bunny.datasource.host}:${bunny.datasource.port}/${bunny.datasource.sqlData}?serverTimezone=GMT%2B8&useSSL=false&characterEncoding=utf-8&allowPublicKeyRetrieval=true
|
|
||||||
username: ${bunny.datasource.username}
|
|
||||||
password: ${bunny.datasource.password}
|
|
||||||
aop:
|
|
||||||
enabled: true
|
|
||||||
```
|
|
||||||
|
|
||||||
## 中间件配置
|
|
||||||
|
|
||||||
### mybatis-plus
|
|
||||||
|
|
||||||
#### 配置详情
|
|
||||||
|
|
||||||
配置乐观锁、防止全表删除、最大分页100页
|
|
||||||
|
|
||||||
`common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java`
|
|
||||||
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* Mybatis-Plus配置类
|
|
||||||
*/
|
|
||||||
@EnableTransactionManagement
|
|
||||||
@Configuration
|
|
||||||
@Slf4j
|
|
||||||
public class MybatisPlusConfig {
|
|
||||||
@Bean
|
|
||||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
|
||||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
|
||||||
// 分页插件
|
|
||||||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
|
||||||
paginationInnerInterceptor.setMaxLimit(100L);// 设置最大分页为100
|
|
||||||
interceptor.addInnerInterceptor(paginationInnerInterceptor);
|
|
||||||
// 乐观锁
|
|
||||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
|
||||||
// 防止全表删除
|
|
||||||
interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
|
|
||||||
|
|
||||||
return interceptor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
如果如要插入和修改时,自定义时间或者其它可以在这设置
|
|
||||||
|
|
||||||
`common/service-utils/src/main/java/cn/bunny/common/service/config/MyBatisPlusFieldConfig.java`
|
|
||||||
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 配置MP在修改和新增时的操作
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class MyBatisPlusFieldConfig implements MetaObjectHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用mp做添加操作时候,这个方法执行
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void insertFill(MetaObject metaObject) {
|
|
||||||
// 设置属性值
|
|
||||||
this.setFieldValByName("createTime", new Date(), metaObject);
|
|
||||||
this.setFieldValByName("updateTime", new Date(), metaObject);
|
|
||||||
this.setFieldValByName("deleteStatus", 1, metaObject);
|
|
||||||
if (BaseContext.getUsername() != null) {
|
|
||||||
this.setFieldValByName("createBy", BaseContext.getUsername(), metaObject);
|
|
||||||
this.setFieldValByName("updateBy", BaseContext.getUsername(), metaObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用mp做修改操作时候,这个方法执行
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void updateFill(MetaObject metaObject) {
|
|
||||||
this.setFieldValByName("updateTime", new Date(), metaObject);
|
|
||||||
this.setFieldValByName("updateBy", BaseContext.getUsername(), metaObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Redis
|
|
||||||
|
|
||||||
#### 配置详情
|
|
||||||
|
|
||||||
分别设置了过期30天、1小时、3分钟
|
|
||||||
|
|
||||||
`common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java`
|
|
||||||
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* * 配置Redis过期时间30天
|
|
||||||
* 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@Primary
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public CacheManager cacheManagerWithMouth(RedisConnectionFactory factory) {
|
|
||||||
// 配置序列化
|
|
||||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
|
||||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
|
||||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
|
||||||
.entryTtl(Duration.ofDays(30));
|
|
||||||
|
|
||||||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* * 配置redis过期时间3分钟
|
|
||||||
*
|
|
||||||
* @param factory
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public CacheManager cacheManagerWithMinutes(RedisConnectionFactory factory) {
|
|
||||||
// 配置序列化
|
|
||||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
|
||||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
|
||||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
|
||||||
.entryTtl(Duration.ofMinutes(3));
|
|
||||||
|
|
||||||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* * 配置Redis过期时间1小时
|
|
||||||
*
|
|
||||||
* @param factory
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public CacheManager cacheManagerWithHours(RedisConnectionFactory factory) {
|
|
||||||
// 配置序列化
|
|
||||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
|
||||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
|
||||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
|
||||||
.entryTtl(Duration.ofHours(1));
|
|
||||||
|
|
||||||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 使用详情
|
|
||||||
|
|
||||||
如果需要指定Redis配置,`cacheManager="Redis配置中方法名"`
|
|
||||||
|
|
||||||
使用springCache只需要在方法上加上下面代码
|
|
||||||
|
|
||||||
```java
|
|
||||||
@Cacheable(value = "TaskStatistics", key = "'ByDepartment::'+#departmentName",
|
|
||||||
cacheManager = "cacheManagerWithMinutes")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Minio
|
|
||||||
|
|
||||||
#### 配置详情
|
|
||||||
|
|
||||||
Minio没有给出SpringBoot的配置文件,下面是自定义实现
|
|
||||||
|
|
||||||
`module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java`
|
|
||||||
|
|
||||||
在配置文件中有这4个配置字段
|
|
||||||
|
|
||||||
```java
|
|
||||||
@Configuration
|
|
||||||
@ConfigurationProperties(prefix = "bunny.minio")
|
|
||||||
@ConditionalOnProperty(name = "bunny.minio.bucket-name")// 当属性有值时这个配置才生效
|
|
||||||
@Data
|
|
||||||
@Slf4j
|
|
||||||
public class MinioProperties {
|
|
||||||
private String endpointUrl;
|
|
||||||
private String accessKey;
|
|
||||||
private String secretKey;
|
|
||||||
private String bucketName;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public MinioClient minioClient() {
|
|
||||||
log.info("注册MinioClient...");
|
|
||||||
return MinioClient.builder().endpoint(endpointUrl).credentials(accessKey, secretKey).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
在项目中加入了Minio常用工具方法,对Minio二次封装
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 邮箱发送
|
|
||||||
|
|
||||||
邮箱发送配置的是动态邮件,发件人是动态的不是写死在配置文件中
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### 配置文件
|
|
||||||
|
|
||||||
如果不需要动态配置可以在`SpringBoot`配置文件中加入下面的配置
|
|
||||||
|
|
||||||
```properties
|
|
||||||
mail:
|
|
||||||
host: smtp.qq.com # 邮箱地址
|
|
||||||
port: 465 # 邮箱端口号
|
|
||||||
username: xxx@qq.com # 设置发送邮箱
|
|
||||||
password: xx # 如果是纯数字要加引号
|
|
||||||
default-encoding: UTF-8 # 设置编码格式
|
|
||||||
protocol: smtps
|
|
||||||
properties:
|
|
||||||
mail:
|
|
||||||
debug: true # 是否开启debug模式发送邮件
|
|
||||||
smtp:
|
|
||||||
auth: true
|
|
||||||
connectionTimeout: 5000 # 设置连接延迟
|
|
||||||
timeout: 5000 # 延迟时间
|
|
||||||
writeTimeout: 5000 # 写入邮箱延迟
|
|
||||||
allow8BitMime: true
|
|
||||||
sendPartial: true
|
|
||||||
ssl:
|
|
||||||
enabled: true # 是否开启SSL连接
|
|
||||||
socketFactory:
|
|
||||||
class: javax.net.ssl.SSLSocketFactory # 必要设置!!!
|
|
||||||
```
|
|
||||||
|
|
||||||
### SpringSecurity
|
|
||||||
|
|
||||||
因为项目做的是开发模板,在admin模板中集成了安全框架
|
|
||||||
|
|
||||||
`module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java`
|
|
||||||
|
|
||||||
在这个文件最下面是排除路径,不需要Security检测的路径,根据自己需求进行修改,因为整合了knife4j在测试时,需要放开swagger配置响应请求等。
|
|
||||||
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* * 排出鉴定路径
|
|
||||||
*
|
|
||||||
* @return WebSecurityCustomizer
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
|
||||||
String[] annotations = {"/", "/test/**", "/diagram-viewer/**", "/editor-app/**", "/*.html",
|
|
||||||
"/*/*/noAuth/**", "/*/noAuth/**", "/favicon.ico", "/swagger-resources/**", "/webjars/**",
|
|
||||||
"/v3/**", "/swagger-ui.html/**", "/doc.html"};
|
|
||||||
return web -> web.ignoring().requestMatchers(annotations);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
|
@ -19,6 +19,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
public class MybatisPlusConfig {
|
public class MybatisPlusConfig {
|
||||||
@Bean
|
@Bean
|
||||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||||
|
log.info("MybatisPlusInterceptor===>注入Mybatis-Plus配置...");
|
||||||
|
|
||||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||||
// 分页插件
|
// 分页插件
|
||||||
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
|
||||||
|
|
|
@ -13,7 +13,6 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.cache.CacheManager;
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Primary;
|
|
||||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
@ -41,6 +40,8 @@ public class RedisConfiguration {
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
|
public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory connectionFactory) {
|
||||||
|
log.info("RedisConfiguration===>使用StringRedisSerializer序列化为字符串");
|
||||||
|
|
||||||
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
|
||||||
redisTemplate.setConnectionFactory(connectionFactory);
|
redisTemplate.setConnectionFactory(connectionFactory);
|
||||||
// 设置key序列化为string
|
// 设置key序列化为string
|
||||||
|
@ -54,13 +55,13 @@ public class RedisConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* * 配置Redis过期时间30天
|
|
||||||
* 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题
|
* 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
@Primary
|
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public CacheManager cacheManagerWithMouth(RedisConnectionFactory factory) {
|
public CacheManager cacheManager(RedisConnectionFactory factory) {
|
||||||
|
log.info("RedisConfiguration===>解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题");
|
||||||
|
|
||||||
// 配置序列化
|
// 配置序列化
|
||||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
||||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
||||||
|
@ -70,46 +71,12 @@ public class RedisConfiguration {
|
||||||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* * 配置redis过期时间3分钟
|
|
||||||
*
|
|
||||||
* @param factory
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public CacheManager cacheManagerWithMinutes(RedisConnectionFactory factory) {
|
|
||||||
// 配置序列化
|
|
||||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
|
||||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
|
||||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
|
||||||
.entryTtl(Duration.ofMinutes(3));
|
|
||||||
|
|
||||||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* * 配置Redis过期时间1小时
|
|
||||||
*
|
|
||||||
* @param factory
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
public CacheManager cacheManagerWithHours(RedisConnectionFactory factory) {
|
|
||||||
// 配置序列化
|
|
||||||
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
|
|
||||||
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
|
|
||||||
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jsonRedisSerializer()))
|
|
||||||
.entryTtl(Duration.ofHours(1));
|
|
||||||
|
|
||||||
return RedisCacheManager.builder(factory).cacheDefaults(config).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 指定的日期模式
|
* 指定的日期模式
|
||||||
*/
|
*/
|
||||||
public Jackson2JsonRedisSerializer<Object> jsonRedisSerializer() {
|
public Jackson2JsonRedisSerializer<Object> jsonRedisSerializer() {
|
||||||
|
log.info("RedisConfiguration===>指定的日期模式");
|
||||||
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
// 设置ObjectMapper访问权限
|
// 设置ObjectMapper访问权限
|
||||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
|
|
|
@ -30,6 +30,8 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
|
// log.info("WebMvcConfiguration===>开始注册自定义拦截器...");
|
||||||
|
|
||||||
// TODO Spring Security 和这个拦截器任选一个
|
// TODO Spring Security 和这个拦截器任选一个
|
||||||
String[] excludeList = {"/", "/test/**", "/*.html", "/*/*/noAuth/**", "/*/noAuth/**", "/favicon.ico",
|
String[] excludeList = {"/", "/test/**", "/*.html", "/*/*/noAuth/**", "/*/noAuth/**", "/favicon.ico",
|
||||||
"/swagger-resources/**", "/swagger-ui.html/**", "/admin/login", "/v3/**", "/api/**"};
|
"/swagger-resources/**", "/swagger-ui.html/**", "/admin/login", "/v3/**", "/api/**"};
|
||||||
|
|
|
@ -10,7 +10,6 @@ import cn.bunny.vo.system.login.LoginVo;
|
||||||
import com.alibaba.fastjson2.JSONObject;
|
import com.alibaba.fastjson2.JSONObject;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.NonNull;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
@ -21,10 +20,6 @@ import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
|
||||||
* * 微服务请求其它模块找不到Token,无法从线程中获取值
|
|
||||||
* 传递请求头,在微服务中
|
|
||||||
*/
|
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class UserTokenInterceptor implements HandlerInterceptor {
|
public class UserTokenInterceptor implements HandlerInterceptor {
|
||||||
|
@ -32,7 +27,8 @@ public class UserTokenInterceptor implements HandlerInterceptor {
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preHandle(HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||||
|
log.info("UserTokenInterceptor===>设置拦截器");
|
||||||
String token = request.getHeader("token");
|
String token = request.getHeader("token");
|
||||||
Map<String, Object> mapByToken = JwtHelper.getMapByToken(token);
|
Map<String, Object> mapByToken = JwtHelper.getMapByToken(token);
|
||||||
LoginVo loginVo = JSONObject.parseObject(JSONObject.toJSONString(mapByToken), LoginVo.class);
|
LoginVo loginVo = JSONObject.parseObject(JSONObject.toJSONString(mapByToken), LoginVo.class);
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package cn.bunny.common.service.properties;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConfigurationProperties(prefix = "bunny.snowflake")
|
||||||
|
@Data
|
||||||
|
public class SnowflakeProperties {
|
||||||
|
// 数据中心id
|
||||||
|
private Long datacenterId;
|
||||||
|
// 数据中心id位数
|
||||||
|
private Long datacenterBits;
|
||||||
|
// 机器id
|
||||||
|
private Long workerId;
|
||||||
|
// 机器id位数
|
||||||
|
private Long workerBits;
|
||||||
|
// 序列id所占位数
|
||||||
|
private Long sequenceBits;
|
||||||
|
// 时间戳起始点(毫秒)
|
||||||
|
private Long twepoch;
|
||||||
|
// 单次批量生成id的最大数量
|
||||||
|
private Integer maxBatchCount;
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
package cn.bunny.common.service.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.NameValuePair;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||||
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpPut;
|
||||||
|
import org.apache.http.conn.ClientConnectionManager;
|
||||||
|
import org.apache.http.conn.scheme.Scheme;
|
||||||
|
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||||
|
import org.apache.http.conn.ssl.SSLSocketFactory;
|
||||||
|
import org.apache.http.entity.ByteArrayEntity;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
|
import org.apache.http.message.BasicNameValuePair;
|
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext;
|
||||||
|
import javax.net.ssl.TrustManager;
|
||||||
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class HttpUtil {
|
||||||
|
public static HttpResponse doGet(String host, String path, String method, Map<String, String> headers, Map<String, String> querys) throws Exception {
|
||||||
|
HttpClient httpClient = wrapClient(host);
|
||||||
|
|
||||||
|
HttpGet request = new HttpGet(buildUrl(host, path, querys));
|
||||||
|
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||||
|
request.addHeader(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient.execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, Map<String, String> bodys) throws Exception {
|
||||||
|
HttpClient httpClient = wrapClient(host);
|
||||||
|
|
||||||
|
HttpPost request = new HttpPost(buildUrl(host, path, querys));
|
||||||
|
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||||
|
request.addHeader(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bodys != null) {
|
||||||
|
List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
|
||||||
|
|
||||||
|
for (String key : bodys.keySet()) {
|
||||||
|
nameValuePairList.add(new BasicNameValuePair(key, bodys.get(key)));
|
||||||
|
}
|
||||||
|
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nameValuePairList, "utf-8");
|
||||||
|
formEntity.setContentType("application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
|
request.setEntity(formEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient.execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, String body) throws Exception {
|
||||||
|
HttpClient httpClient = wrapClient(host);
|
||||||
|
|
||||||
|
HttpPost request = new HttpPost(buildUrl(host, path, querys));
|
||||||
|
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||||
|
request.addHeader(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(body)) {
|
||||||
|
request.setEntity(new StringEntity(body, "utf-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient.execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HttpResponse doPost(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, byte[] body) throws Exception {
|
||||||
|
HttpClient httpClient = wrapClient(host);
|
||||||
|
|
||||||
|
HttpPost request = new HttpPost(buildUrl(host, path, querys));
|
||||||
|
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||||
|
request.addHeader(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body != null) {
|
||||||
|
request.setEntity(new ByteArrayEntity(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient.execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HttpResponse doPut(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, String body) throws Exception {
|
||||||
|
HttpClient httpClient = wrapClient(host);
|
||||||
|
|
||||||
|
HttpPut request = new HttpPut(buildUrl(host, path, querys));
|
||||||
|
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||||
|
request.addHeader(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(body)) {
|
||||||
|
request.setEntity(new StringEntity(body, "utf-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient.execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HttpResponse doPut(String host, String path, String method, Map<String, String> headers, Map<String, String> querys, byte[] body) throws Exception {
|
||||||
|
HttpClient httpClient = wrapClient(host);
|
||||||
|
|
||||||
|
HttpPut request = new HttpPut(buildUrl(host, path, querys));
|
||||||
|
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||||
|
request.addHeader(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (body != null) {
|
||||||
|
request.setEntity(new ByteArrayEntity(body));
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient.execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static HttpResponse doDelete(String host, String path, String method, Map<String, String> headers, Map<String, String> querys) throws Exception {
|
||||||
|
HttpClient httpClient = wrapClient(host);
|
||||||
|
|
||||||
|
HttpDelete request = new HttpDelete(buildUrl(host, path, querys));
|
||||||
|
for (Map.Entry<String, String> e : headers.entrySet()) {
|
||||||
|
request.addHeader(e.getKey(), e.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient.execute(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildUrl(String host, String path, Map<String, String> querys) throws UnsupportedEncodingException {
|
||||||
|
StringBuilder sbUrl = new StringBuilder();
|
||||||
|
sbUrl.append(host);
|
||||||
|
if (!StringUtils.isBlank(path)) {
|
||||||
|
sbUrl.append(path);
|
||||||
|
}
|
||||||
|
if (null != querys) {
|
||||||
|
StringBuilder sbQuery = new StringBuilder();
|
||||||
|
for (Map.Entry<String, String> query : querys.entrySet()) {
|
||||||
|
if (!sbQuery.isEmpty()) {
|
||||||
|
sbQuery.append("&");
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(query.getKey()) && !StringUtils.isBlank(query.getValue())) {
|
||||||
|
sbQuery.append(query.getValue());
|
||||||
|
}
|
||||||
|
if (!StringUtils.isBlank(query.getKey())) {
|
||||||
|
sbQuery.append(query.getKey());
|
||||||
|
if (!StringUtils.isBlank(query.getValue())) {
|
||||||
|
sbQuery.append("=");
|
||||||
|
sbQuery.append(URLEncoder.encode(query.getValue(), StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!sbQuery.isEmpty()) {
|
||||||
|
sbUrl.append("?").append(sbQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sbUrl.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HttpClient wrapClient(String host) {
|
||||||
|
HttpClient httpClient = new DefaultHttpClient();
|
||||||
|
if (host.startsWith("https://")) {
|
||||||
|
sslClient(httpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sslClient(HttpClient httpClient) {
|
||||||
|
try {
|
||||||
|
SSLContext ctx = SSLContext.getInstance("TLS");
|
||||||
|
X509TrustManager tm = new X509TrustManager() {
|
||||||
|
public X509Certificate[] getAcceptedIssuers() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkClientTrusted(X509Certificate[] xcs, String str) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void checkServerTrusted(X509Certificate[] xcs, String str) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ctx.init(null, new TrustManager[]{tm}, null);
|
||||||
|
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
|
||||||
|
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
|
||||||
|
ClientConnectionManager ccm = httpClient.getConnectionManager();
|
||||||
|
SchemeRegistry registry = ccm.getSchemeRegistry();
|
||||||
|
registry.register(new Scheme("https", 443, ssf));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
package cn.bunny.common.service.utils;
|
package cn.bunny.common.service.utils;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.lionsoul.ip2region.xdb.Searcher;
|
import org.lionsoul.ip2region.xdb.Searcher;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.util.FileCopyUtils;
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
@ -10,7 +9,6 @@ import java.io.InputStream;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class IpUtil {
|
public class IpUtil {
|
||||||
private static Searcher searcher;
|
private static Searcher searcher;
|
||||||
|
|
||||||
|
@ -34,7 +32,7 @@ public class IpUtil {
|
||||||
byte[] bytes = FileCopyUtils.copyToByteArray(inputStream);
|
byte[] bytes = FileCopyUtils.copyToByteArray(inputStream);
|
||||||
searcher = Searcher.newWithBuffer(bytes);
|
searcher = Searcher.newWithBuffer(bytes);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
log.error(exception.getMessage());
|
exception.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +66,8 @@ public class IpUtil {
|
||||||
return splitIpInfo[0];
|
return splitIpInfo[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception exception) {
|
} catch (Exception e) {
|
||||||
log.error(exception.getMessage());
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,149 @@
|
||||||
|
package cn.bunny.common.service.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.bunny.common.service.properties.SnowflakeProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class SnowflakeIdGenerator {
|
||||||
|
// 数据中心id
|
||||||
|
private final long datacenterId;
|
||||||
|
// 数据中心id位数
|
||||||
|
private final long datacenterBits;
|
||||||
|
// 机器id
|
||||||
|
private final long workerId;
|
||||||
|
// 机器id位数
|
||||||
|
private final long workerBits;
|
||||||
|
// 序列id所占位数
|
||||||
|
private final long sequenceBits;
|
||||||
|
// 时间戳起始点(毫秒)
|
||||||
|
private final long twepoch;
|
||||||
|
|
||||||
|
// 数据中心最大id
|
||||||
|
private final long maxDatacenterId;
|
||||||
|
// 机器最大id
|
||||||
|
private final long maxWorkerId;
|
||||||
|
// 最大序列号
|
||||||
|
private final long maxSequence;
|
||||||
|
|
||||||
|
// 机器id左移位数
|
||||||
|
private final long workerIdShift;
|
||||||
|
// 数据中心id左移位数
|
||||||
|
private final long datacenterIdShift;
|
||||||
|
// 毫秒数左移位数
|
||||||
|
private final long timestampLeftShift;
|
||||||
|
|
||||||
|
// 单次批量生成id的最大数量
|
||||||
|
private final int maxBatchCount;
|
||||||
|
|
||||||
|
// 序列号
|
||||||
|
private long sequence = 0L;
|
||||||
|
// 上一次时间戳
|
||||||
|
private long lastTimestamp = -1L;
|
||||||
|
|
||||||
|
public SnowflakeIdGenerator(SnowflakeProperties properties) {
|
||||||
|
// 数据中心id
|
||||||
|
this.datacenterId = properties.getDatacenterId();
|
||||||
|
// 数据中心id位数
|
||||||
|
this.datacenterBits = properties.getDatacenterBits();
|
||||||
|
// 机器id
|
||||||
|
this.workerId = properties.getWorkerId();
|
||||||
|
// 机器id位数
|
||||||
|
this.workerBits = properties.getWorkerBits();
|
||||||
|
// 序列id所占位数
|
||||||
|
this.sequenceBits = properties.getSequenceBits();
|
||||||
|
// 时间戳起始点(毫秒)
|
||||||
|
this.twepoch = properties.getTwepoch();
|
||||||
|
// 数据中心最大id
|
||||||
|
this.maxDatacenterId = -1L ^ (-1L << properties.getDatacenterBits());
|
||||||
|
// 机器最大id
|
||||||
|
this.maxWorkerId = -1L ^ (-1L << properties.getWorkerBits());
|
||||||
|
// 最大序列号
|
||||||
|
this.maxSequence = -1L ^ (-1L << properties.getSequenceBits());
|
||||||
|
|
||||||
|
this.workerIdShift = properties.getSequenceBits();
|
||||||
|
// 数据中心id左移位数
|
||||||
|
this.datacenterIdShift = properties.getSequenceBits() + properties.getWorkerBits();
|
||||||
|
// 毫秒数左移位数
|
||||||
|
this.timestampLeftShift = properties.getSequenceBits() + properties.getWorkerBits() + properties.getSequenceBits();
|
||||||
|
// 单次批量生成id的最大数量
|
||||||
|
this.maxBatchCount = properties.getMaxBatchCount();
|
||||||
|
|
||||||
|
// 校验datacenterId和workerId是否超出最大值
|
||||||
|
if (datacenterId > maxDatacenterId || datacenterId < 0) {
|
||||||
|
throw new IllegalArgumentException(String.format("数据中心Id不能大于%d或小于0", maxDatacenterId));
|
||||||
|
}
|
||||||
|
if (workerId > maxWorkerId || workerId < 0) {
|
||||||
|
throw new IllegalArgumentException(String.format("机器Id不能大于%d或小于0", maxWorkerId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id生成方法(单个)
|
||||||
|
*/
|
||||||
|
public synchronized long nextId() {
|
||||||
|
// 获取当前时间的毫秒数
|
||||||
|
long timestamp = currentTime();
|
||||||
|
|
||||||
|
// 判断时钟是否回拨
|
||||||
|
if (timestamp < lastTimestamp) {
|
||||||
|
throw new RuntimeException(String.format("时钟回拨,回拨毫秒数:%d", lastTimestamp - timestamp));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置序列号
|
||||||
|
if (lastTimestamp == timestamp) {
|
||||||
|
// 设置序列号递增,如果当前毫秒内序列号已经达到最大值,则直到下一毫秒在重新从0开始计算序列号
|
||||||
|
sequence = (sequence + 1) & maxSequence;
|
||||||
|
if (sequence == 0) {
|
||||||
|
timestamp = tilNextMillis(lastTimestamp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sequence = 0L;
|
||||||
|
}
|
||||||
|
lastTimestamp = timestamp;
|
||||||
|
|
||||||
|
// 计算id
|
||||||
|
return ((timestamp - twepoch) << timestampLeftShift) |
|
||||||
|
(datacenterId << datacenterIdShift) |
|
||||||
|
(workerId << workerIdShift) |
|
||||||
|
sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id生成方法(批量)
|
||||||
|
*/
|
||||||
|
public synchronized List<Long> nextIds(int count) {
|
||||||
|
if (count > maxBatchCount || count < 0) {
|
||||||
|
throw new IllegalArgumentException(String.format("批量生成id的数量不能大于%d或小于0", maxBatchCount));
|
||||||
|
}
|
||||||
|
List<Long> ids = new ArrayList<>(count);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
ids.add(nextId());
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 循环等待直至获取到新的毫秒时间戳
|
||||||
|
* 确保生成的时间戳总是向前移动的,即使在相同的毫秒内请求多个ID时也能保持唯一性。
|
||||||
|
*/
|
||||||
|
private long tilNextMillis(long lastTimestamp) {
|
||||||
|
long timestamp = currentTime();
|
||||||
|
// 循环等待直至获取到新的毫秒时间戳
|
||||||
|
while (timestamp <= lastTimestamp) {
|
||||||
|
timestamp = currentTime();
|
||||||
|
}
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前时间的毫秒数
|
||||||
|
*/
|
||||||
|
private long currentTime() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.bunny.pojo.result;
|
package cn.bunny.vo.page;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 109 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 64 KiB |
|
@ -1,3 +1,5 @@
|
||||||
|
Configuration example
|
||||||
|
|
||||||
mail:
|
mail:
|
||||||
host: smtp.qq.com # 邮箱地址
|
host: smtp.qq.com # 邮箱地址
|
||||||
port: 465 # 邮箱端口号
|
port: 465 # 邮箱端口号
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>cn.bunny</groupId>
|
||||||
|
<artifactId>module</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>module-task</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>module-task</name>
|
||||||
|
<url>https://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.bunny.service.task;
|
package cn.bunny.module.task;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
|
@ -0,0 +1,27 @@
|
||||||
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>cn.bunny</groupId>
|
||||||
|
<artifactId>module</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>module-websocket</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>module-websocket</name>
|
||||||
|
<url>https://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- websocket -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -1,4 +1,4 @@
|
||||||
package cn.bunny.service.ws;
|
package cn.bunny.module.websocket;
|
||||||
|
|
||||||
import jakarta.websocket.OnClose;
|
import jakarta.websocket.OnClose;
|
||||||
import jakarta.websocket.OnMessage;
|
import jakarta.websocket.OnMessage;
|
|
@ -0,0 +1,13 @@
|
||||||
|
package cn.bunny.module.websocket.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class WebSocketConfiguration {
|
||||||
|
@Bean
|
||||||
|
public ServerEndpointExporter endpointExporter() {
|
||||||
|
return new ServerEndpointExporter();
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,9 @@
|
||||||
<module>module-minio</module>
|
<module>module-minio</module>
|
||||||
<module>module-mail</module>
|
<module>module-mail</module>
|
||||||
<module>module-rabbitMQ</module>
|
<module>module-rabbitMQ</module>
|
||||||
|
<module>module-websocket</module>
|
||||||
<module>spring-security</module>
|
<module>spring-security</module>
|
||||||
|
<module>module-task</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
|
@ -18,6 +18,4 @@ COPY target/*.jar /home/bunny/app.jar
|
||||||
ENTRYPOINT ["java","-jar","/home/bunny/app.jar"]
|
ENTRYPOINT ["java","-jar","/home/bunny/app.jar"]
|
||||||
|
|
||||||
#暴露 8800 端口
|
#暴露 8800 端口
|
||||||
EXPOSE 8080
|
EXPOSE 8800
|
||||||
|
|
||||||
# maven 打包:mvn clean package -Pprod -DskipTests
|
|
|
@ -70,11 +70,6 @@
|
||||||
<groupId>org.aspectj</groupId>
|
<groupId>org.aspectj</groupId>
|
||||||
<artifactId>aspectjweaver</artifactId>
|
<artifactId>aspectjweaver</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- websocket -->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<!-- 多数据库源插件 -->
|
<!-- 多数据库源插件 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
|
|
|
@ -1,8 +1,3 @@
|
||||||
# 线上禁用文档
|
|
||||||
knife4j:
|
|
||||||
enable: true
|
|
||||||
production: true
|
|
||||||
|
|
||||||
bunny:
|
bunny:
|
||||||
datasource:
|
datasource:
|
||||||
host: 192.168.3.100
|
host: 192.168.3.100
|
||||||
|
|
|
@ -68,3 +68,12 @@ bunny:
|
||||||
accessKey: ${bunny.minio.accessKey}
|
accessKey: ${bunny.minio.accessKey}
|
||||||
secretKey: ${bunny.minio.secretKey}
|
secretKey: ${bunny.minio.secretKey}
|
||||||
bucket-name: ${bunny.minio.bucket-name}
|
bucket-name: ${bunny.minio.bucket-name}
|
||||||
|
|
||||||
|
snowflake:
|
||||||
|
datacenterBits: 5 # 数据中心id位数
|
||||||
|
workerBits: 5 # 机器id位数
|
||||||
|
sequenceBits: 12 # 序列id所占位数
|
||||||
|
datacenterId: 1 # 数据中心id,范围0-2^5-1
|
||||||
|
workerId: 1 # 机器id,范围0-2^5-1
|
||||||
|
twepoch: 1704038400000 # 时间戳起始点(2024-01-01 00::00:00 的毫秒数)
|
||||||
|
maxBatchCount: 100000 #单次批量生成id的最大数量 默认10万
|