parent
e71702e335
commit
dacd014980
418 changed files with 29589 additions and 0 deletions
@ -0,0 +1,17 @@ |
|||||||
|
target/ |
||||||
|
!**/src/main/**/target/ |
||||||
|
!**/src/test/**/target/ |
||||||
|
|
||||||
|
### IntelliJ IDEA ### |
||||||
|
.idea |
||||||
|
*.iws |
||||||
|
*.iml |
||||||
|
*.ipr |
||||||
|
**/src/main/resources/rebel.xml |
||||||
|
**/src/main/resources/rebel-remote.xml |
||||||
|
**/**/src/main/resources/rebel.xml |
||||||
|
**/**/src/main/resources/rebel-remote.xml |
||||||
|
|
||||||
|
# 业务相关 |
||||||
|
logs |
||||||
|
docker/*.jar |
@ -0,0 +1,22 @@ |
|||||||
|
# 说明 |
||||||
|
|
||||||
|
### 组件&服务 部署 |
||||||
|
|
||||||
|
mysql、redis、minio |
||||||
|
|
||||||
|
见[部署文档](doc/部署) |
||||||
|
|
||||||
|
### 本地运行 |
||||||
|
|
||||||
|
1. 修改`app/src/main/resources/application-dev.yml`配置 |
||||||
|
2. 启动`App` |
||||||
|
|
||||||
|
--- |
||||||
|
|
||||||
|
### 数据清理 |
||||||
|
|
||||||
|
```sql |
||||||
|
SELECT CONCAT('DELETE FROM ', TABLE_NAME, ' where is_deleted=1;') |
||||||
|
FROM information_schema.TABLES |
||||||
|
WHERE table_schema = 'smallboot'; |
||||||
|
``` |
@ -0,0 +1,56 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<artifactId>smallboot-api</artifactId> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<version>${revision}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<artifactId>app</artifactId> |
||||||
|
|
||||||
|
<name>${project.artifactId}</name> |
||||||
|
<version>${revision}</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
|
||||||
|
<description>业务开发模块</description> |
||||||
|
|
||||||
|
<properties> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>core</artifactId> |
||||||
|
<exclusions> |
||||||
|
<exclusion> |
||||||
|
<artifactId>servlet-api</artifactId> |
||||||
|
<groupId>javax.servlet</groupId> |
||||||
|
</exclusion> |
||||||
|
</exclusions> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>system</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
</dependencies> |
||||||
|
|
||||||
|
|
||||||
|
<build> |
||||||
|
<finalName>${project.name}</finalName> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId> |
||||||
|
</plugin> |
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,17 @@ |
|||||||
|
package com.zhengqing.app; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.constant.ServiceConstant; |
||||||
|
import org.springframework.boot.SpringApplication; |
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||||
|
import org.springframework.context.annotation.ComponentScan; |
||||||
|
import org.springframework.transaction.annotation.EnableTransactionManagement; |
||||||
|
|
||||||
|
|
||||||
|
@SpringBootApplication |
||||||
|
@EnableTransactionManagement |
||||||
|
@ComponentScan(basePackages = {ServiceConstant.SERVICE_BASE_PACKAGE}) |
||||||
|
public class App { |
||||||
|
public static void main(String[] args) { |
||||||
|
SpringApplication.run(App.class, args); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
# TODO 在这里修改相关配置信息 |
||||||
|
# ================================== ↓↓↓↓↓↓ smallboot配置 ↓↓↓↓↓↓ ================================== |
||||||
|
smallboot: |
||||||
|
ip: 127.0.0.1 |
||||||
|
minio: |
||||||
|
url: http://172.16.16.244:9001 |
||||||
|
accessKey: admin |
||||||
|
secretKey: password |
||||||
|
bucketName: test |
||||||
|
# redis密码 |
||||||
|
redis: |
||||||
|
database: 0 |
||||||
|
host: ${smallboot.ip} |
||||||
|
port: 6379 |
||||||
|
password: 123456 |
||||||
|
# mysql数据源连接参数 |
||||||
|
mysql: |
||||||
|
# MySQL在高版本需要指明是否进行SSL连接 解决则加上 &useSSL=false |
||||||
|
# &serverTimezone=Asia/Shanghai :解决fastjson时差13小时问题 |
||||||
|
# rewriteBatchedStatements=true =》可实现多条sql合并提交给mysql,解决MybatisPlus批量插入慢问题 |
||||||
|
url-params: ?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true |
||||||
|
master: |
||||||
|
ip: ${smallboot.ip} |
||||||
|
port: 3306 |
||||||
|
db-name: smallboot |
||||||
|
username: root |
||||||
|
password: root |
||||||
|
url-params: ${smallboot.mysql.url-params} |
||||||
|
# sql日志,默认关闭 true:开启 false:关闭 |
||||||
|
mybatis-plus-sql-log: false |
||||||
|
# api日志,默认关闭 true:开启 false:关闭 |
||||||
|
api-log: true |
@ -0,0 +1,27 @@ |
|||||||
|
server: |
||||||
|
port: 888 |
||||||
|
|
||||||
|
spring: |
||||||
|
application: |
||||||
|
name: app # 应用名称 |
||||||
|
profiles: |
||||||
|
active: dev # 环境配置 |
||||||
|
# 聚合各个基础组件配置 |
||||||
|
include: |
||||||
|
- auth |
||||||
|
- base |
||||||
|
- db |
||||||
|
- file |
||||||
|
- log |
||||||
|
- redis |
||||||
|
- swagger |
||||||
|
- web |
||||||
|
|
||||||
|
management: |
||||||
|
endpoints: |
||||||
|
web: |
||||||
|
exposure: |
||||||
|
include: '*' # 暴露监控端点 |
||||||
|
# security: |
||||||
|
# enabled: false # 关闭安全验证 |
||||||
|
|
@ -0,0 +1,46 @@ |
|||||||
|
package com.zhengqing.app; |
||||||
|
|
||||||
|
import cn.hutool.core.date.DatePattern; |
||||||
|
import cn.hutool.core.date.DateUtil; |
||||||
|
import cn.hutool.core.lang.UUID; |
||||||
|
import cn.hutool.crypto.SecureUtil; |
||||||
|
import cn.hutool.crypto.symmetric.AES; |
||||||
|
import com.zhengqing.common.base.constant.AppConstant; |
||||||
|
import com.zhengqing.common.base.util.AutoUpgradeUtil; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
public class AppTest { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void test() throws Exception { |
||||||
|
System.out.println("hello"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void test02() throws Exception { |
||||||
|
AES aes = SecureUtil.aes(AppConstant.AES_KEY); |
||||||
|
// 加密
|
||||||
|
String encrypt = aes.encryptHex("123456"); |
||||||
|
System.out.println(encrypt); |
||||||
|
// 解密
|
||||||
|
String decryptStr = aes.decryptStr(encrypt); |
||||||
|
System.out.println(decryptStr); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void test03() throws Exception { |
||||||
|
String today = DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN); |
||||||
|
String s = AutoUpgradeUtil.autoUpgrade(null); |
||||||
|
System.out.println(s); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void test04() throws Exception { |
||||||
|
System.out.println(UUID.randomUUID().toString()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.zhengqing.app; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
class ColorMain { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void test() throws Exception { |
||||||
|
|
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<artifactId>common</artifactId> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<version>${revision}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<artifactId>auth</artifactId> |
||||||
|
|
||||||
|
<name>${project.artifactId}</name> |
||||||
|
<version>${revision}</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
|
||||||
|
<properties> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>base</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- Sa-Token 权限认证, 在线文档:http://sa-token.dev33.cn/ --> |
||||||
|
<dependency> |
||||||
|
<groupId>cn.dev33</groupId> |
||||||
|
<artifactId>sa-token-spring-boot-starter</artifactId> |
||||||
|
<version>1.27.0</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- Sa-Token 整合 Redis (使用jackson序列化方式) --> |
||||||
|
<dependency> |
||||||
|
<groupId>cn.dev33</groupId> |
||||||
|
<artifactId>sa-token-dao-redis-jackson</artifactId> |
||||||
|
<version>1.27.0</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- 提供Redis连接池 --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.apache.commons</groupId> |
||||||
|
<artifactId>commons-pool2</artifactId> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,94 @@ |
|||||||
|
package com.zhengqing.common.auth.api; |
||||||
|
|
||||||
|
|
||||||
|
import cn.dev33.satoken.SaManager; |
||||||
|
import cn.dev33.satoken.config.SaTokenConfig; |
||||||
|
import cn.dev33.satoken.stp.StpUtil; |
||||||
|
import cn.dev33.satoken.util.SaResult; |
||||||
|
import cn.hutool.core.date.DateTime; |
||||||
|
import io.swagger.annotations.Api; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.web.bind.annotation.GetMapping; |
||||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 测试api </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/10/5 2:36 下午 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@RestController |
||||||
|
@RequestMapping("/test") |
||||||
|
@Api(tags = "测试api") |
||||||
|
public class TestController { |
||||||
|
|
||||||
|
@GetMapping("time") |
||||||
|
@ApiOperation("time") |
||||||
|
public String time() { |
||||||
|
log.info("time: {}", DateTime.now()); |
||||||
|
return DateTime.now().toString(); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("getSaTokenConfig") |
||||||
|
@ApiOperation("Sa-Token配置") |
||||||
|
public SaTokenConfig getSaTokenConfig() { |
||||||
|
log.info("Sa-Token配置:{}", SaManager.getConfig()); |
||||||
|
return SaManager.getConfig(); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("doLogin") |
||||||
|
@ApiOperation("登录") |
||||||
|
public String doLogin(String username, String password) { |
||||||
|
// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对
|
||||||
|
if ("zhang".equals(username) && "123456".equals(password)) { |
||||||
|
StpUtil.logout(); |
||||||
|
StpUtil.login(10001); |
||||||
|
return "登录成功" + StpUtil.getLoginId(); |
||||||
|
} |
||||||
|
StpUtil.logout(); |
||||||
|
return "登录失败"; |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("isLogin") |
||||||
|
@ApiOperation("查询登录状态") |
||||||
|
public String isLogin() { |
||||||
|
return "当前会话是否登录:" + StpUtil.isLogin(); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("logout") |
||||||
|
@ApiOperation("退出登录") |
||||||
|
public String logout(String loginId) { |
||||||
|
StpUtil.logoutByLoginId(loginId); |
||||||
|
return "SUCCESS"; |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("checkLogin") |
||||||
|
@ApiOperation("检查是否登录") |
||||||
|
public String checkLogin() { |
||||||
|
try { |
||||||
|
StpUtil.checkLogin(); |
||||||
|
} catch (Exception e) { |
||||||
|
log.info("登录认证失效:{}", e.getMessage()); |
||||||
|
return "FAIL:" + e.getMessage(); |
||||||
|
} |
||||||
|
log.info("登录了..."); |
||||||
|
return "SUCCESS"; |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("tokenInfo") |
||||||
|
@ApiOperation("查询Token信息") |
||||||
|
public SaResult tokenInfo() { |
||||||
|
return SaResult.data(StpUtil.getTokenInfo()); |
||||||
|
} |
||||||
|
|
||||||
|
@GetMapping("getTokenValueByLoginId") |
||||||
|
@ApiOperation("获取账号id为10001的token令牌值") |
||||||
|
public String getTokenValueByLoginId() { |
||||||
|
return StpUtil.getTokenValueByLoginId(10001); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
package com.zhengqing.common.auth.config; |
||||||
|
|
||||||
|
import cn.dev33.satoken.strategy.SaStrategy; |
||||||
|
import cn.dev33.satoken.util.SaFoxUtil; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 自定义sa-token生成策略 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/11/3 12:06 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class SaTokenCustomConfig { |
||||||
|
|
||||||
|
/** |
||||||
|
* 重写 Sa-Token 框架内部算法策略 |
||||||
|
*/ |
||||||
|
@Autowired |
||||||
|
public void rewriteSaStrategy() { |
||||||
|
// 重写 Token 生成策略
|
||||||
|
SaStrategy.me.createToken = (loginId, loginType) -> { |
||||||
|
// 随机60位长度字符串
|
||||||
|
return SaFoxUtil.getRandomString(60); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.zhengqing.common.auth.config; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* <p> Sa-Token 拦截/开放 URL 配置类 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/11/3 8:54 下午 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@Configuration |
||||||
|
@ConfigurationProperties(prefix = "sa-token", ignoreUnknownFields = true) |
||||||
|
public class SaTokenUrlConfig { |
||||||
|
|
||||||
|
/** |
||||||
|
* 拦截url |
||||||
|
*/ |
||||||
|
private List<String> interceptUrlList; |
||||||
|
|
||||||
|
/** |
||||||
|
* 开放url |
||||||
|
*/ |
||||||
|
private List<String> openUrlList; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.zhengqing.common.auth.config; |
||||||
|
|
||||||
|
import cn.dev33.satoken.interceptor.SaRouteInterceptor; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||||
|
|
||||||
|
import javax.annotation.Resource; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 注册 Sa-Token 路由拦截器 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/11/3 12:06 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class SaTokenWebMvcConfig implements WebMvcConfigurer { |
||||||
|
|
||||||
|
@Resource |
||||||
|
private SaTokenUrlConfig saTokenUrlConfig; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addInterceptors(InterceptorRegistry registry) { |
||||||
|
// 注册1个登录认证拦截器
|
||||||
|
registry.addInterceptor(new SaRouteInterceptor()) |
||||||
|
.addPathPatterns(this.saTokenUrlConfig.getInterceptUrlList()) |
||||||
|
.excludePathPatterns(this.saTokenUrlConfig.getOpenUrlList()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.zhengqing.common.auth.model.bo; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.model.bo.BaseBO; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.experimental.SuperBuilder; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 用户token信息 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/11/28 23:16 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@SuperBuilder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
public class JwtUserBO extends BaseBO { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户ID") |
||||||
|
private Integer userId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户名") |
||||||
|
private String userName; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package com.zhengqing.common.auth.model.dto; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Builder; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 登录参数 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/4/15 20:55 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@Builder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
public class AuthLoginDTO { |
||||||
|
|
||||||
|
@NotBlank(message = "账号不能为空!") |
||||||
|
@ApiModelProperty(value = "账号", required = true, example = "test") |
||||||
|
private String username; |
||||||
|
|
||||||
|
@NotBlank(message = "密码不能为空!") |
||||||
|
@ApiModelProperty(value = "密码", example = "123456") |
||||||
|
private String password; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.zhengqing.common.auth.model.vo; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Builder; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 登录参数 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/4/15 20:55 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@Builder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
public class AuthLoginVO { |
||||||
|
|
||||||
|
@ApiModelProperty("认证请求头名") |
||||||
|
private String tokenName; |
||||||
|
|
||||||
|
@ApiModelProperty("认证值") |
||||||
|
private String tokenValue; |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
# Sa-Token配置 |
||||||
|
sa-token: |
||||||
|
# token名称 (同时也是cookie名称) 注意不能带冒号: |
||||||
|
token-name: Authorization-smallboot |
||||||
|
# token值前缀 |
||||||
|
# token-prefix: Bearer |
||||||
|
# token有效期,单位s 这里设置1天, -1代表永不过期 |
||||||
|
timeout: 86400 |
||||||
|
# token临时有效期 (指定时间内无操作就视为token过期) 单位: 秒 |
||||||
|
activity-timeout: -1 |
||||||
|
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录) |
||||||
|
is-concurrent: false |
||||||
|
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token) |
||||||
|
is-share: false |
||||||
|
# token风格 |
||||||
|
token-style: random-128 |
||||||
|
# 是否从cookie中读取token |
||||||
|
is-read-cookie: false |
||||||
|
# 是否从请求体里读取token |
||||||
|
is-read-body: false |
||||||
|
# 是否从head中读取token |
||||||
|
is-read-head: true |
||||||
|
# 是否输出操作日志 |
||||||
|
is-log: true |
||||||
|
# 是否在初始化配置时打印版本字符画 |
||||||
|
is-print: false |
||||||
|
# 拦截url |
||||||
|
intercept-url-list: |
||||||
|
- /** |
||||||
|
# 开放url |
||||||
|
open-url-list: |
||||||
|
# Knife4j |
||||||
|
- /webjars/** |
||||||
|
- /doc.html |
||||||
|
- /swagger-resources/** |
||||||
|
- /v2/api-docs |
||||||
|
- /v3/api-docs |
||||||
|
- /favicon.ico |
||||||
|
# 其它 |
||||||
|
- /auth/** |
@ -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"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<artifactId>common</artifactId> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<version>${revision}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<artifactId>base</artifactId> |
||||||
|
|
||||||
|
<name>${project.artifactId}</name> |
||||||
|
<version>${revision}</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
|
||||||
|
<properties> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>swagger</artifactId> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,165 @@ |
|||||||
|
package com.zhengqing.common.base.constant; |
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 全局常用变量 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/10/12 14:47 |
||||||
|
*/ |
||||||
|
public interface AppConstant extends BaseConstant { |
||||||
|
|
||||||
|
/** |
||||||
|
* 解决返回json字符串中文乱码问题 |
||||||
|
*/ |
||||||
|
String CONTENT_TYPE = "application/json;charset=utf-8"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 实体类名 |
||||||
|
*/ |
||||||
|
String ENTITY_NAME = "${entity}"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 接口url |
||||||
|
*/ |
||||||
|
Map<String, String> URL_MAPPING_MAP = new HashMap<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 密码加密相关 |
||||||
|
*/ |
||||||
|
String DEFAULT_PASSWORD = "123456"; |
||||||
|
String SALT = "zhengqing"; |
||||||
|
int HASH_ITERATIONS = 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* 用于登录密码加密解密 |
||||||
|
*/ |
||||||
|
String DES_KEY = "deskeyzq"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 用于DB中的密码加密解密 |
||||||
|
*/ |
||||||
|
byte[] AES_KEY = "123456789abcdefg".getBytes(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
/** |
||||||
|
* 请求头类型: application/x-www-form-urlencoded : form表单格式 application/json : json格式 |
||||||
|
*/ |
||||||
|
String REQUEST_HEADERS_CONTENT_TYPE = "application/json"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 系统超级管理员id |
||||||
|
*/ |
||||||
|
Integer SYSTEM_SUPER_ADMIN_USER_ID = 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* 登录者角色 |
||||||
|
*/ |
||||||
|
String ROLE_LOGIN = "role_login"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 所有第一级项目关联包父类id、父包名 |
||||||
|
*/ |
||||||
|
Integer PROJECT_RE_PACKAGE_PARENT_ID = 0; |
||||||
|
String PROJECT_RE_PACKAGE_PARENT_NAME = "com.zhengqing.demo"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 系统权限相关 |
||||||
|
*/ |
||||||
|
String WRONG_PASSWORD = "密码错误!"; |
||||||
|
String WRONG_OLD_PASSWORD = "原密码错误!"; |
||||||
|
String NO_USERNAME = "用户名不存在!"; |
||||||
|
String NO_PERMISSION = "请联系管理员为其分配角色!"; |
||||||
|
String NO_TOKEN = "TOKEN已过期请重新登录!"; |
||||||
|
String WRONG_USERNAME_PASSWORD = "用户名或密码错误!"; |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ 文件系列 ↓↓↓↓↓↓ ============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
/** |
||||||
|
* liunx系统分隔符 |
||||||
|
*/ |
||||||
|
String SEPARATOR_SPRIT = "/"; |
||||||
|
/** |
||||||
|
* win系统分隔符 |
||||||
|
*/ |
||||||
|
String SEPARATOR_BACKSLASH = "\\\\"; |
||||||
|
/** |
||||||
|
* 分隔符 - 逗号 |
||||||
|
*/ |
||||||
|
String SEPARATOR_COMMA = ","; |
||||||
|
/** |
||||||
|
* 分隔符 - 点 |
||||||
|
*/ |
||||||
|
String SEPARATOR_SPOT = "."; |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取项目根目录 |
||||||
|
*/ |
||||||
|
String PROJECT_ROOT_DIRECTORY = System.getProperty("user.dir") |
||||||
|
.replaceAll("\\\\", SEPARATOR_SPRIT); |
||||||
|
|
||||||
|
String IMG_DOMAIN = ""; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* csdn域名前缀 |
||||||
|
*/ |
||||||
|
String CSDN_DOMAIN_PREFIX = "https://blog.csdn.net/"; |
||||||
|
/** |
||||||
|
* csdn博客临时存储路径 |
||||||
|
*/ |
||||||
|
String FILE_PATH_CSDN_BLOG_IMPORT_SRC = PROJECT_ROOT_DIRECTORY + "/tmp/import/blog"; |
||||||
|
String FILE_PATH_CSDN_BLOG_IMPORT_ZIP = PROJECT_ROOT_DIRECTORY + "/tmp/import/blog.zip"; |
||||||
|
String FILE_PATH_CSDN_BLOG_EXPORT_SRC = PROJECT_ROOT_DIRECTORY + "/tmp/export/blog"; |
||||||
|
String FILE_PATH_CSDN_BLOG_EXPORT_ZIP = PROJECT_ROOT_DIRECTORY + "/tmp/export/blog.zip"; |
||||||
|
String FILE_PATH_CSDN_BLOG_EXPORT_EXCEL = PROJECT_ROOT_DIRECTORY + "/tmp/export/blog/excel.xls"; |
||||||
|
String FILE_PATH_CSDN_BLOG_EXPORT_HTML = |
||||||
|
PROJECT_ROOT_DIRECTORY + "/tmp/export/blog/html/" + System.currentTimeMillis() + "/"; |
||||||
|
/** |
||||||
|
* 代码生成临时存储路径 |
||||||
|
*/ |
||||||
|
String FILE_PATH_CODE_GENERATOR_DATA_PATH = PROJECT_ROOT_DIRECTORY + "/tmp/upload"; |
||||||
|
String FILE_PATH_CODE_GENERATOR_SRC_CODE = PROJECT_ROOT_DIRECTORY + "/tmp/upload/generate_code"; |
||||||
|
String FILE_PATH_CODE_GENERATOR_TEMPLATE_CODE = |
||||||
|
PROJECT_ROOT_DIRECTORY + "/tmp/upload/template_code"; |
||||||
|
|
||||||
|
String FILE_PATH_CODE_GENERATOR_FILE_NAME_DATA = |
||||||
|
PROJECT_ROOT_DIRECTORY + "/tmp/upload/handle_generate_file_name_data"; |
||||||
|
String FILE_PATH_CODE_GENERATOR_ZIP = PROJECT_ROOT_DIRECTORY + "/tmp/upload/code.zip"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据库导出word文档路径 |
||||||
|
*/ |
||||||
|
String FILE_PATH_DB_WORD = PROJECT_ROOT_DIRECTORY + "/tmp/db/数据库信息.doc"; |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ redis缓存系列 ↓↓↓↓↓↓ ============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ 业务系列 ↓↓↓↓↓↓ ============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
/** |
||||||
|
* 缓存默认过期时间 - 24小时 |
||||||
|
*/ |
||||||
|
Long DEFAULT_EXPIRES_TIME = 24 * 60 * 60 * 1000L; |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ 其它 ↓↓↓↓↓↓ ============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
/** |
||||||
|
* 限流测试 |
||||||
|
*/ |
||||||
|
String API_LIMIT_KEY = "API_LIMIT"; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.zhengqing.common.base.constant; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 全局常用变量 - 工程使用 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/7/20 18:16 |
||||||
|
*/ |
||||||
|
public interface ProjectConstant { |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ 文件系列 ↓↓↓↓↓↓ ============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
/** |
||||||
|
* 系统分隔符 |
||||||
|
*/ |
||||||
|
String SYSTEM_SEPARATOR = "/"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取项目根目录 |
||||||
|
*/ |
||||||
|
String PROJECT_ROOT_DIRECTORY = System.getProperty("user.dir").replaceAll("\\\\", SYSTEM_SEPARATOR); |
||||||
|
|
||||||
|
/** |
||||||
|
* 临时文件相关 |
||||||
|
*/ |
||||||
|
String DEFAULT_FOLDER_TMP = PROJECT_ROOT_DIRECTORY + "/tmp"; |
||||||
|
String DEFAULT_FOLDER_TMP_GENERATE = PROJECT_ROOT_DIRECTORY + "/tmp-generate"; |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ other ↓↓↓↓↓↓ ==============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
/** |
||||||
|
* 实体类 |
||||||
|
*/ |
||||||
|
String ENTITY_PACKAGE = "com.zhengqing.*.entity"; |
||||||
|
/** |
||||||
|
* mapper |
||||||
|
*/ |
||||||
|
String MAPPER_PACKAGE = "com.zhengqing.*.mapper"; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.zhengqing.common.base.constant; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* <p> RPC常用变量 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/7/20 18:16 |
||||||
|
*/ |
||||||
|
public interface RpcConstant extends BaseConstant { |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ rpc ↓↓↓↓↓↓ ================================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ other ↓↓↓↓↓↓ ==============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
package com.zhengqing.common.base.constant; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 全局常用变量 - 安全认证 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2022/6/13 7:14 PM |
||||||
|
*/ |
||||||
|
public interface SecurityConstant { |
||||||
|
|
||||||
|
/** |
||||||
|
* 认证请求头key |
||||||
|
*/ |
||||||
|
String AUTHORIZATION_KEY = "Authorization-smallboot"; |
||||||
|
|
||||||
|
/** |
||||||
|
* Basic认证前缀 |
||||||
|
*/ |
||||||
|
String BASIC_PREFIX = "Basic "; |
||||||
|
|
||||||
|
/** |
||||||
|
* JWT令牌前缀 |
||||||
|
*/ |
||||||
|
String JWT_PREFIX = "Bearer "; |
||||||
|
|
||||||
|
/** |
||||||
|
* JWT存储权限前缀 |
||||||
|
*/ |
||||||
|
String AUTHORITY_PREFIX = "ROLE_"; |
||||||
|
|
||||||
|
/** |
||||||
|
* JWT存储权限属性 |
||||||
|
*/ |
||||||
|
String JWT_AUTHORITIES_KEY = "authorities"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 客户端ID |
||||||
|
*/ |
||||||
|
String CLIENT_ID_KEY = "client_id"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 刷新token |
||||||
|
*/ |
||||||
|
String REFRESH_TOKEN_KEY = "refresh_token"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 认证身份标识 |
||||||
|
*/ |
||||||
|
String GRANT_TYPE = "authGrantType"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 超级管理员角色标识 |
||||||
|
*/ |
||||||
|
String SUPER_ADMIN_ROLE_CODE = "super_admin"; |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ redis缓存 ↓↓↓↓↓↓ ============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
/** |
||||||
|
* jwt自定义用户信息 |
||||||
|
*/ |
||||||
|
String JWT_CUSTOM_USER = "smallboot:system:jwt_custom_user:"; |
||||||
|
|
||||||
|
/** |
||||||
|
* url权限关联角色 |
||||||
|
* [ {接口路径:[角色编码]},...] |
||||||
|
*/ |
||||||
|
String URL_PERM_RE_ROLES = "smallboot:system:perm_rule:url"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 验证码 |
||||||
|
*/ |
||||||
|
String CAPTCHA_CODE = "smallboot:auth:captcha:"; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,30 @@ |
|||||||
|
package com.zhengqing.common.base.constant; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 全局常用变量 - 工程使用 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/7/20 18:16 |
||||||
|
*/ |
||||||
|
public interface ServiceConstant { |
||||||
|
|
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ service ↓↓↓↓↓↓ ============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
String SERVICE_BASE_PACKAGE = "com.zhengqing"; |
||||||
|
/** |
||||||
|
* api基础前缀 |
||||||
|
*/ |
||||||
|
String SERVICE_API_PREFIX_WEB = "/web/api"; |
||||||
|
String SERVICE_API_PREFIX_MINI = "/mini/api"; |
||||||
|
/** |
||||||
|
* 各服务api前缀 |
||||||
|
*/ |
||||||
|
String SERVICE_API_PREFIX_WEB_SYSTEM = SERVICE_API_PREFIX_WEB + "/system"; |
||||||
|
String SERVICE_API_PREFIX_WEB_BUS = SERVICE_API_PREFIX_WEB + "/bus"; |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package com.zhengqing.common.base.constant; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 全局常用变量 - 线程池 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/5/27 10:52 |
||||||
|
*/ |
||||||
|
public interface ThreadPoolConstant { |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ 线程池 ↓↓↓↓↓↓ ==============================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
/** |
||||||
|
* SmallTools线程池 |
||||||
|
*/ |
||||||
|
String SMALL_TOOLS_THREAD_POOL = "smallToolsThreadPoolTaskExecutor"; |
||||||
|
|
||||||
|
// ===============================================================================
|
||||||
|
// ============================ ↓↓↓↓↓↓ 线程池 - 线程名前缀 ↓↓↓↓↓↓ ===================
|
||||||
|
// ===============================================================================
|
||||||
|
|
||||||
|
/** |
||||||
|
* 替换原生Spring默认线程池-线程名前缀 |
||||||
|
*/ |
||||||
|
String SPRING_DEFAULT_THREAD_NAME_PREFIX = "MyTaskExecutorInit-"; |
||||||
|
/** |
||||||
|
* SmallTools线程池-线程名前缀 |
||||||
|
*/ |
||||||
|
String SMALL_TOOLS_THREAD_NAME_PREFIX = "SmallToolsTaskExecutor-"; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.zhengqing.common.base.context; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 认证来源上下文 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description {@link com.zhengqing.common.base.enums.AuthSourceEnum} |
||||||
|
* @date 2021/6/30 9:24 下午 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class AuthSourceContext { |
||||||
|
|
||||||
|
public static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>(); |
||||||
|
|
||||||
|
public static void set(String authSource) { |
||||||
|
THREAD_LOCAL.set(authSource); |
||||||
|
} |
||||||
|
|
||||||
|
public static String get() { |
||||||
|
return THREAD_LOCAL.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void remove() { |
||||||
|
THREAD_LOCAL.remove(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
package com.zhengqing.common.base.context; |
||||||
|
|
||||||
|
|
||||||
|
import com.zhengqing.common.base.model.bo.JwtCustomUserBO; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> jwt自定义用户信息上下文 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 请务必在请求结束时, 调用 @Method remove() |
||||||
|
* @date 2020/8/1 19:07 |
||||||
|
*/ |
||||||
|
public class JwtCustomUserContext { |
||||||
|
|
||||||
|
public static final ThreadLocal<JwtCustomUserBO> THREAD_LOCAL = new ThreadLocal<>(); |
||||||
|
|
||||||
|
public static JwtCustomUserBO get() { |
||||||
|
return THREAD_LOCAL.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void set(JwtCustomUserBO jwtCustomUserBO) { |
||||||
|
THREAD_LOCAL.set(jwtCustomUserBO); |
||||||
|
} |
||||||
|
|
||||||
|
public static void remove() { |
||||||
|
THREAD_LOCAL.remove(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package com.zhengqing.common.base.context; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.constant.BaseConstant; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> B端系统用户上下文 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 请务必在请求结束时, 调用 @Method remove() |
||||||
|
* @date 2020/8/1 19:07 |
||||||
|
*/ |
||||||
|
public class SysUserContext { |
||||||
|
|
||||||
|
public static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = new ThreadLocal<>(); |
||||||
|
|
||||||
|
public static Object get(String key) { |
||||||
|
Map<String, Object> map = THREAD_LOCAL.get(); |
||||||
|
if (map == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return map.get(key); |
||||||
|
} |
||||||
|
|
||||||
|
public static Integer getUserId() { |
||||||
|
Object value = get(BaseConstant.CONTEXT_KEY_SYS_USER_ID); |
||||||
|
return value == null ? Integer.valueOf(BaseConstant.DEFAULT_CONTEXT_KEY_USER_ID) : (Integer) value; |
||||||
|
} |
||||||
|
|
||||||
|
public static String getUsername() { |
||||||
|
Object value = get(BaseConstant.CONTEXT_KEY_USERNAME); |
||||||
|
return value == null ? BaseConstant.DEFAULT_CONTEXT_KEY_USERNAME : (String) value; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setUserId(Integer userId) { |
||||||
|
set(BaseConstant.CONTEXT_KEY_SYS_USER_ID, userId); |
||||||
|
} |
||||||
|
|
||||||
|
public static void setUsername(String username) { |
||||||
|
set(BaseConstant.CONTEXT_KEY_USERNAME, username); |
||||||
|
} |
||||||
|
|
||||||
|
private static void set(String key, Object value) { |
||||||
|
Map<String, Object> map = THREAD_LOCAL.get(); |
||||||
|
if (map == null) { |
||||||
|
map = new HashMap<>(1); |
||||||
|
THREAD_LOCAL.set(map); |
||||||
|
} |
||||||
|
map.put(key, value); |
||||||
|
} |
||||||
|
|
||||||
|
public static void remove() { |
||||||
|
THREAD_LOCAL.remove(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
package com.zhengqing.common.base.context; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 租户ID上下文 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/6/30 9:24 下午 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class TenantIdContext { |
||||||
|
|
||||||
|
/** |
||||||
|
* 租户ID |
||||||
|
*/ |
||||||
|
public static final ThreadLocal<Integer> TENANT_ID_THREAD_LOCAL = new ThreadLocal<>(); |
||||||
|
/** |
||||||
|
* 租户ID是否启用标识 |
||||||
|
* true : 是 -> 执行sql时,自动拼接租户ID |
||||||
|
* false: 否 -> 执行sql时,不自动拼接租户ID |
||||||
|
*/ |
||||||
|
public static final ThreadLocal<Boolean> TENANT_ID_FLAG_THREAD_LOCAL = new ThreadLocal<>(); |
||||||
|
|
||||||
|
public static void setTenantId(Integer tenantId) { |
||||||
|
TENANT_ID_THREAD_LOCAL.set(tenantId); |
||||||
|
TENANT_ID_FLAG_THREAD_LOCAL.set(true); |
||||||
|
} |
||||||
|
|
||||||
|
public static Integer getTenantId() { |
||||||
|
return TENANT_ID_THREAD_LOCAL.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void removeFlag() { |
||||||
|
TENANT_ID_FLAG_THREAD_LOCAL.set(false); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static Boolean getFlag() { |
||||||
|
return TENANT_ID_FLAG_THREAD_LOCAL.get(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void remove() { |
||||||
|
TENANT_ID_THREAD_LOCAL.remove(); |
||||||
|
TENANT_ID_FLAG_THREAD_LOCAL.remove(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
package com.zhengqing.common.base.context; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.constant.BaseConstant; |
||||||
|
|
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> C端用户上下文 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 请务必在请求结束时, 调用 @Method remove() |
||||||
|
* @date 2020/8/1 19:07 |
||||||
|
*/ |
||||||
|
public class UmsUserContext { |
||||||
|
|
||||||
|
public static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = new ThreadLocal<>(); |
||||||
|
|
||||||
|
public static Object get(String key) { |
||||||
|
Map<String, Object> map = THREAD_LOCAL.get(); |
||||||
|
if (map == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return map.get(key); |
||||||
|
} |
||||||
|
|
||||||
|
public static Long getUserId() { |
||||||
|
Object value = get(BaseConstant.CONTEXT_KEY_UMS_USER_ID); |
||||||
|
return value == null ? Long.valueOf(BaseConstant.DEFAULT_CONTEXT_KEY_USER_ID) : (Long) value; |
||||||
|
} |
||||||
|
|
||||||
|
public static String getUsername() { |
||||||
|
Object value = get(BaseConstant.CONTEXT_KEY_USERNAME); |
||||||
|
return value == null ? BaseConstant.DEFAULT_CONTEXT_KEY_USERNAME : (String) value; |
||||||
|
} |
||||||
|
|
||||||
|
public static void setUserId(Long userId) { |
||||||
|
set(BaseConstant.CONTEXT_KEY_UMS_USER_ID, userId); |
||||||
|
} |
||||||
|
|
||||||
|
public static void setUsername(String username) { |
||||||
|
set(BaseConstant.CONTEXT_KEY_USERNAME, username); |
||||||
|
} |
||||||
|
|
||||||
|
private static void set(String key, Object value) { |
||||||
|
Map<String, Object> map = THREAD_LOCAL.get(); |
||||||
|
if (map == null) { |
||||||
|
map = new HashMap<>(1); |
||||||
|
THREAD_LOCAL.set(map); |
||||||
|
} |
||||||
|
map.put(key, value); |
||||||
|
} |
||||||
|
|
||||||
|
public static void remove() { |
||||||
|
THREAD_LOCAL.remove(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package com.zhengqing.common.base.enums; |
||||||
|
|
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Getter; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 响应码枚举 - 可参考HTTP状态码的语义 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/8/22 11:09 |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
@AllArgsConstructor |
||||||
|
public enum ApiResultCodeEnum { |
||||||
|
|
||||||
|
// 成功
|
||||||
|
SUCCESS(200, "SUCCESS"), |
||||||
|
// 失败
|
||||||
|
FAILURE(400, "FAILURE"), |
||||||
|
// 参数校验失败
|
||||||
|
PARAM_VALID_ERROR(400, "参数校验失败"), |
||||||
|
// 未登录
|
||||||
|
UN_LOGIN(401, "请求未授权"), |
||||||
|
// 未通过认证
|
||||||
|
USER_UNAUTHORIZED(402, "用户名或密码不正确"), |
||||||
|
// 用户不存在
|
||||||
|
USER_NOT_EXIST(402, "用户不存在"), |
||||||
|
// 未认证(签名错误、token错误)
|
||||||
|
UNAUTHORIZED(403, "未认证"), |
||||||
|
// 客户端认证失败
|
||||||
|
CLIENT_AUTHENTICATION_FAILED(405, "客户端认证失败"), |
||||||
|
// 接口不存在
|
||||||
|
NOT_FOUND(404, "接口不存在"), |
||||||
|
// token过期
|
||||||
|
TOKEN_EXPIRED(-1, "token过期"), |
||||||
|
// token丢失
|
||||||
|
TOKEN_NOT_EXIST(-1, "token丢失"), |
||||||
|
// token已被禁止访问
|
||||||
|
TOKEN_ACCESS_FORBIDDEN(-1, "token已被禁止访问"), |
||||||
|
// 服务器内部错误
|
||||||
|
INTERNAL_SERVER_ERROR(500, "服务器内部错误"), |
||||||
|
; |
||||||
|
|
||||||
|
private final int code; |
||||||
|
private final String desc; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.zhengqing.common.base.enums; |
||||||
|
|
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Getter; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 认证来源枚举 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2022/6/10 15:18 |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
@AllArgsConstructor |
||||||
|
public enum AuthSourceEnum implements IBaseEnum<String> { |
||||||
|
|
||||||
|
B("B", "B端系统用户"), |
||||||
|
C("C", "C端用户"); |
||||||
|
|
||||||
|
private String value; |
||||||
|
private String desc; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
package com.zhengqing.common.base.enums; |
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil; |
||||||
|
|
||||||
|
import java.util.EnumSet; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 通用枚举接口 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 符号 T、E、?代表的意思: |
||||||
|
* T 表示一种特定的类型 |
||||||
|
* E 也是一种类型的意思,只不过通常代表集合中的元素 |
||||||
|
* ? 这是一种无限的符号,代表任何类型都可以 |
||||||
|
* @date 2019/8/22 11:00 |
||||||
|
*/ |
||||||
|
public interface IBaseEnum<T> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 值 |
||||||
|
*/ |
||||||
|
T getValue(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 描述 |
||||||
|
*/ |
||||||
|
String getDesc(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取所有枚举值 |
||||||
|
*/ |
||||||
|
static <E extends Enum<E>> List<E> list(Class<E> clazz) { |
||||||
|
EnumSet<E> allEnums = EnumSet.allOf(clazz); |
||||||
|
return allEnums.stream().collect(Collectors.toList()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据值获取枚举 |
||||||
|
*/ |
||||||
|
static <E extends Enum<E> & IBaseEnum> E getEnumByValue(Object value, Class<E> clazz) { |
||||||
|
Objects.requireNonNull(value); |
||||||
|
EnumSet<E> allEnums = EnumSet.allOf(clazz); |
||||||
|
E matchEnum = allEnums.stream() |
||||||
|
.filter(e -> ObjectUtil.equal(e.getValue(), value)) |
||||||
|
.findFirst() |
||||||
|
.orElse(null); |
||||||
|
return matchEnum; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据值获取描述 |
||||||
|
*/ |
||||||
|
static <E extends Enum<E> & IBaseEnum> Object getValueByDesc(String desc, Class<E> clazz) { |
||||||
|
Objects.requireNonNull(desc); |
||||||
|
EnumSet<E> allEnums = EnumSet.allOf(clazz); |
||||||
|
String finalDesc = desc; |
||||||
|
E matchEnum = allEnums.stream() |
||||||
|
.filter(e -> ObjectUtil.equal(e.getDesc(), finalDesc)) |
||||||
|
.findFirst() |
||||||
|
.orElse(null); |
||||||
|
|
||||||
|
Object value = null; |
||||||
|
if (matchEnum != null) { |
||||||
|
value = matchEnum.getValue(); |
||||||
|
} |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据描述获取值 |
||||||
|
*/ |
||||||
|
static <E extends Enum<E> & IBaseEnum> String getDescByValue(Object value, Class<E> clazz) { |
||||||
|
Objects.requireNonNull(value); |
||||||
|
EnumSet<E> allEnums = EnumSet.allOf(clazz); |
||||||
|
E matchEnum = allEnums.stream() |
||||||
|
.filter(e -> ObjectUtil.equal(e.getValue(), value)) |
||||||
|
.findFirst() |
||||||
|
.orElse(null); |
||||||
|
String desc = null; |
||||||
|
if (matchEnum != null) { |
||||||
|
desc = matchEnum.getDesc(); |
||||||
|
} |
||||||
|
return desc; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
package com.zhengqing.common.base.enums; |
||||||
|
|
||||||
|
import com.google.common.collect.Lists; |
||||||
|
import com.zhengqing.common.base.exception.MyException; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Getter; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 是/否 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/4/12 0:01 |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
@AllArgsConstructor |
||||||
|
public enum YesNoEnum implements IBaseEnum<Integer> { |
||||||
|
|
||||||
|
/** |
||||||
|
* 是 |
||||||
|
*/ |
||||||
|
YES(1, "是"), |
||||||
|
/** |
||||||
|
* 否 |
||||||
|
*/ |
||||||
|
NO(0, "否"); |
||||||
|
|
||||||
|
/** |
||||||
|
* 类型 |
||||||
|
*/ |
||||||
|
private final Integer value; |
||||||
|
/** |
||||||
|
* 描述 |
||||||
|
*/ |
||||||
|
private final String desc; |
||||||
|
|
||||||
|
private static final List<YesNoEnum> LIST = Lists.newArrayList(); |
||||||
|
|
||||||
|
static { |
||||||
|
LIST.addAll(Arrays.asList(YesNoEnum.values())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据指定类型查找相应枚举类 |
||||||
|
*/ |
||||||
|
public static YesNoEnum getEnum(Integer value) { |
||||||
|
for (YesNoEnum itemEnum : LIST) { |
||||||
|
if (itemEnum.getValue().equals(value)) { |
||||||
|
return itemEnum; |
||||||
|
} |
||||||
|
} |
||||||
|
throw new MyException("未找到指定类型枚举数据!"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
package com.zhengqing.common.base.exception; |
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.util.CollectionUtils; |
||||||
|
|
||||||
|
import java.util.Collection; |
||||||
|
import java.util.function.Supplier; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 业务断言 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/5/21 20:27 |
||||||
|
*/ |
||||||
|
public final class MyAssert extends Assert { |
||||||
|
|
||||||
|
/** |
||||||
|
* 断言对象不为空 |
||||||
|
* |
||||||
|
* @param object 对象 |
||||||
|
* @param msg 不满足断言的异常信息 |
||||||
|
*/ |
||||||
|
public static void notNull(Object object, String msg) { |
||||||
|
state(object != null, msg); |
||||||
|
} |
||||||
|
|
||||||
|
public static void notNull(Object object, Supplier<String> supplier) { |
||||||
|
state(object != null, supplier); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 断言字符串不为空 |
||||||
|
* |
||||||
|
* @param str 字符串 |
||||||
|
* @param msg 不满足断言的异常信息 |
||||||
|
*/ |
||||||
|
public static void notEmpty(String str, String msg) { |
||||||
|
state(!StringUtils.isEmpty(str), msg); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 断言集合不为空 |
||||||
|
* |
||||||
|
* @param collection 集合 |
||||||
|
* @param msg 不满足断言的异常信息 |
||||||
|
*/ |
||||||
|
public static void notEmpty(Collection<?> collection, String msg) { |
||||||
|
state(!CollectionUtils.isEmpty(collection), msg); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 断言一个boolean表达式 |
||||||
|
* |
||||||
|
* @param expression boolean表达式 |
||||||
|
* @param message 不满足断言的异常信息 |
||||||
|
*/ |
||||||
|
public static void state(boolean expression, String message) { |
||||||
|
if (!expression) { |
||||||
|
throw new MyException(message); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 断言一个boolean表达式,用于需要大量拼接字符串以及一些其他操作等 |
||||||
|
* |
||||||
|
* @param expression boolean表达式 |
||||||
|
* @param supplier msg生产者 |
||||||
|
*/ |
||||||
|
public static void state(boolean expression, Supplier<String> supplier) { |
||||||
|
if (!expression) { |
||||||
|
throw new MyException(supplier.get()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
package com.zhengqing.common.base.exception; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.enums.ApiResultCodeEnum; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 自定义异常类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/8/26 15:11 |
||||||
|
*/ |
||||||
|
public class MyException extends RuntimeException { |
||||||
|
|
||||||
|
/** |
||||||
|
* 异常状态码 |
||||||
|
*/ |
||||||
|
private Integer code; |
||||||
|
|
||||||
|
public MyException(Throwable cause) { |
||||||
|
super(cause); |
||||||
|
} |
||||||
|
|
||||||
|
public MyException(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
public MyException(Integer code, String message) { |
||||||
|
super(message); |
||||||
|
this.code = code; |
||||||
|
} |
||||||
|
|
||||||
|
public MyException(String message, Integer code) { |
||||||
|
super(message); |
||||||
|
this.code = code; |
||||||
|
} |
||||||
|
|
||||||
|
public MyException(ApiResultCodeEnum response) { |
||||||
|
super(response.getDesc()); |
||||||
|
this.code = response.getCode(); |
||||||
|
} |
||||||
|
|
||||||
|
public MyException(String message, Throwable cause) { |
||||||
|
super(message, cause); |
||||||
|
} |
||||||
|
|
||||||
|
public Integer getCode() { |
||||||
|
return code; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.zhengqing.common.base.exception; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 参数异常 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/8/1 18:07 |
||||||
|
*/ |
||||||
|
public class ParameterException extends MyException { |
||||||
|
|
||||||
|
public ParameterException(String message) { |
||||||
|
super(message); |
||||||
|
} |
||||||
|
|
||||||
|
public ParameterException(String message, Throwable e) { |
||||||
|
super(message, e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package com.zhengqing.common.base.model.bo; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.experimental.SuperBuilder; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 基类参数 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/8/17 15:38 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@SuperBuilder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
@ApiModel("基类参数") |
||||||
|
public class BaseBO implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
@JsonIgnore // jackson
|
||||||
|
// @JSONField(serialize = false, deserialize = false) // fastjson
|
||||||
|
@ApiModelProperty(value = "隐藏字段-解决子类lombok部分注解(ex:构造器@NoArgsConstructor、@AllArgsConstructor)无法使用问题", hidden = true) |
||||||
|
private String xxx; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.zhengqing.common.base.model.bo; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.enums.AuthSourceEnum; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.experimental.SuperBuilder; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> jwt中自定义的用户信息 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description {@link com.zhengqing.auth.security.config.CustomAdditionalInformation} |
||||||
|
* @date 2022/6/15 10:34 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@SuperBuilder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
public class JwtCustomUserBO { |
||||||
|
/** |
||||||
|
* {@link AuthSourceEnum} |
||||||
|
*/ |
||||||
|
@ApiModelProperty(value = "认证来源") |
||||||
|
private String authSource; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "jwt") |
||||||
|
private String token; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "jwt的唯一身份标识") |
||||||
|
private String jti; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "过期时间(2022-06-01 23:06:53)") |
||||||
|
private String expireTime; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "租户ID") |
||||||
|
private String tenantId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "B端系统用户ID") |
||||||
|
private String sysUserId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "C端用户ID") |
||||||
|
private String umsUserId; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户名") |
||||||
|
private String username; |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
package com.zhengqing.common.base.model.dto; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.experimental.SuperBuilder; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 基类查询参数 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/9/13 0013 1:57 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@SuperBuilder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
@ApiModel("基类查询参数") |
||||||
|
public class BaseDTO implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
@JsonIgnore // 字段忽略
|
||||||
|
// @JsonProperty("userId") // 字段别名
|
||||||
|
@ApiModelProperty(value = "当前用户ID", hidden = true) |
||||||
|
private Integer currentUserId; |
||||||
|
|
||||||
|
@JsonIgnore |
||||||
|
@ApiModelProperty(value = "当前用户名称", hidden = true) |
||||||
|
private String currentUsername; |
||||||
|
|
||||||
|
// @JsonIgnore
|
||||||
|
// @ApiModelProperty(value = "令牌", hidden = true)
|
||||||
|
// private String token;
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package com.zhengqing.common.base.model.dto; |
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.EqualsAndHashCode; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.experimental.SuperBuilder; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 基础分页检索参数 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/9/13 0013 1:57 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@SuperBuilder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
@EqualsAndHashCode(callSuper = true) |
||||||
|
@ApiModel("基础分页检索参数") |
||||||
|
public class BasePageDTO extends BaseDTO { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "当前页", required = true, position = 0, example = "1") |
||||||
|
private Integer pageNum = 1; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "每页显示数量", required = true, position = 1, example = "10") |
||||||
|
private Integer pageSize = 10; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,155 @@ |
|||||||
|
package com.zhengqing.common.base.model.vo; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.enums.ApiResultCodeEnum; |
||||||
|
import com.zhengqing.common.base.exception.MyException; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Getter; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.Setter; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* API返回参数 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/7/20 11:09 |
||||||
|
*/ |
||||||
|
@Setter |
||||||
|
@Getter |
||||||
|
@AllArgsConstructor |
||||||
|
@NoArgsConstructor |
||||||
|
@ApiModel(value = "API返回参数") |
||||||
|
public class ApiResult<T> { |
||||||
|
|
||||||
|
@ApiModelProperty(value = "响应码", required = true) |
||||||
|
private Integer code; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "消息内容", required = false) |
||||||
|
private String msg; |
||||||
|
|
||||||
|
@ApiModelProperty(value = "响应数据", required = false) |
||||||
|
private T data; |
||||||
|
|
||||||
|
public ApiResult(T data) { |
||||||
|
this.code = ApiResultCodeEnum.SUCCESS.getCode(); |
||||||
|
this.msg = "OK"; |
||||||
|
this.data = data; |
||||||
|
} |
||||||
|
|
||||||
|
public ApiResult(Integer code, String msg) { |
||||||
|
this.code = code; |
||||||
|
this.msg = msg; |
||||||
|
} |
||||||
|
|
||||||
|
public static ApiResult ok() { |
||||||
|
return new ApiResult(ApiResultCodeEnum.SUCCESS.getCode(), "OK", null); |
||||||
|
} |
||||||
|
|
||||||
|
public static <E> ApiResult<E> ok(E o) { |
||||||
|
// 支持Controller层直接返回ApiResult
|
||||||
|
ApiResult result = new ApiResult(ApiResultCodeEnum.SUCCESS); |
||||||
|
if (o instanceof ApiResult) { |
||||||
|
result = ((ApiResult) o); |
||||||
|
} else { |
||||||
|
// 其他obj封装进data,保持返回格式统一
|
||||||
|
result.setData(o); |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
public static ApiResult ok(String data) { |
||||||
|
return new ApiResult(ApiResultCodeEnum.SUCCESS.getCode(), "OK", data); |
||||||
|
} |
||||||
|
|
||||||
|
public static ApiResult ok(Object data, String msg) { |
||||||
|
return new ApiResult(ApiResultCodeEnum.SUCCESS.getCode(), msg, data); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 自定义返回码 |
||||||
|
*/ |
||||||
|
public static ApiResult ok(Integer code, String msg) { |
||||||
|
return new ApiResult(code, msg); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 自定义 |
||||||
|
* |
||||||
|
* @param code 验证码 |
||||||
|
* @param msg 返回消息内容 |
||||||
|
* @param data 返回数据 |
||||||
|
* @return 响应体 |
||||||
|
*/ |
||||||
|
public static ApiResult ok(Integer code, String msg, Object data) { |
||||||
|
return new ApiResult(code, msg, data); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 过期 |
||||||
|
* |
||||||
|
* @param msg 消息内容 |
||||||
|
* @return 响应体 |
||||||
|
*/ |
||||||
|
public static ApiResult expired(String msg) { |
||||||
|
return new ApiResult(ApiResultCodeEnum.TOKEN_EXPIRED.getCode(), msg, null); |
||||||
|
} |
||||||
|
|
||||||
|
public static ApiResult fail(String msg) { |
||||||
|
return new ApiResult(ApiResultCodeEnum.FAILURE.getCode(), msg, null); |
||||||
|
} |
||||||
|
|
||||||
|
public static ApiResult busy() { |
||||||
|
return new ApiResult(ApiResultCodeEnum.FAILURE.getCode(), "服务繁忙", null); |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* 自定义错误返回码 |
||||||
|
* |
||||||
|
* @param code 验证码 |
||||||
|
* @param msg 消息内容 |
||||||
|
* @return 响应体 |
||||||
|
*/ |
||||||
|
public static ApiResult fail(Integer code, String msg) { |
||||||
|
return new ApiResult(code, msg, null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否成功 |
||||||
|
* |
||||||
|
* @return true->成功,false->失败 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/6/1 12:45 |
||||||
|
*/ |
||||||
|
public boolean checkIsSuccess() { |
||||||
|
return ApiResultCodeEnum.SUCCESS.getCode() == this.code; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 是否失败 |
||||||
|
* |
||||||
|
* @return true->失败,false->成功 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/6/1 12:45 |
||||||
|
*/ |
||||||
|
public boolean checkIsFail() { |
||||||
|
return ApiResultCodeEnum.SUCCESS.getCode() != this.code; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* rpc校验结果是否异常 |
||||||
|
* |
||||||
|
* @return void |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/10/14 16:21 |
||||||
|
*/ |
||||||
|
public void checkForRpc() { |
||||||
|
if (this.checkIsFail()) { |
||||||
|
throw new MyException(this.msg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
package com.zhengqing.common.base.model.vo; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import io.swagger.annotations.ApiModelProperty; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.experimental.SuperBuilder; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 基类响应参数 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/8/18 16:14 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@SuperBuilder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
@ApiModel("基类响应参数") |
||||||
|
public class BaseVO implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
@JsonIgnore |
||||||
|
@ApiModelProperty(value = "隐藏字段-解决子类lombok部分注解(ex:构造器@NoArgsConstructor、@AllArgsConstructor)无法使用问题", hidden = true) |
||||||
|
private String xxx; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
package com.zhengqing.common.base.model.vo; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonGenerator; |
||||||
|
import com.fasterxml.jackson.databind.JsonSerializer; |
||||||
|
import com.fasterxml.jackson.databind.SerializerProvider; |
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
||||||
|
import io.swagger.annotations.ApiModel; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.experimental.SuperBuilder; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.Serializable; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 分页响应参数 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description {@link com.baomidou.mybatisplus.core.metadata.IPage } |
||||||
|
* @date 2021/8/18 16:14 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
@SuperBuilder |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
@ApiModel("基类响应参数") |
||||||
|
public class PageVO<T> implements Serializable { |
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; |
||||||
|
|
||||||
|
/** |
||||||
|
* 当前页 |
||||||
|
*/ |
||||||
|
@JsonSerialize(using = ToLongSerializer.class) |
||||||
|
Long current; |
||||||
|
/** |
||||||
|
* 每页显示条数 |
||||||
|
*/ |
||||||
|
@JsonSerialize(using = ToLongSerializer.class) |
||||||
|
Long size; |
||||||
|
/** |
||||||
|
* 当前分页总页数 |
||||||
|
*/ |
||||||
|
@JsonSerialize(using = ToLongSerializer.class) |
||||||
|
Long pages; |
||||||
|
/** |
||||||
|
* 当前满足条件总行数 |
||||||
|
*/ |
||||||
|
@JsonSerialize(using = ToLongSerializer.class) |
||||||
|
Long total; |
||||||
|
/** |
||||||
|
* 分页记录列表 |
||||||
|
*/ |
||||||
|
List<T> records; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> jackson 转Long类型 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 解决全局将Long类型转换为String类型后,部分类又需要Long还是原本Long类型问题 |
||||||
|
* 使用方式:添加注解`@JsonSerialize(using = ToLongSerializer.class)` |
||||||
|
* {@link com.zhengqing.common.web.config.jackson.ToLongSerializer } |
||||||
|
* 由于此为base包,不额外引用其它包中的东西,这里单独处理一下 |
||||||
|
* @date 2022/8/5 17:22 |
||||||
|
*/ |
||||||
|
class ToLongSerializer extends JsonSerializer<Long> { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void serialize(Long value, JsonGenerator gen, SerializerProvider serializers) throws IOException { |
||||||
|
gen.writeNumber(value); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.beans.BeansException; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.context.ApplicationContextAware; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 全局上下文工具类配置 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 解决静态方法中mapper的调用 |
||||||
|
* @date 2019/10/17 14:44 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Component |
||||||
|
public class ApplicationContextUtil implements ApplicationContextAware { |
||||||
|
private static ApplicationContext applicationContext; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||||
|
ApplicationContextUtil.applicationContext = applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
public static ApplicationContext getApplicationContext() { |
||||||
|
return applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
public static Object getBean(String beanName) { |
||||||
|
return applicationContext.getBean(beanName); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,40 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 自增编号工具类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/10/13$ 14:08$ |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class AutoUpgradeUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 自增编号 ex:0001 -> 0002 |
||||||
|
* |
||||||
|
* @param code 编号 |
||||||
|
* @return 自增后的编号 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/10/13 14:13 |
||||||
|
*/ |
||||||
|
public static String autoUpgrade(String code) { |
||||||
|
String codeNew = "0001"; |
||||||
|
if (StringUtils.isNotBlank(code)) { |
||||||
|
code = code.trim(); |
||||||
|
codeNew = String.format("%0" + code.length() + "d", Long.valueOf(code) + 1); |
||||||
|
} |
||||||
|
log.debug("《自增编号》 旧数据:【{}】 新数据:【{}】", code, codeNew); |
||||||
|
return codeNew; |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
autoUpgrade("9999"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 自增版本号工具类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/10/13 14:08 |
||||||
|
*/ |
||||||
|
public class AutoUpgradeVersionUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 自动自增版本号 ex:0.0.1 -> 0.0.2 |
||||||
|
* |
||||||
|
* @param version 版本号 |
||||||
|
* @return 自增后的版本号 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/10/13 14:13 |
||||||
|
*/ |
||||||
|
public static String autoUpgradeVersion(String version) { |
||||||
|
if (StringUtils.isBlank(version)) { |
||||||
|
version = "0.0.1"; |
||||||
|
} |
||||||
|
// 将版本号拆解成整数数组
|
||||||
|
String[] versionArray = version.split("\\."); |
||||||
|
int[] versionList = Arrays.stream(versionArray).mapToInt(Integer::valueOf).toArray(); |
||||||
|
|
||||||
|
// 递归调用
|
||||||
|
autoUpgradeVersion(versionList, versionList.length - 1); |
||||||
|
|
||||||
|
// 数组转字符串
|
||||||
|
version = StringUtils.join(versionList, '.'); |
||||||
|
return version; |
||||||
|
} |
||||||
|
|
||||||
|
private static void autoUpgradeVersion(int[] ints, int index) { |
||||||
|
if (index == 0) { |
||||||
|
ints[0] = ints[0] + 1; |
||||||
|
} else { |
||||||
|
int value = ints[index] + 1; |
||||||
|
if (value < 10) { |
||||||
|
ints[index] = value; |
||||||
|
} else { |
||||||
|
ints[index] = 0; |
||||||
|
autoUpgradeVersion(ints, index - 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,282 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
|
||||||
|
import java.math.BigDecimal; |
||||||
|
import java.text.NumberFormat; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> BigDecimal工具类 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/8/24 10:45 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class BigDecimalUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认除法运算精度 |
||||||
|
*/ |
||||||
|
private static final int DEF_DIV_SCALE = 2; |
||||||
|
|
||||||
|
/** |
||||||
|
* 建立货币格式化引用 |
||||||
|
*/ |
||||||
|
private static final NumberFormat CURRENCY = NumberFormat.getCurrencyInstance(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 建立百分比格式化引用 |
||||||
|
*/ |
||||||
|
private static final NumberFormat PERCENT = NumberFormat.getPercentInstance(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 加法 |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @return 计算结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:49 |
||||||
|
*/ |
||||||
|
public static BigDecimal add(BigDecimal num1, BigDecimal num2) { |
||||||
|
return num1.add(num2); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 加法(默认四舍五入,根据scale保留小数位数) |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @param scale 保留小数位数 |
||||||
|
* @return 计算结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:52 |
||||||
|
*/ |
||||||
|
public static BigDecimal add(BigDecimal num1, BigDecimal num2, int scale) { |
||||||
|
return num1.add(num2).setScale(scale, BigDecimal.ROUND_HALF_UP); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 减法 |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @return 计算结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:54 |
||||||
|
*/ |
||||||
|
public static BigDecimal sub(BigDecimal num1, BigDecimal num2) { |
||||||
|
return num1.subtract(num2); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 减法(默认四舍五入,根据scale保留小数位数) |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @return 计算结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:54 |
||||||
|
*/ |
||||||
|
public static BigDecimal sub(BigDecimal num1, BigDecimal num2, int scale) { |
||||||
|
return num1.subtract(num2).setScale(scale, BigDecimal.ROUND_HALF_UP); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 乘法 |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @return 计算结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:54 |
||||||
|
*/ |
||||||
|
public static BigDecimal multiply(BigDecimal num1, BigDecimal num2) { |
||||||
|
return num1.multiply(num2); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 乘法(默认四舍五入,根据scale保留小数位数) |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @return 计算结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:54 |
||||||
|
*/ |
||||||
|
public static BigDecimal multiply(BigDecimal num1, BigDecimal num2, int scale) { |
||||||
|
return num1.multiply(num2).setScale(scale, BigDecimal.ROUND_HALF_UP); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 除法(除不尽会抛异常) |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @return 计算结果 = 数1 / 数2 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:54 |
||||||
|
*/ |
||||||
|
public static BigDecimal divide(BigDecimal num1, BigDecimal num2) { |
||||||
|
return num1.divide(num2, DEF_DIV_SCALE); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 除法(默认四舍五入保留两位小数) |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @return 计算结果 = 数1 / 数2 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:54 |
||||||
|
*/ |
||||||
|
public static BigDecimal divide(BigDecimal num1, BigDecimal num2, int scale) { |
||||||
|
return num1.divide(num2, scale, BigDecimal.ROUND_HALF_UP); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 比较BigDecimal |
||||||
|
* |
||||||
|
* @param num1 数1 |
||||||
|
* @param num2 数2 |
||||||
|
* @return 相等返回0, num1>num2返回1, num1<num2返回-1 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 10:54 |
||||||
|
*/ |
||||||
|
public static int compareTo(BigDecimal num1, BigDecimal num2) { |
||||||
|
return num1.compareTo(num2); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* BigDecimal货币格式化 |
||||||
|
* |
||||||
|
* @param money 钱钱 |
||||||
|
* @return java.lang.String |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 11:03 |
||||||
|
*/ |
||||||
|
public static String currencyFormat(BigDecimal money) { |
||||||
|
return CURRENCY.format(money); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* BigDecimal百分比格式化 |
||||||
|
* |
||||||
|
* @param money 钱钱 |
||||||
|
* @return java.lang.String |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 11:03 |
||||||
|
*/ |
||||||
|
public static String rateFormat(BigDecimal money) { |
||||||
|
return PERCENT.format(money); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 保留小数位数 |
||||||
|
* |
||||||
|
* @param x 目标小数 |
||||||
|
* @param scale 要保留小数位数 |
||||||
|
* @return 结果四舍五入 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 11:04 |
||||||
|
*/ |
||||||
|
public static BigDecimal scale(BigDecimal x, int scale) { |
||||||
|
if (x == null) { |
||||||
|
x = BigDecimal.valueOf(0.00); |
||||||
|
} |
||||||
|
return x.setScale(scale, BigDecimal.ROUND_HALF_UP); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 元转分 |
||||||
|
* |
||||||
|
* @param money 钱钱 (如果2位小数以上,自动四舍五入) |
||||||
|
* @return 分 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 11:04 |
||||||
|
*/ |
||||||
|
public static Integer yuanToFen(BigDecimal money) { |
||||||
|
if (money == null) { |
||||||
|
money = BigDecimal.valueOf(0.00); |
||||||
|
} |
||||||
|
return Integer.valueOf(multiply(money, BigDecimal.valueOf(100), 0).toString()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 分转元 |
||||||
|
* |
||||||
|
* @param money 钱钱 (如果2位小数以上,自动四舍五入) |
||||||
|
* @return 分 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 11:04 |
||||||
|
*/ |
||||||
|
public static String fenToYuan(String money) { |
||||||
|
if (StringUtils.isBlank(money)) { |
||||||
|
money = "0"; |
||||||
|
} |
||||||
|
return String.valueOf(divide(new BigDecimal(money), BigDecimal.valueOf(100), 2)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 分转元 |
||||||
|
* |
||||||
|
* @param money 钱钱 (如果2位小数以上,自动四舍五入) |
||||||
|
* @return 分 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 11:04 |
||||||
|
*/ |
||||||
|
public static String fenToYuan(Integer money) { |
||||||
|
if (money == null) { |
||||||
|
money = 0; |
||||||
|
} |
||||||
|
return String.valueOf(divide(BigDecimal.valueOf(money), BigDecimal.valueOf(100), 2)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* BigDecimal转Integer |
||||||
|
* |
||||||
|
* @param money 钱钱 (如果有小数,自动四舍五入) |
||||||
|
* @return Integer |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/24 11:04 |
||||||
|
*/ |
||||||
|
public static Integer toInt(BigDecimal money) { |
||||||
|
if (money == null) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
return Integer.valueOf(scale(money, 0).toString()); |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
BigDecimal num1 = new BigDecimal("80"); |
||||||
|
BigDecimal num2 = new BigDecimal("60"); |
||||||
|
|
||||||
|
// 加法
|
||||||
|
log.info("加法: {}", BigDecimalUtil.add(num1, num2)); |
||||||
|
log.info("加法: {}", BigDecimalUtil.add(num1, num2, 1)); |
||||||
|
|
||||||
|
// 减法
|
||||||
|
log.info("减法: {}", BigDecimalUtil.sub(num1, num2)); |
||||||
|
log.info("减法: {}", BigDecimalUtil.sub(num1, num2, 2)); |
||||||
|
|
||||||
|
// 乘法
|
||||||
|
log.info("乘法: {}", BigDecimalUtil.multiply(num1, num2)); |
||||||
|
log.info("乘法: {}", BigDecimalUtil.multiply(num1, num2, 3)); |
||||||
|
|
||||||
|
// 除法
|
||||||
|
log.info("除法: {}", BigDecimalUtil.divide(num1, num2)); |
||||||
|
log.info("除法: {}", BigDecimalUtil.divide(num1, num2, 4)); |
||||||
|
|
||||||
|
log.info("比较: {}", BigDecimalUtil.compareTo(num1, num2)); |
||||||
|
log.info("货币格式化: {}", BigDecimalUtil.currencyFormat(num2)); |
||||||
|
log.info("百分比格式化: {}", BigDecimalUtil.rateFormat(num2)); |
||||||
|
log.info("元转分: {}", BigDecimalUtil.yuanToFen(num2)); |
||||||
|
log.info("分转元: {}", BigDecimalUtil.fenToYuan("0.01")); |
||||||
|
log.info("分转元: {}", BigDecimalUtil.fenToYuan(0)); |
||||||
|
log.info("BigDecimal转Integer: {}", BigDecimalUtil.toInt(num2)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,195 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil; |
||||||
|
import com.google.common.collect.Lists; |
||||||
|
import com.google.common.collect.Maps; |
||||||
|
import lombok.SneakyThrows; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.springframework.beans.BeanUtils; |
||||||
|
import org.springframework.beans.BeansException; |
||||||
|
import org.springframework.util.CollectionUtils; |
||||||
|
|
||||||
|
import java.lang.reflect.Field; |
||||||
|
import java.lang.reflect.InvocationTargetException; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* bean 工具类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya <br/> |
||||||
|
* @date 2019/11/6$ 18:34$ <br/> |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class MyBeanUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 对象属性拷贝 |
||||||
|
* |
||||||
|
* @param source 源对象 |
||||||
|
* @param target 目标对象 |
||||||
|
* @return void |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/9/5 23:22 |
||||||
|
*/ |
||||||
|
public static void copyProperties(Object source, Object target) { |
||||||
|
BeanUtils.copyProperties(source, target); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 对象属性拷贝 : 将源对象的属性拷贝到目标对象 |
||||||
|
* |
||||||
|
* @param source 源对象 |
||||||
|
* @param clz 目标对象class |
||||||
|
* @return 对象数据 |
||||||
|
*/ |
||||||
|
public static <T> T copyProperties(Object source, Class<T> clz) { |
||||||
|
if (source == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
T target = BeanUtils.instantiate(clz); |
||||||
|
try { |
||||||
|
BeanUtils.copyProperties(source, target); |
||||||
|
} catch (BeansException e) { |
||||||
|
log.error("BeanUtil property copy failed :BeansException", e); |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("BeanUtil property copy failed:Exception", e); |
||||||
|
} |
||||||
|
return target; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 拷贝list |
||||||
|
* |
||||||
|
* @param inList 输入list |
||||||
|
* @param outClz 输出目标对象class |
||||||
|
* @return 返回集合 |
||||||
|
*/ |
||||||
|
public static <E, T> List<T> copyList(List<E> inList, Class<T> outClz) { |
||||||
|
List<T> output = Lists.newArrayList(); |
||||||
|
if (!CollectionUtils.isEmpty(inList)) { |
||||||
|
for (E source : inList) { |
||||||
|
T target = BeanUtils.instantiate(outClz); |
||||||
|
BeanUtils.copyProperties(source, target); |
||||||
|
output.add(target); |
||||||
|
} |
||||||
|
} |
||||||
|
return output; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取任意泛型类的指定属性值(暂只支持同时访问父类和子类属性,如果有多继承的话,还需要再修改) |
||||||
|
* |
||||||
|
* @param tList list对象数据 |
||||||
|
* @param field 要获取指定字段属性值对应的字段名 |
||||||
|
* @param isSuperfield true->父类 false->子类 |
||||||
|
* @return 字段属性值 |
||||||
|
*/ |
||||||
|
public static <T> List<?> getFieldList(List<T> tList, String field, Boolean isSuperfield) { |
||||||
|
if (StringUtils.isBlank(field)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
// 拼接方法
|
||||||
|
field = |
||||||
|
new StringBuffer("get").append(field.substring(0, 1).toUpperCase()).append(field.substring(1)).toString(); |
||||||
|
List<Object> idList = Lists.newArrayList(); |
||||||
|
Method method = null; |
||||||
|
try { |
||||||
|
if (isSuperfield) { |
||||||
|
method = tList.get(0).getClass().getSuperclass().getMethod(field); |
||||||
|
} else { |
||||||
|
method = tList.get(0).getClass().getMethod(field); |
||||||
|
} |
||||||
|
} catch (NoSuchMethodException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
try { |
||||||
|
for (T t : tList) { |
||||||
|
Object s = method.invoke(t); |
||||||
|
idList.add(s); |
||||||
|
} |
||||||
|
} catch (NullPointerException | InvocationTargetException | IllegalAccessException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
return idList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取任意泛型类的指定属性值(暂只支持同时访问父类和子类属性,如果有多继承的话,还需要再修改) |
||||||
|
* |
||||||
|
* @param tList list对象数据 |
||||||
|
* @param field 要获取指定字段属性值对应的字段名 |
||||||
|
* @return 字段属性值 |
||||||
|
*/ |
||||||
|
public static <T> List<?> getFieldList(List<Map<String, Object>> tList, String field) { |
||||||
|
if (StringUtils.isBlank(field)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
List<Object> idList = Lists.newArrayList(); |
||||||
|
for (Map<String, Object> map : tList) { |
||||||
|
idList.add(map.get(field)); |
||||||
|
} |
||||||
|
return idList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将Map转换为对象 (注:暂无法转换含嵌套对象的map数据源) |
||||||
|
* |
||||||
|
* @param map 需转换map |
||||||
|
* @param clz 目标对象class |
||||||
|
* @return T |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/11/27 18:39 |
||||||
|
*/ |
||||||
|
public static <T> T mapToObj(Map<String, Object> map, Class<T> clz) { |
||||||
|
return JSONUtil.toBean(JSONUtil.toJsonStr(map), clz); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 对象 转 map (通过反射获取类里面的值和名称) |
||||||
|
* |
||||||
|
* @param obj 对象 |
||||||
|
* @return map |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/1/26 16:09 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Map<String, String> objToMapStr(Object obj) { |
||||||
|
Map<String, String> map = Maps.newHashMap(); |
||||||
|
Class<?> clazz = obj.getClass(); |
||||||
|
for (Field field : clazz.getDeclaredFields()) { |
||||||
|
field.setAccessible(true); |
||||||
|
String fieldName = field.getName(); |
||||||
|
Object value = field.get(obj); |
||||||
|
map.put(fieldName, value == null ? "" : value.toString()); |
||||||
|
} |
||||||
|
return map; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 对象 转 map (通过反射获取类里面的值和名称) |
||||||
|
* |
||||||
|
* @param obj 对象 |
||||||
|
* @return map |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/1/26 16:09 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Map<String, Object> objToMap(Object obj) { |
||||||
|
Map<String, Object> map = Maps.newHashMap(); |
||||||
|
Class<?> clazz = obj.getClass(); |
||||||
|
for (Field field : clazz.getDeclaredFields()) { |
||||||
|
field.setAccessible(true); |
||||||
|
String fieldName = field.getName(); |
||||||
|
Object value = field.get(obj); |
||||||
|
map.put(fieldName, value == null ? "" : value.toString()); |
||||||
|
} |
||||||
|
return map; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,421 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import lombok.SneakyThrows; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
import java.lang.management.ManagementFactory; |
||||||
|
import java.text.SimpleDateFormat; |
||||||
|
import java.time.LocalDate; |
||||||
|
import java.time.LocalDateTime; |
||||||
|
import java.time.LocalTime; |
||||||
|
import java.time.ZoneId; |
||||||
|
import java.time.format.DateTimeFormatter; |
||||||
|
import java.util.Calendar; |
||||||
|
import java.util.Date; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 时间工具类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/8/18 12:57 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class MyDateUtil { |
||||||
|
|
||||||
|
public static final String GMT = "GMT+8"; |
||||||
|
public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss"; |
||||||
|
public static final String DATE_TIME_MS_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"; |
||||||
|
public static final String DATE_TIME_START_FORMAT = "yyyy-MM-dd 00:00:00"; |
||||||
|
public static final String DATE_TIME_END_FORMAT = "yyyy-MM-dd 23:59:59"; |
||||||
|
public static final String MINUTE_FORMAT = "yyyy-MM-dd HH:mm"; |
||||||
|
public static final String HOUR_FORMAT = "yyyy-MM-dd HH"; |
||||||
|
public static final String DATE_FORMAT = "yyyy-MM-dd"; |
||||||
|
public static final String MONTH_FORMAT = "yyyy-MM"; |
||||||
|
public static final String YEAR_FORMAT = "yyyy"; |
||||||
|
public static final String MINUTE_ONLY_FORMAT = "mm"; |
||||||
|
public static final String HOUR_ONLY_FORMAT = "HH"; |
||||||
|
public static final String MINUTE_JAVA_AUTHOR_FORMAT = "yyyy/MM/dd HH:mm"; |
||||||
|
public static final String DAY_FORMAT = "yyyy/MM/dd"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前时间的字符串格式时间 -- 秒 |
||||||
|
* |
||||||
|
* @return 字符串格式时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/22 13:07 |
||||||
|
*/ |
||||||
|
public static String nowStr() { |
||||||
|
return new SimpleDateFormat(DATE_TIME_FORMAT).format(new Date()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前时间的字符串格式时间 -- 毫秒 |
||||||
|
* |
||||||
|
* @return 字符串格式时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/22 13:07 |
||||||
|
*/ |
||||||
|
public static String nowMsStr() { |
||||||
|
return new SimpleDateFormat(DATE_TIME_MS_FORMAT).format(new Date()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前时间字符串的指定格式时间 |
||||||
|
* |
||||||
|
* @param format 时间格式 |
||||||
|
* @return 字符串格式时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/22 13:07 |
||||||
|
*/ |
||||||
|
public static String nowStr(String format) { |
||||||
|
return new SimpleDateFormat(format).format(new Date()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Date转Str |
||||||
|
* |
||||||
|
* @param date 时间 |
||||||
|
* @param format 时间格式 |
||||||
|
* @return 字符串时间类型 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/22 13:07 |
||||||
|
*/ |
||||||
|
public static String dateToStr(Date date, String format) { |
||||||
|
return new SimpleDateFormat(format).format(date); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Date转Str {@link MyDateUtil.DATE_TIME_FORMAT} |
||||||
|
* |
||||||
|
* @param date 时间 |
||||||
|
* @return 字符串时间类型 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/22 13:07 |
||||||
|
*/ |
||||||
|
public static String dateToStr(Date date) { |
||||||
|
return new SimpleDateFormat(DATE_TIME_FORMAT).format(date); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Date 时间开始处理 (ex: Sat Sep 04 10:11:25 CST 2021 -> 2021-09-04 00:00:00) |
||||||
|
* |
||||||
|
* @param date 时间 |
||||||
|
* @return 时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/9/4 10:12 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Date dateToStartTime(Date date) { |
||||||
|
String dateTime = dateToStr(date, DATE_TIME_START_FORMAT); |
||||||
|
return strToDate(dateTime, DATE_TIME_FORMAT); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Date 时间结束处理 (ex: Sat Sep 04 10:11:25 CST 2021 -> 2021-09-04 23:59:59) |
||||||
|
* |
||||||
|
* @param date 时间 |
||||||
|
* @return 时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/9/4 10:12 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Date dateToEndTime(Date date) { |
||||||
|
String dateTime = dateToStr(date, DATE_TIME_END_FORMAT); |
||||||
|
return strToDate(dateTime, DATE_TIME_FORMAT); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Str(yyyy-MM-dd HH:mm:ss)转DateTime |
||||||
|
* |
||||||
|
* @param dateStr 字符串时间 |
||||||
|
* @return Date时间类型 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/20 9:41 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Date strToDateTime(String dateStr) { |
||||||
|
return new SimpleDateFormat(DATE_TIME_FORMAT).parse(dateStr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Str转Date |
||||||
|
* |
||||||
|
* @param dateStr 字符串时间 |
||||||
|
* @param format 时间格式 |
||||||
|
* @return Date时间类型 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/22 13:07 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Date strToDate(String dateStr, String format) { |
||||||
|
return new SimpleDateFormat(format).parse(dateStr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Str转long(10位-秒级别) |
||||||
|
* |
||||||
|
* @param dateStr 字符串时间 |
||||||
|
* @param format 时间格式 |
||||||
|
* @return 秒 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/22 13:07 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static long strToLong(String dateStr, String format) { |
||||||
|
return new SimpleDateFormat(format).parse(dateStr).getTime() / 1000; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取服务器启动时间 |
||||||
|
*/ |
||||||
|
public static Date getServerStartDate() { |
||||||
|
long time = ManagementFactory.getRuntimeMXBean().getStartTime(); |
||||||
|
return new Date(time); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断结束时间是否大于等于开始时间 |
||||||
|
* |
||||||
|
* @param startTime 开始时间 |
||||||
|
* @param endTime 结束时间 |
||||||
|
* @param format 时间格式 |
||||||
|
* @return 判断结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/22 13:21 |
||||||
|
*/ |
||||||
|
public static boolean verifyTime(String startTime, String endTime, String format) { |
||||||
|
Date startDate = strToDate(startTime, format); |
||||||
|
Date endDate = strToDate(endTime, format); |
||||||
|
return endDate.getTime() >= startDate.getTime(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取今日开始时间 |
||||||
|
* |
||||||
|
* @return 今日开始时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/18 18:58 |
||||||
|
*/ |
||||||
|
public static Date todayStartTime() { |
||||||
|
LocalDateTime todayStartTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN); |
||||||
|
return Date.from(todayStartTime.atZone(ZoneId.systemDefault()).toInstant()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取今日开始时间 |
||||||
|
* |
||||||
|
* @return 今日开始时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/18 18:58 |
||||||
|
*/ |
||||||
|
public static String todayStartTimeStr() { |
||||||
|
DateTimeFormatter df = DateTimeFormatter.ofPattern(DATE_TIME_START_FORMAT); |
||||||
|
LocalDateTime time = LocalDateTime.now(); |
||||||
|
return df.format(time); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取今日结束时间 |
||||||
|
* |
||||||
|
* @return 今日结束时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/18 18:58 |
||||||
|
*/ |
||||||
|
public static Date todayEndTime() { |
||||||
|
LocalDateTime todayStartTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX); |
||||||
|
return Date.from(todayStartTime.atZone(ZoneId.systemDefault()).toInstant()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取今日结束时间 |
||||||
|
* |
||||||
|
* @return 今日结束时间 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/8/18 18:58 |
||||||
|
*/ |
||||||
|
public static String todayEndTimeStr() { |
||||||
|
DateTimeFormatter df = DateTimeFormatter.ofPattern(DATE_TIME_END_FORMAT); |
||||||
|
LocalDateTime time = LocalDateTime.now(); |
||||||
|
return df.format(time); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 计算两时间分钟差 |
||||||
|
* |
||||||
|
* @param startTimeStr 开始时间 ex: 2020-09-09 10:00:10 |
||||||
|
* @param endTimeStr 结束时间 ex: 2020-09-09 10:30:10 |
||||||
|
* @return 分钟差 ex:30 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/9/9 10:21 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static int diffMinute(String startTimeStr, String endTimeStr) { |
||||||
|
SimpleDateFormat simpleFormat = new SimpleDateFormat(MINUTE_FORMAT); |
||||||
|
long startTime = simpleFormat.parse(startTimeStr).getTime(); |
||||||
|
long endTime = simpleFormat.parse(endTimeStr).getTime(); |
||||||
|
return (int) ((endTime - startTime) / (1000 * 60)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 计算两时间毫秒差 |
||||||
|
* |
||||||
|
* @param startTime 开始时间 |
||||||
|
* @param endTime 结束时间 |
||||||
|
* @return 毫秒差 ex:30 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/9/9 10:21 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static long diffMillisecond(Date startTime, Date endTime) { |
||||||
|
return endTime.getTime() - startTime.getTime(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 计算两个时间差 |
||||||
|
* |
||||||
|
* @param startDate 开始时间 |
||||||
|
* @param endDate 结束时间 |
||||||
|
* @return 时间差 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/7/23 10:17 |
||||||
|
*/ |
||||||
|
public static String getDateDiffStr(Date startDate, Date endDate) { |
||||||
|
long nd = 1000 * 24 * 60 * 60; |
||||||
|
long nh = 1000 * 60 * 60; |
||||||
|
long nm = 1000 * 60; |
||||||
|
// long ns = 1000;
|
||||||
|
// 获得两个时间的毫秒时间差异
|
||||||
|
long diff = endDate.getTime() - startDate.getTime(); |
||||||
|
// 计算差多少天
|
||||||
|
long day = diff / nd; |
||||||
|
// 计算差多少小时
|
||||||
|
long hour = diff % nd / nh; |
||||||
|
// 计算差多少分钟
|
||||||
|
long min = diff % nd % nh / nm; |
||||||
|
// 计算差多少秒//输出结果
|
||||||
|
// long sec = diff % nd % nh % nm / ns;
|
||||||
|
return day + "天" + hour + "小时" + min + "分钟"; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在当前时间上加指定时间 |
||||||
|
* |
||||||
|
* @param timeUnit 时间单位 |
||||||
|
* @param time 时间 |
||||||
|
* @return 结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/10/18 16:57 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Date addTime(TimeUnit timeUnit, int time) { |
||||||
|
return addAndSubTime(timeUnit, +time); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在当前时间上减指定时间 |
||||||
|
* |
||||||
|
* @param timeUnit 时间单位 |
||||||
|
* @param time 时间 |
||||||
|
* @return 结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/10/18 16:57 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Date subTime(TimeUnit timeUnit, int time) { |
||||||
|
return addAndSubTime(timeUnit, -time); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在当前时间上 加或减 指定时间 |
||||||
|
* |
||||||
|
* @param timeUnit 时间单位 |
||||||
|
* @param time 时间 |
||||||
|
* @return 结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/10/18 16:57 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Date addAndSubTime(TimeUnit timeUnit, int time) { |
||||||
|
return timeAddAndSubTime(new Date(), timeUnit, time); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在指定时间上 加或减 指定时间 |
||||||
|
* |
||||||
|
* @param timeUnit 时间单位 |
||||||
|
* @param time 时间 |
||||||
|
* @return 结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2021/10/18 16:57 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static Date timeAddAndSubTime(Date sourceDate, TimeUnit timeUnit, int time) { |
||||||
|
Calendar calendar = Calendar.getInstance(); |
||||||
|
calendar.setTime(sourceDate); |
||||||
|
switch (timeUnit) { |
||||||
|
case SECONDS: |
||||||
|
calendar.add(Calendar.SECOND, time); |
||||||
|
break; |
||||||
|
case MINUTES: |
||||||
|
calendar.add(Calendar.MINUTE, time); |
||||||
|
break; |
||||||
|
case HOURS: |
||||||
|
calendar.add(Calendar.HOUR, time); |
||||||
|
break; |
||||||
|
case DAYS: |
||||||
|
calendar.add(Calendar.DATE, time); |
||||||
|
break; |
||||||
|
default: |
||||||
|
throw new Exception("暂不支持该时间类型!"); |
||||||
|
} |
||||||
|
return calendar.getTime(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
Date nowDateTime = new Date(); |
||||||
|
String dateStr = dateToStr(nowDateTime, DATE_TIME_FORMAT); |
||||||
|
System.out.println(nowStr(YEAR_FORMAT)); |
||||||
|
boolean result = verifyTime("2020-08-18 00:00:00", "2020-08-17 23:59:59", DATE_TIME_FORMAT); |
||||||
|
System.out.println(result); |
||||||
|
|
||||||
|
Date todayStartTime = todayStartTime(); |
||||||
|
Date todayEndTime = todayEndTime(); |
||||||
|
|
||||||
|
String todayStartTimeStr = todayStartTimeStr(); |
||||||
|
String todayEndTimeStr = todayEndTimeStr(); |
||||||
|
|
||||||
|
Date date = strToDateTime("2020-08-18 00:00:10"); |
||||||
|
|
||||||
|
Long aLong = strToLong("2020-08-18 00:00:10", DATE_TIME_FORMAT); |
||||||
|
|
||||||
|
Date dateTimeStartFormat = dateToStartTime(nowDateTime); |
||||||
|
Date dateTimeEndFormat = dateToEndTime(nowDateTime); |
||||||
|
|
||||||
|
int diffMinute = diffMinute("2020-09-09 10:00:10", "2020-09-09 10:30:10"); |
||||||
|
long diffMillisecond = diffMillisecond(todayStartTime, todayEndTime); |
||||||
|
|
||||||
|
log.info("nowTime:{} addTime: {}", nowStr(), dateToStr(addTime(TimeUnit.SECONDS, 20), DATE_TIME_FORMAT)); |
||||||
|
log.info("nowTime:{} addTime: {}", nowStr(), dateToStr(addTime(TimeUnit.MINUTES, 10), DATE_TIME_FORMAT)); |
||||||
|
log.info("nowTime:{} addTime: {}", nowStr(), dateToStr(addTime(TimeUnit.HOURS, 10), DATE_TIME_FORMAT)); |
||||||
|
log.info("nowTime:{} addTime: {}", nowStr(), dateToStr(addTime(TimeUnit.DAYS, 10), DATE_TIME_FORMAT)); |
||||||
|
|
||||||
|
log.info("--------------------------------"); |
||||||
|
|
||||||
|
log.info("nowTime:{} subTime: {}", nowStr(), dateToStr(subTime(TimeUnit.SECONDS, 20), DATE_TIME_FORMAT)); |
||||||
|
log.info("nowTime:{} subTime: {}", nowStr(), dateToStr(subTime(TimeUnit.MINUTES, 10), DATE_TIME_FORMAT)); |
||||||
|
log.info("nowTime:{} subTime: {}", nowStr(), dateToStr(subTime(TimeUnit.HOURS, 10), DATE_TIME_FORMAT)); |
||||||
|
log.info("nowTime:{} subTime: {}", nowStr(), dateToStr(subTime(TimeUnit.DAYS, 10), DATE_TIME_FORMAT)); |
||||||
|
|
||||||
|
log.info("--------------------------------"); |
||||||
|
|
||||||
|
log.info("nowTime:{} timeAddAndsubTime: {}", nowStr(), dateToStr(timeAddAndSubTime(todayStartTime, TimeUnit.MINUTES, 10), DATE_TIME_FORMAT)); |
||||||
|
|
||||||
|
log.info("当前时间到今天开始时间差:{}", MyDateUtil.getDateDiffStr(MyDateUtil.todayStartTime(), new Date())); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import cn.hutool.core.io.FileUtil; |
||||||
|
import cn.hutool.core.util.ZipUtil; |
||||||
|
import lombok.SneakyThrows; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 文件工具类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/8/27 19:21 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class MyFileUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 多文件或目录压缩:将`srcPath`目录以及其目录下的所有文件目录打包到`zipPath`+`suffixFileName`文件中 【采用hutool工具类进行打包文件】 |
||||||
|
* |
||||||
|
* @param srcPath 需打包的源目录 |
||||||
|
* @param zipPath 打包后的路径+文件后缀名 |
||||||
|
* @param isWithSrcDir 是否带目录显示 (true:表示带目录显示) |
||||||
|
* @param isDeleteSrcZip 是否删除源目录 |
||||||
|
* @return java.lang.String |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/27 19:25 |
||||||
|
*/ |
||||||
|
public static File zip(String srcPath, String zipPath, boolean isWithSrcDir, boolean isDeleteSrcZip) { |
||||||
|
log.debug("【压缩文件】 源目录路径: 【{}】 打包后的路径+文件后缀名: 【{}】", srcPath, zipPath); |
||||||
|
File zipFile = ZipUtil.zip(srcPath, zipPath, isWithSrcDir); |
||||||
|
// 删除目录 -> 保证下次生成文件的时候不会累计上次留下的文件
|
||||||
|
if (isDeleteSrcZip) { |
||||||
|
MyFileUtil.deleteFileOrFolder(srcPath); |
||||||
|
} |
||||||
|
return zipFile; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据路径删除指定的目录或文件,无论存在与否 |
||||||
|
* |
||||||
|
* @param fullFileOrDirPath 要删除的目录或文件 |
||||||
|
* @return 删除成功返回 true,否则返回 false |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/9/5 20:56 |
||||||
|
*/ |
||||||
|
public static boolean deleteFileOrFolder(String fullFileOrDirPath) { |
||||||
|
return FileUtil.del(fullFileOrDirPath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据路径创建文件 |
||||||
|
* |
||||||
|
* @param fullFilePath 文件生成路径 |
||||||
|
* @return 文件信息 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/9/8 21:41 |
||||||
|
*/ |
||||||
|
public static File touch(String fullFilePath) { |
||||||
|
return FileUtil.touch(fullFilePath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解压 |
||||||
|
* |
||||||
|
* @param inputStream 流 |
||||||
|
* @param zipFilePath zip文件路径 |
||||||
|
* @param outFileDir 解压后的目录路径 |
||||||
|
* @param isDeleteZip 是否删除源zip文件 |
||||||
|
* @return 解压后的文件File信息 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/9/5 20:50 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static File unzip(InputStream inputStream, String zipFilePath, String outFileDir, boolean isDeleteZip) { |
||||||
|
log.debug("【解压文件】 zip文件路径: 【{}】 解压后的目录路径: 【{}】", zipFilePath, outFileDir); |
||||||
|
// zip压缩文件
|
||||||
|
// File zipFile = FileUtil.newFile(zipFilePath);
|
||||||
|
// 写入文件
|
||||||
|
// FileUtils.copyInputStreamToFile(inputStream, zipFile);
|
||||||
|
// 编码方式 "UTF-8" 、"GBK" 【注: gbk编码才能解决报错: java.lang.IllegalArgumentException: MALFORMED】
|
||||||
|
File outFile = ZipUtil.unzip(zipFilePath, outFileDir, Charset.forName("GBK")); |
||||||
|
// 删除zip -> 保证下次解压后的文件数据不会累计上次解压留下的文件
|
||||||
|
if (isDeleteZip) { |
||||||
|
MyFileUtil.deleteFileOrFolder(zipFilePath); |
||||||
|
} |
||||||
|
return outFile; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 读取文件内容 |
||||||
|
* |
||||||
|
* @param file 文件数据 |
||||||
|
* @return 文件内容 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/9/5 23:00 |
||||||
|
*/ |
||||||
|
public static String readFileContent(File file) { |
||||||
|
return FileUtil.readUtf8String(file); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 读取文件内容 |
||||||
|
* |
||||||
|
* @param filePath 文件路径 |
||||||
|
* @return 文件内容 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/9/5 23:00 |
||||||
|
*/ |
||||||
|
public static String readFileContent(String filePath) { |
||||||
|
return FileUtil.readUtf8String(filePath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 读取文件数据 |
||||||
|
* |
||||||
|
* @param filePath 文件路径 |
||||||
|
* @return 文件字节码 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/9/5 23:00 |
||||||
|
*/ |
||||||
|
public static byte[] readBytes(String filePath) { |
||||||
|
return FileUtil.readBytes(filePath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 写入文件内容 |
||||||
|
* |
||||||
|
* @param fileContent 文件内容 |
||||||
|
* @param filePath 文件路径 |
||||||
|
* @return 文件信息 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/11/17 21:38 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static File writeFileContent(String fileContent, String filePath) { |
||||||
|
return FileUtil.writeUtf8String(fileContent, filePath); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 字节码写入文件 |
||||||
|
* |
||||||
|
* @param data 字节码 |
||||||
|
* @param filePath 文件路径 |
||||||
|
* @return 文件信息 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/11/24 14:36 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static File writeFileContent(byte[] data, String filePath) { |
||||||
|
return FileUtil.writeBytes(data, filePath); |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
try { |
||||||
|
String filePath = "E:\\IT_zhengqing\\code\\me-workspace\\最新代码生成器\\code-api\\document\\import\\blog.zip"; |
||||||
|
String filePathX = "E:\\IT_zhengqing\\code\\me-workspace\\最新代码生成器\\code-api\\document\\import"; |
||||||
|
// File file =
|
||||||
|
// FileUtil.newFile(filePath);
|
||||||
|
// InputStream fileInputStream = new FileInputStream(file);
|
||||||
|
File unzip = ZipUtil.unzip(filePath, filePathX); |
||||||
|
System.out.println(unzip); |
||||||
|
|
||||||
|
String fileContent = FileUtil.readUtf8String(filePath); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,118 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import com.google.common.base.CaseFormat; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
|
||||||
|
import java.util.StringJoiner; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 字符串工具类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya <br/> |
||||||
|
* @date 2019/9/14 0014$ 19:49$ <br/> |
||||||
|
*/ |
||||||
|
public class MyStringUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 将驼峰式命名的字符串转换为下划线大写方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。</br> |
||||||
|
* 例如:HelloWorld->HELLO_WORLD |
||||||
|
* |
||||||
|
* @param name 转换前的驼峰式命名的字符串 |
||||||
|
* @return 转换后下划线大写方式命名的字符串 |
||||||
|
*/ |
||||||
|
public static String humpToMark(String name) { |
||||||
|
StringBuilder result = new StringBuilder(); |
||||||
|
if (name != null && name.length() > 0) { |
||||||
|
// 将第一个字符处理成大写
|
||||||
|
result.append(name.substring(0, 1).toUpperCase()); |
||||||
|
// 循环处理其余字符
|
||||||
|
for (int i = 1; i < name.length(); i++) { |
||||||
|
String s = name.substring(i, i + 1); |
||||||
|
// 在大写字母前添加下划线
|
||||||
|
if (s.equals(s.toUpperCase()) && !Character.isDigit(s.charAt(0))) { |
||||||
|
result.append("_"); |
||||||
|
} |
||||||
|
// 其他字符直接转成大写
|
||||||
|
result.append(s.toUpperCase()); |
||||||
|
} |
||||||
|
} |
||||||
|
return result.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 将字符串转换为驼峰式 </br> |
||||||
|
* 例如:HELLO.WORLD -> HelloWorld |
||||||
|
* <p> |
||||||
|
* System.out.println(CaseFormat.LOWER_HYPHEN.to(CaseFormat.LOWER_CAMEL, "test-data"));//testData
|
||||||
|
* System.out.println(CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "test_data"));//testData
|
||||||
|
* System.out.println(CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, "test_data"));//TestData
|
||||||
|
* <p> |
||||||
|
* System.out.println(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "testdata"));//testdata
|
||||||
|
* System.out.println(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "TestData"));//test_data
|
||||||
|
* System.out.println(CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, "testData"));//test-data
|
||||||
|
* |
||||||
|
* @param str 转换前的字符串 |
||||||
|
* @param mark 以xx符号分割 ex: `,` `_` `-` `.` |
||||||
|
* @return 转换后的驼峰式命名的字符串 |
||||||
|
*/ |
||||||
|
public static String strToHumpByMark(String str, String mark) { |
||||||
|
// 替换指定的符号mark为`_`
|
||||||
|
String replaceStr = StringUtils.replace(str, mark, "_"); |
||||||
|
// 再使用谷歌开发工具包转驼峰命名 ex:test_data -> TestData
|
||||||
|
String result = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, replaceStr); |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据库表名/表字段 转 首字母小写驼峰命名 ex:test_data -> testData |
||||||
|
* |
||||||
|
* @param dbStr 数据库表名/表字段 |
||||||
|
* @return 驼峰命名字符串 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/11/15 17:38 |
||||||
|
*/ |
||||||
|
public static String dbStrToHumpLower(String dbStr) { |
||||||
|
// 使用谷歌开发工具包转驼峰命名
|
||||||
|
return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, dbStr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据库表名/表字段 转 首字母大写驼峰命名 ex:test_data -> TestData |
||||||
|
* |
||||||
|
* @param dbStr 数据库表名/表字段 |
||||||
|
* @return 驼峰命名字符串 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/11/15 17:38 |
||||||
|
*/ |
||||||
|
public static String dbStrToHumpUpper(String dbStr) { |
||||||
|
// 使用谷歌开发工具包转驼峰命名
|
||||||
|
return CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, dbStr); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取表名缩写 |
||||||
|
* |
||||||
|
* @param tableName 表名 |
||||||
|
* @return 缩写 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/12/12 15:29 |
||||||
|
*/ |
||||||
|
public static String tableNameToAbbr(String tableName) { |
||||||
|
StringJoiner sj = new StringJoiner(""); |
||||||
|
String[] strArray = tableName.split("_"); |
||||||
|
for (String str : strArray) { |
||||||
|
sj.add(str.substring(0, 1)); |
||||||
|
} |
||||||
|
return sj.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
String str = dbStrToHumpLower("t_sys_user"); |
||||||
|
String str2 = dbStrToHumpUpper("t_sys_user"); |
||||||
|
String str3 = tableNameToAbbr("t_sys_user"); |
||||||
|
System.out.println(str3); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,55 @@ |
|||||||
|
package com.zhengqing.common.base.util; |
||||||
|
|
||||||
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer; |
||||||
|
import org.springframework.expression.EvaluationContext; |
||||||
|
import org.springframework.expression.ExpressionParser; |
||||||
|
import org.springframework.expression.spel.standard.SpelExpressionParser; |
||||||
|
import org.springframework.expression.spel.support.StandardEvaluationContext; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
/** |
||||||
|
* spring El表达式 工具类 |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/4/12 0:40 |
||||||
|
*/ |
||||||
|
public class SpringElUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 表达式解析器 |
||||||
|
*/ |
||||||
|
private static final ExpressionParser PARSER = new SpelExpressionParser(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取方法的参数名 |
||||||
|
*/ |
||||||
|
private static final LocalVariableTableParameterNameDiscoverer DISCOVERER = new LocalVariableTableParameterNameDiscoverer(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 解析spring EL表达式 |
||||||
|
* |
||||||
|
* @param key 表达式 |
||||||
|
* @param method 方法 |
||||||
|
* @param args 方法参数 |
||||||
|
* @return 结果 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/1/18 14:35 |
||||||
|
*/ |
||||||
|
public static String parse(String key, Method method, Object[] args) { |
||||||
|
// 获取方法的参数名
|
||||||
|
String[] paramNameArray = DISCOVERER.getParameterNames(method); |
||||||
|
if (paramNameArray == null) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
// 表达式的上下文
|
||||||
|
EvaluationContext context = new StandardEvaluationContext(); |
||||||
|
for (int i = 0; i < paramNameArray.length; i++) { |
||||||
|
// 为了让表达式可以访问该对象, 先把对象放到上下文中
|
||||||
|
context.setVariable(paramNameArray[i], args[i]); |
||||||
|
} |
||||||
|
return PARSER.parseExpression(key).getValue(context, String.class); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<artifactId>common</artifactId> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<version>${revision}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<artifactId>core</artifactId> |
||||||
|
|
||||||
|
<name>${project.artifactId}</name> |
||||||
|
<version>${revision}</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
|
||||||
|
<properties> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>base</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>auth</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>log</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>swagger</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>web</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>db</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>redis</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>file</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
|
||||||
|
<!-- AOP依赖 --> |
||||||
|
<!-- <dependency>--> |
||||||
|
<!-- <groupId>org.springframework.boot</groupId>--> |
||||||
|
<!-- <artifactId>spring-boot-starter-aop</artifactId>--> |
||||||
|
<!-- </dependency>--> |
||||||
|
|
||||||
|
|
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,32 @@ |
|||||||
|
package com.zhengqing.common.core.api; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.context.SysUserContext; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.web.bind.annotation.RestController; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* Controller基类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/8/17 0017 19:53 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@RestController |
||||||
|
//@Api(tags = "base接口")
|
||||||
|
public class BaseController { |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取当前登录人ID |
||||||
|
* |
||||||
|
* @return 当前登录人ID |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2020/8/30 15:41 |
||||||
|
*/ |
||||||
|
protected Integer appGetCurrentUserId() { |
||||||
|
return SysUserContext.getUserId(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
package com.zhengqing.common.core.aspect; |
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil; |
||||||
|
import com.zhengqing.common.base.context.SysUserContext; |
||||||
|
import com.zhengqing.common.web.util.ServletUtil; |
||||||
|
import io.swagger.annotations.ApiOperation; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.aspectj.lang.JoinPoint; |
||||||
|
import org.aspectj.lang.annotation.Aspect; |
||||||
|
import org.aspectj.lang.annotation.Before; |
||||||
|
import org.aspectj.lang.annotation.Pointcut; |
||||||
|
import org.aspectj.lang.reflect.MethodSignature; |
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 日志切面 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/8/1 19:09 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Aspect |
||||||
|
@Component |
||||||
|
@ConditionalOnProperty( |
||||||
|
value = {"smallboot.api-log"}, |
||||||
|
havingValue = "true", |
||||||
|
// true表示缺少此配置属性时也会加载该bean
|
||||||
|
matchIfMissing = true |
||||||
|
) |
||||||
|
public class ApiLogAspect { |
||||||
|
|
||||||
|
/** |
||||||
|
* 配置织入点 |
||||||
|
*/ |
||||||
|
@Pointcut("execution(* com.zhengqing.*..*.*Controller.*(..))") |
||||||
|
public void logPointCut() { |
||||||
|
} |
||||||
|
|
||||||
|
@Before("logPointCut()") |
||||||
|
public void doAround(JoinPoint joinPoint) throws Throwable { |
||||||
|
HttpServletRequest request = ServletUtil.getRequest(); |
||||||
|
if (request != null) { |
||||||
|
Integer userId = SysUserContext.getUserId(); |
||||||
|
String username = SysUserContext.getUsername(); |
||||||
|
|
||||||
|
// 从切面织入点处通过反射机制获取织入点处的方法
|
||||||
|
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); |
||||||
|
// 获取切入点所在的方法
|
||||||
|
ApiOperation apiOperation = signature.getMethod().getAnnotation(ApiOperation.class); |
||||||
|
|
||||||
|
// log.debug("========================== ↓↓↓↓↓↓ 《ApiLogAspect》 Start... ↓↓↓↓↓↓ ==========================");
|
||||||
|
// log.debug("《ApiLogAspect》 controller method: {}",
|
||||||
|
// signature.getDeclaringTypeName() + "." + signature.getName());
|
||||||
|
// log.debug("《ApiLogAspect》 controller method description: {}", apiOperation.value());
|
||||||
|
// log.debug("《ApiLogAspect》 operatorId: {}", userId);
|
||||||
|
// log.debug("《ApiLogAspect》 operatorName: {}", username);
|
||||||
|
// log.debug("《ApiLogAspect》 request header: {}", request.getHeader(SwaggerConstant.REQUEST_HEADER_AUTHORIZATION));
|
||||||
|
// log.debug("《ApiLogAspect》 request ip: {}", request.getRemoteAddr());
|
||||||
|
// log.debug("《ApiLogAspect》 request url: {}", request.getRequestURL().toString());
|
||||||
|
// log.debug("《ApiLogAspect》 request http method: {}", request.getMethod());
|
||||||
|
// log.debug("《ApiLogAspect》 request params: {}", this.getRequestValue(request));
|
||||||
|
// log.debug("========================== ↑↑↑↑↑↑ 《ApiLogAspect》 End... ↑↑↑↑↑↑ ==========================");
|
||||||
|
|
||||||
|
log.debug("[{}] [{}] 请求参数:{}", |
||||||
|
request.getMethod(), |
||||||
|
signature.getDeclaringTypeName() + "." + signature.getName(), |
||||||
|
this.getRequestValue(request) |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取请求的参数 |
||||||
|
*/ |
||||||
|
private String getRequestValue(HttpServletRequest request) { |
||||||
|
Map<String, String[]> map = request.getParameterMap(); |
||||||
|
String params = JSONUtil.toJsonStr(map); |
||||||
|
return StringUtils.substring(params, 0, 2000); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
package com.zhengqing.common.core.aspect; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.context.SysUserContext; |
||||||
|
import com.zhengqing.common.base.model.dto.BaseDTO; |
||||||
|
import com.zhengqing.common.core.custom.parameter.ParamCheck; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.aspectj.lang.JoinPoint; |
||||||
|
import org.aspectj.lang.annotation.Aspect; |
||||||
|
import org.aspectj.lang.annotation.Before; |
||||||
|
import org.aspectj.lang.annotation.Pointcut; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* Controller 切面 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/1/9 17:47 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Aspect |
||||||
|
@Component |
||||||
|
public class ControllerAspect { |
||||||
|
|
||||||
|
/** |
||||||
|
* 配置织入点 |
||||||
|
*/ |
||||||
|
@Pointcut("execution(* com.zhengqing.*..*.*Controller.*(..))") |
||||||
|
public void controllerPointCut() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Before增强:在目标方法被执行的时候织入增强 |
||||||
|
* <p> |
||||||
|
* Controller所有方法为切入点 |
||||||
|
*/ |
||||||
|
@Before("controllerPointCut()") |
||||||
|
public void controllerPointCut(JoinPoint joinPoint) { |
||||||
|
// 参数处理
|
||||||
|
Object[] paramObjArray = joinPoint.getArgs(); |
||||||
|
// 遍历所有传入参数,赋值
|
||||||
|
for (Object paramObj : paramObjArray) { |
||||||
|
// dto参数处理
|
||||||
|
if (paramObj instanceof BaseDTO) { |
||||||
|
BaseDTO baseDTO = (BaseDTO) paramObj; |
||||||
|
baseDTO.setCurrentUserId(SysUserContext.getUserId()); |
||||||
|
baseDTO.setCurrentUsername(SysUserContext.getUsername()); |
||||||
|
} |
||||||
|
|
||||||
|
// 参数校验处理
|
||||||
|
if (paramObj instanceof ParamCheck) { |
||||||
|
ParamCheck paramCheck = (ParamCheck) paramObj; |
||||||
|
paramCheck.checkParam(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,61 @@ |
|||||||
|
package com.zhengqing.common.core.aspect; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||||
|
import com.zhengqing.common.base.exception.ParameterException; |
||||||
|
import com.zhengqing.common.swagger.constant.SwaggerConstant; |
||||||
|
import com.zhengqing.common.web.util.ServletUtil; |
||||||
|
import org.aspectj.lang.JoinPoint; |
||||||
|
import org.aspectj.lang.annotation.Aspect; |
||||||
|
import org.aspectj.lang.annotation.Before; |
||||||
|
import org.aspectj.lang.annotation.Pointcut; |
||||||
|
import org.springframework.core.annotation.Order; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* Mapper分页参数注入切面 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/8/1 18:18 |
||||||
|
*/ |
||||||
|
@Aspect |
||||||
|
@Order(2) |
||||||
|
@Component |
||||||
|
public class MapperAspect { |
||||||
|
|
||||||
|
private static final int DEFAULT_PAGE_NUM = 1; |
||||||
|
private static final int DEFAULT_PAGE_SIZE = 10; |
||||||
|
|
||||||
|
/** |
||||||
|
* 配置织入点 |
||||||
|
*/ |
||||||
|
@Pointcut("execution(* com.zhengqing.*..*.*Mapper.*(..))") |
||||||
|
public void mapperPointCut() { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Before增强:在目标方法被执行的时候织入增强 |
||||||
|
* <p> |
||||||
|
* Mapper所有方法作为切入点 |
||||||
|
*/ |
||||||
|
@Before("mapperPointCut()") |
||||||
|
public void mapperPointCut(JoinPoint joinPoint) { |
||||||
|
Object[] paramObjArray = joinPoint.getArgs(); |
||||||
|
int pageNum = ServletUtil.getParameterToInt(SwaggerConstant.PAGE_NUM, DEFAULT_PAGE_NUM); |
||||||
|
int pageSize = ServletUtil.getParameterToInt(SwaggerConstant.PAGE_SIZE, DEFAULT_PAGE_SIZE); |
||||||
|
// 遍历所有传入参数,赋值
|
||||||
|
for (Object paramObj : paramObjArray) { |
||||||
|
if (paramObj instanceof IPage) { |
||||||
|
if (pageNum < 1 || pageSize < 1) { |
||||||
|
throw new ParameterException("传递的分页参数有误!"); |
||||||
|
} |
||||||
|
IPage<?> page = ((IPage<?>) paramObj); |
||||||
|
page.setCurrent(pageNum); |
||||||
|
page.setSize(pageSize); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.zhengqing.common.core.aspect.config; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 定义BeanPostProcessor 需要使用的标识接口 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 即我们自定义的BeanPostProcessor (InjectBeanSelfProcessor)如果发现我们的Bean是实现了该标识接口就调用setSelf注入代理对象 |
||||||
|
* @date 2021/1/9 1:10 |
||||||
|
*/ |
||||||
|
public interface BeanSelfAware { |
||||||
|
void setSelf(Object proxyBean); |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
package com.zhengqing.common.core.aspect.config; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.aop.support.AopUtils; |
||||||
|
import org.springframework.beans.BeansException; |
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.context.ApplicationContextAware; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 通过BeanPostProcessor 在目标对象中注入代理对象 -> 解决Spring AOP不拦截对象内部调用方法问题 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/1/9 0:55 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Component |
||||||
|
public class InjectBeanSelfProcessor implements BeanPostProcessor, ApplicationContextAware { |
||||||
|
|
||||||
|
private ApplicationContext applicationContext; |
||||||
|
|
||||||
|
/** |
||||||
|
* 1、注入ApplicationContext |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
||||||
|
this.applicationContext = applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { |
||||||
|
// 2、如果Bean没有实现BeanSelfAware标识接口 跳过
|
||||||
|
if (!(bean instanceof BeanSelfAware)) { |
||||||
|
return bean; |
||||||
|
} |
||||||
|
|
||||||
|
log.debug("inject proxy:【{}】", bean.getClass()); |
||||||
|
|
||||||
|
if (AopUtils.isAopProxy(bean)) { |
||||||
|
// 3、如果当前对象是AOP代理对象,直接注入
|
||||||
|
((BeanSelfAware) bean).setSelf(bean); |
||||||
|
} else { |
||||||
|
// 4、如果当前对象不是AOP代理,则通过context.getBean(beanName)获取代理对象并注入
|
||||||
|
// 此种方式不适合解决prototype Bean的代理对象注入
|
||||||
|
((BeanSelfAware) bean).setSelf(this.applicationContext.getBean(beanName)); |
||||||
|
} |
||||||
|
return bean; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { |
||||||
|
return bean; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.zhengqing.common.core.config; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.boot.CommandLineRunner; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 服务初始化之后,执行方法 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/5/22 19:29 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Component |
||||||
|
public abstract class AppCommonRunner implements CommandLineRunner { |
||||||
|
|
||||||
|
public void appRun() { |
||||||
|
log.info("《AppCommonRunner》: 服务初始化之后,执行方法 start..."); |
||||||
|
|
||||||
|
log.info("《AppCommonRunner》: 服务初始化之后,执行方法 end..."); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
package com.zhengqing.common.core.config; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 公共基础配置参数 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/8/19 9:07 |
||||||
|
*/ |
||||||
|
@Data |
||||||
|
public class CommonProperty { |
||||||
|
|
||||||
|
/** |
||||||
|
* ip |
||||||
|
*/ |
||||||
|
private String ip; |
||||||
|
|
||||||
|
/** |
||||||
|
* MySQL参数 |
||||||
|
*/ |
||||||
|
private Mysql mysql; |
||||||
|
|
||||||
|
@Data |
||||||
|
public static class Mysql { |
||||||
|
private MysqlConn master; |
||||||
|
private MysqlConn dbTest; |
||||||
|
} |
||||||
|
|
||||||
|
@Data |
||||||
|
public static class MysqlConn { |
||||||
|
private String ip; |
||||||
|
private String port; |
||||||
|
private String username; |
||||||
|
private String password; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.zhengqing.common.core.config; |
||||||
|
|
||||||
|
import com.zhengqing.common.core.config.interceptor.HandlerInterceptorForTokenUser; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; |
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 注册拦截器 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description ex: 租户ID |
||||||
|
* @date 2021/1/13 14:41 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class WebAppConfig implements WebMvcConfigurer { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addInterceptors(InterceptorRegistry registry) { |
||||||
|
// 可添加多个
|
||||||
|
registry.addInterceptor(new HandlerInterceptorForTokenUser()).addPathPatterns("/**"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
package com.zhengqing.common.core.config.interceptor; |
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil; |
||||||
|
import cn.hutool.json.JSONUtil; |
||||||
|
import com.zhengqing.common.auth.model.bo.JwtUserBO; |
||||||
|
import com.zhengqing.common.base.constant.AppConstant; |
||||||
|
import com.zhengqing.common.base.context.JwtCustomUserContext; |
||||||
|
import com.zhengqing.common.base.context.SysUserContext; |
||||||
|
import com.zhengqing.common.base.context.UmsUserContext; |
||||||
|
import com.zhengqing.common.core.config.WebAppConfig; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.springframework.web.servlet.HandlerInterceptor; |
||||||
|
import org.springframework.web.servlet.ModelAndView; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import javax.servlet.http.HttpServletResponse; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 拦截器 -- token用户信息 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 注册使用参考 {@link WebAppConfig} |
||||||
|
* @date 2022/1/10 16:28 |
||||||
|
*/ |
||||||
|
public class HandlerInterceptorForTokenUser implements HandlerInterceptor { |
||||||
|
|
||||||
|
/** |
||||||
|
* 在业务处理器处理请求之前被调用。预处理,可以进行编码、安全控制、权限校验等处理 |
||||||
|
* {@link com.zhengqing.gateway.filter.AuthFilter#filter } |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { |
||||||
|
String token = request.getHeader(AppConstant.REQUEST_HEADER_TOKEN); |
||||||
|
if (StringUtils.isBlank(token)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
JwtUserBO jwtUserBO = JSONUtil.toBean(StpUtil.getLoginId().toString(), JwtUserBO.class); |
||||||
|
SysUserContext.setUserId(jwtUserBO.getUserId()); |
||||||
|
SysUserContext.setUsername(jwtUserBO.getUserName()); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在业务处理器处理请求执行完成后,生成视图之前执行。 |
||||||
|
* 后处理(调用了Service并返回ModelAndView,但未进行页面渲染),有机会修改ModelAndView |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { |
||||||
|
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { |
||||||
|
HandlerInterceptor.super.afterCompletion(request, response, handler, ex); |
||||||
|
SysUserContext.remove(); |
||||||
|
UmsUserContext.remove(); |
||||||
|
JwtCustomUserContext.remove(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,71 @@ |
|||||||
|
package com.zhengqing.common.core.config.threadpool; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.constant.ThreadPoolConstant; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.scheduling.annotation.AsyncConfigurer; |
||||||
|
import org.springframework.scheduling.annotation.EnableAsync; |
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
||||||
|
|
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.concurrent.Executor; |
||||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 原生(Spring)异步任务线程池装配类 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 配置默认的线程池,重写spring默认线程池的方式使用的时候,只需要加@Async注解就可以,不用去声明线程池类。 |
||||||
|
* @date 2021/5/27 10:31 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@EnableAsync |
||||||
|
@Configuration |
||||||
|
public class NativeAsyncTaskExecutePool implements AsyncConfigurer { |
||||||
|
|
||||||
|
@Override |
||||||
|
public Executor getAsyncExecutor() { |
||||||
|
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); |
||||||
|
// 核心线程池大小
|
||||||
|
threadPoolTaskExecutor.setCorePoolSize(5); |
||||||
|
// 最大线程数
|
||||||
|
threadPoolTaskExecutor.setMaxPoolSize(10); |
||||||
|
// 队列容量
|
||||||
|
threadPoolTaskExecutor.setQueueCapacity(200); |
||||||
|
// 活跃时间 60s
|
||||||
|
threadPoolTaskExecutor.setKeepAliveSeconds(60); |
||||||
|
// 线程名字前缀
|
||||||
|
threadPoolTaskExecutor |
||||||
|
.setThreadNamePrefix(ThreadPoolConstant.SPRING_DEFAULT_THREAD_NAME_PREFIX); |
||||||
|
// 设置在关闭线程池时是否等待任务完成
|
||||||
|
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); |
||||||
|
// 允许核心线程超时
|
||||||
|
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true); |
||||||
|
// 等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止
|
||||||
|
threadPoolTaskExecutor.setAwaitTerminationSeconds(60); |
||||||
|
// 修改拒绝策略为使用当前线程执行
|
||||||
|
// setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
|
||||||
|
// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
|
||||||
|
threadPoolTaskExecutor |
||||||
|
.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); |
||||||
|
//初始化线程池
|
||||||
|
threadPoolTaskExecutor.initialize(); |
||||||
|
return threadPoolTaskExecutor; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 异步任务中异常处理 (注:只能捕获到@Async下无返回值的方法) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { |
||||||
|
return new AsyncUncaughtExceptionHandler() { |
||||||
|
@Override |
||||||
|
public void handleUncaughtException(Throwable throwable, Method method, |
||||||
|
Object... objects) { |
||||||
|
log.error("exception method: 【{}】", method); |
||||||
|
log.error("exception msg: ", throwable); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
package com.zhengqing.common.core.config.threadpool; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.constant.ThreadPoolConstant; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
||||||
|
|
||||||
|
import java.util.concurrent.Executor; |
||||||
|
import java.util.concurrent.ThreadPoolExecutor; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 自定义线程池配置 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/5/27 10:03 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class SmallToolsThreadPoolConfig { |
||||||
|
|
||||||
|
@Bean(ThreadPoolConstant.SMALL_TOOLS_THREAD_POOL) |
||||||
|
public Executor threadPoolExecutor() { |
||||||
|
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); |
||||||
|
// 核心线程池大小
|
||||||
|
threadPoolTaskExecutor.setCorePoolSize(5); |
||||||
|
// 最大线程数
|
||||||
|
threadPoolTaskExecutor.setMaxPoolSize(10); |
||||||
|
// 队列容量
|
||||||
|
threadPoolTaskExecutor.setQueueCapacity(200); |
||||||
|
// 活跃时间 60s
|
||||||
|
threadPoolTaskExecutor.setKeepAliveSeconds(60); |
||||||
|
// 线程名字前缀
|
||||||
|
threadPoolTaskExecutor |
||||||
|
.setThreadNamePrefix(ThreadPoolConstant.SMALL_TOOLS_THREAD_NAME_PREFIX); |
||||||
|
// 设置在关闭线程池时是否等待任务完成
|
||||||
|
threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); |
||||||
|
// 允许核心线程超时
|
||||||
|
threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true); |
||||||
|
// 等待时间 (默认为0,此时立即停止),并没等待xx秒后强制停止
|
||||||
|
threadPoolTaskExecutor.setAwaitTerminationSeconds(60); |
||||||
|
// 修改拒绝策略为使用当前线程执行
|
||||||
|
// setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
|
||||||
|
// CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
|
||||||
|
threadPoolTaskExecutor |
||||||
|
.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); |
||||||
|
//初始化线程池
|
||||||
|
threadPoolTaskExecutor.initialize(); |
||||||
|
return threadPoolTaskExecutor; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
package com.zhengqing.common.core.custom.fieldrepeat; |
||||||
|
|
||||||
|
import javax.validation.Constraint; |
||||||
|
import javax.validation.Payload; |
||||||
|
import java.lang.annotation.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 自定义字段对应数据库内容重复校验 注解 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/9/10 9:32 |
||||||
|
*/ |
||||||
|
// 元注解: 给其他普通的标签进行解释说明 【@Retention、@Documented、@Target、@Inherited、@Repeatable】
|
||||||
|
@Documented |
||||||
|
/** |
||||||
|
* 指明生命周期: RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。 RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。 |
||||||
|
* RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。 |
||||||
|
*/ |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
/** |
||||||
|
* 指定注解运用的地方: ElementType.ANNOTATION_TYPE 可以给一个注解进行注解 ElementType.CONSTRUCTOR 可以给构造方法进行注解 ElementType.FIELD 可以给属性进行注解 |
||||||
|
* ElementType.LOCAL_VARIABLE 可以给局部变量进行注解 ElementType.METHOD 可以给方法进行注解 ElementType.PACKAGE 可以给一个包进行注解 |
||||||
|
* ElementType.PARAMETER 可以给一个方法内的参数进行注解 ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举 |
||||||
|
*/ |
||||||
|
@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.TYPE}) |
||||||
|
@Constraint(validatedBy = FieldRepeatValidatorClass.class) |
||||||
|
// @Repeatable(LinkVals.class)(可重复注解同一字段,或者类,java1.8后支持)
|
||||||
|
public @interface FieldRepeatValidator { |
||||||
|
|
||||||
|
/** |
||||||
|
* 表名 |
||||||
|
*/ |
||||||
|
String tableName(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据库主键id字段属性名 - 默认为id (该值可无) |
||||||
|
*/ |
||||||
|
String idDbName() default "id"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 注解属性 - 对应校验字段名组 ex: { "name", "code" } 【 注:第一个字段名为所校验的字段,其后的字段为辅助校验字段!!! 】 |
||||||
|
*/ |
||||||
|
String[] fieldNames() default {}; |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据库校验字段属性名 - 对应数据库校验字段名组 ex: { "name", "code" } |
||||||
|
*/ |
||||||
|
String[] dbFieldNames() default {}; |
||||||
|
|
||||||
|
/** |
||||||
|
* 校验字段组固定值 ex: { "", "1" } |
||||||
|
*/ |
||||||
|
String[] fieldFixedValues() default {}; |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认错误提示信息 |
||||||
|
*/ |
||||||
|
String message() default "字段内容重复!"; |
||||||
|
|
||||||
|
Class<?>[] groups() default {}; |
||||||
|
|
||||||
|
Class<? extends Payload>[] payload() default {}; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package com.zhengqing.common.core.custom.fieldrepeat; |
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator; |
||||||
|
import javax.validation.ConstraintValidatorContext; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* `@FieldRepeatValidator`注解接口实现类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 技巧01:必须实现ConstraintValidator接口 |
||||||
|
* 技巧02:实现了ConstraintValidator接口后即使不进行Bean配置,spring也会将这个类进行Bean管理 |
||||||
|
* 技巧03:可以在实现了ConstraintValidator接口的类中依赖注入其它Bean |
||||||
|
* 技巧04:实现了ConstraintValidator接口后必须重写 initialize 和 isValid 这两个方法 |
||||||
|
* initialize 方法主要来进行初始化,通常用来获取自定义注解的属性值; |
||||||
|
* isValid 方法主要进行校验逻辑,返回true表示校验通过,返回false表示校验失败,通常根据注解属性值和实体类属性值进行校验判断 [Object:校验字段的属性值] |
||||||
|
* @date 2019/9/10 9:22 |
||||||
|
*/ |
||||||
|
public class FieldRepeatValidatorClass implements ConstraintValidator<FieldRepeatValidator, Object> { |
||||||
|
|
||||||
|
private String tableName; |
||||||
|
private String idDbName; |
||||||
|
private String[] fieldNames; |
||||||
|
private String[] dbFieldNames; |
||||||
|
private String[] fieldFixedValues; |
||||||
|
private String message; |
||||||
|
|
||||||
|
@Override |
||||||
|
public void initialize(FieldRepeatValidator fieldRepeatValidator) { |
||||||
|
this.tableName = fieldRepeatValidator.tableName(); |
||||||
|
this.idDbName = fieldRepeatValidator.idDbName(); |
||||||
|
this.fieldNames = fieldRepeatValidator.fieldNames(); |
||||||
|
this.dbFieldNames = fieldRepeatValidator.dbFieldNames(); |
||||||
|
this.fieldFixedValues = fieldRepeatValidator.fieldFixedValues(); |
||||||
|
this.message = fieldRepeatValidator.message(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) { |
||||||
|
FieldRepeatValidatorUtil fieldRepeatValidator = |
||||||
|
new FieldRepeatValidatorUtil(this.tableName, this.idDbName, this.fieldNames, this.dbFieldNames, this.fieldFixedValues, o, this.message); |
||||||
|
return fieldRepeatValidator.fieldRepeat(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,210 @@ |
|||||||
|
package com.zhengqing.common.core.custom.fieldrepeat; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.exception.MyException; |
||||||
|
import com.zhengqing.common.db.mapper.MyBaseMapper; |
||||||
|
import com.zhengqing.common.base.util.ApplicationContextUtil; |
||||||
|
import com.zhengqing.common.base.util.MyBeanUtil; |
||||||
|
import com.zhengqing.common.base.util.MyStringUtil; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.springframework.util.CollectionUtils; |
||||||
|
|
||||||
|
import java.lang.reflect.Field; |
||||||
|
import java.util.*; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 数据库字段内容重复判断处理工具类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2019/9/10 9:28 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class FieldRepeatValidatorUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 表名 |
||||||
|
*/ |
||||||
|
private String TABLE_NAME; |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据库对应实体类主键id字段属性名 |
||||||
|
*/ |
||||||
|
private String idName; |
||||||
|
/** |
||||||
|
* 数据库主键id字段属性名 |
||||||
|
*/ |
||||||
|
private String idDbName; |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据库主键id字段属性值 |
||||||
|
*/ |
||||||
|
private Integer idValue; |
||||||
|
|
||||||
|
/** |
||||||
|
* 校验字段名组 |
||||||
|
*/ |
||||||
|
private String[] fieldNames; |
||||||
|
|
||||||
|
/** |
||||||
|
* 校验辅助字段组值 - 字符串、数字、对象... |
||||||
|
*/ |
||||||
|
private List<Object> fieldValues = new LinkedList<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 校验辅助字段组固定值 - 字符串、数字、对象... |
||||||
|
*/ |
||||||
|
private String[] fieldFixedValues; |
||||||
|
|
||||||
|
/** |
||||||
|
* 储存校验辅助字段对应数据库字段值 |
||||||
|
*/ |
||||||
|
private List<String> DB_FIELDS; |
||||||
|
|
||||||
|
/** |
||||||
|
* 顶级父包名 |
||||||
|
*/ |
||||||
|
private final String PACKAGE_NAME = "java.lang.Object"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 实体类对象值 |
||||||
|
*/ |
||||||
|
private Object object; |
||||||
|
|
||||||
|
/** |
||||||
|
* 错误提示信息 |
||||||
|
*/ |
||||||
|
private String message; |
||||||
|
|
||||||
|
/** |
||||||
|
* @param tableName: 数据库表名 |
||||||
|
* @param idDbName: 数据库主键id字段属性名 |
||||||
|
* @param fieldNames:校验字段组 |
||||||
|
* @param dbFieldNames:数据库校验字段组 |
||||||
|
* @param fieldFixedValues:校验字段组固定值 |
||||||
|
* @param object:对象数据 |
||||||
|
* @param message:回调到前端提示消息 |
||||||
|
*/ |
||||||
|
public FieldRepeatValidatorUtil(String tableName, String idDbName, String[] fieldNames, String[] dbFieldNames, |
||||||
|
String[] fieldFixedValues, Object object, String message) { |
||||||
|
this.TABLE_NAME = tableName; |
||||||
|
|
||||||
|
this.idDbName = idDbName; |
||||||
|
// id 转 驼峰命名
|
||||||
|
this.idName = MyStringUtil.dbStrToHumpLower(idDbName); |
||||||
|
|
||||||
|
this.fieldNames = fieldNames; |
||||||
|
this.DB_FIELDS = new ArrayList<>(Arrays.asList(dbFieldNames)); |
||||||
|
this.fieldFixedValues = fieldFixedValues; |
||||||
|
this.object = object; |
||||||
|
this.message = message; |
||||||
|
this.getFieldValue(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 校验数据 |
||||||
|
* |
||||||
|
* @return boolean |
||||||
|
*/ |
||||||
|
public boolean fieldRepeat() { |
||||||
|
// 4、 校验字段内容是否重复
|
||||||
|
// 工厂模式 + ar动态语法 -> 【这里已修改为`MyBaseMapper`方式查询数据】
|
||||||
|
// Model entity = (Model)object;
|
||||||
|
// if (object.getClass().getAnnotation(TableName.class) == null) {
|
||||||
|
// try {
|
||||||
|
// entity = (Model)object.getClass().getSuperclass().newInstance();
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// list = entity.selectList((Wrapper)new QueryWrapper().allEq(queryMap));
|
||||||
|
|
||||||
|
// 5、拼接sql
|
||||||
|
Map<Object, Object> queryMap = new HashMap<>(5); |
||||||
|
if (this.fieldFixedValues.length == 0) { |
||||||
|
for (int i = 0; i < this.fieldNames.length; i++) { |
||||||
|
queryMap.put(this.DB_FIELDS.get(i), this.fieldValues.get(i)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (int i = 0; i < this.fieldNames.length; i++) { |
||||||
|
if (StringUtils.isBlank(this.fieldFixedValues[i])) { |
||||||
|
queryMap.put(this.DB_FIELDS.get(i), this.fieldValues.get(i)); |
||||||
|
} else { |
||||||
|
queryMap.put(this.DB_FIELDS.get(i), this.fieldFixedValues[i]); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
List<Map<String, Object>> list = |
||||||
|
ApplicationContextUtil.getApplicationContext().getBean(MyBaseMapper.class).selectList(this.TABLE_NAME, queryMap); |
||||||
|
|
||||||
|
// 6、如果数据重复返回false -> 再返回自定义错误消息到前端
|
||||||
|
if (!CollectionUtils.isEmpty(list)) { |
||||||
|
if (this.idValue == null) { |
||||||
|
throw new MyException(this.message); |
||||||
|
} else { |
||||||
|
if (list.size() > 1) { |
||||||
|
throw new MyException(this.message); |
||||||
|
} |
||||||
|
// 获取list中指定字段属性值 - 这里只获取主键id
|
||||||
|
List<Object> idList = (List<Object>) MyBeanUtil.getFieldList(list, this.idDbName); |
||||||
|
boolean isContainsIdValue = false; |
||||||
|
for (Object itemId : idList) { |
||||||
|
if (itemId.toString().equals(this.idValue.toString())) { |
||||||
|
isContainsIdValue = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if (!isContainsIdValue) { |
||||||
|
throw new MyException(this.message); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取主键id、校验字段、以及对应数据库字段值 |
||||||
|
*/ |
||||||
|
private void getFieldValue() { |
||||||
|
try { |
||||||
|
// 1、获取所有的字段
|
||||||
|
Class clz = this.object.getClass(); |
||||||
|
// 当父类为null的时候说明到达了最上层的父类(Object类) -> 作递归取父类属性值使用
|
||||||
|
Map<String, Field> fieldMap = new HashMap<>(); |
||||||
|
while (clz != null && !this.PACKAGE_NAME.equals(clz.getName().toLowerCase())) { |
||||||
|
fieldMap.putAll( |
||||||
|
Arrays.stream(clz.getDeclaredFields()).collect(Collectors.toMap(Field::getName, field -> field))); |
||||||
|
// 得到父类,然后赋给自己
|
||||||
|
clz = clz.getSuperclass(); |
||||||
|
} |
||||||
|
|
||||||
|
// 2、取校验字段值
|
||||||
|
for (int i = 0; i < this.fieldNames.length; i++) { |
||||||
|
Field field = fieldMap.get(this.fieldNames[i]); |
||||||
|
if (field == null) { |
||||||
|
this.fieldValues.add(null); |
||||||
|
} else { |
||||||
|
// 设置对象中成员 属性private为可读
|
||||||
|
field.setAccessible(true); |
||||||
|
// 校验字段名的值 【 fieldNames中第一个字段为校验字段,其后为辅助校验字段 】
|
||||||
|
Object fieldValue = field.get(this.object); |
||||||
|
this.fieldValues.add(fieldValue); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 3、取主键id字段值 -> 作用:判断是插入还是更新操作
|
||||||
|
Field fieldId = fieldMap.get(this.idName); |
||||||
|
if (fieldId != null) { |
||||||
|
fieldId.setAccessible(true); |
||||||
|
this.idValue = (Integer) fieldId.get(this.object); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
throw new MyException("数据库字段内容验重校验取值失败:" + e.toString()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package com.zhengqing.common.core.custom.limit; |
||||||
|
|
||||||
|
import org.redisson.api.RateIntervalUnit; |
||||||
|
import org.redisson.api.RateType; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 自定义注解-基于redis的限流 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/10/8 9:31 |
||||||
|
*/ |
||||||
|
// 作用到方法上
|
||||||
|
@Target(ElementType.METHOD) |
||||||
|
// 运行时有效
|
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
public @interface ApiLimit { |
||||||
|
|
||||||
|
/** |
||||||
|
* 限流器key |
||||||
|
*/ |
||||||
|
String key(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 速率类型,默认所有客户端加总限流 |
||||||
|
*/ |
||||||
|
RateType rateType() default RateType.OVERALL; |
||||||
|
|
||||||
|
/** |
||||||
|
* 速率数,默认1 |
||||||
|
*/ |
||||||
|
long rate() default 1; |
||||||
|
|
||||||
|
/** |
||||||
|
* 速率间隔时间,默认3秒 |
||||||
|
*/ |
||||||
|
long rateInterval() default 3; |
||||||
|
|
||||||
|
/** |
||||||
|
* 速率间隔时间单位,默认秒 |
||||||
|
*/ |
||||||
|
RateIntervalUnit rateIntervalUnit() default RateIntervalUnit.SECONDS; |
||||||
|
|
||||||
|
/** |
||||||
|
* 默认限流提示信息 |
||||||
|
*/ |
||||||
|
String msg() default "操作频繁,请稍后再试!"; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
package com.zhengqing.common.core.custom.limit; |
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert; |
||||||
|
import com.zhengqing.common.base.exception.MyException; |
||||||
|
import com.zhengqing.common.base.util.SpringElUtil; |
||||||
|
import lombok.SneakyThrows; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.aspectj.lang.ProceedingJoinPoint; |
||||||
|
import org.aspectj.lang.annotation.Around; |
||||||
|
import org.aspectj.lang.annotation.Aspect; |
||||||
|
import org.aspectj.lang.reflect.MethodSignature; |
||||||
|
import org.redisson.api.RRateLimiter; |
||||||
|
import org.redisson.api.RateIntervalUnit; |
||||||
|
import org.redisson.api.RateType; |
||||||
|
import org.redisson.api.RedissonClient; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import javax.annotation.Resource; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> aop切面-redis限流处理 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/10/8 9:36 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Aspect |
||||||
|
@Component |
||||||
|
public class ApiLimitAspect { |
||||||
|
|
||||||
|
@Resource |
||||||
|
private RedissonClient redissonClient; |
||||||
|
|
||||||
|
/** |
||||||
|
* 【环绕通知】 用于拦截指定方法,判断用户表单保存操作是否属于重复提交 |
||||||
|
* 定义切入点表达式: execution(public * (…)) 表达式解释: execution:主体 public:可省略 *:标识方法的任意返回值 任意包+类+方法(…) 任意参数 |
||||||
|
* com.zhengqing.mall.controller.*Controller.*(..)) : 标识AOP所切服务的包名,即需要进行横切的业务类 .*Controller : 标识类名,*即所有类 .*(..) : |
||||||
|
* 标识任何方法名,括号表示参数,两个点表示任何参数类型 |
||||||
|
* |
||||||
|
* @param pjp 切入点对象 |
||||||
|
* @param apiLimit 自定义的注解对象 |
||||||
|
* @return java.lang.Object |
||||||
|
*/ |
||||||
|
@SneakyThrows |
||||||
|
@Around("( execution(* com.zhengqing.*..*.*Controller.*(..))" + |
||||||
|
" || execution(* com.zhengqing.*.feign.*(..)) ) && @annotation(apiLimit)") |
||||||
|
public Object doAround(ProceedingJoinPoint pjp, ApiLimit apiLimit) { |
||||||
|
// 数据获取
|
||||||
|
String key = apiLimit.key(); |
||||||
|
Assert.notBlank(key, "限流器key不能为空!"); |
||||||
|
|
||||||
|
// 支持spring El表达式
|
||||||
|
Method method = ((MethodSignature) pjp.getSignature()).getMethod(); |
||||||
|
Object[] args = pjp.getArgs(); |
||||||
|
key = SpringElUtil.parse(key, method, args); |
||||||
|
|
||||||
|
RateType rateType = apiLimit.rateType(); |
||||||
|
long rate = apiLimit.rate(); |
||||||
|
long rateInterval = apiLimit.rateInterval(); |
||||||
|
RateIntervalUnit rateIntervalUnit = apiLimit.rateIntervalUnit(); |
||||||
|
String msg = apiLimit.msg(); |
||||||
|
|
||||||
|
// 1、声明一个限流器
|
||||||
|
RRateLimiter rRateLimiter = this.redissonClient.getRateLimiter(key); |
||||||
|
// 2、设置速率,[rateInterval]秒中产生[rate]个令牌
|
||||||
|
rRateLimiter.trySetRate(rateType, rate, rateInterval, rateIntervalUnit); |
||||||
|
rRateLimiter.expire(1, TimeUnit.HOURS); |
||||||
|
// 3、试图获取一个令牌,获取到返回true
|
||||||
|
if (rRateLimiter.tryAcquire()) { |
||||||
|
return pjp.proceed(); |
||||||
|
} else { |
||||||
|
throw new MyException(msg); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
package com.zhengqing.common.core.custom.lock; |
||||||
|
|
||||||
|
import java.lang.annotation.*; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 自定义注解-基于redisson的分布式锁 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2022/1/18 14:22 |
||||||
|
*/ |
||||||
|
@Target({ElementType.METHOD}) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@Inherited |
||||||
|
public @interface RedisLock { |
||||||
|
|
||||||
|
/** |
||||||
|
* 锁的资源,key。支持spring El表达式 |
||||||
|
*/ |
||||||
|
// @AliasFor("key")
|
||||||
|
String key() default "'smallboot:redis_lock'"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 锁类型 |
||||||
|
*/ |
||||||
|
RedisLockType lockType() default RedisLockType.REENTRANT_LOCK; |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取锁等待时间,默认3秒 |
||||||
|
*/ |
||||||
|
long waitTime() default 3000L; |
||||||
|
|
||||||
|
/** |
||||||
|
* 锁自动释放时间,默认30秒 |
||||||
|
*/ |
||||||
|
long leaseTime() default 30000L; |
||||||
|
|
||||||
|
/** |
||||||
|
* 时间单位,默认毫秒(获取锁等待时间和持锁时间都用此单位) |
||||||
|
*/ |
||||||
|
TimeUnit unit() default TimeUnit.MILLISECONDS; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,82 @@ |
|||||||
|
package com.zhengqing.common.core.custom.lock; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.util.SpringElUtil; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.aspectj.lang.ProceedingJoinPoint; |
||||||
|
import org.aspectj.lang.annotation.Around; |
||||||
|
import org.aspectj.lang.annotation.Aspect; |
||||||
|
import org.aspectj.lang.reflect.MethodSignature; |
||||||
|
import org.redisson.api.RLock; |
||||||
|
import org.redisson.api.RedissonClient; |
||||||
|
import org.redisson.spring.starter.RedissonAutoConfiguration; |
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter; |
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import javax.annotation.Resource; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> aop切面-redisson分布式锁 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2022/1/18 14:24 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Aspect |
||||||
|
@Component |
||||||
|
@ConditionalOnBean(RedissonClient.class) |
||||||
|
@AutoConfigureAfter(RedissonAutoConfiguration.class) |
||||||
|
public class RedisLockAspect { |
||||||
|
|
||||||
|
@Resource |
||||||
|
private RedissonClient redissonClient; |
||||||
|
|
||||||
|
@Around("@annotation(com.zhengqing.common.core.custom.lock.RedisLock)") |
||||||
|
public Object around(ProceedingJoinPoint pjp) throws Throwable { |
||||||
|
Method method = ((MethodSignature) pjp.getSignature()).getMethod(); |
||||||
|
RedisLock redisLock = method.getAnnotation(RedisLock.class); |
||||||
|
String key = redisLock.key(); |
||||||
|
Object[] args = pjp.getArgs(); |
||||||
|
// 支持spring El表达式
|
||||||
|
key = SpringElUtil.parse(key, method, args); |
||||||
|
// 获取锁
|
||||||
|
RLock lock = this.getLock(key, redisLock); |
||||||
|
lock.lock(redisLock.leaseTime(), redisLock.unit()); |
||||||
|
try { |
||||||
|
return pjp.proceed(); |
||||||
|
} catch (Exception e) { |
||||||
|
throw e; |
||||||
|
} finally { |
||||||
|
// 释放锁
|
||||||
|
lock.unlock(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 获取锁 |
||||||
|
* |
||||||
|
* @param key key |
||||||
|
* @param redisLock 分布式锁注解 |
||||||
|
* @return 锁 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/1/18 14:28 |
||||||
|
*/ |
||||||
|
private RLock getLock(String key, RedisLock redisLock) { |
||||||
|
switch (redisLock.lockType()) { |
||||||
|
case REENTRANT_LOCK: |
||||||
|
return this.redissonClient.getLock(key); |
||||||
|
case FAIR_LOCK: |
||||||
|
return this.redissonClient.getFairLock(key); |
||||||
|
case READ_LOCK: |
||||||
|
return this.redissonClient.getReadWriteLock(key).readLock(); |
||||||
|
case WRITE_LOCK: |
||||||
|
return this.redissonClient.getReadWriteLock(key).writeLock(); |
||||||
|
default: |
||||||
|
throw new RuntimeException("do not support lock type:" + redisLock.lockType().name()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
package com.zhengqing.common.core.custom.lock; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 基于redisson的分布式锁类型 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description https://www.bookstack.cn/read/redisson-wiki-zh/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8.md
|
||||||
|
* @date 2022/1/18 14:23 |
||||||
|
*/ |
||||||
|
public enum RedisLockType { |
||||||
|
|
||||||
|
/** |
||||||
|
* 可重入锁 |
||||||
|
*/ |
||||||
|
REENTRANT_LOCK, |
||||||
|
|
||||||
|
/** |
||||||
|
* 公平锁 |
||||||
|
*/ |
||||||
|
FAIR_LOCK, |
||||||
|
|
||||||
|
/** |
||||||
|
* 读锁 |
||||||
|
*/ |
||||||
|
READ_LOCK, |
||||||
|
|
||||||
|
/** |
||||||
|
* 写锁 |
||||||
|
*/ |
||||||
|
WRITE_LOCK; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
package com.zhengqing.common.core.custom.parameter; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.exception.ParameterException; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 参数校验 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/8/1 18:08 |
||||||
|
*/ |
||||||
|
public interface ParamCheck { |
||||||
|
|
||||||
|
/** |
||||||
|
* 传入参数验证 |
||||||
|
* |
||||||
|
* @throws ParameterException 参数异常 |
||||||
|
*/ |
||||||
|
void checkParam() throws ParameterException; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package com.zhengqing.common.core.custom.post; |
||||||
|
|
||||||
|
import org.springframework.core.annotation.AliasFor; |
||||||
|
import org.springframework.web.bind.annotation.ValueConstants; |
||||||
|
|
||||||
|
import java.lang.annotation.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 自定义注解`RequestPostSingleParam` - 处理接收单个参数的`post`请求 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 使用时注意,用了`RequestPostSingleParam`接收参数就只能作用于一个参数,不能在controller层方法中写多个参数了!!! |
||||||
|
* @date 2021/1/13 14:41 |
||||||
|
*/ |
||||||
|
@Target(ElementType.PARAMETER) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@Documented |
||||||
|
public @interface RequestPostSingleParam { |
||||||
|
|
||||||
|
/** |
||||||
|
* Alias for {@link #name}. |
||||||
|
*/ |
||||||
|
@AliasFor("name") |
||||||
|
String value() default ""; |
||||||
|
|
||||||
|
/** |
||||||
|
* The name of the request parameter to bind to. |
||||||
|
* |
||||||
|
* @since 4.2 |
||||||
|
*/ |
||||||
|
@AliasFor("value") |
||||||
|
String name() default ""; |
||||||
|
|
||||||
|
/** |
||||||
|
* Whether the parameter is required. |
||||||
|
* <p> |
||||||
|
* Defaults to {@code true}, leading to an exception being thrown if the parameter is missing in the request. Switch |
||||||
|
* this to {@code false} if you prefer a {@code null} value if the parameter is not present in the request. |
||||||
|
* <p> |
||||||
|
* Alternatively, provide a {@link #defaultValue}, which implicitly sets this flag to {@code false}. |
||||||
|
*/ |
||||||
|
boolean required() default true; |
||||||
|
|
||||||
|
/** |
||||||
|
* The default value to use as a fallback when the request parameter is not provided or has an empty value. |
||||||
|
* <p> |
||||||
|
* Supplying a default value implicitly sets {@link #required} to {@code false}. |
||||||
|
*/ |
||||||
|
String defaultValue() default ValueConstants.DEFAULT_NONE; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
package com.zhengqing.common.core.custom.post; |
||||||
|
|
||||||
|
import cn.hutool.json.JSONUtil; |
||||||
|
import com.google.common.collect.Maps; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.commons.beanutils.ConvertUtils; |
||||||
|
import org.apache.commons.lang3.StringUtils; |
||||||
|
import org.springframework.core.MethodParameter; |
||||||
|
import org.springframework.web.bind.support.WebDataBinderFactory; |
||||||
|
import org.springframework.web.context.request.NativeWebRequest; |
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver; |
||||||
|
import org.springframework.web.method.support.ModelAndViewContainer; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 编写参数解析器 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/1/13 14:41 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class RequestPostSingleParamMethodArgumentResolver implements HandlerMethodArgumentResolver { |
||||||
|
|
||||||
|
private static final String POST = "post"; |
||||||
|
private static final String APPLICATION_JSON = "application/json"; |
||||||
|
|
||||||
|
/** |
||||||
|
* 判断是否需要处理该参数 |
||||||
|
* |
||||||
|
* @param parameter the method parameter to check |
||||||
|
* @return {@code true} if this resolver supports the supplied parameter; {@code false} otherwise |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public boolean supportsParameter(MethodParameter parameter) { |
||||||
|
// 只处理带有@RequestPostSingleParam注解的参数
|
||||||
|
return parameter.hasParameterAnnotation(RequestPostSingleParam.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, |
||||||
|
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { |
||||||
|
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); |
||||||
|
String contentType = Objects.requireNonNull(servletRequest).getContentType(); |
||||||
|
|
||||||
|
if (contentType == null || !contentType.contains(APPLICATION_JSON)) { |
||||||
|
log.error("《RequestPostSingleParam》 contentType需为【{}】", APPLICATION_JSON); |
||||||
|
throw new RuntimeException("《RequestPostSingleParam》 contentType需为application/json"); |
||||||
|
} |
||||||
|
|
||||||
|
if (!POST.equalsIgnoreCase(servletRequest.getMethod())) { |
||||||
|
log.error("《RequestPostSingleParam》 请求类型必须为post"); |
||||||
|
throw new RuntimeException("《RequestPostSingleParam》 请求类型必须为post"); |
||||||
|
} |
||||||
|
return this.bindRequestParams(parameter, servletRequest); |
||||||
|
} |
||||||
|
|
||||||
|
private Object bindRequestParams(MethodParameter parameter, HttpServletRequest servletRequest) { |
||||||
|
RequestPostSingleParam requestPostSingleParam = parameter.getParameterAnnotation(RequestPostSingleParam.class); |
||||||
|
Class<?> parameterType = parameter.getParameterType(); |
||||||
|
String requestBody = this.getRequestBody(servletRequest); |
||||||
|
Map paramObj = JSONUtil.toBean(requestBody, Map.class); |
||||||
|
if (paramObj == null) { |
||||||
|
paramObj = Maps.newHashMap(); |
||||||
|
} |
||||||
|
// if (paramObj.size() > 1) {
|
||||||
|
// throw new RuntimeException("《RequestPostSingleParam》 post请求只支持接收单个参数!");
|
||||||
|
// }
|
||||||
|
|
||||||
|
String parameterName = StringUtils.isBlank(requestPostSingleParam.value()) ? parameter.getParameterName() |
||||||
|
: requestPostSingleParam.value(); |
||||||
|
Object value = paramObj.get(parameterName); |
||||||
|
|
||||||
|
if (requestPostSingleParam.required()) { |
||||||
|
if (value == null) { |
||||||
|
log.error("《RequestPostSingleParam》 require=true,参数【{}】不能为空!", parameterName); |
||||||
|
throw new RuntimeException("《RequestPostSingleParam》 " + parameterName + "不能为空!"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ConvertUtils.convert(value, parameterType); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取请求body |
||||||
|
* |
||||||
|
* @param servletRequest: request |
||||||
|
* @return 请求body |
||||||
|
*/ |
||||||
|
private String getRequestBody(HttpServletRequest servletRequest) { |
||||||
|
StringBuilder stringBuilder = new StringBuilder(); |
||||||
|
try { |
||||||
|
BufferedReader reader = servletRequest.getReader(); |
||||||
|
char[] buf = new char[1024]; |
||||||
|
int length; |
||||||
|
while ((length = reader.read(buf)) != -1) { |
||||||
|
stringBuilder.append(buf, 0, length); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
log.error("《RequestPostSingleParam》 读取流异常", e); |
||||||
|
throw new RuntimeException("《RequestPostSingleParam》 读取流异常"); |
||||||
|
} |
||||||
|
return stringBuilder.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.zhengqing.common.core.custom.post; |
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver; |
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 注册参数解析器 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2021/1/13 14:41 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class RequestPostSingleParamResolverConfig implements WebMvcConfigurer { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { |
||||||
|
resolvers.add(new RequestPostSingleParamMethodArgumentResolver()); |
||||||
|
WebMvcConfigurer.super.addArgumentResolvers(resolvers); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
package com.zhengqing.common.core.custom.repeatsubmit; |
||||||
|
|
||||||
|
import java.lang.annotation.ElementType; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
import java.lang.annotation.Target; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 注解:校验表单重复提交 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/11/27 9:59 |
||||||
|
*/ |
||||||
|
// 作用到方法上
|
||||||
|
@Target(ElementType.METHOD) |
||||||
|
// 运行时有效
|
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
public @interface NoRepeatSubmit { |
||||||
|
/** |
||||||
|
* 默认时间3秒 |
||||||
|
*/ |
||||||
|
int time() default 3 * 1000; |
||||||
|
} |
@ -0,0 +1,84 @@ |
|||||||
|
package com.zhengqing.common.core.custom.repeatsubmit; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.exception.MyException; |
||||||
|
import com.zhengqing.common.redis.util.RedisUtil; |
||||||
|
import com.zhengqing.common.web.util.IpUtil; |
||||||
|
import lombok.SneakyThrows; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.aspectj.lang.ProceedingJoinPoint; |
||||||
|
import org.aspectj.lang.annotation.Around; |
||||||
|
import org.aspectj.lang.annotation.Aspect; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
import org.springframework.web.context.request.RequestContextHolder; |
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes; |
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest; |
||||||
|
import java.util.Objects; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* aop切面 校验表单重复提交 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/11/27 11:26 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Aspect |
||||||
|
@Component |
||||||
|
public class NoRepeatSubmitAop { |
||||||
|
|
||||||
|
/** |
||||||
|
* 【环绕通知】 用于拦截指定方法,判断用户表单保存操作是否属于重复提交 |
||||||
|
* 定义切入点表达式: execution(public * (…)) 表达式解释: |
||||||
|
* - execution => 主体 |
||||||
|
* - public => 可省略 |
||||||
|
* - * => 标识方法的任意返回值 任意包+类+方法(…) 任意参数 |
||||||
|
* - com.zhengqing.*.*.api => 标识AOP所切服务的包名,即需要进行横切的业务类 |
||||||
|
* - .*Controller => 标识类名,*即所有类 |
||||||
|
* - .*(..) => 标识任何方法名,括号表示参数,两个点表示任何参数类型 |
||||||
|
* |
||||||
|
* @param pjp 切入点对象 |
||||||
|
* @param noRepeatSubmit 自定义的注解对象 |
||||||
|
* @return java.lang.Object |
||||||
|
*/ |
||||||
|
@SneakyThrows |
||||||
|
@Around("( execution(* com.zhengqing.*.api.*Controller.*(..)) || execution(* com.zhengqing..*.api.*Controller.*(..)) ) && @annotation(noRepeatSubmit)") |
||||||
|
public Object doAround(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) { |
||||||
|
HttpServletRequest request = |
||||||
|
((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())) |
||||||
|
.getRequest(); |
||||||
|
|
||||||
|
// 拿到ip地址、请求路径
|
||||||
|
String ip = IpUtil.getIpAdrress(request); |
||||||
|
String url = request.getRequestURL().toString(); |
||||||
|
|
||||||
|
// 现在时间
|
||||||
|
long now = System.currentTimeMillis(); |
||||||
|
|
||||||
|
// 自定义key值方式
|
||||||
|
String key = "REQUEST_FORM_" + ip; |
||||||
|
if (RedisUtil.hasKey(key)) { |
||||||
|
// 上次表单提交时间
|
||||||
|
long lastTime = Long.parseLong(RedisUtil.get(key)); |
||||||
|
// 如果现在距离上次提交时间小于设置的默认时间 则 判断为重复提交 否则 正常提交 -> 进入业务处理
|
||||||
|
if ((now - lastTime) > noRepeatSubmit.time()) { |
||||||
|
// 非重复提交操作 - 重新记录操作时间
|
||||||
|
RedisUtil.set(key, String.valueOf(now)); |
||||||
|
// 进入处理业务
|
||||||
|
// ApiResult result = (ApiResult)pjp.proceed();
|
||||||
|
return pjp.proceed(); |
||||||
|
} else { |
||||||
|
throw new MyException("请勿重复提交!"); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// 这里是第一次操作
|
||||||
|
RedisUtil.set(key, String.valueOf(now)); |
||||||
|
// ApiResult result = (ApiResult)pjp.proceed();
|
||||||
|
return pjp.proceed(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package com.zhengqing.common.core.custom.requestparamalias; |
||||||
|
|
||||||
|
import java.lang.annotation.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 自定义注解`@RequestParamAlias` - get请求`@ModelAttribute`接收对象属性中的字段别名设置 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 通过自定义spring属性编辑器解决 |
||||||
|
* tips: 字段别名注解`@JsonProperty`和`@JsonAlias`应用于post请求,不支持get请求,所以这里才单独自定义注解 |
||||||
|
* @date 2022/10/20 10:58 |
||||||
|
*/ |
||||||
|
@Target(ElementType.FIELD) |
||||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||||
|
@Documented |
||||||
|
public @interface RequestParamAlias { |
||||||
|
String value(); |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package com.zhengqing.common.core.custom.requestparamalias; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 如果是实体类,并且用到了注解@RequestParamAlias,请实现本类 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2022/10/20 11:18 |
||||||
|
*/ |
||||||
|
public interface RequestParamAliasBean { |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
package com.zhengqing.common.core.custom.requestparamalias; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.springframework.beans.BeansException; |
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.beans.factory.config.BeanPostProcessor; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.web.method.support.HandlerMethodArgumentResolver; |
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 请求的字段别名-参数解析器 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 添加到第一个参数解析器中的bean配置,通过自定义spring属性编辑器解决 |
||||||
|
* @date 2022/10/20 10:58 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
public class RequestParamAliasConfig { |
||||||
|
private ApplicationContext applicationContext; |
||||||
|
|
||||||
|
@Autowired |
||||||
|
public void setApplicationContext(ApplicationContext applicationContext) { |
||||||
|
this.applicationContext = applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
public RequestParamAliasProcessor requestParamAliasProcessor() { |
||||||
|
return new RequestParamAliasProcessor(this.applicationContext); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
public BeanPostProcessor beanPostProcessor() { |
||||||
|
return new BeanPostProcessor() { |
||||||
|
@Override |
||||||
|
public Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException { |
||||||
|
return bean; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException { |
||||||
|
if (bean instanceof RequestMappingHandlerAdapter) { |
||||||
|
RequestMappingHandlerAdapter adapter = (RequestMappingHandlerAdapter) bean; |
||||||
|
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); |
||||||
|
resolvers.add(RequestParamAliasConfig.this.requestParamAliasProcessor()); |
||||||
|
if (adapter.getArgumentResolvers() != null) { |
||||||
|
resolvers.addAll(adapter.getArgumentResolvers()); |
||||||
|
} |
||||||
|
adapter.setArgumentResolvers(resolvers); |
||||||
|
} |
||||||
|
return bean; |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
package com.zhengqing.common.core.custom.requestparamalias; |
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.springframework.beans.MutablePropertyValues; |
||||||
|
import org.springframework.beans.PropertyValue; |
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ExtendedServletRequestDataBinder; |
||||||
|
|
||||||
|
import javax.servlet.ServletRequest; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 请求的字段别名-数据绑定处理 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 通过自定义spring属性编辑器解决 |
||||||
|
* @date 2022/10/20 10:59 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
public class RequestParamAliasDataBinder extends ExtendedServletRequestDataBinder { |
||||||
|
private final Map<String, String> cacheMap; |
||||||
|
|
||||||
|
public RequestParamAliasDataBinder(Object target, String objectName, Map<String, String> asMap) { |
||||||
|
super(target, objectName); |
||||||
|
this.cacheMap = asMap; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 复写addBindValues方法 |
||||||
|
* |
||||||
|
* @param mpv 这里面存的就是请求参数的key-value |
||||||
|
* @param req 请求本身, 这里没有用到 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
protected void addBindValues(@NotNull MutablePropertyValues mpv, @NotNull ServletRequest req) { |
||||||
|
super.addBindValues(mpv, req); |
||||||
|
Object obj; |
||||||
|
PropertyValue pv; |
||||||
|
log.info("字段别名-缓存,{}", this.cacheMap); |
||||||
|
for (Map.Entry<String, String> entry : this.cacheMap.entrySet()) { |
||||||
|
String alias = entry.getKey(), fieldName = entry.getValue(); |
||||||
|
if (mpv.contains(alias)) { |
||||||
|
pv = mpv.getPropertyValue(alias); |
||||||
|
if (null == pv) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
obj = pv.getValue(); |
||||||
|
log.debug("字段别名-数据绑定处理,{}<>{},{}", alias, fieldName, obj); |
||||||
|
// 给原始字段赋值
|
||||||
|
mpv.add(fieldName, obj); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package com.zhengqing.common.core.custom.requestparamalias; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
import org.springframework.beans.BeanUtils; |
||||||
|
import org.springframework.context.ApplicationContext; |
||||||
|
import org.springframework.core.MethodParameter; |
||||||
|
import org.springframework.web.bind.WebDataBinder; |
||||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||||
|
import org.springframework.web.context.request.NativeWebRequest; |
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; |
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor; |
||||||
|
|
||||||
|
import javax.servlet.ServletRequest; |
||||||
|
import java.lang.reflect.Field; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Objects; |
||||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> 请求的字段别名-参数解析器 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 通过自定义spring属性编辑器解决 |
||||||
|
* @date 2022/10/20 11:00 |
||||||
|
*/ |
||||||
|
public class RequestParamAliasProcessor extends ServletModelAttributeMethodProcessor { |
||||||
|
private static final Map<Class<?>, Map<String, String>> PARAM_CACHE_MAP = new ConcurrentHashMap<>(); |
||||||
|
private final ApplicationContext context; |
||||||
|
|
||||||
|
public RequestParamAliasProcessor(ApplicationContext applicationContext) { |
||||||
|
super(true); |
||||||
|
this.context = applicationContext; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean supportsParameter(MethodParameter parameter) { |
||||||
|
// 接口方法不含 注解 RequestParam、入参不是 简单参数、并且参数对象内的字段包含自定义注解 RequestParamAlias
|
||||||
|
return !parameter.hasParameterAnnotation(RequestParam.class) |
||||||
|
&& !parameter.hasParameterAnnotation(RequestBody.class) |
||||||
|
&& !BeanUtils.isSimpleProperty(parameter.getParameterType()) |
||||||
|
&& Arrays.stream(parameter.getParameterType().getDeclaredFields()) |
||||||
|
.anyMatch(field -> field.getAnnotation(RequestParamAlias.class) != null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void bindRequestParameters(@NotNull WebDataBinder binder, @NotNull NativeWebRequest request) { |
||||||
|
Map<String, String> asMap = this.cacheMap(Objects.requireNonNull(binder.getTarget()).getClass()); |
||||||
|
RequestParamAliasDataBinder dataBinder = new RequestParamAliasDataBinder(binder.getTarget(), binder.getObjectName(), asMap); |
||||||
|
RequestMappingHandlerAdapter adapter = this.context.getBean(RequestMappingHandlerAdapter.class); |
||||||
|
Objects.requireNonNull(adapter.getWebBindingInitializer()).initBinder(dataBinder); |
||||||
|
dataBinder.bind(Objects.requireNonNull(request.getNativeRequest(ServletRequest.class))); |
||||||
|
super.bindRequestParameters(binder, request); |
||||||
|
} |
||||||
|
|
||||||
|
private Map<String, String> cacheMap(Class<?> target) { |
||||||
|
if (PARAM_CACHE_MAP.containsKey(target)) { |
||||||
|
return PARAM_CACHE_MAP.get(target); |
||||||
|
} |
||||||
|
Map<String, String> map = this.analyzeClass(target, "", ""); |
||||||
|
PARAM_CACHE_MAP.put(target, map); |
||||||
|
return map; |
||||||
|
} |
||||||
|
|
||||||
|
private Map<String, String> analyzeClass(Class<?> target, String prtAs, String prtField) { |
||||||
|
Field[] fields = target.getDeclaredFields(); |
||||||
|
Map<String, String> map = new HashMap<>(fields.length); |
||||||
|
RequestParamAlias rpa; |
||||||
|
boolean boo; |
||||||
|
String as; |
||||||
|
for (Field field : fields) { |
||||||
|
boo = (null == (rpa = field.getAnnotation(RequestParamAlias.class)) || rpa.value().isEmpty()); |
||||||
|
as = boo ? field.getName() : rpa.value(); |
||||||
|
if (this.isExtendBean(field.getType().getInterfaces())) { |
||||||
|
// 如果字段类实现了自定义接口,就认为是自定义是对象类,继续解析字段
|
||||||
|
// 通过自定义注解在类上面使用,在这里判断也可以
|
||||||
|
map.putAll(this.analyzeClass(field.getType(), prtAs + as + '.', prtField + field.getName() + '.')); |
||||||
|
} else if (!boo || !"".equals(prtAs)) { |
||||||
|
// 只绑定需要的映射
|
||||||
|
map.put(prtAs + as, prtField + field.getName()); |
||||||
|
} |
||||||
|
} |
||||||
|
return map; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isExtendBean(Class<?>[] interfaces) { |
||||||
|
for (Class<?> face : interfaces) { |
||||||
|
if (face == RequestParamAliasBean.class) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.zhengqing.common.core.custom.validator.common; |
||||||
|
|
||||||
|
import javax.validation.groups.Default; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 使用groups的校验 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 同一个对象要复用, 比如UserDTO在更新时候要校验userId, 在保存的时候不需要校验userId, 在两种情况下都要校验username, 那就用上groups了 |
||||||
|
* 在需要校验的地方@Validated声明校验组 ` update(@RequestBody @Validated(Update.class) UserDTO userDTO) ` |
||||||
|
* 在DTO中的字段上定义好groups = {}的分组类型 ` @NotNull(message = "用户id不能为空", groups = Update.class) |
||||||
|
* 或 groups = {Create.class, Update.class} private Long userId; ` |
||||||
|
* 【注】注意:在声明分组的时候尽量加上 extend javax.validation.groups.Default |
||||||
|
* 否则,在你声明@Validated(Update.class)的时候,就会出现你在默认没添加groups = {}的时候的校验组@Email(message = "邮箱格式不对"),会不去校验,因为默认的校验组是groups = {Default.class}. |
||||||
|
* @date 2019/9/9 16:51 |
||||||
|
*/ |
||||||
|
public interface CreateGroup extends Default { |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.zhengqing.common.core.custom.validator.common; |
||||||
|
|
||||||
|
import javax.validation.groups.Default; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 使用groups的校验 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 同一个对象要复用, 比如UserDTO在更新时候要校验userId, 在保存的时候不需要校验userId, 在两种情况下都要校验username, 那就用上groups了 |
||||||
|
* 在需要校验的地方@Validated声明校验组 ` update(@RequestBody @Validated(Update.class) UserDTO userDTO) ` |
||||||
|
* 在DTO中的字段上定义好groups = {}的分组类型 ` @NotNull(message = "用户id不能为空", groups = Update.class) |
||||||
|
* 或 groups = {Create.class, Update.class} private Long userId; ` |
||||||
|
* 【注】注意:在声明分组的时候尽量加上 extend javax.validation.groups.Default |
||||||
|
* 否则,在你声明@Validated(Update.class)的时候,就会出现你在默认没添加groups = {}的时候的校验组@Email(message = "邮箱格式不对"),会不去校验,因为默认的校验组是groups = {Default.class}. |
||||||
|
* @date 2019/9/9 16:51 |
||||||
|
*/ |
||||||
|
public interface UpdateGroup extends Default { |
||||||
|
} |
@ -0,0 +1,143 @@ |
|||||||
|
package com.zhengqing.common.core.custom.validator.common; |
||||||
|
|
||||||
|
import javax.validation.Valid; |
||||||
|
import java.util.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 自定义list |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 解决`@Validated`只能验证单个实体类,在验证List集合时不生效问题 |
||||||
|
* @date 2021/1/20 15:16 |
||||||
|
*/ |
||||||
|
public class ValidList<E> implements List<E> { |
||||||
|
|
||||||
|
@Valid |
||||||
|
private List<E> list = new LinkedList<>(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public int size() { |
||||||
|
return this.list.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isEmpty() { |
||||||
|
return this.list.isEmpty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean contains(Object o) { |
||||||
|
return this.list.contains(o); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Iterator<E> iterator() { |
||||||
|
return this.list.iterator(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object[] toArray() { |
||||||
|
return this.list.toArray(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public <T> T[] toArray(T[] a) { |
||||||
|
return this.list.toArray(a); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean add(E e) { |
||||||
|
return this.list.add(e); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean remove(Object o) { |
||||||
|
return this.list.remove(o); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean containsAll(Collection<?> c) { |
||||||
|
return this.list.containsAll(c); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean addAll(Collection<? extends E> c) { |
||||||
|
return this.list.addAll(c); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean addAll(int index, Collection<? extends E> c) { |
||||||
|
return this.list.addAll(index, c); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean removeAll(Collection<?> c) { |
||||||
|
return this.list.removeAll(c); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean retainAll(Collection<?> c) { |
||||||
|
return this.list.retainAll(c); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void clear() { |
||||||
|
this.list.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public E get(int index) { |
||||||
|
return this.list.get(index); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public E set(int index, E element) { |
||||||
|
return this.list.set(index, element); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void add(int index, E element) { |
||||||
|
this.list.add(index, element); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public E remove(int index) { |
||||||
|
return this.list.remove(index); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int indexOf(Object o) { |
||||||
|
return this.list.indexOf(o); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int lastIndexOf(Object o) { |
||||||
|
return this.list.lastIndexOf(o); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ListIterator<E> listIterator() { |
||||||
|
return this.list.listIterator(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public ListIterator<E> listIterator(int index) { |
||||||
|
return this.list.listIterator(index); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public List<E> subList(int fromIndex, int toIndex) { |
||||||
|
return this.list.subList(fromIndex, toIndex); |
||||||
|
} |
||||||
|
|
||||||
|
public List<E> getList() { |
||||||
|
return this.list; |
||||||
|
} |
||||||
|
|
||||||
|
public void setList(List<E> list) { |
||||||
|
this.list = list; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
package com.zhengqing.common.core.enums; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.EnumValue; |
||||||
|
import com.google.common.collect.Lists; |
||||||
|
import com.zhengqing.common.base.exception.MyException; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Getter; |
||||||
|
|
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 性别枚举类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 0->未知;1->男;2->女 |
||||||
|
* @date 2020/11/28 22:56 |
||||||
|
*/ |
||||||
|
@Getter |
||||||
|
@AllArgsConstructor |
||||||
|
public enum UserSexEnum { |
||||||
|
|
||||||
|
/** |
||||||
|
* 未知 |
||||||
|
*/ |
||||||
|
未知((byte) 0, "未知"), |
||||||
|
/** |
||||||
|
* 男 |
||||||
|
*/ |
||||||
|
男((byte) 1, "男"), |
||||||
|
/** |
||||||
|
* 女 |
||||||
|
*/ |
||||||
|
女((byte) 2, "女"); |
||||||
|
|
||||||
|
/** |
||||||
|
* mybatis-plus 需配置扫包 `type-enums-package` |
||||||
|
* 类型值 |
||||||
|
* {@link com.baomidou.mybatisplus.annotation.EnumValue} 标记数据库存的值是type |
||||||
|
*/ |
||||||
|
@EnumValue |
||||||
|
private final Byte type; |
||||||
|
/** |
||||||
|
* 类型描述 |
||||||
|
* 标识前端展示 |
||||||
|
*/ |
||||||
|
// @JsonValue
|
||||||
|
private final String desc; |
||||||
|
|
||||||
|
|
||||||
|
private static final List<UserSexEnum> LIST = Lists.newArrayList(); |
||||||
|
|
||||||
|
static { |
||||||
|
LIST.addAll(Arrays.asList(UserSexEnum.values())); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 根据指定的规则类型查找相应枚举类 |
||||||
|
* |
||||||
|
* @param type 类型 |
||||||
|
* @return 类型枚举信息 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/1/10 12:52 |
||||||
|
*/ |
||||||
|
public static UserSexEnum getEnum(Byte type) { |
||||||
|
for (UserSexEnum itemEnum : LIST) { |
||||||
|
if (itemEnum.getType().equals(type)) { |
||||||
|
return itemEnum; |
||||||
|
} |
||||||
|
} |
||||||
|
throw new MyException("未找到指定类型数据!"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,179 @@ |
|||||||
|
package com.zhengqing.common.core.exception; |
||||||
|
|
||||||
|
import cn.dev33.satoken.exception.NotLoginException; |
||||||
|
import com.zhengqing.common.base.exception.MyException; |
||||||
|
import com.zhengqing.common.base.exception.ParameterException; |
||||||
|
import com.zhengqing.common.base.model.vo.ApiResult; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.dao.DuplicateKeyException; |
||||||
|
import org.springframework.validation.BindException; |
||||||
|
import org.springframework.web.bind.MethodArgumentNotValidException; |
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice; |
||||||
|
import org.springframework.web.servlet.NoHandlerFoundException; |
||||||
|
|
||||||
|
import javax.validation.ConstraintViolationException; |
||||||
|
import javax.validation.ValidationException; |
||||||
|
import java.lang.reflect.UndeclaredThrowableException; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* 全局异常处理器 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 在spring 3.2中,新增了@ControllerAdvice |
||||||
|
* 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中 |
||||||
|
* @date 2019/8/25 0025 18:56 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@RestControllerAdvice |
||||||
|
public class MyGlobalExceptionHandler { |
||||||
|
|
||||||
|
/** |
||||||
|
* 自定义异常处理 |
||||||
|
*/ |
||||||
|
@ExceptionHandler(value = MyException.class) |
||||||
|
public ApiResult myException(MyException e) { |
||||||
|
log.error("自定义异常:", e); |
||||||
|
if (e.getCode() != null) { |
||||||
|
return ApiResult.fail(e.getCode(), e.getMessage()); |
||||||
|
} |
||||||
|
return ApiResult.fail(e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
@ExceptionHandler(value = ParameterException.class) |
||||||
|
public ApiResult myException(ParameterException e) { |
||||||
|
log.error("参数异常:", e); |
||||||
|
if (e.getCode() != null) { |
||||||
|
return ApiResult.fail(e.getCode(), e.getMessage()); |
||||||
|
} |
||||||
|
return ApiResult.fail(e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
// 参数校验异常处理 ===========================================================================
|
||||||
|
// MethodArgumentNotValidException是springBoot中进行绑定参数校验时的异常,需要在springBoot中处理,其他需要处理ConstraintViolationException异常进行处理.
|
||||||
|
|
||||||
|
/** |
||||||
|
* 方法参数校验 |
||||||
|
*/ |
||||||
|
@ExceptionHandler(MethodArgumentNotValidException.class) |
||||||
|
public ApiResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { |
||||||
|
log.error("方法参数校验:" + e.getMessage(), e); |
||||||
|
return ApiResult.fail(e.getBindingResult().getFieldError().getDefaultMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ValidationException |
||||||
|
*/ |
||||||
|
@ExceptionHandler(ValidationException.class) |
||||||
|
public ApiResult handleValidationException(ValidationException e) { |
||||||
|
log.error("ValidationException:", e); |
||||||
|
Throwable cause = e.getCause(); |
||||||
|
if (cause == null) { |
||||||
|
return ApiResult.fail(e.getMessage()); |
||||||
|
} |
||||||
|
String message = cause.getMessage(); |
||||||
|
return ApiResult.fail(message); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* jsr303参数校验异常 |
||||||
|
*/ |
||||||
|
@ExceptionHandler({BindException.class}) |
||||||
|
public ApiResult<String> exception(BindException e) { |
||||||
|
log.error("BindException:", e); |
||||||
|
return ApiResult.fail(e.getBindingResult().getFieldError().getDefaultMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* ConstraintViolationException |
||||||
|
*/ |
||||||
|
@ExceptionHandler(ConstraintViolationException.class) |
||||||
|
public ApiResult handleConstraintViolationException(ConstraintViolationException e) { |
||||||
|
log.error("ValidationException:" + e.getMessage(), e); |
||||||
|
return ApiResult.fail(e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
@ExceptionHandler(IllegalArgumentException.class) |
||||||
|
public ApiResult<String> handleRuntimeException(IllegalArgumentException e) { |
||||||
|
log.error("参数不合法:", e); |
||||||
|
return ApiResult.fail(e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
@ExceptionHandler(NoHandlerFoundException.class) |
||||||
|
public ApiResult handlerNoFoundException(Exception e) { |
||||||
|
log.error("404:", e); |
||||||
|
return ApiResult.fail(404, "路径不存在,请检查路径是否正确"); |
||||||
|
} |
||||||
|
|
||||||
|
@ExceptionHandler(DuplicateKeyException.class) |
||||||
|
public ApiResult handleDuplicateKeyException(DuplicateKeyException e) { |
||||||
|
log.error("数据重复,请检查后提交:", e); |
||||||
|
return ApiResult.fail("数据重复,请检查后提交:" + e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
// ===============================================
|
||||||
|
|
||||||
|
@ExceptionHandler(RuntimeException.class) |
||||||
|
public ApiResult handleRuntimeException(RuntimeException e) { |
||||||
|
log.error("系统异常:", e); |
||||||
|
return ApiResult.fail("系统异常,操作失败:" + e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
@ExceptionHandler(NotLoginException.class) |
||||||
|
public ApiResult handleNotLoginException(NotLoginException e) { |
||||||
|
log.error("认证异常:", e); |
||||||
|
return ApiResult.expired(e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 空指针异常 |
||||||
|
*/ |
||||||
|
@ExceptionHandler(NullPointerException.class) |
||||||
|
public ApiResult nullPointerExceptionHandler(NullPointerException e) { |
||||||
|
log.error("空指针异常:", e); |
||||||
|
return ApiResult.fail("空指针异常:" + e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 类型转换异常 |
||||||
|
*/ |
||||||
|
@ExceptionHandler(ClassCastException.class) |
||||||
|
public ApiResult classCastExceptionHandler(ClassCastException e) { |
||||||
|
log.error("类型转换异常:", e); |
||||||
|
return ApiResult.fail("类型转换异常:" + e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 数组越界异常 |
||||||
|
*/ |
||||||
|
@ExceptionHandler(ArrayIndexOutOfBoundsException.class) |
||||||
|
public ApiResult arrayIndexOutOfBoundsException(ArrayIndexOutOfBoundsException e) { |
||||||
|
log.error("数组越界异常:", e); |
||||||
|
return ApiResult.fail("数组越界异常:" + e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 包含调用处理程序抛出的未声明的检查异常 |
||||||
|
*/ |
||||||
|
@ExceptionHandler({UndeclaredThrowableException.class}) |
||||||
|
public ApiResult exception(UndeclaredThrowableException e) { |
||||||
|
log.error("UndeclaredThrowableException:", e); |
||||||
|
Throwable cause = e.getCause(); |
||||||
|
if (cause == null) { |
||||||
|
return ApiResult.fail(e.getMessage()); |
||||||
|
} |
||||||
|
return ApiResult.fail(500, cause.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 其他错误 |
||||||
|
*/ |
||||||
|
@ExceptionHandler({Exception.class}) |
||||||
|
public ApiResult exception(Exception e) { |
||||||
|
log.error("其他错误:", e); |
||||||
|
return ApiResult.fail(500, "其他错误:" + e); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,94 @@ |
|||||||
|
package com.zhengqing.common.core.util; |
||||||
|
|
||||||
|
import com.zhengqing.common.base.constant.AppConstant; |
||||||
|
import lombok.SneakyThrows; |
||||||
|
import sun.misc.BASE64Decoder; |
||||||
|
import sun.misc.BASE64Encoder; |
||||||
|
|
||||||
|
import javax.crypto.Cipher; |
||||||
|
import javax.crypto.SecretKey; |
||||||
|
import javax.crypto.SecretKeyFactory; |
||||||
|
import javax.crypto.spec.DESKeySpec; |
||||||
|
import java.security.SecureRandom; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* DES 加密/解密工具类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2020/4/12 0:40 |
||||||
|
*/ |
||||||
|
public class DesUtil { |
||||||
|
|
||||||
|
private static final String DES_ALGORITHM = "DES"; |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
String helloWorld = encrypt("hello world", AppConstant.DES_KEY); |
||||||
|
System.out.println(helloWorld); |
||||||
|
System.out.println(decrypt(helloWorld, AppConstant.DES_KEY)); |
||||||
|
System.out.println(decrypt("UhWnn6jPKf5a+b+fzS7BqQ==", AppConstant.DES_KEY)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* des加密 |
||||||
|
* |
||||||
|
* @param data 需要加密的参数 |
||||||
|
* @param secretKey 自定义密钥字符串 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static String encrypt(String data, String secretKey) { |
||||||
|
//将自定义密钥转为字节型
|
||||||
|
byte[] encryptKey = secretKey.getBytes(); |
||||||
|
|
||||||
|
String encryptedData = null; |
||||||
|
//DES算法要求一个可信任的随机数源
|
||||||
|
SecureRandom sr = new SecureRandom(); |
||||||
|
DESKeySpec deskey = new DESKeySpec(encryptKey); |
||||||
|
|
||||||
|
//创建一个密钥工厂,然后用它把DESKeySpec转换成一个SecretKey对象
|
||||||
|
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM); |
||||||
|
SecretKey key = keyFactory.generateSecret(deskey); |
||||||
|
|
||||||
|
//加密对象
|
||||||
|
Cipher cipher = Cipher.getInstance(DES_ALGORITHM); |
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key, sr); |
||||||
|
|
||||||
|
//加密,把字节数组编码成字符串
|
||||||
|
encryptedData = new BASE64Encoder().encode(cipher.doFinal(data.getBytes())); |
||||||
|
|
||||||
|
return encryptedData; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 解密字符串 |
||||||
|
* |
||||||
|
* @param secretData 需要解密的参数 |
||||||
|
* @param secretKey 自定义密钥字符串 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
public static String decrypt(String secretData, String secretKey) { |
||||||
|
//将自定义密钥转为字节型
|
||||||
|
byte[] encryptKey = secretKey.getBytes(); |
||||||
|
|
||||||
|
String decryptedData = null; |
||||||
|
|
||||||
|
//DES算法要求有一个可信任的随机数源
|
||||||
|
SecureRandom sr = new SecureRandom(); |
||||||
|
DESKeySpec desKey = new DESKeySpec(encryptKey); |
||||||
|
|
||||||
|
//创建一个密钥工厂,把DESKeySpec转换成一个SecretKey对象
|
||||||
|
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM); |
||||||
|
SecretKey key = keyFactory.generateSecret(desKey); |
||||||
|
|
||||||
|
//解密对象
|
||||||
|
Cipher cipher = Cipher.getInstance(DES_ALGORITHM); |
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key, sr); |
||||||
|
|
||||||
|
//字符串解码 ——> 字节数组,并解密
|
||||||
|
decryptedData = new String(cipher.doFinal(new BASE64Decoder().decodeBuffer(secretData))); |
||||||
|
return decryptedData; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,126 @@ |
|||||||
|
package com.zhengqing.common.core.util; |
||||||
|
|
||||||
|
import cn.hutool.core.lang.Snowflake; |
||||||
|
import cn.hutool.core.net.NetUtil; |
||||||
|
import cn.hutool.core.util.IdUtil; |
||||||
|
import cn.hutool.core.util.RandomUtil; |
||||||
|
import com.zhengqing.common.base.exception.MyException; |
||||||
|
import com.zhengqing.common.base.util.MyDateUtil; |
||||||
|
import com.zhengqing.common.redis.constant.RedisConstant; |
||||||
|
import com.zhengqing.common.redis.util.RedisUtil; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import javax.annotation.PostConstruct; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* <p> Hutool之雪花算法生成唯一ID配置 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 可参考 https://www.bookstack.cn/read/hutool/bfd2d43bcada297e.md
|
||||||
|
* @date 2021/10/29 16:57 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Component |
||||||
|
public class IdGeneratorUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 终端ID |
||||||
|
*/ |
||||||
|
private static long workerId = 0; |
||||||
|
/** |
||||||
|
* 数据中心ID |
||||||
|
*/ |
||||||
|
private static long datacenterId = 1; |
||||||
|
|
||||||
|
private static Snowflake snowflake = IdUtil.createSnowflake(workerId, datacenterId); |
||||||
|
|
||||||
|
@PostConstruct |
||||||
|
public void init() { |
||||||
|
try { |
||||||
|
workerId = NetUtil.ipv4ToLong(NetUtil.getLocalhostStr()); |
||||||
|
log.info("当前机器的IP:[{}], workerId:[{}]", NetUtil.getLocalhostStr(), workerId); |
||||||
|
} catch (Exception e) { |
||||||
|
log.error("获取当前机器workerId 异常", e); |
||||||
|
workerId = NetUtil.getLocalhostStr().hashCode(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 使用默认的 workerId 和 datacenterId |
||||||
|
*/ |
||||||
|
public synchronized static long snowflakeId() { |
||||||
|
return snowflake.nextId(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 字符串类型 |
||||||
|
*/ |
||||||
|
public static String nextStrId() { |
||||||
|
return String.valueOf(snowflakeId()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 使用自定义的 workerId 和 datacenterId |
||||||
|
*/ |
||||||
|
public synchronized static long snowflakeId(long workerId, long datacenterId) { |
||||||
|
return IdUtil.getSnowflake(workerId, datacenterId).nextId(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 使用redis确保分布式系统主键ID唯一性 |
||||||
|
* |
||||||
|
* @return ID |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/7/4 12:47 |
||||||
|
*/ |
||||||
|
public synchronized static long nextId() { |
||||||
|
long id = snowflake.nextId(); |
||||||
|
String key = RedisConstant.ID_GENERATE_KEY_PREFIX + id; |
||||||
|
if (!RedisUtil.setIfAbsent(key, String.valueOf(id))) { |
||||||
|
// 记录下重复数据
|
||||||
|
RedisUtil.hPutIfAbsent(RedisConstant.ID_GENERATE_REPEAT_KEY, String.valueOf(id), MyDateUtil.nowStr()); |
||||||
|
// 循环继续获取
|
||||||
|
return snowflakeId(); |
||||||
|
} |
||||||
|
// 设置3分钟过期
|
||||||
|
RedisUtil.expire(key, 3, TimeUnit.MINUTES); |
||||||
|
return id; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 随机码生成 -- 12位英文大写字母+数字 |
||||||
|
* |
||||||
|
* @return 随机码 |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/4/20 16:48 |
||||||
|
*/ |
||||||
|
public static String generateRandomCode() { |
||||||
|
String key = RedisConstant.GENERATE_RANDOM_CODE_KEY; |
||||||
|
String code = RandomUtil.randomStringUpper(12); |
||||||
|
if (!RedisUtil.hPutIfAbsent(key, code, code)) { |
||||||
|
// 如果重试次数超过5次则告警...
|
||||||
|
Long retryNum = RedisUtil.incrBy(RedisConstant.GENERATE_RANDOM_CODE_RETRY_NUM_KEY, 1); |
||||||
|
if (retryNum > RedisConstant.GENERATE_RANDOM_CODE_MAX_RETRY_NUM) { |
||||||
|
// 先删除key,防止下次进来直接异常退出程序
|
||||||
|
RedisUtil.delete(RedisConstant.GENERATE_RANDOM_CODE_RETRY_NUM_KEY); |
||||||
|
throw new MyException("随机码已用尽,请联系系统管理员!"); |
||||||
|
} |
||||||
|
|
||||||
|
// 如果存在了,继续拿数据
|
||||||
|
return generateRandomCode(); |
||||||
|
} |
||||||
|
// 正常拿到数据返回
|
||||||
|
return code; |
||||||
|
} |
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
for (int i = 0; i < 10; i++) { |
||||||
|
// log.info("ID: {}", IdGeneratorUtil.snowflakeId(i % 2, i % 2));
|
||||||
|
log.info("ID: {}", IdGeneratorUtil.snowflakeId()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
|
<parent> |
||||||
|
<artifactId>common</artifactId> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<version>${revision}</version> |
||||||
|
</parent> |
||||||
|
|
||||||
|
<artifactId>db</artifactId> |
||||||
|
|
||||||
|
<name>${project.artifactId}</name> |
||||||
|
<version>${revision}</version> |
||||||
|
<packaging>jar</packaging> |
||||||
|
|
||||||
|
<properties> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>base</artifactId> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>com.zhengqing</groupId> |
||||||
|
<artifactId>swagger</artifactId> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- 数据库相关 --> |
||||||
|
<dependency> |
||||||
|
<groupId>org.springframework.boot</groupId> |
||||||
|
<artifactId>spring-boot-starter-jdbc</artifactId> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- mysql --> |
||||||
|
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> |
||||||
|
<dependency> |
||||||
|
<groupId>mysql</groupId> |
||||||
|
<artifactId>mysql-connector-java</artifactId> |
||||||
|
<version>8.0.25</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- 阿里druid数据库连接池 --> |
||||||
|
<!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter --> |
||||||
|
<dependency> |
||||||
|
<groupId>com.alibaba</groupId> |
||||||
|
<artifactId>druid-spring-boot-starter</artifactId> |
||||||
|
<version>1.2.11</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- 动态数据源 --> |
||||||
|
<!-- https://mvnrepository.com/artifact/com.baomidou/dynamic-datasource-spring-boot-starter --> |
||||||
|
<dependency> |
||||||
|
<groupId>com.baomidou</groupId> |
||||||
|
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> |
||||||
|
<version>3.5.1</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<!-- mybatis-plus --> |
||||||
|
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> |
||||||
|
<dependency> |
||||||
|
<groupId>com.baomidou</groupId> |
||||||
|
<artifactId>mybatis-plus-boot-starter</artifactId> |
||||||
|
<version>3.5.2</version> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,141 @@ |
|||||||
|
//package com.zhengqing.common.db.config;
|
||||||
|
//
|
||||||
|
//import com.alibaba.druid.pool.DruidDataSource;
|
||||||
|
//import com.alibaba.druid.support.http.StatViewServlet;
|
||||||
|
//import com.alibaba.druid.support.http.WebStatFilter;
|
||||||
|
//import com.alibaba.druid.support.spring.stat.DruidStatInterceptor;
|
||||||
|
//import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||||
|
//import org.springframework.aop.support.JdkRegexpMethodPointcut;
|
||||||
|
//import org.springframework.beans.factory.annotation.Value;
|
||||||
|
//import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
//import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
//import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
|
//import org.springframework.context.annotation.Bean;
|
||||||
|
//import org.springframework.context.annotation.Configuration;
|
||||||
|
//import org.springframework.context.annotation.Scope;
|
||||||
|
//import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||||
|
//
|
||||||
|
//import javax.sql.DataSource;
|
||||||
|
//import java.util.HashMap;
|
||||||
|
//import java.util.Map;
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * <p>
|
||||||
|
// * Druid核心配置类 - 注册bean
|
||||||
|
// * </p>
|
||||||
|
// *
|
||||||
|
// * @author zhengqingya
|
||||||
|
// * @description Druid连接池监控平台 http://127.0.0.1:5000/druid/index.html
|
||||||
|
// * @date 2019/12/19 18:20
|
||||||
|
// */
|
||||||
|
//@Configuration
|
||||||
|
//public class DruidConfig {
|
||||||
|
//
|
||||||
|
// @Value("${spring.datasource.druid.stat-view-servlet.login-username}")
|
||||||
|
// private String loginUsername;
|
||||||
|
//
|
||||||
|
// @Value("${spring.datasource.druid.stat-view-servlet.login-password}")
|
||||||
|
// private String loginPassword;
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 配置Druid监控
|
||||||
|
// */
|
||||||
|
// @Bean
|
||||||
|
// public ServletRegistrationBean druidServlet() {
|
||||||
|
// // 注册服务
|
||||||
|
// ServletRegistrationBean servletRegistrationBean =
|
||||||
|
// new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
|
||||||
|
// // IP白名单(为空表示,所有的都可以访问,多个IP的时候用逗号隔开)
|
||||||
|
// servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
|
||||||
|
// // IP黑名单 (存在共同时,deny优先于allow)
|
||||||
|
// servletRegistrationBean.addInitParameter("deny", "127.0.0.2");
|
||||||
|
// // 设置控制台登录的用户名和密码
|
||||||
|
// servletRegistrationBean.addInitParameter("loginUsername", this.loginUsername);
|
||||||
|
// servletRegistrationBean.addInitParameter("loginPassword", this.loginPassword);
|
||||||
|
// // 是否能够重置数据
|
||||||
|
// servletRegistrationBean.addInitParameter("resetEnable", "false");
|
||||||
|
// return servletRegistrationBean;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 配置web监控的filter
|
||||||
|
// */
|
||||||
|
// @Bean
|
||||||
|
// public FilterRegistrationBean webStatFilter() {
|
||||||
|
// FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
|
||||||
|
// // 添加过滤规则
|
||||||
|
// Map<String, String> initParams = new HashMap<>(1);
|
||||||
|
// // 设置忽略请求
|
||||||
|
// initParams.put("exclusions", "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
|
||||||
|
// filterRegistrationBean.setInitParameters(initParams);
|
||||||
|
// filterRegistrationBean.addInitParameter("profileEnable", "true");
|
||||||
|
// filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
|
||||||
|
// filterRegistrationBean.addInitParameter("principalSessionName", "");
|
||||||
|
// filterRegistrationBean.addInitParameter("aopPatterns", "com.example.demo.service");
|
||||||
|
// // 验证所有请求
|
||||||
|
// filterRegistrationBean.addUrlPatterns("/*");
|
||||||
|
// return filterRegistrationBean;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 配置数据源 【 将所有前缀为spring.datasource下的配置项都加载到DataSource中 】
|
||||||
|
// */
|
||||||
|
// @Bean(name = "dataSource")
|
||||||
|
// @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.master")
|
||||||
|
// public DataSource dataSource() {
|
||||||
|
// return new DruidDataSource();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // @Bean(name = "dataSource2")
|
||||||
|
// // @ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.db-test")
|
||||||
|
// // public DataSource dataSource2() {
|
||||||
|
// // return new DruidDataSource();
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 配置事物管理器
|
||||||
|
// */
|
||||||
|
// @Bean(name = "transactionManager")
|
||||||
|
// public DataSourceTransactionManager transactionManager() {
|
||||||
|
// return new DataSourceTransactionManager(this.dataSource());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // @Bean(name = "transactionManager2")
|
||||||
|
// // public DataSourceTransactionManager transactionManager2() {
|
||||||
|
// // return new DataSourceTransactionManager(dataSource2());
|
||||||
|
// // }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * ↓↓↓↓↓↓ 配置spring监控 ↓↓↓↓↓↓
|
||||||
|
// * DruidStatInterceptor: druid提供的拦截器
|
||||||
|
// */
|
||||||
|
// @Bean
|
||||||
|
// public DruidStatInterceptor druidStatInterceptor() {
|
||||||
|
// DruidStatInterceptor dsInterceptor = new DruidStatInterceptor();
|
||||||
|
// return dsInterceptor;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 使用正则表达式配置切点
|
||||||
|
// */
|
||||||
|
// @Bean
|
||||||
|
// @Scope("prototype")
|
||||||
|
// public JdkRegexpMethodPointcut druidStatPointcut() {
|
||||||
|
// JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
|
||||||
|
// pointcut.setPattern("com.zhengqing.*.api.*");
|
||||||
|
// return pointcut;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * DefaultPointcutAdvisor类定义advice及 pointcut 属性。advice指定使用的通知方式,也就是druid提供的DruidStatInterceptor类,pointcut指定切入点
|
||||||
|
// */
|
||||||
|
// @Bean
|
||||||
|
// public DefaultPointcutAdvisor druidStatAdvisor(DruidStatInterceptor druidStatInterceptor,
|
||||||
|
// JdkRegexpMethodPointcut druidStatPointcut) {
|
||||||
|
// DefaultPointcutAdvisor defaultPointAdvisor = new DefaultPointcutAdvisor();
|
||||||
|
// defaultPointAdvisor.setPointcut(druidStatPointcut);
|
||||||
|
// defaultPointAdvisor.setAdvice(druidStatInterceptor);
|
||||||
|
// return defaultPointAdvisor;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
@ -0,0 +1,88 @@ |
|||||||
|
package com.zhengqing.common.db.config.mybatis; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
||||||
|
import com.zhengqing.common.base.constant.BaseConstant; |
||||||
|
import com.zhengqing.common.base.context.JwtCustomUserContext; |
||||||
|
import com.zhengqing.common.base.enums.AuthSourceEnum; |
||||||
|
import com.zhengqing.common.base.model.bo.JwtCustomUserBO; |
||||||
|
import com.zhengqing.common.db.constant.MybatisConstant; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.apache.ibatis.reflection.MetaObject; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import java.util.Date; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* MyBatisPlus自定义字段自动填充处理类 - 实体类中使用 @TableField注解 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description 注意前端传值时要为null |
||||||
|
* @date 2019/8/18 1:46 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@Component |
||||||
|
public class MyMetaObjectHandler implements MetaObjectHandler { |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void insertFill(MetaObject metaObject) { |
||||||
|
// 用户id
|
||||||
|
Long userId = this.getUserId(); |
||||||
|
// 当前时间
|
||||||
|
Date nowDate = new Date(); |
||||||
|
|
||||||
|
// 判断对象中是否存在该参数,如果存在则插入数据
|
||||||
|
if (metaObject.hasGetter(MybatisConstant.IS_DELETED)) { |
||||||
|
this.setFieldValByName(MybatisConstant.IS_DELETED, false, metaObject); |
||||||
|
} |
||||||
|
if (metaObject.hasGetter(MybatisConstant.CREATE_BY)) { |
||||||
|
this.setFieldValByName(MybatisConstant.CREATE_BY, userId, metaObject); |
||||||
|
} |
||||||
|
if (metaObject.hasGetter(MybatisConstant.CREATE_TIME)) { |
||||||
|
this.setFieldValByName(MybatisConstant.CREATE_TIME, nowDate, metaObject); |
||||||
|
} |
||||||
|
if (metaObject.hasGetter(MybatisConstant.UPDATE_BY)) { |
||||||
|
this.setFieldValByName(MybatisConstant.UPDATE_BY, userId, metaObject); |
||||||
|
} |
||||||
|
if (metaObject.hasGetter(MybatisConstant.UPDATE_TIME)) { |
||||||
|
this.setFieldValByName(MybatisConstant.UPDATE_TIME, nowDate, metaObject); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 更新 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void updateFill(MetaObject metaObject) { |
||||||
|
if (metaObject.hasGetter(MybatisConstant.UPDATE_BY)) { |
||||||
|
this.setFieldValByName(MybatisConstant.UPDATE_BY, this.getUserId(), metaObject); |
||||||
|
} |
||||||
|
if (metaObject.hasGetter(MybatisConstant.UPDATE_TIME)) { |
||||||
|
this.setFieldValByName(MybatisConstant.UPDATE_TIME, new Date(), metaObject); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取上下文中的用户id |
||||||
|
* |
||||||
|
* @return 用户id |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/7/8 18:15 |
||||||
|
*/ |
||||||
|
private Long getUserId() { |
||||||
|
JwtCustomUserBO jwtCustomUserBO = JwtCustomUserContext.get(); |
||||||
|
if (jwtCustomUserBO != null) { |
||||||
|
if (AuthSourceEnum.B.getValue().equals(jwtCustomUserBO.getAuthSource())) { |
||||||
|
return Long.valueOf(jwtCustomUserBO.getSysUserId()); |
||||||
|
} else { |
||||||
|
return Long.valueOf(jwtCustomUserBO.getUmsUserId()); |
||||||
|
} |
||||||
|
} |
||||||
|
return Long.valueOf(BaseConstant.DEFAULT_CONTEXT_KEY_USER_ID); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,157 @@ |
|||||||
|
package com.zhengqing.common.db.config.mybatis; |
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert; |
||||||
|
import com.baomidou.mybatisplus.annotation.DbType; |
||||||
|
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 com.zhengqing.common.base.context.TenantIdContext; |
||||||
|
import com.zhengqing.common.db.config.mybatis.data.permission.first.DataPermissionInterceptor; |
||||||
|
import com.zhengqing.common.db.config.mybatis.data.permission.second.MyDataPermissionHandler; |
||||||
|
import com.zhengqing.common.db.config.mybatis.data.permission.second.MyDataPermissionInterceptor; |
||||||
|
import com.zhengqing.common.db.config.mybatis.plugins.LogicDeleteInterceptor; |
||||||
|
import com.zhengqing.common.db.config.mybatis.plugins.SqlLogInterceptor; |
||||||
|
import net.sf.jsqlparser.expression.Expression; |
||||||
|
import net.sf.jsqlparser.expression.LongValue; |
||||||
|
import org.mybatis.spring.annotation.MapperScan; |
||||||
|
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.transaction.annotation.EnableTransactionManagement; |
||||||
|
|
||||||
|
import javax.sql.DataSource; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
/** |
||||||
|
* <p> |
||||||
|
* MybatisPlus配置类 |
||||||
|
* </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2019/8/23 9:46 |
||||||
|
*/ |
||||||
|
@Configuration |
||||||
|
@EnableTransactionManagement |
||||||
|
@MapperScan({"com.zhengqing.*.mapper", "com.zhengqing.*.*.mapper"}) |
||||||
|
public class MybatisPlusConfig { |
||||||
|
|
||||||
|
/** |
||||||
|
* 需要设置租户ID的表 |
||||||
|
*/ |
||||||
|
public static Set<String> TENANT_ID_TABLE = new HashSet<>(); |
||||||
|
|
||||||
|
/** |
||||||
|
* 需要逻辑删除的表 |
||||||
|
*/ |
||||||
|
public static Set<String> LOGIC_DELETE_TABLE = new HashSet<>(); |
||||||
|
|
||||||
|
static { |
||||||
|
// TENANT_ID_TABLE.add("t_demo");
|
||||||
|
LOGIC_DELETE_TABLE.add("t_sys_menu"); |
||||||
|
LOGIC_DELETE_TABLE.add("t_sys_user"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* mybatis-plus分页插件 |
||||||
|
* 文档:https://baomidou.com/pages/2976a3/#spring-boot
|
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
public MybatisPlusInterceptor mybatisPlusInterceptor() { |
||||||
|
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); |
||||||
|
/** |
||||||
|
* 1、mybatis-plus多租户插件 |
||||||
|
* 文档:https://baomidou.com/pages/aef2f2/#tenantlineinnerinterceptor
|
||||||
|
*/ |
||||||
|
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() { |
||||||
|
@Override |
||||||
|
public Expression getTenantId() { |
||||||
|
return new LongValue(TenantIdContext.getTenantId()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getTenantIdColumn() { |
||||||
|
return "tenant_id"; |
||||||
|
} |
||||||
|
|
||||||
|
// 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件
|
||||||
|
@Override |
||||||
|
public boolean ignoreTable(String tableName) { |
||||||
|
if (!TENANT_ID_TABLE.contains(tableName)) { |
||||||
|
// 不需要租户id
|
||||||
|
return true; |
||||||
|
} |
||||||
|
Boolean tenantIdFlag = TenantIdContext.getFlag(); |
||||||
|
Assert.notNull(tenantIdFlag, "租户id不能为空!"); |
||||||
|
return !tenantIdFlag; |
||||||
|
} |
||||||
|
})); |
||||||
|
|
||||||
|
// tips: 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor
|
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 2、添加数据权限插件 |
||||||
|
*/ |
||||||
|
MyDataPermissionInterceptor dataPermissionInterceptor = new MyDataPermissionInterceptor(); |
||||||
|
// 添加自定义的数据权限处理器
|
||||||
|
dataPermissionInterceptor.setDataPermissionHandler(new MyDataPermissionHandler()); |
||||||
|
interceptor.addInnerInterceptor(dataPermissionInterceptor); |
||||||
|
|
||||||
|
/** |
||||||
|
* 3、mybatis-plus分页插件 |
||||||
|
* 文档:https://baomidou.com/pages/2976a3/#spring-boot
|
||||||
|
*/ |
||||||
|
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); |
||||||
|
return interceptor; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* mybatis-plus SQL性能分析插件【生产环境可以关闭】 性能分析拦截器,用于输出每条 SQL 语句及其执行时间 【注:3.2.0+ 已移除`PerformanceInterceptor`】 |
||||||
|
*/ |
||||||
|
// @Bean
|
||||||
|
// @Profile({"dev", "test"}) // 设置 dev test 环境开启
|
||||||
|
// public PerformanceInterceptor performanceInterceptor() {
|
||||||
|
// SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长
|
||||||
|
// performanceInterceptor.setMaxTime(3000);
|
||||||
|
// SQL是否格式化 默认false
|
||||||
|
// performanceInterceptor.setFormat(true);
|
||||||
|
// return new PerformanceInterceptor();
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** |
||||||
|
* sql 日志 |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
@ConditionalOnProperty( |
||||||
|
value = "smallboot.mybatis-plus-sql-log", |
||||||
|
havingValue = "true", |
||||||
|
// true表示缺少此配置属性时也会加载该bean
|
||||||
|
matchIfMissing = true) |
||||||
|
public SqlLogInterceptor sqlLogInterceptor() { |
||||||
|
return new SqlLogInterceptor(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据权限插件 |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
@ConditionalOnMissingBean |
||||||
|
public DataPermissionInterceptor dataScopeInterceptor(DataSource dataSource) { |
||||||
|
return new DataPermissionInterceptor(dataSource); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 逻辑删除插件 |
||||||
|
*/ |
||||||
|
@Bean |
||||||
|
@ConditionalOnMissingBean |
||||||
|
public LogicDeleteInterceptor logicDeleteInterceptor() { |
||||||
|
return new LogicDeleteInterceptor(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,160 @@ |
|||||||
|
package com.zhengqing.common.db.config.mybatis.data.permission.first; |
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.PluginUtils; |
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.SneakyThrows; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import net.sf.jsqlparser.JSQLParserException; |
||||||
|
import net.sf.jsqlparser.expression.Alias; |
||||||
|
import net.sf.jsqlparser.expression.operators.conditional.AndExpression; |
||||||
|
import net.sf.jsqlparser.parser.CCJSqlParserManager; |
||||||
|
import net.sf.jsqlparser.parser.CCJSqlParserUtil; |
||||||
|
import net.sf.jsqlparser.schema.Table; |
||||||
|
import net.sf.jsqlparser.statement.select.PlainSelect; |
||||||
|
import net.sf.jsqlparser.statement.select.Select; |
||||||
|
import net.sf.jsqlparser.statement.select.SelectBody; |
||||||
|
import net.sf.jsqlparser.statement.select.SetOperationList; |
||||||
|
import org.apache.ibatis.executor.statement.StatementHandler; |
||||||
|
import org.apache.ibatis.mapping.BoundSql; |
||||||
|
import org.apache.ibatis.mapping.MappedStatement; |
||||||
|
import org.apache.ibatis.mapping.SqlCommandType; |
||||||
|
import org.apache.ibatis.plugin.*; |
||||||
|
import org.apache.ibatis.reflection.MetaObject; |
||||||
|
import org.apache.ibatis.reflection.SystemMetaObject; |
||||||
|
|
||||||
|
import javax.sql.DataSource; |
||||||
|
import java.io.StringReader; |
||||||
|
import java.sql.Connection; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Properties; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* <p> mybatis-plus 数据权限插件 </p> |
||||||
|
* |
||||||
|
* @author zhengqingya |
||||||
|
* @description |
||||||
|
* @date 2022/1/12 14:36 |
||||||
|
*/ |
||||||
|
@Slf4j |
||||||
|
@AllArgsConstructor |
||||||
|
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})}) |
||||||
|
public class DataPermissionInterceptor implements Interceptor { |
||||||
|
|
||||||
|
/** |
||||||
|
* 数据源 |
||||||
|
*/ |
||||||
|
private DataSource dataSource; |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object intercept(Invocation invocation) throws Throwable { |
||||||
|
StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget()); |
||||||
|
MetaObject metaObject = SystemMetaObject.forObject(statementHandler); |
||||||
|
// 先判断是不是SELECT操作 不是直接过滤
|
||||||
|
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement"); |
||||||
|
if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) { |
||||||
|
return invocation.proceed(); |
||||||
|
} |
||||||
|
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql"); |
||||||
|
// 执行的SQL语句
|
||||||
|
String originalSql = boundSql.getSql(); |
||||||
|
// SQL语句的参数
|
||||||
|
Object parameterObject = boundSql.getParameterObject(); |
||||||
|
|
||||||
|
// TODO 这里对执行SQL进行自定义处理...
|
||||||
|
// String finalSql = this.handleSql(originalSql);
|
||||||
|
// System.err.println("数据权限处理过后的SQL: " + finalSql);
|
||||||
|
|
||||||
|
metaObject.setValue("delegate.boundSql.sql", originalSql); |
||||||
|
return invocation.proceed(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 改写SQL |
||||||
|
* {@link com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor} |
||||||
|
* |
||||||
|
* @param originalSql 执行的SQL语句 |
||||||
|
* @return 处理后的SQL |
||||||
|
* @author zhengqingya |
||||||
|
* @date 2022/1/13 10:43 |
||||||
|
*/ |
||||||
|
private String handleSql(String originalSql) throws JSQLParserException { |
||||||
|
CCJSqlParserManager parserManager = new CCJSqlParserManager(); |
||||||
|
Select select = (Select) parserManager.parse(new StringReader(originalSql)); |
||||||
|
SelectBody selectBody = select.getSelectBody(); |
||||||
|
if (selectBody instanceof PlainSelect) { |
||||||
|
this.setWhere((PlainSelect) selectBody); |
||||||
|
} else if (selectBody instanceof SetOperationList) { |
||||||
|
SetOperationList setOperationList = (SetOperationList) selectBody; |
||||||
|
List<SelectBody> selectBodyList = setOperationList.getSelects(); |
||||||
|
selectBodyList.forEach(s -> this.setWhere((PlainSelect) s)); |
||||||
|
} |
||||||
|
return select.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 设置 where 条件 -- 使用CCJSqlParser将原SQL进行解析并改写 |
||||||
|
* |
||||||
|
* @param plainSelect 查询对象 |
||||||
|
*/ |
||||||
|
@SneakyThrows(Exception.class) |
||||||
|
protected void setWhere(PlainSelect plainSelect) { |
||||||
|
Table fromItem = (Table) plainSelect.getFromItem(); |
||||||
|
// 有别名用别名,无别名用表名,防止字段冲突报错
|
||||||
|
Alias fromItemAlias = fromItem.getAlias(); |
||||||
|
String mainTableName = fromItemAlias == null ? fromItem.getName() : fromItemAlias.getName(); |
||||||
|
// 构建子查询 -- 数据权限过滤SQL
|
||||||
|
String dataPermissionSql = mainTableName + ".create_by in ( 1, 2, 3 )"; |
||||||
|
if (plainSelect.getWhere() == null) { |
||||||
|
plainSelect.setWhere(CCJSqlParserUtil.parseCondExpression(dataPermissionSql)); |
||||||
|
} else { |
||||||
|
plainSelect.setWhere(new AndExpression(plainSelect.getWhere(), CCJSqlParserUtil.parseCondExpression(dataPermissionSql))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* 生成拦截对象的代理 |
||||||
|
* |
||||||
|
* @param target 目标对象 |
||||||
|
* @return 代理对象 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Object plugin(Object target) { |
||||||
|
if (target instanceof StatementHandler) { |
||||||
|
return Plugin.wrap(target, this); |
||||||
|
} |
||||||
|
return target; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* mybatis配置的属性 |
||||||
|
* |
||||||
|
* @param properties mybatis配置的属性 |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public void setProperties(Properties properties) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 查找参数是否包括DataScope对象 |
||||||
|
* |
||||||
|
* @param parameterObj 参数列表 |
||||||
|
* @return DataScope |
||||||
|
*/ |
||||||
|
// private DataScope findDataScopeObject(Object parameterObj) {
|
||||||
|
// if (parameterObj instanceof DataScope) {
|
||||||
|
// return (DataScope) parameterObj;
|
||||||
|
// } else if (parameterObj instanceof Map) {
|
||||||
|
// for (Object val : ((Map<?, ?>) parameterObj).values()) {
|
||||||
|
// if (val instanceof DataScope) {
|
||||||
|
// return (DataScope) val;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue