diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..7528da8 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,412 @@ +# Spring微服务模板 + +每个服务下都有`Dockerfile`文件,几乎是写好的模板,如果要添加在这基础上即可。 + +- 基础包有 + - 微服务基础功能,openFein + - 邮件发送 + - WebSocket + - Minio + - Redis + - rabbitMq + - velocity + - IP地址查询 + - knife4j + - 数据库多源配置 + +- 启动类一共有两个,网关是必不可少的 + 1. web端 + 2. admin端 + 3. gateway网关 + +## 基础配置 + +### 微服务模块 + +微服务请求其它模块无法从线程中获取到值,将用户Token放在请求头中这样从请求头中获取Token即可 + +`common/common-service/src/main/java/cn/bunny/common/service/interceptor/UserTokenFeignInterceptor.java` + +```java +/** + * * 微服务请求其它模块找不到Token,无法从线程中获取值 + * 传递请求头,在微服务中 + */ +public class UserTokenFeignInterceptor implements RequestInterceptor { + @Override + public void apply(RequestTemplate requestTemplate) { + ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + assert requestAttributes != null; + HttpServletRequest request = requestAttributes.getRequest(); + String token = request.getHeader("token"); + requestTemplate.header("token", token); + } +} +``` + +### 配置文件详情 + +### 打包命令 + +命令解释:清理之前内容,打包,使用生产环境,跳过测试 + +```shell +mvn clean package -Pprod -DskipTests +``` + +#### SpringBoot配置文件 + +在开发中需要使用到开发环境、上线需要生产环境,在环境中设置`@profiles.active@`可以根据不同环境切换 + +```yaml +spring: + profiles: + active: @profiles.active@ + application: + name: service-admin +``` + +只需要在IDE中勾选相关环境即可 + +![image-20240822093552802](./images/image-20240822093552802.png) + +> 注意!!! +> +> 因为Java每次启动都需要生成target,有缓存在里面,很有可能明明选择了配置但是没有生效的情况。 +> +> 解决办法就是,每次改变环境执行`mvn clean`或者点击IDE中`mvn clean` +> +> ![image-20240822093803078](./images/image-20240822093803078.png) + +### Dockerfile配置 + +如果需要访问宿主机文件目录,这个是Docker内部地址 + +```dockerfile +# 程序内部挂在目录 +VOLUME /home/server/uploads +``` + +#### IDE中配置 + +![image-20240822094021339](./images/image-20240822094021339.png) + +![image-20240822094000542](./images/image-20240822094000542.png) + +### 整体返回响应 + +整体返回响应如下 + +```java +// 状态码 +private Integer code; +// 返回消息 +private String message; +// 返回数据 +private T data; +``` + +![image-20240822092441020](./images/image-20240822092441020.png) + +和分页返回 + +```java +/** + * 封装分页查询结果 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ResultPage implements Serializable { + // 当前页 + private Integer pageNo; + // 每页记录数 + private Integer pageSize; + // 总记录数 + private long total; + // 当前页数据集合 + private List list; +} +``` + +以及常用的枚举状态码(展示部分) + +![image-20240822092510151](./images/image-20240822092510151.png) + +### 多数据库源配置 + +开发中有时会使用到多个数据库源,这个配置也是来自MybatisPlus官方推荐的库 + +```xml + + + com.baomidou + dynamic-datasource-spring-boot3-starter + 4.3.1 + +``` + +#### 配置简介 + +如果不需要多数据库,移除包之后将注释的部分放开,删除`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二次封装 + +![image-20240822091720866](./images/image-20240822091720866.png) + +### 邮箱发送 + +邮箱发送配置的是动态邮件,发件人是动态的不是写死在配置文件中 + +![image-20240822091810597](./images/image-20240822091810597.png) + +#### 配置文件 + +如果不需要动态配置可以在`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); +} +``` + diff --git a/common/common-service/src/main/java/cn/bunny/common/service/interceptor/UserTokenFeignInterceptor.java b/common/common-service/src/main/java/cn/bunny/common/service/interceptor/UserTokenFeignInterceptor.java index 20026cb..bdab317 100644 --- a/common/common-service/src/main/java/cn/bunny/common/service/interceptor/UserTokenFeignInterceptor.java +++ b/common/common-service/src/main/java/cn/bunny/common/service/interceptor/UserTokenFeignInterceptor.java @@ -7,6 +7,7 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; /** + * * 微服务请求其它模块找不到Token,无法从线程中获取值 * 传递请求头,在微服务中 */ public class UserTokenFeignInterceptor implements RequestInterceptor { diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java b/common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java index 519a465..a892f53 100644 --- a/common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java +++ b/common/service-utils/src/main/java/cn/bunny/common/service/config/MybatisPlusConfig.java @@ -19,12 +19,10 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { - log.info("MybatisPlusInterceptor===>注入Mybatis-Plus配置..."); - MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 分页插件 PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); - paginationInnerInterceptor.setMaxLimit(100L);// ? 设置最大分页为100 + paginationInnerInterceptor.setMaxLimit(100L);// 设置最大分页为100 interceptor.addInnerInterceptor(paginationInnerInterceptor); // 乐观锁 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java b/common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java index 1b5c8d7..34a15b3 100644 --- a/common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java +++ b/common/service-utils/src/main/java/cn/bunny/common/service/config/RedisConfiguration.java @@ -13,6 +13,7 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import lombok.extern.slf4j.Slf4j; import org.springframework.cache.CacheManager; 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.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; @@ -55,13 +56,13 @@ public class RedisConfiguration { } /** + * * 配置Redis过期时间30天 * 解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题 */ @Bean + @Primary @SuppressWarnings("all") - public CacheManager cacheManager(RedisConnectionFactory factory) { - log.info("RedisConfiguration===>解决cache(@Cacheable)把数据缓存到redis中的value是乱码问题"); - + public CacheManager cacheManagerWithMouth(RedisConnectionFactory factory) { // 配置序列化 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) @@ -71,6 +72,43 @@ public class RedisConfiguration { 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(); + } + + /** * 指定的日期模式 */ diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/properties/SnowflakeProperties.java b/common/service-utils/src/main/java/cn/bunny/common/service/properties/SnowflakeProperties.java deleted file mode 100644 index 19b0057..0000000 --- a/common/service-utils/src/main/java/cn/bunny/common/service/properties/SnowflakeProperties.java +++ /dev/null @@ -1,25 +0,0 @@ -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; -} \ No newline at end of file diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/utils/HttpUtil.java b/common/service-utils/src/main/java/cn/bunny/common/service/utils/HttpUtil.java deleted file mode 100644 index bfb9b51..0000000 --- a/common/service-utils/src/main/java/cn/bunny/common/service/utils/HttpUtil.java +++ /dev/null @@ -1,206 +0,0 @@ -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 headers, Map querys) throws Exception { - HttpClient httpClient = wrapClient(host); - - HttpGet request = new HttpGet(buildUrl(host, path, querys)); - for (Map.Entry e : headers.entrySet()) { - request.addHeader(e.getKey(), e.getValue()); - } - - return httpClient.execute(request); - } - - public static HttpResponse doPost(String host, String path, String method, Map headers, Map querys, Map bodys) throws Exception { - HttpClient httpClient = wrapClient(host); - - HttpPost request = new HttpPost(buildUrl(host, path, querys)); - for (Map.Entry e : headers.entrySet()) { - request.addHeader(e.getKey(), e.getValue()); - } - - if (bodys != null) { - List nameValuePairList = new ArrayList(); - - 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 headers, Map querys, String body) throws Exception { - HttpClient httpClient = wrapClient(host); - - HttpPost request = new HttpPost(buildUrl(host, path, querys)); - for (Map.Entry 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 headers, Map querys, byte[] body) throws Exception { - HttpClient httpClient = wrapClient(host); - - HttpPost request = new HttpPost(buildUrl(host, path, querys)); - for (Map.Entry 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 headers, Map querys, String body) throws Exception { - HttpClient httpClient = wrapClient(host); - - HttpPut request = new HttpPut(buildUrl(host, path, querys)); - for (Map.Entry 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 headers, Map querys, byte[] body) throws Exception { - HttpClient httpClient = wrapClient(host); - - HttpPut request = new HttpPut(buildUrl(host, path, querys)); - for (Map.Entry 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 headers, Map querys) throws Exception { - HttpClient httpClient = wrapClient(host); - - HttpDelete request = new HttpDelete(buildUrl(host, path, querys)); - for (Map.Entry e : headers.entrySet()) { - request.addHeader(e.getKey(), e.getValue()); - } - - return httpClient.execute(request); - } - - private static String buildUrl(String host, String path, Map 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 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(); - } - } -} \ No newline at end of file diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/utils/IpUtil.java b/common/service-utils/src/main/java/cn/bunny/common/service/utils/IpUtil.java index c1d83f6..da4ffe7 100644 --- a/common/service-utils/src/main/java/cn/bunny/common/service/utils/IpUtil.java +++ b/common/service-utils/src/main/java/cn/bunny/common/service/utils/IpUtil.java @@ -1,6 +1,7 @@ package cn.bunny.common.service.utils; import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; import org.lionsoul.ip2region.xdb.Searcher; import org.springframework.core.io.ClassPathResource; import org.springframework.util.FileCopyUtils; @@ -9,6 +10,7 @@ import java.io.InputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; +@Slf4j public class IpUtil { private static Searcher searcher; @@ -32,7 +34,7 @@ public class IpUtil { byte[] bytes = FileCopyUtils.copyToByteArray(inputStream); searcher = Searcher.newWithBuffer(bytes); } catch (Exception exception) { - exception.printStackTrace(); + log.error(exception.getMessage()); } } @@ -66,8 +68,8 @@ public class IpUtil { return splitIpInfo[0]; } } - } catch (Exception e) { - e.printStackTrace(); + } catch (Exception exception) { + log.error(exception.getMessage()); } return ""; } else { diff --git a/common/service-utils/src/main/java/cn/bunny/common/service/utils/SnowflakeIdGenerator.java b/common/service-utils/src/main/java/cn/bunny/common/service/utils/SnowflakeIdGenerator.java deleted file mode 100644 index 8504b27..0000000 --- a/common/service-utils/src/main/java/cn/bunny/common/service/utils/SnowflakeIdGenerator.java +++ /dev/null @@ -1,149 +0,0 @@ -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 nextIds(int count) { - if (count > maxBatchCount || count < 0) { - throw new IllegalArgumentException(String.format("批量生成id的数量不能大于%d或小于0", maxBatchCount)); - } - List 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(); - } -} \ No newline at end of file diff --git a/dao/src/main/java/cn/bunny/vo/page/PageResult.java b/dao/src/main/java/cn/bunny/pojo/result/ResultPage.java similarity index 84% rename from dao/src/main/java/cn/bunny/vo/page/PageResult.java rename to dao/src/main/java/cn/bunny/pojo/result/ResultPage.java index feb66b2..78959ec 100644 --- a/dao/src/main/java/cn/bunny/vo/page/PageResult.java +++ b/dao/src/main/java/cn/bunny/pojo/result/ResultPage.java @@ -1,4 +1,4 @@ -package cn.bunny.vo.page; +package cn.bunny.pojo.result; import lombok.AllArgsConstructor; import lombok.Builder; @@ -15,7 +15,7 @@ import java.util.List; @AllArgsConstructor @NoArgsConstructor @Builder -public class PageResult implements Serializable { +public class ResultPage implements Serializable { // 当前页 private Integer pageNo; // 每页记录数 diff --git a/images/image-20240822091720866.png b/images/image-20240822091720866.png new file mode 100644 index 0000000..cefa1e5 Binary files /dev/null and b/images/image-20240822091720866.png differ diff --git a/images/image-20240822091810597.png b/images/image-20240822091810597.png new file mode 100644 index 0000000..2cfbd29 Binary files /dev/null and b/images/image-20240822091810597.png differ diff --git a/images/image-20240822092441020.png b/images/image-20240822092441020.png new file mode 100644 index 0000000..5b92d62 Binary files /dev/null and b/images/image-20240822092441020.png differ diff --git a/images/image-20240822092510151.png b/images/image-20240822092510151.png new file mode 100644 index 0000000..e822d45 Binary files /dev/null and b/images/image-20240822092510151.png differ diff --git a/images/image-20240822093552802.png b/images/image-20240822093552802.png new file mode 100644 index 0000000..0d6e6a9 Binary files /dev/null and b/images/image-20240822093552802.png differ diff --git a/images/image-20240822093803078.png b/images/image-20240822093803078.png new file mode 100644 index 0000000..cd507f3 Binary files /dev/null and b/images/image-20240822093803078.png differ diff --git a/images/image-20240822094000542.png b/images/image-20240822094000542.png new file mode 100644 index 0000000..ca37d8f Binary files /dev/null and b/images/image-20240822094000542.png differ diff --git a/images/image-20240822094021339.png b/images/image-20240822094021339.png new file mode 100644 index 0000000..ade461f Binary files /dev/null and b/images/image-20240822094021339.png differ diff --git a/module/module-mail/src/main/java/cn/bunny/module/mail/template-propties b/module/module-mail/src/main/java/cn/bunny/module/mail/template-propties index cc51c94..2ce3978 100644 --- a/module/module-mail/src/main/java/cn/bunny/module/mail/template-propties +++ b/module/module-mail/src/main/java/cn/bunny/module/mail/template-propties @@ -1,5 +1,3 @@ -Configuration example - mail: host: smtp.qq.com # 邮箱地址 port: 465 # 邮箱端口号 diff --git a/module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java b/module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java index 8ee36f7..f7e8f02 100644 --- a/module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java +++ b/module/module-minio/src/main/java/cn/bunny/module/minio/properties/MinioProperties.java @@ -21,7 +21,6 @@ public class MinioProperties { @Bean public MinioClient minioClient() { - log.info("注册MinioClient..."); return MinioClient.builder().endpoint(endpointUrl).credentials(accessKey, secretKey).build(); } } diff --git a/module/module-task/pom.xml b/module/module-task/pom.xml deleted file mode 100644 index 3ab04f3..0000000 --- a/module/module-task/pom.xml +++ /dev/null @@ -1,23 +0,0 @@ - - 4.0.0 - - cn.bunny - module - 0.0.1-SNAPSHOT - - - module-task - jar - - module-task - https://maven.apache.org - - - UTF-8 - - - - - - diff --git a/module/module-websocket/pom.xml b/module/module-websocket/pom.xml deleted file mode 100644 index e3b9b40..0000000 --- a/module/module-websocket/pom.xml +++ /dev/null @@ -1,27 +0,0 @@ - - 4.0.0 - - cn.bunny - module - 0.0.1-SNAPSHOT - - - module-websocket - jar - - module-websocket - https://maven.apache.org - - - UTF-8 - - - - - - org.springframework.boot - spring-boot-starter-websocket - - - diff --git a/module/module-websocket/src/main/java/cn/bunny/module/websocket/config/WebSocketConfiguration.java b/module/module-websocket/src/main/java/cn/bunny/module/websocket/config/WebSocketConfiguration.java deleted file mode 100644 index 8df905f..0000000 --- a/module/module-websocket/src/main/java/cn/bunny/module/websocket/config/WebSocketConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -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(); - } -} diff --git a/module/pom.xml b/module/pom.xml index cc91545..53463d9 100644 --- a/module/pom.xml +++ b/module/pom.xml @@ -16,9 +16,7 @@ module-minio module-mail module-rabbitMQ - module-websocket spring-security - module-task @@ -31,6 +29,11 @@ service-utils 0.0.1-SNAPSHOT + + com.google.protobuf + protobuf-java + 4.27.2 + org.springframework.boot spring-boot-starter-web diff --git a/module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java b/module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java index 2dce153..22a8cbf 100644 --- a/module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java +++ b/module/spring-security/src/main/java/cn/bunny/security/config/WebSecurityConfig.java @@ -85,7 +85,11 @@ public class WebSecurityConfig { return new SessionRegistryImpl(); } - // 排出鉴定路径 + /** + * * 排出鉴定路径 + * + * @return WebSecurityCustomizer + */ @Bean public WebSecurityCustomizer webSecurityCustomizer() { String[] annotations = {"/", "/test/**", "/diagram-viewer/**", "/editor-app/**", "/*.html", diff --git a/server-gateway/Dockerfile b/server-gateway/Dockerfile index f07f8d1..c799d18 100644 --- a/server-gateway/Dockerfile +++ b/server-gateway/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:17 +FROM openjdk:21 MAINTAINER bunny #系统编码 @@ -17,5 +17,7 @@ COPY target/*.jar /home/bunny/app.jar #启动容器时的进程 ENTRYPOINT ["java","-jar","/home/bunny/app.jar"] -#暴露 8080 端口 -EXPOSE 8500 \ No newline at end of file +#暴露 8800 端口 +EXPOSE 8080 + +# maven 打包:mvn clean package -Pprod -DskipTests \ No newline at end of file diff --git a/service/service-admin/Dockerfile b/service/service-admin/Dockerfile index 447a2bc..c799d18 100644 --- a/service/service-admin/Dockerfile +++ b/service/service-admin/Dockerfile @@ -18,4 +18,6 @@ COPY target/*.jar /home/bunny/app.jar ENTRYPOINT ["java","-jar","/home/bunny/app.jar"] #暴露 8800 端口 -EXPOSE 8800 \ No newline at end of file +EXPOSE 8080 + +# maven 打包:mvn clean package -Pprod -DskipTests \ No newline at end of file diff --git a/service/service-admin/src/main/resources/application-prod.yml b/service/service-admin/src/main/resources/application-prod.yml index 31e4a4e..c4d9a4d 100644 --- a/service/service-admin/src/main/resources/application-prod.yml +++ b/service/service-admin/src/main/resources/application-prod.yml @@ -1,3 +1,8 @@ +# 线上禁用文档 +knife4j: + enable: true + production: true + bunny: datasource: host: 192.168.3.100 diff --git a/service/service-web/Dockerfile b/service/service-web/Dockerfile index 447a2bc..c799d18 100644 --- a/service/service-web/Dockerfile +++ b/service/service-web/Dockerfile @@ -18,4 +18,6 @@ COPY target/*.jar /home/bunny/app.jar ENTRYPOINT ["java","-jar","/home/bunny/app.jar"] #暴露 8800 端口 -EXPOSE 8800 \ No newline at end of file +EXPOSE 8080 + +# maven 打包:mvn clean package -Pprod -DskipTests \ No newline at end of file diff --git a/service/service-web/pom.xml b/service/service-web/pom.xml index e3e4ca5..7ae9465 100644 --- a/service/service-web/pom.xml +++ b/service/service-web/pom.xml @@ -31,11 +31,11 @@ module-minio 0.0.1-SNAPSHOT - - - - - + + + org.springframework.boot + spring-boot-starter-websocket + diff --git a/service/service-web/src/main/java/cn/bunny/service/web/config/WebMvcConfiguration.java b/service/service-web/src/main/java/cn/bunny/service/web/config/WebMvcConfiguration.java index 8fb4af5..fe774b8 100644 --- a/service/service-web/src/main/java/cn/bunny/service/web/config/WebMvcConfiguration.java +++ b/service/service-web/src/main/java/cn/bunny/service/web/config/WebMvcConfiguration.java @@ -3,10 +3,12 @@ package cn.bunny.service.web.config; import cn.bunny.service.web.interceptor.UserTokenInterceptor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration @Slf4j @@ -14,6 +16,16 @@ public class WebMvcConfiguration implements WebMvcConfigurer { @Autowired private UserTokenInterceptor userTokenInterceptor; + /** + * * 配置WebSocket + * + * @return ServerEndpointExporter + */ + @Bean + public ServerEndpointExporter endpointExporter() { + return new ServerEndpointExporter(); + } + /** * 跨域配置 * diff --git a/module/module-task/src/main/java/cn/bunny/module/task/TemplateTask.java b/service/service-web/src/main/java/cn/bunny/service/web/task/TemplateTask.java similarity index 89% rename from module/module-task/src/main/java/cn/bunny/module/task/TemplateTask.java rename to service/service-web/src/main/java/cn/bunny/service/web/task/TemplateTask.java index 2dbf0b7..8d5e569 100644 --- a/module/module-task/src/main/java/cn/bunny/module/task/TemplateTask.java +++ b/service/service-web/src/main/java/cn/bunny/service/web/task/TemplateTask.java @@ -1,4 +1,4 @@ -package cn.bunny.module.task; +package cn.bunny.service.web.task; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; diff --git a/module/module-websocket/src/main/java/cn/bunny/module/websocket/WebSocketServer.java b/service/service-web/src/main/java/cn/bunny/service/web/ws/WebSocketServer.java similarity index 98% rename from module/module-websocket/src/main/java/cn/bunny/module/websocket/WebSocketServer.java rename to service/service-web/src/main/java/cn/bunny/service/web/ws/WebSocketServer.java index 2d84cc6..bb8baf5 100644 --- a/module/module-websocket/src/main/java/cn/bunny/module/websocket/WebSocketServer.java +++ b/service/service-web/src/main/java/cn/bunny/service/web/ws/WebSocketServer.java @@ -1,4 +1,4 @@ -package cn.bunny.module.websocket; +package cn.bunny.service.web.ws; import jakarta.websocket.OnClose; import jakarta.websocket.OnMessage; diff --git a/service/service-web/src/main/resources/application-prod.yml b/service/service-web/src/main/resources/application-prod.yml index a629da2..758b01e 100644 --- a/service/service-web/src/main/resources/application-prod.yml +++ b/service/service-web/src/main/resources/application-prod.yml @@ -1,3 +1,8 @@ +# 线上禁用文档 +knife4j: + enable: true + production: true + bunny: datasource: host: 192.168.3.100 diff --git a/service/service-web/src/main/resources/application.yml b/service/service-web/src/main/resources/application.yml index 772cb81..5d4c9f1 100644 --- a/service/service-web/src/main/resources/application.yml +++ b/service/service-web/src/main/resources/application.yml @@ -87,13 +87,4 @@ bunny: endpointUrl: ${bunny.minio.endpointUrl} accessKey: ${bunny.minio.accessKey} secretKey: ${bunny.minio.secretKey} - 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万 \ No newline at end of file + bucket-name: ${bunny.minio.bucket-name} \ No newline at end of file