mirror of
https://github.com/chillzhuang/blade-tool
synced 2024-11-16 23:49:34 +08:00
commit
f61eaf9c8b
15
README.md
15
README.md
@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/license-LGPL%20v3-blue.svg" alt="Build Status">
|
||||
<img src="https://img.shields.io/badge/Spring%20Cloud-Greenwich.SR3-blue.svg" alt="Coverage Status">
|
||||
<img src="https://img.shields.io/badge/Spring%20Boot-2.1.9.RELEASE-blue.svg" alt="Downloads">
|
||||
<img src="https://img.shields.io/badge/Spring%20Cloud-Hoxton.SR8-blue.svg" alt="Coverage Status">
|
||||
<img src="https://img.shields.io/badge/Spring%20Boot-2.2.11.RELEASE-blue.svg" alt="Downloads">
|
||||
</p>
|
||||
|
||||
## SpringBlade微服务开发平台
|
||||
@ -33,19 +33,24 @@ blade-tool
|
||||
├── blade-core-secure -- 安全模块
|
||||
├── blade-core-swagger -- swagger拓展封装模块
|
||||
├── blade-core-test -- 单元测试封装模块
|
||||
└── blade-core-tool -- 工具包模块
|
||||
|
||||
├── blade-core-tool -- 单元测试封装模块
|
||||
└── blade-core-transaction -- 分布式事物封装模块
|
||||
```
|
||||
|
||||
## 官网
|
||||
* 官网地址:[https://bladex.vip](https://bladex.vip)
|
||||
* 问答社区:[https://sns.bladex.vip](https://sns.bladex.vip)
|
||||
* 会员计划:[SpringBlade会员计划](https://gitee.com/smallc/SpringBlade/wikis/SpringBlade会员计划)
|
||||
* 交流群:`477853168`
|
||||
* 交流一群:`477853168`(满)
|
||||
* 交流二群:`751253339`(满)
|
||||
* 交流三群:`784729540`(满)
|
||||
* 交流四群:`1034621754`(满)
|
||||
* 交流五群:`946350912`
|
||||
|
||||
## 在线演示
|
||||
* Sword演示地址:[https://sword.bladex.vip](https://sword.bladex.vip)
|
||||
* Saber演示地址:[https://saber.bladex.vip](https://saber.bladex.vip)
|
||||
* 数据大屏演示地址:[https://data.avuejs.com](https://data.avuejs.com)
|
||||
|
||||
## 项目地址
|
||||
* 后端Gitee地址:[https://gitee.com/smallc/SpringBlade](https://gitee.com/smallc/SpringBlade)
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -62,33 +62,6 @@
|
||||
<artifactId>blade-core-swagger</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<!--Swagger-->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
<version>${swagger.models.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<version>${swagger.bootstrapui.version}</version>
|
||||
</dependency>
|
||||
<!--Redis-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<!--MyBatis-->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
@ -115,13 +88,13 @@
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>druid-spring-boot-starter</artifactId>
|
||||
<version>1.1.18</version>
|
||||
<version>1.1.22</version>
|
||||
</dependency>
|
||||
<!-- MySQL -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.15</version>
|
||||
<version>8.0.16</version>
|
||||
</dependency>
|
||||
<!-- PostgreSql -->
|
||||
<!--<dependency>
|
||||
|
@ -26,6 +26,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
/**
|
||||
* 配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@ -41,6 +42,8 @@ public class BladeBootAutoConfiguration {
|
||||
|
||||
/**
|
||||
* 全局变量定义
|
||||
*
|
||||
* @return SystemConstant
|
||||
*/
|
||||
@Bean
|
||||
public SystemConstant fileConst() {
|
||||
|
@ -15,14 +15,18 @@
|
||||
*/
|
||||
package org.springblade.core.boot.config;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor;
|
||||
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springblade.core.mp.plugins.SqlLogInterceptor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
|
||||
/**
|
||||
* mybatisplus 配置
|
||||
@ -30,24 +34,43 @@ import org.springframework.context.annotation.Profile;
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@MapperScan("org.springblade.**.mapper.**")
|
||||
public class MybatisPlusConfiguration {
|
||||
|
||||
private final TenantLineHandler tenantLineHandler;
|
||||
|
||||
/**
|
||||
* mybatis-plus 拦截器集合
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(PaginationInterceptor.class)
|
||||
public PaginationInterceptor paginationInterceptor() {
|
||||
return new PaginationInterceptor();
|
||||
@ConditionalOnMissingBean(MybatisPlusInterceptor.class)
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 配置租户拦截器
|
||||
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(tenantLineHandler));
|
||||
// 配置分页拦截器
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL执行效率插件
|
||||
*
|
||||
* @return PerformanceInterceptor
|
||||
* mybatis-plus自3.4.0起采用新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
|
||||
*/
|
||||
@Bean
|
||||
@Profile({AppConstant.DEV_CODE, AppConstant.TEST_CODE})
|
||||
public PerformanceInterceptor performanceInterceptor() {
|
||||
return new PerformanceInterceptor();
|
||||
public ConfigurationCustomizer configurationCustomizer() {
|
||||
return configuration -> configuration.setUseDeprecatedExecutor(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* sql 日志
|
||||
*
|
||||
* @return SqlLogInterceptor
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(value = "blade.mybatis-plus.sql-log", matchIfMissing = true)
|
||||
public SqlLogInterceptor sqlLogInterceptor() {
|
||||
return new SqlLogInterceptor();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -99,8 +99,8 @@ public class BladeFileUtil {
|
||||
/**
|
||||
* 文本排序转换成枚举
|
||||
*
|
||||
* @param sort
|
||||
* @return
|
||||
* @param sort 排序
|
||||
* @return FileSort
|
||||
*/
|
||||
public static FileSort of(String sort) {
|
||||
try {
|
||||
@ -228,6 +228,7 @@ public class BladeFileUtil {
|
||||
* 获取BladeFile封装类
|
||||
*
|
||||
* @param files 文件集合
|
||||
* @param dir 路径
|
||||
* @param path 路径
|
||||
* @param virtualPath 虚拟路径
|
||||
* @return BladeFile
|
||||
|
@ -29,7 +29,7 @@ public interface IFileProxy {
|
||||
*
|
||||
* @param file 文件
|
||||
* @param dir 目录
|
||||
* @return
|
||||
* @return String
|
||||
*/
|
||||
String[] path(File file, String dir);
|
||||
|
||||
@ -38,7 +38,7 @@ public interface IFileProxy {
|
||||
*
|
||||
* @param file 文件
|
||||
* @param path 路径
|
||||
* @return
|
||||
* @return File
|
||||
*/
|
||||
File rename(File file, String path);
|
||||
|
||||
|
@ -27,6 +27,7 @@ import java.io.InputStream;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Spring boot 控制器 请求日志,方便代码调试
|
||||
@ -66,6 +67,7 @@ public class RequestLogAspect {
|
||||
continue;
|
||||
}
|
||||
RequestBody requestBody = methodParam.getParameterAnnotation(RequestBody.class);
|
||||
String parameterName = methodParam.getParameterName();
|
||||
Object value = args[i];
|
||||
// 如果是body的json则是对象
|
||||
if (requestBody != null && value != null) {
|
||||
@ -89,6 +91,19 @@ public class RequestLogAspect {
|
||||
} else if (value instanceof HttpServletResponse) {
|
||||
} else if (value instanceof InputStream) {
|
||||
} else if (value instanceof InputStreamSource) {
|
||||
} else if (value instanceof List) {
|
||||
List<?> list = (List<?>) value;
|
||||
AtomicBoolean isSkip = new AtomicBoolean(false);
|
||||
for (Object o : list) {
|
||||
if ("StandardMultipartFile".equalsIgnoreCase(o.getClass().getSimpleName())) {
|
||||
isSkip.set(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isSkip.get()) {
|
||||
paraMap.put(parameterName, "此参数不能序列化为json");
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// 参数名
|
||||
RequestParam requestParam = methodParam.getParameterAnnotation(RequestParam.class);
|
||||
|
@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springblade.core.boot.tenant;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.sf.jsqlparser.expression.Expression;
|
||||
@ -31,7 +31,7 @@ import org.springblade.core.tool.utils.StringUtil;
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class BladeTenantHandler implements TenantHandler {
|
||||
public class BladeTenantHandler implements TenantLineHandler {
|
||||
|
||||
private final BladeTenantProperties properties;
|
||||
|
||||
@ -59,10 +59,10 @@ public class BladeTenantHandler implements TenantHandler {
|
||||
* 过滤租户表
|
||||
*
|
||||
* @param tableName 表名
|
||||
* @return 是否进行过滤
|
||||
* @return 是否忽略, true:表示忽略,false:需要解析并拼接多租户条件
|
||||
*/
|
||||
@Override
|
||||
public boolean doTableFilter(String tableName) {
|
||||
public boolean ignoreTable(String tableName) {
|
||||
return !(
|
||||
(
|
||||
(properties.getTables().size() > 0 && properties.getTables().contains(tableName))
|
||||
|
@ -46,5 +46,5 @@ public class BladeTenantProperties {
|
||||
/**
|
||||
* 多租户系统数据表
|
||||
*/
|
||||
private List<String> bladeTables = Arrays.asList("blade_notice", "blade_log_api", "blade_log_error", "blade_log_usual");
|
||||
private List<String> bladeTables = Arrays.asList("blade_notice", "blade_post", "blade_log_api", "blade_log_error", "blade_log_usual");
|
||||
}
|
||||
|
@ -15,21 +15,15 @@
|
||||
*/
|
||||
package org.springblade.core.boot.tenant;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.parser.ISqlParser;
|
||||
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
|
||||
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
|
||||
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.boot.config.MybatisPlusConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 多租户配置类
|
||||
*
|
||||
@ -37,7 +31,7 @@ import java.util.List;
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@AutoConfigureBefore(MybatisConfiguration.class)
|
||||
@AutoConfigureBefore(MybatisPlusConfiguration.class)
|
||||
@EnableConfigurationProperties(BladeTenantProperties.class)
|
||||
public class TenantConfiguration {
|
||||
|
||||
@ -52,8 +46,8 @@ public class TenantConfiguration {
|
||||
* @return TenantHandler
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TenantHandler.class)
|
||||
public TenantHandler bladeTenantHandler() {
|
||||
@ConditionalOnMissingBean(TenantLineHandler.class)
|
||||
public TenantLineHandler bladeTenantHandler() {
|
||||
return new BladeTenantHandler(properties);
|
||||
}
|
||||
|
||||
@ -68,21 +62,4 @@ public class TenantConfiguration {
|
||||
return new BladeTenantId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页插件
|
||||
*
|
||||
* @param tenantHandler 自定义租户处理器
|
||||
* @return PaginationInterceptor
|
||||
*/
|
||||
@Bean
|
||||
public PaginationInterceptor paginationInterceptor(TenantHandler tenantHandler) {
|
||||
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
|
||||
List<ISqlParser> sqlParserList = new ArrayList<>();
|
||||
TenantSqlParser tenantSqlParser = new TenantSqlParser();
|
||||
tenantSqlParser.setTenantHandler(tenantHandler);
|
||||
sqlParserList.add(tenantSqlParser);
|
||||
paginationInterceptor.setSqlParserList(sqlParserList);
|
||||
return paginationInterceptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,9 +60,6 @@ spring:
|
||||
#maximum-pool-size: 5
|
||||
#max-lifetime: 1800000
|
||||
#minimum-idle: 1
|
||||
devtools:
|
||||
restart:
|
||||
log-condition-evaluation-delta: false
|
||||
|
||||
|
||||
#配置日志地址
|
||||
@ -80,7 +77,7 @@ mybatis-plus:
|
||||
banner: false
|
||||
db-config:
|
||||
#主键类型 0:"数据库ID自增", 1:"不操作", 2:"用户输入ID",3:"数字型snowflake", 4:"全局唯一ID UUID", 5:"字符串型snowflake";
|
||||
id-type: id_worker
|
||||
id-type: assign_id
|
||||
#字段策略
|
||||
insert-strategy: not_null
|
||||
update-strategy: not_null
|
||||
@ -100,7 +97,7 @@ mybatis-plus:
|
||||
swagger:
|
||||
title: SpringBlade 接口文档系统
|
||||
description: SpringBlade 接口文档系统
|
||||
version: 2.5.4
|
||||
version: 2.8.0
|
||||
license: Powered By SpringBlade
|
||||
licenseUrl: https://bladex.vip
|
||||
terms-of-service-url: https://bladex.vip
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -27,11 +27,6 @@
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<version>2.2.1.RELEASE</version>
|
||||
</dependency>
|
||||
<!--Feign-->
|
||||
<dependency>
|
||||
<groupId>io.github.openfeign</groupId>
|
||||
@ -87,6 +82,12 @@
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
<version>${alibaba.cloud.version}</version>
|
||||
</dependency>
|
||||
<!-- Alibaba依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.74</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -55,7 +55,7 @@ import java.util.ArrayList;
|
||||
public class BladeFeignAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnMissingBean(Targeter.class)
|
||||
public Targeter bladeFeignTargeter() {
|
||||
return new BladeHystrixTargeter();
|
||||
}
|
||||
|
@ -1,32 +0,0 @@
|
||||
package org.springblade.core.cloud.stream;
|
||||
|
||||
import org.springframework.cloud.stream.annotation.Input;
|
||||
import org.springframework.cloud.stream.annotation.Output;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.SubscribableChannel;
|
||||
|
||||
/**
|
||||
* 服务异常 Streams
|
||||
*
|
||||
* @author L.cm
|
||||
*/
|
||||
public interface ServiceErrorStreams {
|
||||
String INPUT = "service-error-in";
|
||||
String OUTPUT = "service-error-out";
|
||||
|
||||
/**
|
||||
* input
|
||||
*
|
||||
* @return SubscribableChannel
|
||||
*/
|
||||
@Input(INPUT)
|
||||
SubscribableChannel subscribablebChannel();
|
||||
|
||||
/**
|
||||
* output
|
||||
*
|
||||
* @return MessageChannel
|
||||
*/
|
||||
@Output(OUTPUT)
|
||||
MessageChannel messageChannel();
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -210,6 +210,7 @@ public class BladeCodeGenerator {
|
||||
map.put("codeName", codeName);
|
||||
map.put("serviceName", serviceName);
|
||||
map.put("servicePackage", servicePackage);
|
||||
map.put("servicePackageLowerCase", servicePackage.toLowerCase());
|
||||
map.put("tenantColumn", tenantColumn);
|
||||
map.put("hasWrapper", hasWrapper);
|
||||
this.setMap(map);
|
||||
|
@ -16,9 +16,9 @@
|
||||
package $!{package.Controller};
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperationSupport;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
|
||||
import lombok.AllArgsConstructor;
|
||||
import javax.validation.Valid;
|
||||
|
||||
@ -160,7 +160,7 @@ public class $!{table.controllerName} {
|
||||
@ApiOperationSupport(order = 7)
|
||||
@ApiOperation(value = "逻辑删除", notes = "传入ids")
|
||||
public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
|
||||
return R.status($!{table.entityPath}Service.deleteLogic(Func.toIntList(ids)));
|
||||
return R.status($!{table.entityPath}Service.deleteLogic(Func.toLongList(ids)));
|
||||
}
|
||||
|
||||
#else
|
||||
@ -172,7 +172,7 @@ public class $!{table.controllerName} {
|
||||
@ApiOperationSupport(order = 8)
|
||||
@ApiOperation(value = "删除", notes = "传入ids")
|
||||
public R remove(@ApiParam(value = "主键集合", required = true) @RequestParam String ids) {
|
||||
return R.status($!{table.entityPath}Service.removeByIds(Func.toIntList(ids)));
|
||||
return R.status($!{table.entityPath}Service.removeByIds(Func.toLongList(ids)));
|
||||
}
|
||||
|
||||
#end
|
||||
|
@ -47,6 +47,10 @@
|
||||
},
|
||||
selectionList: [],
|
||||
option: {
|
||||
height: 'auto',
|
||||
calcHeight: 210,
|
||||
searchShow: true,
|
||||
searchMenuSpan: 6,
|
||||
tip: false,
|
||||
border: true,
|
||||
index: true,
|
||||
@ -90,30 +94,30 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
rowSave(row, loading, done) {
|
||||
rowSave(row, done, loading) {
|
||||
add(row).then(() => {
|
||||
loading();
|
||||
done();
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: "success",
|
||||
message: "操作成功!"
|
||||
});
|
||||
}, error => {
|
||||
done();
|
||||
console.log(error);
|
||||
window.console.log(error);
|
||||
loading();
|
||||
});
|
||||
},
|
||||
rowUpdate(row, index, loading, done) {
|
||||
rowUpdate(row, index, done, loading) {
|
||||
update(row).then(() => {
|
||||
loading();
|
||||
done();
|
||||
this.onLoad(this.page);
|
||||
this.$message({
|
||||
type: "success",
|
||||
message: "操作成功!"
|
||||
});
|
||||
}, error => {
|
||||
done();
|
||||
console.log(error);
|
||||
window.console.log(error);
|
||||
loading();
|
||||
});
|
||||
},
|
||||
rowDel(row) {
|
||||
@ -167,9 +171,11 @@
|
||||
this.query = {};
|
||||
this.onLoad(this.page);
|
||||
},
|
||||
searchChange(params) {
|
||||
searchChange(params, done) {
|
||||
this.query = params;
|
||||
this.page.currentPage = 1;
|
||||
this.onLoad(this.page, params);
|
||||
done();
|
||||
},
|
||||
selectionChange(list) {
|
||||
this.selectionList = list;
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -25,7 +25,7 @@ public interface AppConstant {
|
||||
/**
|
||||
* 应用版本
|
||||
*/
|
||||
String APPLICATION_VERSION = "2.5.4";
|
||||
String APPLICATION_VERSION = "2.8.0";
|
||||
|
||||
/**
|
||||
* 基础包
|
||||
@ -76,6 +76,10 @@ public interface AppConstant {
|
||||
* 链路追踪模块名称
|
||||
*/
|
||||
String APPLICATION_ZIPKIN_NAME = APPLICATION_NAME_PREFIX + "zipkin";
|
||||
/**
|
||||
* 报表系统名称
|
||||
*/
|
||||
String APPLICATION_REPORT_NAME = APPLICATION_NAME_PREFIX + "report";
|
||||
/**
|
||||
* 测试模块名称
|
||||
*/
|
||||
|
@ -22,7 +22,7 @@ package org.springblade.core.launch.constant;
|
||||
*/
|
||||
public interface TokenConstant {
|
||||
|
||||
String SIGN_KEY = "Blade";
|
||||
String SIGN_KEY = "bladexisapowerfulmicroservicearchitectureupgradedandoptimizedfromacommercialproject";
|
||||
String AVATAR = "avatar";
|
||||
String HEADER = "blade-auth";
|
||||
String BEARER = "bearer";
|
||||
@ -36,6 +36,7 @@ public interface TokenConstant {
|
||||
String USER_NAME = "user_name";
|
||||
String ROLE_NAME = "role_name";
|
||||
String TENANT_ID = "tenant_id";
|
||||
String OAUTH_ID = "oauth_id";
|
||||
String CLIENT_ID = "client_id";
|
||||
String LICENSE = "license";
|
||||
String LICENSE_NAME = "powered by blade";
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -20,10 +20,7 @@ import org.springblade.core.launch.props.BladeProperties;
|
||||
import org.springblade.core.launch.server.ServerInfo;
|
||||
import org.springblade.core.log.model.LogAbstract;
|
||||
import org.springblade.core.secure.utils.SecureUtil;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springblade.core.tool.utils.UrlUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springblade.core.tool.utils.*;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
@ -41,6 +38,7 @@ public class LogAbstractUtil {
|
||||
* @param logAbstract 日志基础类
|
||||
*/
|
||||
public static void addRequestInfoToLog(HttpServletRequest request, LogAbstract logAbstract) {
|
||||
if (ObjectUtil.isNotEmpty(request)) {
|
||||
logAbstract.setRemoteIp(WebUtil.getIP(request));
|
||||
logAbstract.setUserAgent(request.getHeader(WebUtil.USER_AGENT_HEADER));
|
||||
logAbstract.setRequestUri(UrlUtil.getPath(request.getRequestURI()));
|
||||
@ -48,6 +46,7 @@ public class LogAbstractUtil {
|
||||
logAbstract.setParams(WebUtil.getRequestParamString(request));
|
||||
logAbstract.setCreateBy(SecureUtil.getUserAccount(request));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向log中添加补齐其他的信息(eg:blade、server等)
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -18,6 +18,8 @@ package org.springblade.core.mp.base;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
@ -37,8 +39,9 @@ public class BaseEntity implements Serializable {
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
@ApiModelProperty(value = "创建人")
|
||||
private Integer createUser;
|
||||
private Long createUser;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
@ -51,8 +54,9 @@ public class BaseEntity implements Serializable {
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
@ApiModelProperty(value = "更新人")
|
||||
private Integer updateUser;
|
||||
private Long updateUser;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
|
@ -32,8 +32,8 @@ public interface BaseService<T> extends IService<T> {
|
||||
* 逻辑删除
|
||||
*
|
||||
* @param ids id集合(逗号分隔)
|
||||
* @return
|
||||
* @return boolean
|
||||
*/
|
||||
boolean deleteLogic(@NotEmpty List<Integer> ids);
|
||||
boolean deleteLogic(@NotEmpty List<Long> ids);
|
||||
|
||||
}
|
||||
|
@ -24,8 +24,6 @@ import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ -39,14 +37,6 @@ import java.util.List;
|
||||
@Validated
|
||||
public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements BaseService<T> {
|
||||
|
||||
private Class<T> modelClass;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public BaseServiceImpl() {
|
||||
Type type = this.getClass().getGenericSuperclass();
|
||||
this.modelClass = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean save(T entity) {
|
||||
BladeUser user = SecureUtil.getUser();
|
||||
@ -75,7 +65,7 @@ public class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> exte
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteLogic(@NotEmpty List<Integer> ids) {
|
||||
public boolean deleteLogic(@NotEmpty List<Long> ids) {
|
||||
return super.removeByIds(ids);
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ package org.springblade.core.mp.base;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 租户基础实体类
|
||||
@ -25,6 +26,7 @@ import lombok.Data;
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TenantEntity extends BaseEntity {
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,185 @@
|
||||
package org.springblade.core.mp.plugins;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
import com.baomidou.mybatisplus.core.toolkit.SystemClock;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.ibatis.executor.statement.StatementHandler;
|
||||
import org.apache.ibatis.mapping.MappedStatement;
|
||||
import org.apache.ibatis.plugin.*;
|
||||
import org.apache.ibatis.reflection.MetaObject;
|
||||
import org.apache.ibatis.reflection.SystemMetaObject;
|
||||
import org.apache.ibatis.session.ResultHandler;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.sql.Statement;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 用于输出每条 SQL 语句及其执行时间
|
||||
*
|
||||
* @author hubin nieqiurong TaoYu
|
||||
* @since 2016-07-07
|
||||
*/
|
||||
@Slf4j
|
||||
@Intercepts({
|
||||
@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}),
|
||||
@Signature(type = StatementHandler.class, method = "update", args = Statement.class),
|
||||
@Signature(type = StatementHandler.class, method = "batch", args = Statement.class)
|
||||
})
|
||||
public class SqlLogInterceptor implements Interceptor {
|
||||
private static final String DRUID_POOLED_PREPARED_STATEMENT = "com.alibaba.druid.pool.DruidPooledPreparedStatement";
|
||||
private static final String T4C_PREPARED_STATEMENT = "oracle.jdbc.driver.T4CPreparedStatement";
|
||||
private static final String ORACLE_PREPARED_STATEMENT_WRAPPER = "oracle.jdbc.driver.OraclePreparedStatementWrapper";
|
||||
|
||||
private Method oracleGetOriginalSqlMethod;
|
||||
private Method druidGetSqlMethod;
|
||||
|
||||
@Override
|
||||
public Object intercept(Invocation invocation) throws Throwable {
|
||||
Statement statement;
|
||||
Object firstArg = invocation.getArgs()[0];
|
||||
if (Proxy.isProxyClass(firstArg.getClass())) {
|
||||
statement = (Statement) SystemMetaObject.forObject(firstArg).getValue("h.statement");
|
||||
} else {
|
||||
statement = (Statement) firstArg;
|
||||
}
|
||||
MetaObject stmtMetaObj = SystemMetaObject.forObject(statement);
|
||||
try {
|
||||
statement = (Statement) stmtMetaObj.getValue("stmt.statement");
|
||||
} catch (Exception e) {
|
||||
// do nothing
|
||||
}
|
||||
if (stmtMetaObj.hasGetter("delegate")) {
|
||||
//Hikari
|
||||
try {
|
||||
statement = (Statement) stmtMetaObj.getValue("delegate");
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String originalSql = null;
|
||||
String stmtClassName = statement.getClass().getName();
|
||||
if (DRUID_POOLED_PREPARED_STATEMENT.equals(stmtClassName)) {
|
||||
try {
|
||||
if (druidGetSqlMethod == null) {
|
||||
Class<?> clazz = Class.forName(DRUID_POOLED_PREPARED_STATEMENT);
|
||||
druidGetSqlMethod = clazz.getMethod("getSql");
|
||||
}
|
||||
Object stmtSql = druidGetSqlMethod.invoke(statement);
|
||||
if (stmtSql instanceof String) {
|
||||
originalSql = (String) stmtSql;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (T4C_PREPARED_STATEMENT.equals(stmtClassName)
|
||||
|| ORACLE_PREPARED_STATEMENT_WRAPPER.equals(stmtClassName)) {
|
||||
try {
|
||||
if (oracleGetOriginalSqlMethod != null) {
|
||||
Object stmtSql = oracleGetOriginalSqlMethod.invoke(statement);
|
||||
if (stmtSql instanceof String) {
|
||||
originalSql = (String) stmtSql;
|
||||
}
|
||||
} else {
|
||||
Class<?> clazz = Class.forName(stmtClassName);
|
||||
oracleGetOriginalSqlMethod = getMethodRegular(clazz, "getOriginalSql");
|
||||
if (oracleGetOriginalSqlMethod != null) {
|
||||
//OraclePreparedStatementWrapper is not a public class, need set this.
|
||||
oracleGetOriginalSqlMethod.setAccessible(true);
|
||||
if (null != oracleGetOriginalSqlMethod) {
|
||||
Object stmtSql = oracleGetOriginalSqlMethod.invoke(statement);
|
||||
if (stmtSql instanceof String) {
|
||||
originalSql = (String) stmtSql;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
if (originalSql == null) {
|
||||
originalSql = statement.toString();
|
||||
}
|
||||
originalSql = originalSql.replaceAll("[\\s]+", StringPool.SPACE);
|
||||
int index = indexOfSqlStart(originalSql);
|
||||
if (index > 0) {
|
||||
originalSql = originalSql.substring(index);
|
||||
}
|
||||
|
||||
// 计算执行 SQL 耗时
|
||||
long start = SystemClock.now();
|
||||
Object result = invocation.proceed();
|
||||
long timing = SystemClock.now() - start;
|
||||
|
||||
// SQL 打印执行结果
|
||||
Object target = PluginUtils.realTarget(invocation.getTarget());
|
||||
MetaObject metaObject = SystemMetaObject.forObject(target);
|
||||
MappedStatement ms = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
|
||||
// 打印 sql
|
||||
System.err.println(
|
||||
StringUtil.format(
|
||||
"\n============== Sql Start ==============" +
|
||||
"\nExecute ID :{}" +
|
||||
"\nExecute SQL :{}" +
|
||||
"\nExecute Time:{} ms" +
|
||||
"\n============== Sql End ==============\n",
|
||||
ms.getId(), originalSql, timing));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object plugin(Object target) {
|
||||
if (target instanceof StatementHandler) {
|
||||
return Plugin.wrap(target, this);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取此方法名的具体 Method
|
||||
*
|
||||
* @param clazz class 对象
|
||||
* @param methodName 方法名
|
||||
* @return 方法
|
||||
*/
|
||||
private Method getMethodRegular(Class<?> clazz, String methodName) {
|
||||
if (Object.class.equals(clazz)) {
|
||||
return null;
|
||||
}
|
||||
for (Method method : clazz.getDeclaredMethods()) {
|
||||
if (method.getName().equals(methodName)) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return getMethodRegular(clazz.getSuperclass(), methodName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取sql语句开头部分
|
||||
*
|
||||
* @param sql ignore
|
||||
* @return ignore
|
||||
*/
|
||||
private int indexOfSqlStart(String sql) {
|
||||
String upperCaseSql = sql.toUpperCase();
|
||||
Set<Integer> set = new HashSet<>();
|
||||
set.add(upperCaseSql.indexOf("SELECT "));
|
||||
set.add(upperCaseSql.indexOf("UPDATE "));
|
||||
set.add(upperCaseSql.indexOf("INSERT "));
|
||||
set.add(upperCaseSql.indexOf("DELETE "));
|
||||
set.remove(-1);
|
||||
if (CollectionUtils.isEmpty(set)) {
|
||||
return -1;
|
||||
}
|
||||
List<Integer> list = new ArrayList<>(set);
|
||||
list.sort(Comparator.naturalOrder());
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
}
|
@ -30,15 +30,17 @@ public abstract class BaseEntityWrapper<E, V> {
|
||||
|
||||
/**
|
||||
* 单个实体类包装
|
||||
* @param entity
|
||||
* @return
|
||||
*
|
||||
* @param entity 实体类
|
||||
* @return V
|
||||
*/
|
||||
public abstract V entityVO(E entity);
|
||||
|
||||
/**
|
||||
* 实体类集合包装
|
||||
* @param list
|
||||
* @return
|
||||
*
|
||||
* @param list 猎豹
|
||||
* @return List V
|
||||
*/
|
||||
public List<V> listVO(List<E> list) {
|
||||
return list.stream().map(this::entityVO).collect(Collectors.toList());
|
||||
@ -46,8 +48,9 @@ public abstract class BaseEntityWrapper<E, V> {
|
||||
|
||||
/**
|
||||
* 分页实体类集合包装
|
||||
* @param pages
|
||||
* @return
|
||||
*
|
||||
* @param pages 分页
|
||||
* @return Page V
|
||||
*/
|
||||
public IPage<V> pageVO(IPage<E> pages) {
|
||||
List<V> records = listVO(pages.getRecords());
|
||||
|
@ -18,6 +18,8 @@ package org.springblade.core.mp.support;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.springblade.core.launch.constant.TokenConstant;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
|
||||
@ -33,8 +35,8 @@ public class Condition {
|
||||
/**
|
||||
* 转化成mybatis plus中的Page
|
||||
*
|
||||
* @param query
|
||||
* @return
|
||||
* @param query 查询条件
|
||||
* @return IPage
|
||||
*/
|
||||
public static <T> IPage<T> getPage(Query query) {
|
||||
Page<T> page = new Page<>(Func.toInt(query.getCurrent(), 1), Func.toInt(query.getSize(), 10));
|
||||
@ -46,9 +48,9 @@ public class Condition {
|
||||
/**
|
||||
* 获取mybatis plus中的QueryWrapper
|
||||
*
|
||||
* @param entity
|
||||
* @param <T>
|
||||
* @return
|
||||
* @param entity 实体
|
||||
* @param <T> 类型
|
||||
* @return QueryWrapper
|
||||
*/
|
||||
public static <T> QueryWrapper<T> getQueryWrapper(T entity) {
|
||||
return new QueryWrapper<>(entity);
|
||||
@ -57,16 +59,28 @@ public class Condition {
|
||||
/**
|
||||
* 获取mybatis plus中的QueryWrapper
|
||||
*
|
||||
* @param query
|
||||
* @param clazz
|
||||
* @param <T>
|
||||
* @return
|
||||
* @param query 查询条件
|
||||
* @param clazz 实体类
|
||||
* @param <T> 类型
|
||||
* @return QueryWrapper
|
||||
*/
|
||||
public static <T> QueryWrapper<T> getQueryWrapper(Map<String, Object> query, Class<T> clazz) {
|
||||
query.remove("current");
|
||||
query.remove("size");
|
||||
query.remove("ascs");
|
||||
query.remove("descs");
|
||||
Kv exclude = Kv.init().set(TokenConstant.HEADER, TokenConstant.HEADER)
|
||||
.set("current", "current").set("size", "size").set("ascs", "ascs").set("descs", "descs");
|
||||
return getQueryWrapper(query, exclude, clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取mybatis plus中的QueryWrapper
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param exclude 排除的查询条件
|
||||
* @param clazz 实体类
|
||||
* @param <T> 类型
|
||||
* @return QueryWrapper
|
||||
*/
|
||||
public static <T> QueryWrapper<T> getQueryWrapper(Map<String, Object> query, Map<String, Object> exclude, Class<T> clazz) {
|
||||
exclude.forEach((k, v) -> query.remove(k));
|
||||
QueryWrapper<T> qw = new QueryWrapper<>();
|
||||
qw.setEntity(BeanUtil.newInstance(clazz));
|
||||
SqlKeyword.buildCondition(query, qw);
|
||||
|
@ -88,7 +88,7 @@ public class SqlKeyword {
|
||||
*
|
||||
* @param column 字段名
|
||||
* @param keyword 关键字
|
||||
* @return
|
||||
* @return String
|
||||
*/
|
||||
private static String getColumn(String column, String keyword) {
|
||||
return StringUtil.humpToUnderline(StringUtil.removeSuffix(column, keyword));
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -27,6 +27,12 @@
|
||||
<artifactId>qiniu-java-sdk</artifactId>
|
||||
<version>7.2.18</version>
|
||||
</dependency>
|
||||
<!--alioss-->
|
||||
<dependency>
|
||||
<groupId>com.aliyun.oss</groupId>
|
||||
<artifactId>aliyun-sdk-oss</artifactId>
|
||||
<version>3.1.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -0,0 +1,283 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.oss;
|
||||
|
||||
import com.aliyun.oss.OSSClient;
|
||||
import com.aliyun.oss.common.utils.BinaryUtil;
|
||||
import com.aliyun.oss.model.MatchMode;
|
||||
import com.aliyun.oss.model.ObjectMetadata;
|
||||
import com.aliyun.oss.model.PolicyConditions;
|
||||
import com.aliyun.oss.model.PutObjectResult;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springblade.core.oss.model.BladeFile;
|
||||
import org.springblade.core.oss.model.OssFile;
|
||||
import org.springblade.core.oss.props.OssProperties;
|
||||
import org.springblade.core.oss.rule.OssRule;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* AliossTemplate
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class AliossTemplate {
|
||||
private OSSClient ossClient;
|
||||
private OssProperties ossProperties;
|
||||
private OssRule ossRule;
|
||||
|
||||
@SneakyThrows
|
||||
public void makeBucket(String bucketName) {
|
||||
if (!bucketExists(bucketName)) {
|
||||
ossClient.createBucket(getBucketName(bucketName));
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void removeBucket(String bucketName) {
|
||||
ossClient.deleteBucket(getBucketName(bucketName));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public boolean bucketExists(String bucketName) {
|
||||
return ossClient.doesBucketExist(getBucketName(bucketName));
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void copyFile(String bucketName, String fileName, String destBucketName) {
|
||||
ossClient.copyObject(getBucketName(bucketName), fileName, getBucketName(destBucketName), fileName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void copyFile(String bucketName, String fileName, String destBucketName, String destFileName) {
|
||||
ossClient.copyObject(getBucketName(bucketName), fileName, getBucketName(destBucketName), destFileName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public OssFile statFile(String fileName) {
|
||||
return statFile(ossProperties.getBucketName(), fileName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public OssFile statFile(String bucketName, String fileName) {
|
||||
ObjectMetadata stat = ossClient.getObjectMetadata(getBucketName(bucketName), fileName);
|
||||
OssFile ossFile = new OssFile();
|
||||
ossFile.setName(fileName);
|
||||
ossFile.setLink(fileLink(ossFile.getName()));
|
||||
ossFile.setHash(stat.getContentMD5());
|
||||
ossFile.setLength(stat.getContentLength());
|
||||
ossFile.setPutTime(stat.getLastModified());
|
||||
ossFile.setContentType(stat.getContentType());
|
||||
return ossFile;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String filePath(String fileName) {
|
||||
return getOssHost().concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String filePath(String bucketName, String fileName) {
|
||||
return getOssHost(bucketName).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String fileLink(String fileName) {
|
||||
return getOssHost().concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public String fileLink(String bucketName, String fileName) {
|
||||
return getOssHost(bucketName).concat(StringPool.SLASH).concat(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件对象
|
||||
*
|
||||
* @param file 上传文件类
|
||||
* @return
|
||||
*/
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), file.getOriginalFilename(), file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fileName 上传文件名
|
||||
* @param file 上传文件类
|
||||
* @return
|
||||
*/
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, MultipartFile file) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, file);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, MultipartFile file) {
|
||||
return putFile(bucketName, fileName, file.getInputStream());
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String fileName, InputStream stream) {
|
||||
return putFile(ossProperties.getBucketName(), fileName, stream);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile putFile(String bucketName, String fileName, InputStream stream) {
|
||||
return put(bucketName, stream, fileName, false);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public BladeFile put(String bucketName, InputStream stream, String key, boolean cover) {
|
||||
makeBucket(bucketName);
|
||||
String originalName = key;
|
||||
key = getFileName(key);
|
||||
// 覆盖上传
|
||||
if (cover) {
|
||||
ossClient.putObject(getBucketName(bucketName), key, stream);
|
||||
} else {
|
||||
PutObjectResult response = ossClient.putObject(getBucketName(bucketName), key, stream);
|
||||
int retry = 0;
|
||||
int retryCount = 5;
|
||||
while (StringUtils.isEmpty(response.getETag()) && retry < retryCount) {
|
||||
response = ossClient.putObject(getBucketName(bucketName), key, stream);
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
BladeFile file = new BladeFile();
|
||||
file.setOriginalName(originalName);
|
||||
file.setName(key);
|
||||
file.setLink(fileLink(bucketName, key));
|
||||
return file;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void removeFile(String fileName) {
|
||||
ossClient.deleteObject(getBucketName(), fileName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void removeFile(String bucketName, String fileName) {
|
||||
ossClient.deleteObject(getBucketName(bucketName), fileName);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void removeFiles(List<String> fileNames) {
|
||||
fileNames.forEach(this::removeFile);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void removeFiles(String bucketName, List<String> fileNames) {
|
||||
fileNames.forEach(fileName -> removeFile(getBucketName(bucketName), fileName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据规则生成存储桶名称规则
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
private String getBucketName() {
|
||||
return getBucketName(ossProperties.getBucketName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据规则生成存储桶名称规则
|
||||
*
|
||||
* @param bucketName 存储桶名称
|
||||
* @return String
|
||||
*/
|
||||
private String getBucketName(String bucketName) {
|
||||
return ossRule.bucketName(bucketName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据规则生成文件名称规则
|
||||
*
|
||||
* @param originalFilename 原始文件名
|
||||
* @return string
|
||||
*/
|
||||
private String getFileName(String originalFilename) {
|
||||
return ossRule.fileName(originalFilename);
|
||||
}
|
||||
|
||||
public String getUploadToken() {
|
||||
return getUploadToken(ossProperties.getBucketName());
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 过期时间
|
||||
* <p>
|
||||
* 获取上传凭证,普通上传
|
||||
*/
|
||||
public String getUploadToken(String bucketName) {
|
||||
// 默认过期时间2小时
|
||||
return getUploadToken(bucketName, ossProperties.getArgs().get("expireTime", 3600L));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO 上传大小限制、基础路径
|
||||
* <p>
|
||||
* 获取上传凭证,普通上传
|
||||
*/
|
||||
public String getUploadToken(String bucketName, long expireTime) {
|
||||
String baseDir = "upload";
|
||||
|
||||
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
|
||||
Date expiration = new Date(expireEndTime);
|
||||
|
||||
PolicyConditions policyConds = new PolicyConditions();
|
||||
// 默认大小限制10M
|
||||
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, ossProperties.getArgs().get("contentLengthRange", 10485760));
|
||||
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, baseDir);
|
||||
|
||||
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
|
||||
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
|
||||
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
|
||||
String postSignature = ossClient.calculatePostSignature(postPolicy);
|
||||
|
||||
Map<String, String> respMap = new LinkedHashMap<>(16);
|
||||
respMap.put("accessid", ossProperties.getAccessKey());
|
||||
respMap.put("policy", encodedPolicy);
|
||||
respMap.put("signature", postSignature);
|
||||
respMap.put("dir", baseDir);
|
||||
respMap.put("host", getOssHost(bucketName));
|
||||
respMap.put("expire", String.valueOf(expireEndTime / 1000));
|
||||
return JsonUtil.toJson(respMap);
|
||||
}
|
||||
|
||||
public String getOssHost(String bucketName) {
|
||||
String prefix = ossProperties.getEndpoint().contains("https://") ? "https://" : "http://";
|
||||
return prefix + getBucketName(bucketName) + StringPool.DOT + ossProperties.getEndpoint().replaceFirst(prefix, StringPool.EMPTY);
|
||||
}
|
||||
|
||||
public String getOssHost() {
|
||||
return getOssHost(ossProperties.getBucketName());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.oss.config;
|
||||
|
||||
import com.aliyun.oss.ClientConfiguration;
|
||||
import com.aliyun.oss.OSSClient;
|
||||
import com.aliyun.oss.common.auth.CredentialsProvider;
|
||||
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.oss.AliossTemplate;
|
||||
import org.springblade.core.oss.props.OssProperties;
|
||||
import org.springblade.core.oss.rule.BladeOssRule;
|
||||
import org.springblade.core.oss.rule.OssRule;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Alioss配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@AutoConfigureAfter(QiniuConfiguration.class)
|
||||
@EnableConfigurationProperties(OssProperties.class)
|
||||
@ConditionalOnProperty(value = "oss.name", havingValue = "alioss")
|
||||
public class AliossConfiguration {
|
||||
|
||||
private OssProperties ossProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OssRule.class)
|
||||
public OssRule ossRule() {
|
||||
return new BladeOssRule();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(OSSClient.class)
|
||||
public OSSClient ossClient() {
|
||||
// 创建ClientConfiguration。ClientConfiguration是OSSClient的配置类,可配置代理、连接超时、最大连接数等参数。
|
||||
ClientConfiguration conf = new ClientConfiguration();
|
||||
// 设置OSSClient允许打开的最大HTTP连接数,默认为1024个。
|
||||
conf.setMaxConnections(1024);
|
||||
// 设置Socket层传输数据的超时时间,默认为50000毫秒。
|
||||
conf.setSocketTimeout(50000);
|
||||
// 设置建立连接的超时时间,默认为50000毫秒。
|
||||
conf.setConnectionTimeout(50000);
|
||||
// 设置从连接池中获取连接的超时时间(单位:毫秒),默认不超时。
|
||||
conf.setConnectionRequestTimeout(1000);
|
||||
// 设置连接空闲超时时间。超时则关闭连接,默认为60000毫秒。
|
||||
conf.setIdleConnectionTime(60000);
|
||||
// 设置失败请求重试次数,默认为3次。
|
||||
conf.setMaxErrorRetry(5);
|
||||
CredentialsProvider credentialsProvider = new DefaultCredentialProvider(ossProperties.getAccessKey(), ossProperties.getSecretKey());
|
||||
return new OSSClient(ossProperties.getEndpoint(), credentialsProvider, conf);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(AliossTemplate.class)
|
||||
@ConditionalOnBean({OSSClient.class, OssRule.class})
|
||||
public AliossTemplate aliossTemplate(OSSClient ossClient, OssRule ossRule) {
|
||||
return new AliossTemplate(ossClient, ossProperties, ossRule);
|
||||
}
|
||||
|
||||
}
|
@ -39,8 +39,8 @@ import org.springframework.context.annotation.Configuration;
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@EnableConfigurationProperties(OssProperties.class)
|
||||
@ConditionalOnProperty(value = "oss.enable", havingValue = "true")
|
||||
public class OssConfiguration {
|
||||
@ConditionalOnProperty(value = "oss.name", havingValue = "qiniu")
|
||||
public class QiniuConfiguration {
|
||||
|
||||
private OssProperties ossProperties;
|
||||
|
@ -32,4 +32,8 @@ public class BladeFile {
|
||||
* 文件名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 原始文件名
|
||||
*/
|
||||
private String originalName;
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package org.springblade.core.oss.props;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springblade.core.tool.support.Kv;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
@ -30,7 +31,7 @@ public class OssProperties {
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
private Boolean enable;
|
||||
private Boolean enabled;
|
||||
|
||||
/**
|
||||
* 对象存储名称
|
||||
@ -62,4 +63,9 @@ public class OssProperties {
|
||||
*/
|
||||
private String bucketName = "bladex";
|
||||
|
||||
/**
|
||||
* 自定义属性
|
||||
*/
|
||||
private Kv args;
|
||||
|
||||
}
|
||||
|
30
blade-core-report/pom.xml
Normal file
30
blade-core-report/pom.xml
Normal 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>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>blade-core-report</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${blade.tool.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-mybatis</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.bstek.ureport</groupId>
|
||||
<artifactId>ureport2-console</artifactId>
|
||||
<version>2.2.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.config;
|
||||
|
||||
import com.bstek.ureport.UReportPropertyPlaceholderConfigurer;
|
||||
import com.bstek.ureport.console.UReportServlet;
|
||||
import com.bstek.ureport.provider.report.ReportProvider;
|
||||
import org.springblade.core.report.props.ReportDatabaseProperties;
|
||||
import org.springblade.core.report.props.ReportProperties;
|
||||
import org.springblade.core.report.provider.DatabaseProvider;
|
||||
import org.springblade.core.report.provider.ReportPlaceholderProvider;
|
||||
import org.springblade.core.report.service.IReportFileService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
/**
|
||||
* UReport配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Order
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "report.enabled", havingValue = "true", matchIfMissing = true)
|
||||
@EnableConfigurationProperties({ReportProperties.class, ReportDatabaseProperties.class})
|
||||
@ImportResource("classpath:ureport-console-context.xml")
|
||||
public class ReportConfiguration {
|
||||
|
||||
@Bean
|
||||
public ServletRegistrationBean<Servlet> registrationBean() {
|
||||
return new ServletRegistrationBean<>(new UReportServlet(), "/ureport/*");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public UReportPropertyPlaceholderConfigurer uReportPropertyPlaceholderConfigurer(ReportProperties properties) {
|
||||
return new ReportPlaceholderProvider(properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ReportProvider reportProvider(ReportDatabaseProperties properties, IReportFileService service) {
|
||||
return new DatabaseProvider(properties, service);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.datasource;
|
||||
|
||||
import com.bstek.ureport.definition.datasource.BuildinDatasource;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
/**
|
||||
* UReport数据源配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class ReportDataSource implements BuildinDatasource {
|
||||
private static final String NAME = "ReportDataSource";
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() {
|
||||
try {
|
||||
return dataSource.getConnection();
|
||||
} catch (SQLException e) {
|
||||
log.error("report数据源链接失败");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.endpoint;
|
||||
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springblade.core.report.service.IReportFileService;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import springfox.documentation.annotations.ApiIgnore;
|
||||
|
||||
/**
|
||||
* UReport Boot版 API端点
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@ApiIgnore
|
||||
@RestController
|
||||
@RequestMapping(AppConstant.APPLICATION_REPORT_NAME + "/report/rest")
|
||||
public class ReportBootEndpoint extends ReportEndpoint {
|
||||
|
||||
public ReportBootEndpoint(IReportFileService service) {
|
||||
super(service);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.endpoint;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.report.entity.ReportFileEntity;
|
||||
import org.springblade.core.report.service.IReportFileService;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import springfox.documentation.annotations.ApiIgnore;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* UReport API端点
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@ApiIgnore
|
||||
@RestController
|
||||
@AllArgsConstructor
|
||||
@RequestMapping("/report/rest")
|
||||
public class ReportEndpoint {
|
||||
|
||||
private final IReportFileService service;
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
@GetMapping("/detail")
|
||||
public R<ReportFileEntity> detail(ReportFileEntity file) {
|
||||
ReportFileEntity detail = service.getOne(Condition.getQueryWrapper(file));
|
||||
return R.data(detail);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public R<IPage<ReportFileEntity>> list(@RequestParam Map<String, Object> file, Query query) {
|
||||
IPage<ReportFileEntity> pages = service.page(Condition.getPage(query), Condition.getQueryWrapper(file, ReportFileEntity.class));
|
||||
return R.data(pages);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*/
|
||||
@PostMapping("/remove")
|
||||
public R remove(@RequestParam String ids) {
|
||||
boolean temp = service.removeByIds(Func.toLongList(ids));
|
||||
return R.status(temp);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* UReport实体类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@TableName("blade_report_file")
|
||||
public class ReportFileEntity implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.ASSIGN_ID)
|
||||
private Long id;
|
||||
/**
|
||||
* 文件名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 文件内容
|
||||
*/
|
||||
private byte[] content;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
/**
|
||||
* 是否已删除
|
||||
*/
|
||||
@TableLogic
|
||||
private Integer isDeleted;
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.springblade.core.report.entity.ReportFileEntity;
|
||||
|
||||
/**
|
||||
* UReport Mapper
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface ReportFileMapper extends BaseMapper<ReportFileEntity> {
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.props;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* UReport配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "report.database.provider")
|
||||
public class ReportDatabaseProperties {
|
||||
private String name = "数据库文件系统";
|
||||
private String prefix = "blade-";
|
||||
private boolean disabled = false;
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.props;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* UReport配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties(prefix = "report")
|
||||
public class ReportProperties {
|
||||
private Boolean enabled = true;
|
||||
private Boolean disableHttpSessionReportCache = false;
|
||||
private Boolean disableFileProvider = true;
|
||||
private String fileStoreDir = StringPool.EMPTY;
|
||||
private Boolean debug = false;
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.provider;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.bstek.ureport.provider.report.ReportFile;
|
||||
import com.bstek.ureport.provider.report.ReportProvider;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.report.entity.ReportFileEntity;
|
||||
import org.springblade.core.report.props.ReportDatabaseProperties;
|
||||
import org.springblade.core.report.service.IReportFileService;
|
||||
import org.springblade.core.tool.constant.BladeConstant;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据库文件处理
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class DatabaseProvider implements ReportProvider {
|
||||
|
||||
private final ReportDatabaseProperties properties;
|
||||
private final IReportFileService service;
|
||||
|
||||
@Override
|
||||
public InputStream loadReport(String file) {
|
||||
ReportFileEntity reportFileEntity = service.getOne(Wrappers.<ReportFileEntity>lambdaQuery().eq(ReportFileEntity::getName, getFileName(file)));
|
||||
byte[] content = reportFileEntity.getContent();
|
||||
return new ByteArrayInputStream(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteReport(String file) {
|
||||
service.remove(Wrappers.<ReportFileEntity>lambdaUpdate().eq(ReportFileEntity::getName, getFileName(file)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReportFile> getReportFiles() {
|
||||
List<ReportFileEntity> list = service.list();
|
||||
List<ReportFile> reportFiles = new ArrayList<>();
|
||||
list.forEach(reportFileEntity -> reportFiles.add(new ReportFile(reportFileEntity.getName(), reportFileEntity.getUpdateTime())));
|
||||
return reportFiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveReport(String file, String content) {
|
||||
String fileName = getFileName(file);
|
||||
ReportFileEntity reportFileEntity = service.getOne(Wrappers.<ReportFileEntity>lambdaQuery().eq(ReportFileEntity::getName, fileName));
|
||||
Date now = DateUtil.now();
|
||||
if (reportFileEntity == null) {
|
||||
reportFileEntity = new ReportFileEntity();
|
||||
reportFileEntity.setName(fileName);
|
||||
reportFileEntity.setContent(content.getBytes());
|
||||
reportFileEntity.setCreateTime(now);
|
||||
reportFileEntity.setIsDeleted(BladeConstant.DB_NOT_DELETED);
|
||||
} else {
|
||||
reportFileEntity.setContent(content.getBytes());
|
||||
}
|
||||
reportFileEntity.setUpdateTime(now);
|
||||
service.saveOrUpdate(reportFileEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return properties.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disabled() {
|
||||
return properties.isDisabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return properties.getPrefix();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标准格式文件名
|
||||
*
|
||||
* @param name 原文件名
|
||||
*/
|
||||
private String getFileName(String name) {
|
||||
if (name.startsWith(getPrefix())) {
|
||||
name = name.substring(getPrefix().length());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.provider;
|
||||
|
||||
import com.bstek.ureport.UReportPropertyPlaceholderConfigurer;
|
||||
import org.springblade.core.report.props.ReportProperties;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* UReport自定义配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class ReportPlaceholderProvider extends UReportPropertyPlaceholderConfigurer {
|
||||
|
||||
public ReportPlaceholderProvider(ReportProperties properties) {
|
||||
Properties props = new Properties();
|
||||
props.setProperty("ureport.disableHttpSessionReportCache", properties.getDisableHttpSessionReportCache().toString());
|
||||
props.setProperty("ureport.disableFileProvider", properties.getDisableFileProvider().toString());
|
||||
props.setProperty("ureport.fileStoreDir", properties.getFileStoreDir());
|
||||
props.setProperty("ureport.debug", properties.getDebug().toString());
|
||||
this.setProperties(props);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.core.report.entity.ReportFileEntity;
|
||||
|
||||
/**
|
||||
* UReport Service
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface IReportFileService extends IService<ReportFileEntity> {
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.report.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springblade.core.report.entity.ReportFileEntity;
|
||||
import org.springblade.core.report.mapper.ReportFileMapper;
|
||||
import org.springblade.core.report.service.IReportFileService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* UReport Service
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Service
|
||||
public class ReportFileServiceImpl extends ServiceImpl<ReportFileMapper, ReportFileEntity> implements IReportFileService {
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -19,8 +19,13 @@
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
<version>0.9.1</version>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.11.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.2</version>
|
||||
</dependency>
|
||||
<!--Blade-->
|
||||
<dependency>
|
||||
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 jsonwebtoken.io
|
||||
*
|
||||
* 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 io.jsonwebtoken.impl;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* Base64Codec
|
||||
*
|
||||
* @author jjwt
|
||||
*/
|
||||
public class Base64Codec extends AbstractTextCodec {
|
||||
|
||||
@Override
|
||||
public String encode(byte[] data) {
|
||||
return Base64.getEncoder().encodeToString(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decode(String encoded) {
|
||||
return Base64.getDecoder().decode(encoded);
|
||||
}
|
||||
|
||||
}
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springblade.core.secure;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
@ -33,6 +35,13 @@ public class AuthInfo {
|
||||
private String tokenType;
|
||||
@ApiModelProperty(value = "刷新令牌")
|
||||
private String refreshToken;
|
||||
@ApiModelProperty(value = "用户ID")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long userId;
|
||||
@ApiModelProperty(value = "租户ID")
|
||||
private String tenantId;
|
||||
@ApiModelProperty(value = "第三方系统ID")
|
||||
private String oauthId;
|
||||
@ApiModelProperty(value = "头像")
|
||||
private String avatar = "https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png";
|
||||
@ApiModelProperty(value = "角色名")
|
||||
|
@ -39,7 +39,7 @@ public class BladeUser implements Serializable {
|
||||
* 用户id
|
||||
*/
|
||||
@ApiModelProperty(hidden = true)
|
||||
private Integer userId;
|
||||
private Long userId;
|
||||
/**
|
||||
* 租户ID
|
||||
*/
|
||||
|
@ -20,7 +20,6 @@ import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.secure.aspect.AuthAspect;
|
||||
import org.springblade.core.secure.interceptor.ClientInterceptor;
|
||||
import org.springblade.core.secure.interceptor.SecureInterceptor;
|
||||
import org.springblade.core.secure.props.BladeClientProperties;
|
||||
import org.springblade.core.secure.props.BladeSecureProperties;
|
||||
import org.springblade.core.secure.provider.ClientDetailsServiceImpl;
|
||||
import org.springblade.core.secure.provider.IClientDetailsService;
|
||||
@ -42,26 +41,24 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
@Order
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@EnableConfigurationProperties({BladeSecureProperties.class, BladeClientProperties.class})
|
||||
@EnableConfigurationProperties({BladeSecureProperties.class})
|
||||
public class SecureConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final SecureRegistry secureRegistry;
|
||||
|
||||
private final BladeSecureProperties secureProperties;
|
||||
|
||||
private final BladeClientProperties clientProperties;
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
clientProperties.getClient().forEach(cs -> registry.addInterceptor(new ClientInterceptor(cs.getClientId())).addPathPatterns(cs.getPathPatterns()));
|
||||
secureProperties.getClient().forEach(cs -> registry.addInterceptor(new ClientInterceptor(cs.getClientId())).addPathPatterns(cs.getPathPatterns()));
|
||||
|
||||
if (secureRegistry.isEnable()) {
|
||||
if (secureRegistry.isEnabled()) {
|
||||
registry.addInterceptor(new SecureInterceptor())
|
||||
.excludePathPatterns(secureRegistry.getExcludePatterns())
|
||||
.excludePathPatterns(secureRegistry.getDefaultExcludePatterns())
|
||||
.excludePathPatterns(secureProperties.getExcludePatterns());
|
||||
.excludePathPatterns(secureProperties.getSkipUrl());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ public class ClientInterceptor extends HandlerInterceptorAdapter {
|
||||
} else {
|
||||
log.warn("客户端认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(), WebUtil.getIP(request), JsonUtil.toJson(request.getParameterMap()));
|
||||
R result = R.fail(ResultCode.UN_AUTHORIZED);
|
||||
response.setHeader(BladeConstant.CONTENT_TYPE_NAME, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
response.setHeader(BladeConstant.CONTENT_TYPE_NAME, MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding(BladeConstant.UTF_8);
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
try {
|
||||
|
@ -48,7 +48,7 @@ public class SecureInterceptor extends HandlerInterceptorAdapter {
|
||||
log.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", request.getRequestURI(), WebUtil.getIP(request), JsonUtil.toJson(request.getParameterMap()));
|
||||
R result = R.fail(ResultCode.UN_AUTHORIZED);
|
||||
response.setCharacterEncoding(BladeConstant.UTF_8);
|
||||
response.setHeader(BladeConstant.CONTENT_TYPE_NAME, MediaType.APPLICATION_JSON_UTF8_VALUE);
|
||||
response.setHeader(BladeConstant.CONTENT_TYPE_NAME, MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
try {
|
||||
response.getWriter().write(Objects.requireNonNull(JsonUtil.toJson(result)));
|
||||
|
@ -27,9 +27,11 @@ import java.util.List;
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("blade.secure.url")
|
||||
@ConfigurationProperties("blade.secure")
|
||||
public class BladeSecureProperties {
|
||||
|
||||
private final List<String> excludePatterns = new ArrayList<>();
|
||||
private final List<ClientSecure> client = new ArrayList<>();
|
||||
|
||||
private final List<String> skipUrl = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ import java.util.List;
|
||||
@Data
|
||||
public class SecureRegistry {
|
||||
|
||||
private boolean enable = false;
|
||||
private boolean enabled = false;
|
||||
|
||||
private final List<String> defaultExcludePatterns = new ArrayList<>();
|
||||
|
||||
@ -38,7 +38,6 @@ public class SecureRegistry {
|
||||
public SecureRegistry() {
|
||||
this.defaultExcludePatterns.add("/actuator/health/**");
|
||||
this.defaultExcludePatterns.add("/v2/api-docs/**");
|
||||
this.defaultExcludePatterns.add("/v2/api-docs-ext/**");
|
||||
this.defaultExcludePatterns.add("/auth/**");
|
||||
this.defaultExcludePatterns.add("/token/**");
|
||||
this.defaultExcludePatterns.add("/log/**");
|
||||
|
@ -27,6 +27,7 @@ import org.springblade.core.secure.constant.SecureConstant;
|
||||
import org.springblade.core.secure.exception.SecureException;
|
||||
import org.springblade.core.secure.provider.IClientDetails;
|
||||
import org.springblade.core.secure.provider.IClientDetailsService;
|
||||
import org.springblade.core.tool.constant.RoleConstant;
|
||||
import org.springblade.core.tool.utils.*;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
@ -52,9 +53,9 @@ public class SecureUtil {
|
||||
private final static String TENANT_ID = TokenConstant.TENANT_ID;
|
||||
private final static String CLIENT_ID = TokenConstant.CLIENT_ID;
|
||||
private final static Integer AUTH_LENGTH = TokenConstant.AUTH_LENGTH;
|
||||
private static String BASE64_SECURITY = Base64.getEncoder().encodeToString(TokenConstant.SIGN_KEY.getBytes(Charsets.UTF_8));
|
||||
private static final String BASE64_SECURITY = Base64.getEncoder().encodeToString(TokenConstant.SIGN_KEY.getBytes(Charsets.UTF_8));
|
||||
|
||||
private static IClientDetailsService clientDetailsService;
|
||||
private static final IClientDetailsService clientDetailsService;
|
||||
|
||||
static {
|
||||
clientDetailsService = SpringUtil.getBean(IClientDetailsService.class);
|
||||
@ -94,7 +95,7 @@ public class SecureUtil {
|
||||
return null;
|
||||
}
|
||||
String clientId = Func.toStr(claims.get(SecureUtil.CLIENT_ID));
|
||||
Integer userId = Func.toInt(claims.get(SecureUtil.USER_ID));
|
||||
Long userId = Func.toLong(claims.get(SecureUtil.USER_ID));
|
||||
String tenantId = Func.toStr(claims.get(SecureUtil.TENANT_ID));
|
||||
String roleId = Func.toStr(claims.get(SecureUtil.ROLE_ID));
|
||||
String account = Func.toStr(claims.get(SecureUtil.ACCOUNT));
|
||||
@ -111,13 +112,21 @@ public class SecureUtil {
|
||||
return bladeUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为超管
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean isAdministrator() {
|
||||
return StringUtil.containsAny(getUserRole(), RoleConstant.ADMIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户id
|
||||
*
|
||||
* @return userId
|
||||
*/
|
||||
public static Integer getUserId() {
|
||||
public static Long getUserId() {
|
||||
BladeUser user = getUser();
|
||||
return (null == user) ? -1 : user.getUserId();
|
||||
}
|
||||
@ -128,7 +137,7 @@ public class SecureUtil {
|
||||
* @param request request
|
||||
* @return userId
|
||||
*/
|
||||
public static Integer getUserId(HttpServletRequest request) {
|
||||
public static Long getUserId(HttpServletRequest request) {
|
||||
BladeUser user = getUser(request);
|
||||
return (null == user) ? -1 : user.getUserId();
|
||||
}
|
||||
@ -246,12 +255,17 @@ public class SecureUtil {
|
||||
*/
|
||||
public static Claims getClaims(HttpServletRequest request) {
|
||||
String auth = request.getHeader(SecureUtil.HEADER);
|
||||
if ((auth != null) && (auth.length() > AUTH_LENGTH)) {
|
||||
if (StringUtil.isNotBlank(auth) && auth.length() > AUTH_LENGTH) {
|
||||
String headStr = auth.substring(0, 6).toLowerCase();
|
||||
if (headStr.compareTo(SecureUtil.BEARER) == 0) {
|
||||
auth = auth.substring(7);
|
||||
return SecureUtil.parseJWT(auth);
|
||||
}
|
||||
} else {
|
||||
String parameter = request.getParameter(SecureUtil.HEADER);
|
||||
if (StringUtil.isNotBlank(parameter)) {
|
||||
return SecureUtil.parseJWT(parameter);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -283,8 +297,8 @@ public class SecureUtil {
|
||||
*/
|
||||
public static Claims parseJWT(String jsonWebToken) {
|
||||
try {
|
||||
return Jwts.parser()
|
||||
.setSigningKey(Base64.getDecoder().decode(BASE64_SECURITY))
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(Base64.getDecoder().decode(BASE64_SECURITY)).build()
|
||||
.parseClaimsJws(jsonWebToken).getBody();
|
||||
} catch (Exception ex) {
|
||||
return null;
|
||||
@ -325,10 +339,10 @@ public class SecureUtil {
|
||||
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
|
||||
|
||||
//添加构成JWT的类
|
||||
JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JsonWebToken")
|
||||
JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
|
||||
.setIssuer(issuer)
|
||||
.setAudience(audience)
|
||||
.signWith(signatureAlgorithm, signingKey);
|
||||
.signWith(signingKey);
|
||||
|
||||
//设置JWT参数
|
||||
user.forEach(builder::claim);
|
||||
|
36
blade-core-social/pom.xml
Normal file
36
blade-core-social/pom.xml
Normal file
@ -0,0 +1,36 @@
|
||||
<?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>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>blade-core-social</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<version>${blade.tool.version}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!--Blade-->
|
||||
<dependency>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-core-tool</artifactId>
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<!-- 第三方登陆 -->
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
<version>1.15.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.social.config;
|
||||
|
||||
import com.xkcoding.http.HttpUtil;
|
||||
import com.xkcoding.http.support.Http;
|
||||
import com.xkcoding.http.support.httpclient.HttpClientImpl;
|
||||
import org.springblade.core.social.props.SocialProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* SocialConfiguration
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(SocialProperties.class)
|
||||
public class SocialConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(Http.class)
|
||||
public Http simpleHttp() {
|
||||
HttpClientImpl httpClient = new HttpClientImpl();
|
||||
HttpUtil.setHttp(httpClient);
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.social.props;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* SocialProperties
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ConfigurationProperties(prefix = "social")
|
||||
public class SocialProperties {
|
||||
|
||||
/**
|
||||
* 启用
|
||||
*/
|
||||
private Boolean enabled = false;
|
||||
|
||||
/**
|
||||
* 域名地址
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private Map<AuthDefaultSource, AuthConfig> oauth = Maps.newHashMap();
|
||||
|
||||
/**
|
||||
* 别名
|
||||
*/
|
||||
private Map<String, String> alias = Maps.newHashMap();
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.social.utils;
|
||||
|
||||
import me.zhyd.oauth.config.AuthConfig;
|
||||
import me.zhyd.oauth.config.AuthDefaultSource;
|
||||
import me.zhyd.oauth.exception.AuthException;
|
||||
import me.zhyd.oauth.request.*;
|
||||
import org.springblade.core.social.props.SocialProperties;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* SocialUtil
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class SocialUtil {
|
||||
|
||||
/**
|
||||
* 根据具体的授权来源,获取授权请求工具类
|
||||
*
|
||||
* @param source 授权来源
|
||||
* @return AuthRequest
|
||||
*/
|
||||
public static AuthRequest getAuthRequest(String source, SocialProperties socialProperties) {
|
||||
AuthDefaultSource authSource = Objects.requireNonNull(AuthDefaultSource.valueOf(source.toUpperCase()));
|
||||
AuthConfig authConfig = socialProperties.getOauth().get(authSource);
|
||||
if (authConfig == null) {
|
||||
throw new AuthException("未获取到有效的Auth配置");
|
||||
}
|
||||
AuthRequest authRequest = null;
|
||||
switch (authSource) {
|
||||
case GITHUB:
|
||||
authRequest = new AuthGithubRequest(authConfig);
|
||||
break;
|
||||
case GITEE:
|
||||
authRequest = new AuthGiteeRequest(authConfig);
|
||||
break;
|
||||
case OSCHINA:
|
||||
authRequest = new AuthOschinaRequest(authConfig);
|
||||
break;
|
||||
case QQ:
|
||||
authRequest = new AuthQqRequest(authConfig);
|
||||
break;
|
||||
case WECHAT_OPEN:
|
||||
authRequest = new AuthWeChatOpenRequest(authConfig);
|
||||
break;
|
||||
case WECHAT_ENTERPRISE:
|
||||
authRequest = new AuthWeChatEnterpriseRequest(authConfig);
|
||||
break;
|
||||
case WECHAT_MP:
|
||||
authRequest = new AuthWeChatMpRequest(authConfig);
|
||||
break;
|
||||
case DINGTALK:
|
||||
authRequest = new AuthDingTalkRequest(authConfig);
|
||||
break;
|
||||
case ALIPAY:
|
||||
// 支付宝在创建回调地址时,不允许使用localhost或者127.0.0.1,所以这儿的回调地址使用的局域网内的ip
|
||||
authRequest = new AuthAlipayRequest(authConfig);
|
||||
break;
|
||||
case BAIDU:
|
||||
authRequest = new AuthBaiduRequest(authConfig);
|
||||
break;
|
||||
case WEIBO:
|
||||
authRequest = new AuthWeiboRequest(authConfig);
|
||||
break;
|
||||
case CODING:
|
||||
authRequest = new AuthCodingRequest(authConfig);
|
||||
break;
|
||||
case CSDN:
|
||||
authRequest = new AuthCsdnRequest(authConfig);
|
||||
break;
|
||||
case TAOBAO:
|
||||
authRequest = new AuthTaobaoRequest(authConfig);
|
||||
break;
|
||||
case GOOGLE:
|
||||
authRequest = new AuthGoogleRequest(authConfig);
|
||||
break;
|
||||
case FACEBOOK:
|
||||
authRequest = new AuthFacebookRequest(authConfig);
|
||||
break;
|
||||
case DOUYIN:
|
||||
authRequest = new AuthDouyinRequest(authConfig);
|
||||
break;
|
||||
case LINKEDIN:
|
||||
authRequest = new AuthLinkedinRequest(authConfig);
|
||||
break;
|
||||
case MICROSOFT:
|
||||
authRequest = new AuthMicrosoftRequest(authConfig);
|
||||
break;
|
||||
case MI:
|
||||
authRequest = new AuthMiRequest(authConfig);
|
||||
break;
|
||||
case TOUTIAO:
|
||||
authRequest = new AuthToutiaoRequest(authConfig);
|
||||
break;
|
||||
case TEAMBITION:
|
||||
authRequest = new AuthTeambitionRequest(authConfig);
|
||||
break;
|
||||
case PINTEREST:
|
||||
authRequest = new AuthPinterestRequest(authConfig);
|
||||
break;
|
||||
case RENREN:
|
||||
authRequest = new AuthRenrenRequest(authConfig);
|
||||
break;
|
||||
case STACK_OVERFLOW:
|
||||
authRequest = new AuthStackOverflowRequest(authConfig);
|
||||
break;
|
||||
case HUAWEI:
|
||||
authRequest = new AuthHuaweiRequest(authConfig);
|
||||
break;
|
||||
case KUJIALE:
|
||||
authRequest = new AuthKujialeRequest(authConfig);
|
||||
break;
|
||||
case GITLAB:
|
||||
authRequest = new AuthGitlabRequest(authConfig);
|
||||
break;
|
||||
case MEITUAN:
|
||||
authRequest = new AuthMeituanRequest(authConfig);
|
||||
break;
|
||||
case ELEME:
|
||||
authRequest = new AuthElemeRequest(authConfig);
|
||||
break;
|
||||
case TWITTER:
|
||||
authRequest = new AuthTwitterRequest(authConfig);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (null == authRequest) {
|
||||
throw new AuthException("未获取到有效的Auth配置");
|
||||
}
|
||||
return authRequest;
|
||||
}
|
||||
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -23,26 +23,10 @@
|
||||
<version>${blade.tool.version}</version>
|
||||
</dependency>
|
||||
<!--Swagger-->
|
||||
<dependency>
|
||||
<groupId>io.springfox</groupId>
|
||||
<artifactId>springfox-swagger2</artifactId>
|
||||
<version>${swagger.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-models</artifactId>
|
||||
<version>${swagger.models.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>swagger-bootstrap-ui</artifactId>
|
||||
<version>${swagger.bootstrapui.version}</version>
|
||||
<artifactId>knife4j-micro-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.swagger;
|
||||
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Swagger配置开关
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@EnableSwagger2WebMvc
|
||||
public @interface EnableSwagger {
|
||||
}
|
@ -16,23 +16,23 @@
|
||||
package org.springblade.core.swagger;
|
||||
|
||||
|
||||
import com.github.xiaoymin.swaggerbootstrapui.annotations.EnableSwaggerBootstrapUI;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.service.*;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spi.service.contexts.SecurityContext;
|
||||
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ -42,14 +42,14 @@ import java.util.List;
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
@EnableSwaggerBootstrapUI
|
||||
@Profile({"dev", "test"})
|
||||
@EnableSwagger
|
||||
@EnableConfigurationProperties(SwaggerProperties.class)
|
||||
@Import(BeanValidatorPluginsConfiguration.class)
|
||||
public class SwaggerAutoConfiguration {
|
||||
|
||||
private static final String DEFAULT_EXCLUDE_PATH = "/error";
|
||||
private static final String BASE_PATH = "/**";
|
||||
private static final String DEFAULT_MAPPING_PATH = "/";
|
||||
private static final String DEFAULT_BASE_PATH = "/**";
|
||||
private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**");
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ -61,29 +61,27 @@ public class SwaggerAutoConfiguration {
|
||||
public Docket api(SwaggerProperties swaggerProperties) {
|
||||
// base-path处理
|
||||
if (swaggerProperties.getBasePath().size() == 0) {
|
||||
swaggerProperties.getBasePath().add(BASE_PATH);
|
||||
swaggerProperties.getBasePath().add(DEFAULT_BASE_PATH);
|
||||
}
|
||||
//noinspection unchecked
|
||||
List<Predicate<String>> basePath = new ArrayList();
|
||||
swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path)));
|
||||
|
||||
// exclude-path处理
|
||||
if (swaggerProperties.getExcludePath().size() == 0) {
|
||||
swaggerProperties.getExcludePath().add(DEFAULT_EXCLUDE_PATH);
|
||||
swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH);
|
||||
}
|
||||
List<Predicate<String>> excludePath = new ArrayList<>();
|
||||
swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path)));
|
||||
|
||||
//noinspection Guava
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
ApiSelectorBuilder apis = new Docket(DocumentationType.SWAGGER_2)
|
||||
.host(swaggerProperties.getHost())
|
||||
.apiInfo(apiInfo(swaggerProperties)).select()
|
||||
.apis(SwaggerUtil.basePackages(swaggerProperties.getBasePackages()))
|
||||
.paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
|
||||
.build()
|
||||
.apis(SwaggerUtil.basePackages(swaggerProperties.getBasePackages()));
|
||||
|
||||
swaggerProperties.getBasePath().forEach(p -> apis.paths(PathSelectors.ant(p)));
|
||||
swaggerProperties.getExcludePath().forEach(p -> apis.paths(PathSelectors.ant(p).negate()));
|
||||
|
||||
return apis.build()
|
||||
.securitySchemes(Collections.singletonList(securitySchema()))
|
||||
.securityContexts(Collections.singletonList(securityContext()))
|
||||
.pathMapping("/");
|
||||
.securityContexts(Lists.newArrayList(securityContext()))
|
||||
.securitySchemes(Collections.singletonList(securitySchema()))
|
||||
.pathMapping(DEFAULT_MAPPING_PATH);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.swagger;
|
||||
|
||||
import org.springblade.core.launch.constant.AppConstant;
|
||||
import org.springblade.core.launch.service.LauncherService;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* 初始化Swagger配置
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public class SwaggerLauncherServiceImpl implements LauncherService {
|
||||
@Override
|
||||
public void launcher(SpringApplicationBuilder builder, String appName, String profile) {
|
||||
Properties props = System.getProperties();
|
||||
if (profile.equals(AppConstant.PROD_CODE)) {
|
||||
props.setProperty("knife4j.production", "true");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE;
|
||||
}
|
||||
}
|
@ -55,7 +55,7 @@ public class SwaggerProperties {
|
||||
/**
|
||||
* 版本
|
||||
**/
|
||||
private String version = "2.5.4";
|
||||
private String version = "2.8.0";
|
||||
/**
|
||||
* 许可证
|
||||
**/
|
||||
|
@ -17,10 +17,10 @@ package org.springblade.core.swagger;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import springfox.documentation.RequestHandler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Swagger工具类
|
||||
|
@ -0,0 +1 @@
|
||||
org.springblade.core.swagger.SwaggerLauncherServiceImpl
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@ -31,7 +31,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>26.0-jre</version>
|
||||
<version>30.0-jre</version>
|
||||
</dependency>
|
||||
<!-- okhttp -->
|
||||
<dependency>
|
||||
@ -67,6 +67,11 @@
|
||||
<artifactId>protostuff-runtime</artifactId>
|
||||
<version>${protostuff.version}</version>
|
||||
</dependency>
|
||||
<!--Redis-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -16,9 +16,11 @@
|
||||
package org.springblade.core.tool.config;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.jackson.BladeJavaTimeModule;
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
@ -40,6 +42,7 @@ import java.util.TimeZone;
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnClass(ObjectMapper.class)
|
||||
@AutoConfigureBefore(JacksonAutoConfiguration.class)
|
||||
public class JacksonConfiguration {
|
||||
@ -59,8 +62,8 @@ public class JacksonConfiguration {
|
||||
//序列化时,日期的统一格式
|
||||
objectMapper.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA));
|
||||
//序列化处理
|
||||
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
|
||||
objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
|
||||
objectMapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
|
||||
objectMapper.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true);
|
||||
objectMapper.findAndRegisterModules();
|
||||
//失败处理
|
||||
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
|
@ -19,11 +19,7 @@ package org.springblade.core.tool.config;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.jackson.MappingApiJackson2HttpMessageConverter;
|
||||
import org.springblade.core.tool.support.xss.XssFilter;
|
||||
import org.springblade.core.tool.support.xss.XssProperties;
|
||||
import org.springblade.core.tool.utils.Charsets;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@ -31,7 +27,6 @@ import org.springframework.http.converter.*;
|
||||
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -46,8 +41,6 @@ public class MessageConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
private final XssProperties xssProperties;
|
||||
|
||||
/**
|
||||
* 使用 JACKSON 作为JSON MessageConverter
|
||||
*/
|
||||
@ -61,20 +54,4 @@ public class MessageConfiguration implements WebMvcConfigurer {
|
||||
converters.add(new MappingApiJackson2HttpMessageConverter(objectMapper));
|
||||
}
|
||||
|
||||
/**
|
||||
* 防XSS注入
|
||||
*
|
||||
* @return FilterRegistrationBean
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean xssFilterRegistration() {
|
||||
FilterRegistrationBean registration = new FilterRegistrationBean();
|
||||
registration.setDispatcherTypes(DispatcherType.REQUEST);
|
||||
registration.setFilter(new XssFilter(xssProperties));
|
||||
registration.addUrlPatterns("/*");
|
||||
registration.setName("xssFilter");
|
||||
registration.setOrder(Ordered.LOWEST_PRECEDENCE);
|
||||
return registration;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,10 +13,12 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.boot.config;
|
||||
package org.springblade.core.tool.config;
|
||||
|
||||
import org.springblade.core.boot.redis.RedisKeySerializer;
|
||||
import org.springblade.core.tool.redis.RedisKeySerializer;
|
||||
import org.springblade.core.tool.utils.RedisUtil;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.cache.CacheManager;
|
||||
@ -78,4 +80,10 @@ public class RedisTemplateConfiguration {
|
||||
.cacheDefaults(redisCacheConfiguration).build();
|
||||
}
|
||||
|
||||
@Bean(name = "redisUtil")
|
||||
@ConditionalOnBean(RedisTemplate.class)
|
||||
public RedisUtil redisUtils(RedisTemplate<String, Object> redisTemplate) {
|
||||
return new RedisUtil(redisTemplate);
|
||||
}
|
||||
|
||||
}
|
@ -16,9 +16,7 @@
|
||||
package org.springblade.core.tool.config;
|
||||
|
||||
|
||||
import org.springblade.core.tool.support.xss.XssProperties;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
@ -32,7 +30,6 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
*/
|
||||
@Configuration
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@EnableConfigurationProperties(XssProperties.class)
|
||||
public class ToolConfiguration implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
@ -41,7 +38,7 @@ public class ToolConfiguration implements WebMvcConfigurer {
|
||||
* @return SpringUtil
|
||||
*/
|
||||
@Bean
|
||||
public SpringUtil springUtils() {
|
||||
public SpringUtil springUtil() {
|
||||
return new SpringUtil();
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* 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 org.springblade.core.tool.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.support.xss.XssFilter;
|
||||
import org.springblade.core.tool.support.xss.XssProperties;
|
||||
import org.springblade.core.tool.support.xss.XssUrlProperties;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
|
||||
/**
|
||||
* Xss配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Configuration
|
||||
@AllArgsConstructor
|
||||
@ConditionalOnProperty(value = "blade.xss.enabled", havingValue = "true")
|
||||
@EnableConfigurationProperties({XssProperties.class, XssUrlProperties.class})
|
||||
public class XssConfiguration {
|
||||
|
||||
private final XssProperties xssProperties;
|
||||
private final XssUrlProperties xssUrlProperties;
|
||||
|
||||
/**
|
||||
* 防XSS注入
|
||||
*/
|
||||
@Bean
|
||||
public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
|
||||
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
|
||||
registration.setDispatcherTypes(DispatcherType.REQUEST);
|
||||
registration.setFilter(new XssFilter(xssProperties, xssUrlProperties));
|
||||
registration.addUrlPatterns("/*");
|
||||
registration.setName("xssFilter");
|
||||
registration.setOrder(Ordered.LOWEST_PRECEDENCE);
|
||||
return registration;
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ package org.springblade.core.tool.jackson;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.json.JsonReadFeature;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
@ -97,7 +98,7 @@ public class JsonUtil {
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(String content, TypeReference<?> typeReference) {
|
||||
public static <T> T parse(String content, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return getInstance().readValue(content, typeReference);
|
||||
} catch (IOException e) {
|
||||
@ -130,7 +131,7 @@ public class JsonUtil {
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(byte[] bytes, TypeReference<?> typeReference) {
|
||||
public static <T> T parse(byte[] bytes, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return getInstance().readValue(bytes, typeReference);
|
||||
} catch (IOException e) {
|
||||
@ -162,7 +163,7 @@ public class JsonUtil {
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(InputStream in, TypeReference<?> typeReference) {
|
||||
public static <T> T parse(InputStream in, TypeReference<T> typeReference) {
|
||||
try {
|
||||
return getInstance().readValue(in, typeReference);
|
||||
} catch (IOException e) {
|
||||
@ -184,7 +185,7 @@ public class JsonUtil {
|
||||
content = StringPool.LEFT_SQ_BRACKET + content + StringPool.RIGHT_SQ_BRACKET;
|
||||
}
|
||||
|
||||
List<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<T>>() {
|
||||
List<Map<String, Object>> list = getInstance().readValue(content, new TypeReference<List<Map<String, Object>>>() {
|
||||
});
|
||||
List<T> result = new ArrayList<>();
|
||||
for (Map<String, Object> map : list) {
|
||||
@ -208,7 +209,7 @@ public class JsonUtil {
|
||||
|
||||
public static <T> Map<String, T> toMap(String content, Class<T> valueTypeRef) {
|
||||
try {
|
||||
Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, T>>() {
|
||||
Map<String, Map<String, Object>> map = getInstance().readValue(content, new TypeReference<Map<String, Map<String, Object>>>() {
|
||||
});
|
||||
Map<String, T> result = new HashMap<>(16);
|
||||
for (Map.Entry<String, Map<String, Object>> entry : map.entrySet()) {
|
||||
@ -305,8 +306,8 @@ public class JsonUtil {
|
||||
//序列化时,日期的统一格式
|
||||
super.setDateFormat(new SimpleDateFormat(DateUtil.PATTERN_DATETIME, Locale.CHINA));
|
||||
//序列化处理
|
||||
super.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
|
||||
super.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
|
||||
super.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true);
|
||||
super.configure(JsonReadFeature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER.mappedFeature(), true);
|
||||
super.findAndRegisterModules();
|
||||
//失败处理
|
||||
super.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
|
@ -16,6 +16,8 @@
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -29,15 +31,19 @@ import java.util.List;
|
||||
@Data
|
||||
public class BaseNode implements INode {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
protected Integer id;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
protected Long id;
|
||||
|
||||
/**
|
||||
* 父节点ID
|
||||
*/
|
||||
protected Integer parentId;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
protected Long parentId;
|
||||
|
||||
/**
|
||||
* 子孙节点
|
||||
@ -45,4 +51,22 @@ public class BaseNode implements INode {
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
protected List<INode> children = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 是否有子孙节点
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private Boolean hasChildren;
|
||||
|
||||
/**
|
||||
* 是否有子孙节点
|
||||
*/
|
||||
@Override
|
||||
public Boolean getHasChildren() {
|
||||
if (children.size() > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return this.hasChildren;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,12 +28,14 @@ import lombok.EqualsAndHashCode;
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ForestNode extends BaseNode {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 节点内容
|
||||
*/
|
||||
private Object content;
|
||||
|
||||
public ForestNode(Integer id, Integer parentId, Object content) {
|
||||
public ForestNode(Long id, Long parentId, Object content) {
|
||||
this.id = id;
|
||||
this.parentId = parentId;
|
||||
this.content = content;
|
||||
|
@ -33,7 +33,7 @@ public class ForestNodeManager<T extends INode> {
|
||||
/**
|
||||
* 森林的父节点ID
|
||||
*/
|
||||
private List<Integer> parentIds = new ArrayList<>();
|
||||
private List<Long> parentIds = new ArrayList<>();
|
||||
|
||||
public ForestNodeManager(List<T> items) {
|
||||
list = items;
|
||||
@ -45,9 +45,9 @@ public class ForestNodeManager<T extends INode> {
|
||||
* @param id 节点ID
|
||||
* @return 对应的节点对象
|
||||
*/
|
||||
public INode getTreeNodeAT(int id) {
|
||||
public INode getTreeNodeAT(Long id) {
|
||||
for (INode forestNode : list) {
|
||||
if (forestNode.getId() == id) {
|
||||
if (forestNode.getId().longValue() == id) {
|
||||
return forestNode;
|
||||
}
|
||||
}
|
||||
@ -59,7 +59,7 @@ public class ForestNodeManager<T extends INode> {
|
||||
*
|
||||
* @param parentId 父节点ID
|
||||
*/
|
||||
public void addParentId(Integer parentId) {
|
||||
public void addParentId(Long parentId) {
|
||||
parentIds.add(parentId);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -22,21 +23,21 @@ import java.util.List;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
public interface INode {
|
||||
public interface INode extends Serializable {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*
|
||||
* @return Integer
|
||||
*/
|
||||
Integer getId();
|
||||
Long getId();
|
||||
|
||||
/**
|
||||
* 父主键
|
||||
*
|
||||
* @return Integer
|
||||
*/
|
||||
Integer getParentId();
|
||||
Long getParentId();
|
||||
|
||||
/**
|
||||
* 子孙节点
|
||||
@ -45,4 +46,13 @@ public interface INode {
|
||||
*/
|
||||
List<INode> getChildren();
|
||||
|
||||
/**
|
||||
* 是否有子孙节点
|
||||
*
|
||||
* @return Boolean
|
||||
*/
|
||||
default Boolean getHasChildren() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,16 +14,16 @@ public class NodeTest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<ForestNode> list = new ArrayList<>();
|
||||
list.add(new ForestNode(1, 0, "1"));
|
||||
list.add(new ForestNode(2, 0, "2"));
|
||||
list.add(new ForestNode(3, 1, "3"));
|
||||
list.add(new ForestNode(4, 2, "4"));
|
||||
list.add(new ForestNode(5, 3, "5"));
|
||||
list.add(new ForestNode(6, 4, "6"));
|
||||
list.add(new ForestNode(7, 3, "7"));
|
||||
list.add(new ForestNode(8, 5, "8"));
|
||||
list.add(new ForestNode(9, 6, "9"));
|
||||
list.add(new ForestNode(10, 9, "10"));
|
||||
list.add(new ForestNode(1L, 0L, "1"));
|
||||
list.add(new ForestNode(2L, 0L, "2"));
|
||||
list.add(new ForestNode(3L, 1L, "3"));
|
||||
list.add(new ForestNode(4L, 2L, "4"));
|
||||
list.add(new ForestNode(5L, 3L, "5"));
|
||||
list.add(new ForestNode(6L, 4L, "6"));
|
||||
list.add(new ForestNode(7L, 3L, "7"));
|
||||
list.add(new ForestNode(8L, 5L, "8"));
|
||||
list.add(new ForestNode(9L, 6L, "9"));
|
||||
list.add(new ForestNode(10L, 9L, "10"));
|
||||
List<ForestNode> tns = ForestNodeMerger.merge(list);
|
||||
tns.forEach(node ->
|
||||
System.out.println(JsonUtil.toJson(node))
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springblade.core.tool.node;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@ -27,10 +29,14 @@ import lombok.EqualsAndHashCode;
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class TreeNode extends BaseNode {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String title;
|
||||
|
||||
private Integer key;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long key;
|
||||
|
||||
private Integer value;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long value;
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.boot.redis;
|
||||
package org.springblade.core.tool.redis;
|
||||
|
||||
import org.springframework.cache.interceptor.SimpleKey;
|
||||
import org.springframework.core.convert.ConversionService;
|
@ -30,9 +30,8 @@ import java.util.HashMap;
|
||||
*/
|
||||
public class Kv extends LinkedCaseInsensitiveMap<Object> {
|
||||
|
||||
|
||||
private Kv() {
|
||||
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,8 +43,8 @@ public class Kv extends LinkedCaseInsensitiveMap<Object> {
|
||||
return new Kv();
|
||||
}
|
||||
|
||||
public static HashMap newMap() {
|
||||
return new HashMap(16);
|
||||
public static <K, V> HashMap<K, V> newMap() {
|
||||
return new HashMap<>(16);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,7 +197,9 @@ public class Kv extends LinkedCaseInsensitiveMap<Object> {
|
||||
|
||||
@Override
|
||||
public Kv clone() {
|
||||
return (Kv) super.clone();
|
||||
Kv clone = new Kv();
|
||||
clone.putAll(this);
|
||||
return clone;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
package org.springblade.core.tool.support.xss;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springblade.core.tool.utils.StringPool;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -30,6 +31,7 @@ import java.io.IOException;
|
||||
public class XssFilter implements Filter {
|
||||
|
||||
private XssProperties xssProperties;
|
||||
private XssUrlProperties xssUrlProperties;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig config) {
|
||||
@ -39,7 +41,7 @@ public class XssFilter implements Filter {
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
String path = ((HttpServletRequest) request).getServletPath();
|
||||
if (xssProperties.getExcludePatterns().stream().anyMatch(path::contains)) {
|
||||
if (isSkip(path)) {
|
||||
chain.doFilter(request, response);
|
||||
} else {
|
||||
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
|
||||
@ -47,6 +49,11 @@ public class XssFilter implements Filter {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSkip(String path) {
|
||||
return (xssUrlProperties.getExcludePatterns().stream().anyMatch(path::startsWith))
|
||||
|| (xssProperties.getSkipUrl().stream().map(url -> url.replace("/**", StringPool.EMPTY)).anyMatch(path::startsWith));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
|
@ -15,9 +15,7 @@
|
||||
*/
|
||||
package org.springblade.core.tool.support.xss;
|
||||
|
||||
import org.springblade.core.tool.utils.Charsets;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.core.tool.utils.WebUtil;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.MediaType;
|
||||
|
||||
@ -29,6 +27,7 @@ import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -49,15 +48,9 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
*/
|
||||
private final static HtmlFilter HTML_FILTER = new HtmlFilter();
|
||||
|
||||
/**
|
||||
* 缓存报文,支持多次读取流
|
||||
*/
|
||||
private final byte[] body;
|
||||
|
||||
public XssHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
|
||||
public XssHttpServletRequestWrapper(HttpServletRequest request) {
|
||||
super(request);
|
||||
orgRequest = request;
|
||||
body = WebUtil.getRequestBytes(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -67,51 +60,67 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
|
||||
//为空,直接返回
|
||||
if (null == super.getHeader(HttpHeaders.CONTENT_TYPE)) {
|
||||
return super.getInputStream();
|
||||
}
|
||||
|
||||
//非json类型,直接返回
|
||||
if (!super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)
|
||||
&& !super.getHeader(HttpHeaders.CONTENT_TYPE).equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
|
||||
if (super.getHeader(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
|
||||
return super.getInputStream();
|
||||
}
|
||||
|
||||
//为空,直接返回
|
||||
String requestStr = WebUtil.getRequestStr(orgRequest, body);
|
||||
if (StringUtil.isBlank(requestStr)) {
|
||||
return super.getInputStream();
|
||||
}
|
||||
|
||||
requestStr = xssEncode(requestStr);
|
||||
|
||||
final ByteArrayInputStream bis = new ByteArrayInputStream(requestStr.getBytes(Charsets.UTF_8));
|
||||
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(inputHandlers(super.getInputStream()).getBytes());
|
||||
|
||||
return new ServletInputStream() {
|
||||
|
||||
@Override
|
||||
public int read() {
|
||||
return byteArrayInputStream.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener readListener) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return bis.read();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private String inputHandlers(ServletInputStream servletInputStream) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(servletInputStream, StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (servletInputStream != null) {
|
||||
try {
|
||||
servletInputStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return xssEncode(sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -129,7 +138,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
if (parameters == null || parameters.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
parameters[i] = xssEncode(parameters[i]);
|
||||
}
|
||||
@ -165,6 +173,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
/**
|
||||
* 获取最原始的request
|
||||
*
|
||||
* @return HttpServletRequest
|
||||
*/
|
||||
public HttpServletRequest getOrgRequest() {
|
||||
@ -173,6 +182,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
/**
|
||||
* 获取最原始的request
|
||||
*
|
||||
* @param request request
|
||||
* @return HttpServletRequest
|
||||
*/
|
||||
@ -180,7 +190,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
if (request instanceof XssHttpServletRequestWrapper) {
|
||||
return ((XssHttpServletRequestWrapper) request).getOrgRequest();
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,17 @@ import java.util.List;
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("blade.xss.url")
|
||||
@ConfigurationProperties("blade.xss")
|
||||
public class XssProperties {
|
||||
|
||||
private final List<String> excludePatterns = new ArrayList<>();
|
||||
/**
|
||||
* 开启xss
|
||||
*/
|
||||
private Boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 放行url
|
||||
*/
|
||||
private List<String> skipUrl = new ArrayList<>();
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springblade.core.secure.props;
|
||||
package org.springblade.core.tool.support.xss;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
@ -22,14 +22,14 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 客户端校验配置
|
||||
* Xss配置类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@ConfigurationProperties("blade.secure")
|
||||
public class BladeClientProperties {
|
||||
@ConfigurationProperties("blade.xss.url")
|
||||
public class XssUrlProperties {
|
||||
|
||||
private final List<ClientSecure> client = new ArrayList<>();
|
||||
private final List<String> excludePatterns = new ArrayList<>();
|
||||
|
||||
}
|
@ -1124,7 +1124,7 @@ public class Func {
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(byte[] bytes, TypeReference<?> typeReference) {
|
||||
public static <T> T parse(byte[] bytes, TypeReference<T> typeReference) {
|
||||
return JsonUtil.parse(bytes, typeReference);
|
||||
}
|
||||
|
||||
@ -1136,7 +1136,7 @@ public class Func {
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(String jsonString, TypeReference<?> typeReference) {
|
||||
public static <T> T parse(String jsonString, TypeReference<T> typeReference) {
|
||||
return JsonUtil.parse(jsonString, typeReference);
|
||||
}
|
||||
|
||||
@ -1148,7 +1148,7 @@ public class Func {
|
||||
* @param <T> T 泛型标记
|
||||
* @return Bean
|
||||
*/
|
||||
public static <T> T parse(InputStream in, TypeReference<?> typeReference) {
|
||||
public static <T> T parse(InputStream in, TypeReference<T> typeReference) {
|
||||
return JsonUtil.parse(in, typeReference);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,595 @@
|
||||
package org.springblade.core.tool.utils;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Redis工具类
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class RedisUtil {
|
||||
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
//=============================common============================
|
||||
|
||||
/**
|
||||
* 指定缓存失效时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean expire(String key, long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key 获取过期时间
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @return 时间(秒) 返回0代表为永久有效
|
||||
*/
|
||||
public long getExpire(String key) {
|
||||
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断key是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean hasKey(String key) {
|
||||
try {
|
||||
return redisTemplate.hasKey(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除缓存
|
||||
*
|
||||
* @param key 可以传一个值 或多个
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void del(String... key) {
|
||||
if (key != null && key.length > 0) {
|
||||
if (key.length == 1) {
|
||||
redisTemplate.delete(key[0]);
|
||||
} else {
|
||||
redisTemplate.delete(CollectionUtil.arrayToList(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================String=============================
|
||||
|
||||
/**
|
||||
* 普通缓存获取
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Object get(String key) {
|
||||
return key == null ? null : redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public boolean set(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入并设置时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
|
||||
* @return true成功 false 失败
|
||||
*/
|
||||
public boolean set(String key, Object value, long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
|
||||
} else {
|
||||
set(key, value);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 普通缓存放入并设置时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间
|
||||
* @param timeUnit 时间单位
|
||||
* @return true成功 false 失败
|
||||
*/
|
||||
public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.opsForValue().set(key, value, time, timeUnit);
|
||||
} else {
|
||||
set(key, value);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递增
|
||||
*
|
||||
* @param key 键
|
||||
* @param delta 要增加几(大于0)
|
||||
* @return long
|
||||
*/
|
||||
public long incr(String key, long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递增因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递减
|
||||
*
|
||||
* @param key 键
|
||||
* @param delta 要减少几(小于0)
|
||||
* @return long
|
||||
*/
|
||||
public long decr(String key, long delta) {
|
||||
if (delta < 0) {
|
||||
throw new RuntimeException("递减因子必须大于0");
|
||||
}
|
||||
return redisTemplate.opsForValue().increment(key, -delta);
|
||||
}
|
||||
|
||||
//================================Map=================================
|
||||
|
||||
/**
|
||||
* HashGet
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 不能为null
|
||||
* @return 值
|
||||
*/
|
||||
public Object hget(String key, String item) {
|
||||
return redisTemplate.opsForHash().get(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取hashKey对应的所有键值
|
||||
*
|
||||
* @param key 键
|
||||
* @return 对应的多个键值
|
||||
*/
|
||||
public Map<Object, Object> hmget(String key) {
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* HashSet
|
||||
*
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @return true 成功 false 失败
|
||||
*/
|
||||
public boolean hmset(String key, Map<String, Object> map) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* HashSet 并设置时间
|
||||
*
|
||||
* @param key 键
|
||||
* @param map 对应多个键值
|
||||
* @param time 时间(秒)
|
||||
* @return true成功 false失败
|
||||
*/
|
||||
public boolean hmset(String key, Map<String, Object> map, long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public boolean hset(String key, String item, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 向一张hash表中放入数据,如果不存在将创建
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param value 值
|
||||
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
|
||||
* @return true 成功 false失败
|
||||
*/
|
||||
public boolean hset(String key, String item, Object value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForHash().put(key, item, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除hash表中的值
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 可以使多个 不能为null
|
||||
*/
|
||||
public void hdel(String key, Object... item) {
|
||||
redisTemplate.opsForHash().delete(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断hash表中是否有该项的值
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @param item 项 不能为null
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean hHasKey(String key, String item) {
|
||||
return redisTemplate.opsForHash().hasKey(key, item);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要增加几(大于0)
|
||||
* @return double
|
||||
*/
|
||||
public double hincr(String key, String item, double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, by);
|
||||
}
|
||||
|
||||
/**
|
||||
* hash递减
|
||||
*
|
||||
* @param key 键
|
||||
* @param item 项
|
||||
* @param by 要减少记(小于0)
|
||||
* @return double
|
||||
*/
|
||||
public double hdecr(String key, String item, double by) {
|
||||
return redisTemplate.opsForHash().increment(key, item, -by);
|
||||
}
|
||||
|
||||
//============================set=============================
|
||||
|
||||
/**
|
||||
* 根据key获取Set中的所有值
|
||||
*
|
||||
* @param key 键
|
||||
* @return Set
|
||||
*/
|
||||
public Set<Object> sGet(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().members(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据value从一个set中查询,是否存在
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public boolean sHasKey(String key, Object value) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().isMember(key, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将数据放入set缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public long sSet(String key, Object... values) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().add(key, values);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将set数据放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param time 时间(秒)
|
||||
* @param values 值 可以是多个
|
||||
* @return 成功个数
|
||||
*/
|
||||
public long sSetAndTime(String key, long time, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().add(key, values);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取set缓存的长度
|
||||
*
|
||||
* @param key 键
|
||||
* @return long
|
||||
*/
|
||||
public long sGetSetSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForSet().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除值为value的
|
||||
*
|
||||
* @param key 键
|
||||
* @param values 值 可以是多个
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public long setRemove(String key, Object... values) {
|
||||
try {
|
||||
Long count = redisTemplate.opsForSet().remove(key, values);
|
||||
return count;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
//===============================list=================================
|
||||
|
||||
/**
|
||||
* 获取list缓存的内容
|
||||
*
|
||||
* @param key 键
|
||||
* @param start 开始
|
||||
* @param end 结束 0 到 -1代表所有值
|
||||
* @return List
|
||||
*/
|
||||
public List<Object> lGet(String key, long start, long end) {
|
||||
try {
|
||||
return redisTemplate.opsForList().range(key, start, end);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取list缓存的长度
|
||||
*
|
||||
* @param key 键
|
||||
* @return long
|
||||
*/
|
||||
public long lGetListSize(String key) {
|
||||
try {
|
||||
return redisTemplate.opsForList().size(key);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过索引 获取list中的值
|
||||
*
|
||||
* @param key 键
|
||||
* @param index 索引
|
||||
* @return Object
|
||||
*/
|
||||
public Object lGetIndex(String key, long index) {
|
||||
try {
|
||||
return redisTemplate.opsForList().index(key, index);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean lSet(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean lSet(String key, Object value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean lSet(String key, List<Object> value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将list放入缓存
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param time 时间(秒)
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean lSet(String key, List<Object> value, long time) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, value);
|
||||
if (time > 0) {
|
||||
expire(key, time);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引修改list中的某条数据
|
||||
*
|
||||
* @param key 键
|
||||
* @param index 索引
|
||||
* @param value 值
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean lUpdateIndex(String key, long index, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().set(key, index, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除N个值为value
|
||||
*
|
||||
* @param key 键
|
||||
* @param count 移除多少个
|
||||
* @param value 值
|
||||
* @return 移除的个数
|
||||
*/
|
||||
public long lRemove(String key, long count, Object value) {
|
||||
try {
|
||||
Long remove = redisTemplate.opsForList().remove(key, count, value);
|
||||
return remove;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -186,7 +186,7 @@ public class StringUtil extends org.springframework.util.StringUtils {
|
||||
* @return {String}
|
||||
*/
|
||||
public static String escapeHtml(String html) {
|
||||
return HtmlUtils.htmlEscape(html);
|
||||
return StringUtil.isBlank(html) ? StringPool.EMPTY : HtmlUtils.htmlEscape(html);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<groupId>org.springblade</groupId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@ -28,16 +28,14 @@
|
||||
<!-- Seata-->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-alibaba-seata</artifactId>
|
||||
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
|
||||
<version>${alibaba.cloud.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.seata</groupId>
|
||||
<artifactId>seata-all</artifactId>
|
||||
<artifactId>seata-spring-boot-starter</artifactId>
|
||||
<version>${alibaba.seata.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
|
||||
</project>
|
||||
|
@ -15,19 +15,7 @@
|
||||
*/
|
||||
package org.springblade.core.transaction.config;
|
||||
|
||||
import com.alibaba.druid.pool.DruidDataSource;
|
||||
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
|
||||
import io.seata.rm.datasource.DataSourceProxy;
|
||||
import org.apache.ibatis.session.SqlSessionFactory;
|
||||
import org.mybatis.spring.SqlSessionTemplate;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* 分布式事务数据源配置
|
||||
@ -37,43 +25,4 @@ import javax.sql.DataSource;
|
||||
@Configuration
|
||||
public class DataSourceConfiguration {
|
||||
|
||||
@Bean(name = "sqlSessionFactory")
|
||||
public SqlSessionFactory sqlSessionFactoryBean(DataSourceProxy dataSourceProxy) throws Exception {
|
||||
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
|
||||
bean.setDataSource(dataSourceProxy);
|
||||
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
|
||||
bean.setMapperLocations(resolver.getResources("classpath:org/springblade/**/mapper/*Mapper.xml"));
|
||||
|
||||
SqlSessionFactory factory = null;
|
||||
try {
|
||||
factory = bean.getObject();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
|
||||
return new SqlSessionTemplate(sqlSessionFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置文件获取属性构造datasource
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "spring.datasource")
|
||||
public DruidDataSource druidDataSource() {
|
||||
return new DruidDataSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造datasource代理对象
|
||||
*/
|
||||
@Primary
|
||||
@Bean("dataSource")
|
||||
public DataSourceProxy dataSourceProxy(DataSource druidDataSource) {
|
||||
return new DataSourceProxy(druidDataSource);
|
||||
}
|
||||
|
||||
}
|
||||
|
3
blade-core-transaction/src/main/resources/file.conf
Normal file
3
blade-core-transaction/src/main/resources/file.conf
Normal file
@ -0,0 +1,3 @@
|
||||
service {
|
||||
disableGlobalTransaction = false
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
registry {
|
||||
# file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
|
||||
type = "nacos"
|
||||
|
||||
nacos {
|
||||
serverAddr = "localhost"
|
||||
namespace = ""
|
||||
cluster = "default"
|
||||
}
|
||||
}
|
||||
|
||||
config {
|
||||
# file、nacos 、apollo、zk、consul、etcd3
|
||||
type = "nacos"
|
||||
|
||||
nacos {
|
||||
serverAddr = "localhost"
|
||||
namespace = ""
|
||||
}
|
||||
}
|
42
pom.xml
42
pom.xml
@ -5,7 +5,7 @@
|
||||
|
||||
<groupId>org.springblade</groupId>
|
||||
<artifactId>blade-tool</artifactId>
|
||||
<version>2.5.4</version>
|
||||
<version>2.8.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>blade-tool</name>
|
||||
<description>
|
||||
@ -36,24 +36,24 @@
|
||||
</scm>
|
||||
|
||||
<properties>
|
||||
<blade.tool.version>2.5.4</blade.tool.version>
|
||||
<blade.tool.version>2.8.0</blade.tool.version>
|
||||
|
||||
<java.version>1.8</java.version>
|
||||
<maven.plugin.version>3.8.0</maven.plugin.version>
|
||||
<swagger.version>2.9.2</swagger.version>
|
||||
<swagger.models.version>1.5.21</swagger.models.version>
|
||||
<swagger.bootstrapui.version>1.9.6</swagger.bootstrapui.version>
|
||||
<mybatis.plus.version>3.1.2</mybatis.plus.version>
|
||||
<curator.framework.version>4.0.1</curator.framework.version>
|
||||
<swagger.version>2.10.5</swagger.version>
|
||||
<swagger.models.version>1.6.2</swagger.models.version>
|
||||
<knife4j.version>2.0.6</knife4j.version>
|
||||
<mybatis.plus.version>3.4.0</mybatis.plus.version>
|
||||
<protostuff.version>1.6.0</protostuff.version>
|
||||
<disruptor.version>3.4.2</disruptor.version>
|
||||
<spring.boot.admin.version>2.1.5</spring.boot.admin.version>
|
||||
<mica.auto.version>1.1.0</mica.auto.version>
|
||||
<alibaba.cloud.version>2.1.0.RELEASE</alibaba.cloud.version>
|
||||
<alibaba.seata.version>0.9.0</alibaba.seata.version>
|
||||
<spring.boot.admin.version>2.3.0</spring.boot.admin.version>
|
||||
<mica.auto.version>1.2.5</mica.auto.version>
|
||||
<alibaba.cloud.version>2.2.3.RELEASE</alibaba.cloud.version>
|
||||
<alibaba.seata.version>1.3.0</alibaba.seata.version>
|
||||
<spring.plugin.version>2.0.0.RELEASE</spring.plugin.version>
|
||||
|
||||
<spring.boot.version>2.1.9.RELEASE</spring.boot.version>
|
||||
<spring.cloud.version>Greenwich.SR3</spring.cloud.version>
|
||||
<spring.boot.version>2.2.11.RELEASE</spring.boot.version>
|
||||
<spring.cloud.version>Hoxton.SR8</spring.cloud.version>
|
||||
<spring.platform.version>Cairo-SR8</spring.platform.version>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@ -68,15 +68,27 @@
|
||||
<module>blade-core-log</module>
|
||||
<module>blade-core-mybatis</module>
|
||||
<module>blade-core-secure</module>
|
||||
<module>blade-core-social</module>
|
||||
<module>blade-core-swagger</module>
|
||||
<module>blade-core-test</module>
|
||||
<module>blade-core-tool</module>
|
||||
<module>blade-core-oss</module>
|
||||
<module>blade-core-transaction</module>
|
||||
<module>blade-core-report</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.plugin</groupId>
|
||||
<artifactId>spring-plugin-core</artifactId>
|
||||
<version>${spring.plugin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.plugin</groupId>
|
||||
<artifactId>spring-plugin-metadata</artifactId>
|
||||
<version>${spring.plugin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
@ -183,7 +195,7 @@
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>aliyun-repos</id>
|
||||
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
|
||||
<url>https://maven.aliyun.com/nexus/content/groups/public/</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@ -193,7 +205,7 @@
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>aliyun-plugin</id>
|
||||
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
|
||||
<url>https://maven.aliyun.com/nexus/content/groups/public/</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
|
Loading…
Reference in New Issue
Block a user