first commit
commit
d0e494b040
|
@ -0,0 +1,46 @@
|
|||
######################################################################
|
||||
# Build Tools
|
||||
|
||||
.gradle
|
||||
/build/
|
||||
!gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
######################################################################
|
||||
# IDE
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### JRebel ###
|
||||
rebel.xml
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/*
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
.nb-gradle/
|
||||
|
||||
######################################################################
|
||||
# Others
|
||||
*.log
|
||||
*.xml.versionsBackup
|
||||
*.swp
|
||||
|
||||
!*/build/*.java
|
||||
!*/build/*.html
|
||||
!*/build/*.xml
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 若依
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,26 @@
|
|||
## 系统模块
|
||||
|
||||
~~~
|
||||
com.muyu
|
||||
├── muyu-ui // 前端框架 [80]
|
||||
├── muyu-gateway // 网关模块 [8080]
|
||||
├── muyu-auth // 认证中心 [9200]
|
||||
├── muyu-common // 通用模块
|
||||
│ └── muyu-common-core // 核心模块
|
||||
│ └── muyu-common-datascope // 权限范围
|
||||
│ └── muyu-common-datasource // 多数据源
|
||||
│ └── muyu-common-log // 日志记录
|
||||
│ └── muyu-common-redis // 缓存服务
|
||||
│ └── muyu-common-seata // 分布式事务
|
||||
│ └── muyu-common-security // 安全模块
|
||||
│ └── muyu-common-swagger // 系统接口
|
||||
│ └── muyu-common-system // 系统基础
|
||||
├── muyu-modules // 业务模块
|
||||
│ └── muyu-system // 系统模块 [9201]
|
||||
│ └── muyu-gen // 代码生成 [9202]
|
||||
│ └── muyu-job // 定时任务 [9203]
|
||||
│ └── muyu-file // 文件服务 [9300]
|
||||
├── muyu-visual // 图形化管理模块
|
||||
│ └── muyu-visual-monitor // 监控中心 [9100]
|
||||
├──pom.xml // 公共依赖
|
||||
~~~
|
|
@ -0,0 +1,82 @@
|
|||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.muyu</groupId>
|
||||
<artifactId>muyu</artifactId>
|
||||
<version>3.6.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>muyu-auth</artifactId>
|
||||
|
||||
<description>
|
||||
muyu-auth认证授权中心
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Nacos Config -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Alibaba Sentinel -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringBoot Actuator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- MuYu Common Security-->
|
||||
<dependency>
|
||||
<groupId>com.muyu</groupId>
|
||||
<artifactId>muyu-common-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- 加入maven deploy插件,当在deploy时,忽略些model-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,19 @@
|
|||
package com.muyu.auth;
|
||||
|
||||
import com.muyu.common.security.annotation.EnableMyFeignClients;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
|
||||
/**
|
||||
* 认证授权中心
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@EnableMyFeignClients
|
||||
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||
public class MuYuAuthApplication {
|
||||
public static void main (String[] args) {
|
||||
SpringApplication.run(MuYuAuthApplication.class, args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package com.muyu.auth.controller;
|
||||
|
||||
import com.muyu.auth.form.LoginBody;
|
||||
import com.muyu.auth.form.RegisterBody;
|
||||
import com.muyu.auth.service.SysLoginService;
|
||||
import com.muyu.common.core.domain.Result;
|
||||
import com.muyu.common.core.utils.JwtUtils;
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
import com.muyu.common.security.auth.AuthUtil;
|
||||
import com.muyu.common.security.service.TokenService;
|
||||
import com.muyu.common.security.utils.SecurityUtils;
|
||||
import com.muyu.common.system.domain.LoginUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* token 控制
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@RestController
|
||||
public class TokenController {
|
||||
@Autowired
|
||||
private TokenService tokenService;
|
||||
|
||||
@Autowired
|
||||
private SysLoginService sysLoginService;
|
||||
|
||||
@PostMapping("login")
|
||||
public Result<?> login (@RequestBody LoginBody form) {
|
||||
// 用户登录
|
||||
LoginUser userInfo = sysLoginService.login(form.getUsername(), form.getPassword());
|
||||
// 获取登录token
|
||||
return Result.success(tokenService.createToken(userInfo));
|
||||
}
|
||||
|
||||
@DeleteMapping("logout")
|
||||
public Result<?> logout (HttpServletRequest request) {
|
||||
String token = SecurityUtils.getToken(request);
|
||||
if (StringUtils.isNotEmpty(token)) {
|
||||
String username = JwtUtils.getUserName(token);
|
||||
// 删除用户缓存记录
|
||||
AuthUtil.logoutByToken(token);
|
||||
// 记录用户退出日志
|
||||
sysLoginService.logout(username);
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@PostMapping("refresh")
|
||||
public Result<?> refresh (HttpServletRequest request) {
|
||||
LoginUser loginUser = tokenService.getLoginUser(request);
|
||||
if (StringUtils.isNotNull(loginUser)) {
|
||||
// 刷新令牌有效期
|
||||
tokenService.refreshToken(loginUser);
|
||||
return Result.success();
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
@PostMapping("register")
|
||||
public Result<?> register (@RequestBody RegisterBody registerBody) {
|
||||
// 用户注册
|
||||
sysLoginService.register(registerBody.getUsername(), registerBody.getPassword());
|
||||
return Result.success();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.muyu.auth.form;
|
||||
|
||||
/**
|
||||
* 用户登录对象
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class LoginBody {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 用户密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
public String getUsername () {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername (String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword () {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword (String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.muyu.auth.form;
|
||||
|
||||
/**
|
||||
* 用户注册对象
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class RegisterBody extends LoginBody {
|
||||
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package com.muyu.auth.service;
|
||||
|
||||
import com.muyu.common.core.constant.CacheConstants;
|
||||
import com.muyu.common.core.constant.Constants;
|
||||
import com.muyu.common.core.constant.SecurityConstants;
|
||||
import com.muyu.common.core.constant.UserConstants;
|
||||
import com.muyu.common.core.domain.Result;
|
||||
import com.muyu.common.core.enums.UserStatus;
|
||||
import com.muyu.common.core.exception.ServiceException;
|
||||
import com.muyu.common.core.text.Convert;
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
import com.muyu.common.core.utils.ip.IpUtils;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import com.muyu.common.security.utils.SecurityUtils;
|
||||
import com.muyu.common.system.remote.RemoteUserService;
|
||||
import com.muyu.common.system.domain.SysUser;
|
||||
import com.muyu.common.system.domain.LoginUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 登录校验方法
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Component
|
||||
public class SysLoginService {
|
||||
@Autowired
|
||||
private RemoteUserService remoteUserService;
|
||||
|
||||
@Autowired
|
||||
private SysPasswordService passwordService;
|
||||
|
||||
@Autowired
|
||||
private SysRecordLogService recordLogService;
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
public LoginUser login (String username, String password) {
|
||||
// 用户名或密码为空 错误
|
||||
if (StringUtils.isAnyBlank(username, password)) {
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户/密码必须填写");
|
||||
throw new ServiceException("用户/密码必须填写");
|
||||
}
|
||||
// 密码如果不在指定范围内 错误
|
||||
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|
||||
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户密码不在指定范围");
|
||||
throw new ServiceException("用户密码不在指定范围");
|
||||
}
|
||||
// 用户名不在指定范围内 错误
|
||||
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|
||||
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) {
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户名不在指定范围");
|
||||
throw new ServiceException("用户名不在指定范围");
|
||||
}
|
||||
// IP黑名单校验
|
||||
String blackStr = Convert.toStr(redisService.getCacheObject(CacheConstants.SYS_LOGIN_BLACKIPLIST));
|
||||
if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "很遗憾,访问IP已被列入系统黑名单");
|
||||
throw new ServiceException("很遗憾,访问IP已被列入系统黑名单");
|
||||
}
|
||||
// 查询用户信息
|
||||
Result<LoginUser> userResult = remoteUserService.getUserInfo(username, SecurityConstants.INNER);
|
||||
|
||||
if (StringUtils.isNull(userResult) || StringUtils.isNull(userResult.getData())) {
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "登录用户不存在");
|
||||
throw new ServiceException("登录用户:" + username + " 不存在");
|
||||
}
|
||||
|
||||
if (Result.FAIL == userResult.getCode()) {
|
||||
throw new ServiceException(userResult.getMsg());
|
||||
}
|
||||
|
||||
LoginUser userInfo = userResult.getData();
|
||||
SysUser user = userResult.getData().getSysUser();
|
||||
if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) {
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "对不起,您的账号已被删除");
|
||||
throw new ServiceException("对不起,您的账号:" + username + " 已被删除");
|
||||
}
|
||||
if (UserStatus.DISABLE.getCode().equals(user.getStatus())) {
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, "用户已停用,请联系管理员");
|
||||
throw new ServiceException("对不起,您的账号:" + username + " 已停用");
|
||||
}
|
||||
passwordService.validate(user, password);
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功");
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
public void logout (String loginName) {
|
||||
recordLogService.recordLogininfor(loginName, Constants.LOGOUT, "退出成功");
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
public void register (String username, String password) {
|
||||
// 用户名或密码为空 错误
|
||||
if (StringUtils.isAnyBlank(username, password)) {
|
||||
throw new ServiceException("用户/密码必须填写");
|
||||
}
|
||||
if (username.length() < UserConstants.USERNAME_MIN_LENGTH
|
||||
|| username.length() > UserConstants.USERNAME_MAX_LENGTH) {
|
||||
throw new ServiceException("账户长度必须在2到20个字符之间");
|
||||
}
|
||||
if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
|
||||
|| password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
|
||||
throw new ServiceException("密码长度必须在5到20个字符之间");
|
||||
}
|
||||
|
||||
// 注册用户信息
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setUserName(username);
|
||||
sysUser.setNickName(username);
|
||||
sysUser.setPassword(SecurityUtils.encryptPassword(password));
|
||||
Result<?> registerResult = remoteUserService.registerUserInfo(sysUser, SecurityConstants.INNER);
|
||||
|
||||
if (Result.FAIL == registerResult.getCode()) {
|
||||
throw new ServiceException(registerResult.getMsg());
|
||||
}
|
||||
recordLogService.recordLogininfor(username, Constants.REGISTER, "注册成功");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.muyu.auth.service;
|
||||
|
||||
import com.muyu.common.core.constant.CacheConstants;
|
||||
import com.muyu.common.core.constant.Constants;
|
||||
import com.muyu.common.core.exception.ServiceException;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import com.muyu.common.security.utils.SecurityUtils;
|
||||
import com.muyu.common.system.domain.SysUser;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 登录密码方法
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Component
|
||||
public class SysPasswordService {
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
private int maxRetryCount = CacheConstants.PASSWORD_MAX_RETRY_COUNT;
|
||||
|
||||
private Long lockTime = CacheConstants.PASSWORD_LOCK_TIME;
|
||||
|
||||
@Autowired
|
||||
private SysRecordLogService recordLogService;
|
||||
|
||||
/**
|
||||
* 登录账户密码错误次数缓存键名
|
||||
*
|
||||
* @param username 用户名
|
||||
*
|
||||
* @return 缓存键key
|
||||
*/
|
||||
private String getCacheKey (String username) {
|
||||
return CacheConstants.PWD_ERR_CNT_KEY + username;
|
||||
}
|
||||
|
||||
public void validate (SysUser user, String password) {
|
||||
String username = user.getUserName();
|
||||
|
||||
Integer retryCount = redisService.getCacheObject(getCacheKey(username));
|
||||
|
||||
if (retryCount == null) {
|
||||
retryCount = 0;
|
||||
}
|
||||
|
||||
if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) {
|
||||
String errMsg = String.format("密码输入错误%s次,帐户锁定%s分钟", maxRetryCount, lockTime);
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, errMsg);
|
||||
throw new ServiceException(errMsg);
|
||||
}
|
||||
|
||||
if (!matches(user, password)) {
|
||||
retryCount = retryCount + 1;
|
||||
recordLogService.recordLogininfor(username, Constants.LOGIN_FAIL, String.format("密码输入错误%s次", retryCount));
|
||||
redisService.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
|
||||
throw new ServiceException("用户不存在/密码错误");
|
||||
} else {
|
||||
clearLoginRecordCache(username);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean matches (SysUser user, String rawPassword) {
|
||||
return SecurityUtils.matchesPassword(rawPassword, user.getPassword());
|
||||
}
|
||||
|
||||
public void clearLoginRecordCache (String loginName) {
|
||||
if (redisService.hasKey(getCacheKey(loginName))) {
|
||||
redisService.deleteObject(getCacheKey(loginName));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.muyu.auth.service;
|
||||
|
||||
import com.muyu.common.core.constant.Constants;
|
||||
import com.muyu.common.core.constant.SecurityConstants;
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
import com.muyu.common.core.utils.ip.IpUtils;
|
||||
import com.muyu.common.system.remote.RemoteLogService;
|
||||
import com.muyu.common.system.domain.SysLogininfor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 记录日志方法
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Component
|
||||
public class SysRecordLogService {
|
||||
@Autowired
|
||||
private RemoteLogService remoteLogService;
|
||||
|
||||
/**
|
||||
* 记录登录信息
|
||||
*
|
||||
* @param username 用户名
|
||||
* @param status 状态
|
||||
* @param message 消息内容
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public void recordLogininfor (String username, String status, String message) {
|
||||
SysLogininfor logininfor = new SysLogininfor();
|
||||
logininfor.setUserName(username);
|
||||
logininfor.setIpaddr(IpUtils.getIpAddr());
|
||||
logininfor.setMsg(message);
|
||||
// 日志状态
|
||||
if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
|
||||
logininfor.setStatus(Constants.LOGIN_SUCCESS_STATUS);
|
||||
} else if (Constants.LOGIN_FAIL.equals(status)) {
|
||||
logininfor.setStatus(Constants.LOGIN_FAIL_STATUS);
|
||||
}
|
||||
remoteLogService.saveLogininfor(logininfor, SecurityConstants.INNER);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
Spring Boot Version: ${spring-boot.version}
|
||||
Spring Application Name: ${spring.application.name}
|
|
@ -0,0 +1,25 @@
|
|||
# Tomcat
|
||||
server:
|
||||
port: 9200
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: muyu-auth
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: dev
|
||||
cloud:
|
||||
nacos:
|
||||
discovery:
|
||||
# 服务注册地址
|
||||
server-addr: 10.10.10.132:8848
|
||||
config:
|
||||
# 配置中心地址
|
||||
server-addr: 10.10.10.132:8848
|
||||
# 配置文件格式
|
||||
file-extension: yml
|
||||
# 共享配置
|
||||
shared-configs:
|
||||
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="logs/muyu-auth"/>
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="log.pattern" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{20} - [%method,%line] - %msg%n"/>
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 系统日志输出 -->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/info.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>INFO</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${log.path}/error.log</file>
|
||||
<!-- 循环政策:基于时间创建日志文件 -->
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 日志文件名格式 -->
|
||||
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<!-- 过滤的级别 -->
|
||||
<level>ERROR</level>
|
||||
<!-- 匹配时的操作:接收(记录) -->
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<!-- 不匹配时的操作:拒绝(不记录) -->
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<!-- 系统模块日志级别控制 -->
|
||||
<logger name="com.muyu" level="info"/>
|
||||
<!-- Spring日志级别控制 -->
|
||||
<logger name="org.springframework" level="warn"/>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="console"/>
|
||||
</root>
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info"/>
|
||||
<appender-ref ref="file_error"/>
|
||||
</root>
|
||||
</configuration>
|
|
@ -0,0 +1,38 @@
|
|||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
|
@ -0,0 +1,31 @@
|
|||
<?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>
|
||||
<groupId>com.muyu</groupId>
|
||||
<artifactId>muyu-common</artifactId>
|
||||
<version>3.6.3</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>muyu-common-cache</artifactId>
|
||||
|
||||
<description>
|
||||
muyu-common-cache缓存基准
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- common 缓存 -->
|
||||
<dependency>
|
||||
<groupId>com.muyu</groupId>
|
||||
<artifactId>muyu-common-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,37 @@
|
|||
package com.muyu.common.cache;
|
||||
|
||||
import com.muyu.common.cache.decoration.DecorationKey;
|
||||
|
||||
/**
|
||||
* @author DongZl
|
||||
* @description: 原子序列缓存基准
|
||||
* @Date 2024-4-1 下午 08:07
|
||||
*/
|
||||
public interface AtomicSequenceCache<K> extends DecorationKey<K> {
|
||||
|
||||
/**
|
||||
* 获取存储的值
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public Long get(K key);
|
||||
|
||||
/**
|
||||
* 自增
|
||||
*/
|
||||
public Long increment(K key);
|
||||
/**
|
||||
* 自减
|
||||
*/
|
||||
public Long decrement(K key);
|
||||
|
||||
/**
|
||||
* 增加数值
|
||||
*/
|
||||
public Long increment(K key, Long number);
|
||||
|
||||
/**
|
||||
* 减少数值
|
||||
*/
|
||||
public Long decrement(K key, Long number);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.muyu.common.cache;
|
||||
|
||||
import com.muyu.common.cache.decoration.DecorationKey;
|
||||
|
||||
/**
|
||||
* @author DongZl
|
||||
* @description: 缓存接口基类
|
||||
* @Date 2024-3-26 下午 03:25
|
||||
*/
|
||||
public interface Cache <K, V> extends DecorationKey<K> {
|
||||
|
||||
/**
|
||||
* 通过Key获取value值
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public V get(K key);
|
||||
|
||||
/**
|
||||
* 缓存添加/修改
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
public void put(K key, V value);
|
||||
|
||||
/**
|
||||
* 通过键删除
|
||||
* @param key 键
|
||||
*/
|
||||
public void remove(K key);
|
||||
|
||||
/**
|
||||
* 刷新缓存时间
|
||||
* @param key 键
|
||||
*/
|
||||
public void refreshTime (K key);
|
||||
|
||||
/**
|
||||
* 刷新缓存数据
|
||||
* @param key 键
|
||||
*/
|
||||
public void refreshData (K key);
|
||||
}
|
104
muyu-common/muyu-common-cache/src/main/java/com/muyu/common/cache/HashCache.java
vendored
100644
104
muyu-common/muyu-common-cache/src/main/java/com/muyu/common/cache/HashCache.java
vendored
100644
|
@ -0,0 +1,104 @@
|
|||
package com.muyu.common.cache;
|
||||
|
||||
import com.muyu.common.cache.decoration.DecorationKey;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author DongZl
|
||||
* @description: Hash缓存基准
|
||||
* @Date 2024-3-29 下午 03:16
|
||||
*/
|
||||
public interface HashCache <K, HK, HV> extends DecorationKey<K> {
|
||||
|
||||
|
||||
/**
|
||||
* 编码
|
||||
* @param hashKey ID
|
||||
* @return 键
|
||||
*/
|
||||
public String encodeHashKey(HK hashKey);
|
||||
|
||||
/**
|
||||
* 解码
|
||||
* @param redisHashKey 数据库键
|
||||
* @return ID
|
||||
*/
|
||||
public HK decodeHashKey(String redisHashKey);
|
||||
|
||||
/**
|
||||
* 通过Key获取所有的map
|
||||
* @param key 数据库键
|
||||
* @return 所有集合Map
|
||||
*/
|
||||
public Map<HK, HV> get(K key);
|
||||
|
||||
/**
|
||||
* 通过键和hashKey获取数据库hashValue
|
||||
* @param key 键
|
||||
* @param hashKey hash键
|
||||
* @return hash值
|
||||
*/
|
||||
public HV get(K key, HK hashKey);
|
||||
|
||||
/**
|
||||
* 通过键和hashKey获取数据库hashValue
|
||||
* @param key 键
|
||||
* @param hashKeyList hash键集合
|
||||
* @return hash值
|
||||
*/
|
||||
public List<HV> get(K key, HK... hashKeyList);
|
||||
|
||||
/**
|
||||
* 获取hash值集合
|
||||
* @param key 键
|
||||
* @return hash值集合
|
||||
*/
|
||||
public List<HV> getToList(K key);
|
||||
|
||||
/**
|
||||
* 存储数据
|
||||
* @param key redis键
|
||||
* @param map hashMap集合
|
||||
*/
|
||||
public void put(K key, Map<HK, HV> map);
|
||||
|
||||
/**
|
||||
* 存储数据
|
||||
* @param key redis键
|
||||
* @param dataList 数据值
|
||||
* @param hashKey hash键
|
||||
*/
|
||||
public void put(K key, List<HV> dataList, Function<HV, HK> hashKey);
|
||||
|
||||
/**
|
||||
* 存储数据
|
||||
* @param key redis键
|
||||
* @param hashKey hash键
|
||||
* @param hashValue hash值
|
||||
*/
|
||||
public void put(K key, HK hashKey, HV hashValue);
|
||||
|
||||
/**
|
||||
* 通过redis键删除
|
||||
* @param key hash键
|
||||
*/
|
||||
public void remove(K key);
|
||||
|
||||
/**
|
||||
* 通过redis键和hash键删除
|
||||
* @param key redis键
|
||||
* @param hashKey hash键
|
||||
*/
|
||||
public void remove(K key, HK hashKey);
|
||||
|
||||
/**
|
||||
* 判断redis中hashKey是否存在
|
||||
* @param key redis键
|
||||
* @param hashKey hash键
|
||||
*/
|
||||
public boolean hasKey(K key, HK hashKey);
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package com.muyu.common.cache.abs;
|
||||
|
||||
import com.muyu.common.cache.AtomicSequenceCache;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* @author DongZl
|
||||
* @description: 原子序列缓存抽象类
|
||||
* @Date 2024-4-1 下午 08:33
|
||||
*/
|
||||
public abstract class AtomicSequenceCacheAbs<K> implements AtomicSequenceCache<K> {
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
/**
|
||||
* 获取存储的值
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
@Override
|
||||
public Long get (K key) {
|
||||
Long cacheValue = this.redisService.getCacheObject(encode(key));
|
||||
if (cacheValue == null){
|
||||
Long data = getData(key);
|
||||
cacheValue = data == null ? 0L : data;
|
||||
this.redisService.setCacheObject(encode(key), cacheValue);
|
||||
}
|
||||
return cacheValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自增
|
||||
* @param key
|
||||
*/
|
||||
@Override
|
||||
public Long increment (K key) {
|
||||
return this.increment(key, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* 自减
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
@Override
|
||||
public Long decrement (K key) {
|
||||
return this.decrement(key, 1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加数值
|
||||
*
|
||||
* @param key
|
||||
* @param number
|
||||
*/
|
||||
@Override
|
||||
public Long increment (K key, Long number) {
|
||||
Long numberValue = redisService.getCacheObject(encode(key));
|
||||
if (numberValue == null){
|
||||
Long data = getData(key);
|
||||
data = data == null ? 0L : data;
|
||||
redisService.setCacheObject(encode(key), data);
|
||||
}
|
||||
return redisService.increment(encode(key), number);
|
||||
}
|
||||
|
||||
/**
|
||||
* 减少数值
|
||||
*
|
||||
* @param key
|
||||
* @param number
|
||||
*/
|
||||
@Override
|
||||
public Long decrement (K key, Long number) {
|
||||
Long numberValue = redisService.getCacheObject(encode(key));
|
||||
if (numberValue == null){
|
||||
Long data = getData(key);
|
||||
data = data == null ? 0L : data;
|
||||
redisService.setCacheObject(encode(key), data);
|
||||
}
|
||||
return redisService.decrement(encode(key), number);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param key ID
|
||||
*
|
||||
* @return 键
|
||||
*/
|
||||
@Override
|
||||
public String encode (K key) {
|
||||
return keyPre() + key;
|
||||
}
|
||||
|
||||
public abstract Long getData(K key);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package com.muyu.common.cache.abs;
|
||||
|
||||
import com.muyu.common.cache.Cache;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author DongZl
|
||||
* @description: 缓存抽象类
|
||||
* @Date 2024-3-27 下午 03:10
|
||||
*/
|
||||
public abstract class CacheAbs<K, V> implements Cache<K, V> {
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
* @param key ID
|
||||
* @return 键
|
||||
*/
|
||||
@Override
|
||||
public String encode (K key) {
|
||||
return keyPre() + key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Key获取value值
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
@Override
|
||||
public V get (K key) {
|
||||
V value = redisService.getCacheObject(encode(key));
|
||||
if (value == null){
|
||||
value = getData(key);
|
||||
if (value == null){
|
||||
value = defaultValue();
|
||||
}
|
||||
}
|
||||
this.put(key, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存添加/修改
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
@Override
|
||||
public void put (K key, V value) {
|
||||
this.redisService.setCacheObject(encode(key), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过键删除
|
||||
* @param key 键
|
||||
*/
|
||||
@Override
|
||||
public void remove (K key) {
|
||||
this.redisService.deleteObject(encode(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新缓存
|
||||
* @param key 键
|
||||
*/
|
||||
@Override
|
||||
public void refreshTime (K key) {
|
||||
this.redisService.expire(encode(key), 60, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新缓存数据
|
||||
*
|
||||
* @param key 键
|
||||
*/
|
||||
@Override
|
||||
public void refreshData (K key) {
|
||||
this.put(key, getData(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库获取数据
|
||||
* @param key ID
|
||||
* @return 缓存对象
|
||||
*/
|
||||
public abstract V getData(K key);
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
*/
|
||||
public abstract V defaultValue();
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
package com.muyu.common.cache.abs;
|
||||
|
||||
import com.muyu.common.cache.HashCache;
|
||||
import com.muyu.common.redis.service.RedisService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author DongZl
|
||||
* @description: hash缓存抽象类
|
||||
* @Date 2024-3-29 下午 07:40
|
||||
*/
|
||||
public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
|
||||
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*
|
||||
* @param key ID
|
||||
*
|
||||
* @return 键
|
||||
*/
|
||||
@Override
|
||||
public String encode (K key) {
|
||||
return keyPre() + key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码
|
||||
* @param hashKey ID
|
||||
* @return 键
|
||||
*/
|
||||
@Override
|
||||
public String encodeHashKey (HK hashKey) {
|
||||
return hashKey.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Key获取所有的map
|
||||
* @param key 数据库键
|
||||
* @return 所有集合Map
|
||||
*/
|
||||
@Override
|
||||
public Map<HK, HV> get (K key) {
|
||||
// 获取为null的情况
|
||||
Map<String, HV> cacheMap = redisService.getCacheMap(encode(key));
|
||||
if (cacheMap == null || cacheMap.isEmpty()){
|
||||
Map<HK, HV> dataMap = getData(key);
|
||||
if (dataMap != null && !dataMap.isEmpty()){
|
||||
cacheMap = encodeMap(dataMap);
|
||||
}else {
|
||||
cacheMap = encodeMap(defaultValue());
|
||||
}
|
||||
redisService.setCacheMap(encode(key), cacheMap);
|
||||
}
|
||||
return decodeMap(cacheMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过键和hashKey获取数据库hashValue
|
||||
*
|
||||
* @param key 键
|
||||
* @param hashKey hash键
|
||||
*
|
||||
* @return hash值
|
||||
*/
|
||||
@Override
|
||||
public HV get (K key, HK hashKey) {
|
||||
HV hashValue = redisService.getCacheMapValue(encode(key), encodeHashKey(hashKey));
|
||||
if (hashValue == null){
|
||||
HV dataValue = getData(key, hashKey);
|
||||
hashValue = dataValue != null ? dataValue : defaultHashValue();
|
||||
put(key, hashKey, hashValue);
|
||||
}
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过键和hashKey获取数据库hashValue
|
||||
*
|
||||
* @param key 键
|
||||
* @param hashKeyList hash键集合
|
||||
*
|
||||
* @return hash值
|
||||
*/
|
||||
@Override
|
||||
public List<HV> get (K key, HK... hashKeyList) {
|
||||
List<String> encodeHashKeyList = Arrays.stream(hashKeyList).map(this::encodeHashKey).toList();
|
||||
return redisService.getMultiCacheMapValue(encode(key), encodeHashKeyList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取hash值集合
|
||||
*
|
||||
* @param key 键
|
||||
*
|
||||
* @return hash值集合
|
||||
*/
|
||||
@Override
|
||||
public List<HV> getToList (K key) {
|
||||
Map<HK, HV> hkhvMap = get(key);
|
||||
return hkhvMap.values().stream().toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储数据
|
||||
*
|
||||
* @param key redis键
|
||||
* @param map hashMap集合
|
||||
*/
|
||||
@Override
|
||||
public void put (K key, Map<HK, HV> map) {
|
||||
redisService.setCacheMap(encode(key), encodeMap(map));
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储数据
|
||||
*
|
||||
* @param key redis键
|
||||
* @param dataList 数据值
|
||||
* @param hashKey hash键
|
||||
*/
|
||||
@Override
|
||||
public void put (K key, List<HV> dataList, Function<HV, HK> hashKey) {
|
||||
Map<HK, HV> dataMap = new HashMap<>();
|
||||
dataList.forEach((data) -> dataMap.put(hashKey.apply(data), data));
|
||||
redisService.setCacheMap(encode(key), encodeMap(dataMap));
|
||||
}
|
||||
|
||||
/**
|
||||
* 存储数据
|
||||
*
|
||||
* @param key redis键
|
||||
* @param hashKey hash键
|
||||
* @param hashValue hash值
|
||||
*/
|
||||
@Override
|
||||
public void put (K key, HK hashKey, HV hashValue) {
|
||||
redisService.setCacheMapValue(encode(key), encodeHashKey(hashKey), hashValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过redis键删除
|
||||
*
|
||||
* @param key hash键
|
||||
*/
|
||||
@Override
|
||||
public void remove (K key) {
|
||||
redisService.deleteObject(encode(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过redis键和hash键删除
|
||||
*
|
||||
* @param key redis键
|
||||
* @param hashKey hash键
|
||||
*/
|
||||
@Override
|
||||
public void remove (K key, HK hashKey) {
|
||||
redisService.deleteCacheMapValue(encode(key), encodeHashKey(hashKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断redis中hashKey是否存在
|
||||
*
|
||||
* @param key redis键
|
||||
* @param hashKey hash键
|
||||
*/
|
||||
@Override
|
||||
public boolean hasKey (K key, HK hashKey) {
|
||||
return redisService.hashKey(encode(key), encodeHashKey(hashKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 原始数据转编码数据
|
||||
* @param dataMap 原始数据
|
||||
* @return 编码数据
|
||||
*/
|
||||
private Map<String, HV> encodeMap(Map<HK, HV> dataMap){
|
||||
Map<String, HV> encodeDataMap = new HashMap<>();
|
||||
dataMap.forEach((hashKey, HashValue) -> encodeDataMap.put(encodeHashKey(hashKey), HashValue));
|
||||
return encodeDataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编码数据转原始数据
|
||||
* @param encodeDataMap 编码数据
|
||||
* @return 原始数据
|
||||
*/
|
||||
private Map<HK, HV> decodeMap(Map<String, HV> encodeDataMap){
|
||||
Map<HK, HV> dataMap = new HashMap<>();
|
||||
encodeDataMap.forEach((hashKey, hashValue) -> dataMap.put(decodeHashKey(hashKey), hashValue));
|
||||
return dataMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过键获取所有的hash数据
|
||||
* @param key 键
|
||||
* @return
|
||||
*/
|
||||
public abstract Map<HK, HV> getData(K key);
|
||||
|
||||
/**
|
||||
* 通过缓存键和hash键获取hash值
|
||||
* @param key 缓存键
|
||||
* @param hashKey hash键
|
||||
* @return hash值
|
||||
*/
|
||||
public abstract HV getData(K key, HK hashKey);
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
*/
|
||||
public abstract Map<HK, HV> defaultValue();
|
||||
public abstract HV defaultHashValue();
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package com.muyu.common.cache.decoration;
|
||||
|
||||
/**
|
||||
* @author DongZl
|
||||
* @description: 装饰Key
|
||||
* @Date 2024-3-29 下午 03:19
|
||||
*/
|
||||
public interface DecorationKey <K>{
|
||||
|
||||
/**
|
||||
* key前缀
|
||||
* @return key前缀
|
||||
*/
|
||||
public String keyPre();
|
||||
|
||||
|
||||
/**
|
||||
* 编码
|
||||
* @param key ID
|
||||
* @return 键
|
||||
*/
|
||||
public String encode(K key);
|
||||
|
||||
/**
|
||||
* 解码
|
||||
* @param redisKey 数据库键
|
||||
* @return ID
|
||||
*/
|
||||
public K decode(String redisKey);
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.muyu</groupId>
|
||||
<artifactId>muyu-common</artifactId>
|
||||
<version>3.6.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>muyu-common-core</artifactId>
|
||||
|
||||
<description>
|
||||
muyu-common-core核心模块
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- SpringCloud Openfeign -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- SpringCloud Loadbalancer -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Context Support -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context-support</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Transmittable ThreadLocal -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>transmittable-thread-local</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Pagehelper -->
|
||||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- mybatis - plus 依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.3.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.github.jsqlparser</groupId>
|
||||
<artifactId>jsqlparser</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Hibernate Validator -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Alibaba Fastjson -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.fastjson2</groupId>
|
||||
<artifactId>fastjson2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jwt -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jaxb -->
|
||||
<dependency>
|
||||
<groupId>javax.xml.bind</groupId>
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Apache Lang3 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Commons Io -->
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- excel工具 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Java Servlet -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Swagger -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,176 @@
|
|||
package com.muyu.common.core.annotation;
|
||||
|
||||
import com.muyu.common.core.utils.poi.ExcelHandlerAdapter;
|
||||
import org.apache.poi.ss.usermodel.HorizontalAlignment;
|
||||
import org.apache.poi.ss.usermodel.IndexedColors;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 自定义导出Excel数据注解
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Excel {
|
||||
/**
|
||||
* 导出时在excel中排序
|
||||
*/
|
||||
public int sort () default Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* 导出到Excel中的名字.
|
||||
*/
|
||||
public String name () default "";
|
||||
|
||||
/**
|
||||
* 日期格式, 如: yyyy-MM-dd
|
||||
*/
|
||||
public String dateFormat () default "";
|
||||
|
||||
/**
|
||||
* 读取内容转表达式 (如: 0=男,1=女,2=未知)
|
||||
*/
|
||||
public String readConverterExp () default "";
|
||||
|
||||
/**
|
||||
* 分隔符,读取字符串组内容
|
||||
*/
|
||||
public String separator () default ",";
|
||||
|
||||
/**
|
||||
* BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化)
|
||||
*/
|
||||
public int scale () default -1;
|
||||
|
||||
/**
|
||||
* BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN
|
||||
*/
|
||||
public int roundingMode () default BigDecimal.ROUND_HALF_EVEN;
|
||||
|
||||
/**
|
||||
* 导出时在excel中每个列的高度
|
||||
*/
|
||||
public double height () default 14;
|
||||
|
||||
/**
|
||||
* 导出时在excel中每个列的宽度
|
||||
*/
|
||||
public double width () default 16;
|
||||
|
||||
/**
|
||||
* 文字后缀,如% 90 变成90%
|
||||
*/
|
||||
public String suffix () default "";
|
||||
|
||||
/**
|
||||
* 当值为空时,字段的默认值
|
||||
*/
|
||||
public String defaultValue () default "";
|
||||
|
||||
/**
|
||||
* 提示信息
|
||||
*/
|
||||
public String prompt () default "";
|
||||
|
||||
/**
|
||||
* 设置只能选择不能输入的列内容.
|
||||
*/
|
||||
public String[] combo () default {};
|
||||
|
||||
/**
|
||||
* 是否需要纵向合并单元格,应对需求:含有list集合单元格)
|
||||
*/
|
||||
public boolean needMerge () default false;
|
||||
|
||||
/**
|
||||
* 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
|
||||
*/
|
||||
public boolean isExport () default true;
|
||||
|
||||
/**
|
||||
* 另一个类中的属性名称,支持多级获取,以小数点隔开
|
||||
*/
|
||||
public String targetAttr () default "";
|
||||
|
||||
/**
|
||||
* 是否自动统计数据,在最后追加一行统计数据总和
|
||||
*/
|
||||
public boolean isStatistics () default false;
|
||||
|
||||
/**
|
||||
* 导出类型(0数字 1字符串)
|
||||
*/
|
||||
public ColumnType cellType () default ColumnType.STRING;
|
||||
|
||||
/**
|
||||
* 导出列头背景颜色
|
||||
*/
|
||||
public IndexedColors headerBackgroundColor () default IndexedColors.GREY_50_PERCENT;
|
||||
|
||||
/**
|
||||
* 导出列头字体颜色
|
||||
*/
|
||||
public IndexedColors headerColor () default IndexedColors.WHITE;
|
||||
|
||||
/**
|
||||
* 导出单元格背景颜色
|
||||
*/
|
||||
public IndexedColors backgroundColor () default IndexedColors.WHITE;
|
||||
|
||||
/**
|
||||
* 导出单元格字体颜色
|
||||
*/
|
||||
public IndexedColors color () default IndexedColors.BLACK;
|
||||
|
||||
/**
|
||||
* 导出字段对齐方式
|
||||
*/
|
||||
public HorizontalAlignment align () default HorizontalAlignment.CENTER;
|
||||
|
||||
/**
|
||||
* 自定义数据处理器
|
||||
*/
|
||||
public Class<?> handler () default ExcelHandlerAdapter.class;
|
||||
|
||||
/**
|
||||
* 自定义数据处理器参数
|
||||
*/
|
||||
public String[] args () default {};
|
||||
|
||||
/**
|
||||
* 字段类型(0:导出导入;1:仅导出;2:仅导入)
|
||||
*/
|
||||
Type type () default Type.ALL;
|
||||
|
||||
public enum Type {
|
||||
ALL(0), EXPORT(1), IMPORT(2);
|
||||
private final int value;
|
||||
|
||||
Type (int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value () {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
public enum ColumnType {
|
||||
NUMERIC(0), STRING(1), IMAGE(2);
|
||||
private final int value;
|
||||
|
||||
ColumnType (int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value () {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.muyu.common.core.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Excel注解集
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Excels {
|
||||
Excel[] value ();
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* 缓存常量信息
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class CacheConstants {
|
||||
/**
|
||||
* 缓存有效期,默认720(分钟)
|
||||
*/
|
||||
public final static long EXPIRATION = 720;
|
||||
|
||||
/**
|
||||
* 缓存刷新时间,默认120(分钟)
|
||||
*/
|
||||
public final static long REFRESH_TIME = 120;
|
||||
|
||||
/**
|
||||
* 密码最大错误次数
|
||||
*/
|
||||
public final static int PASSWORD_MAX_RETRY_COUNT = 5;
|
||||
|
||||
/**
|
||||
* 密码锁定时间,默认10(分钟)
|
||||
*/
|
||||
public final static long PASSWORD_LOCK_TIME = 10;
|
||||
|
||||
/**
|
||||
* 权限缓存前缀
|
||||
*/
|
||||
public final static String LOGIN_TOKEN_KEY = "login_tokens:";
|
||||
|
||||
/**
|
||||
* 验证码 redis key
|
||||
*/
|
||||
public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
|
||||
|
||||
/**
|
||||
* 参数管理 cache key
|
||||
*/
|
||||
public static final String SYS_CONFIG_KEY = "sys_config:";
|
||||
|
||||
/**
|
||||
* 字典管理 cache key
|
||||
*/
|
||||
public static final String SYS_DICT_KEY = "sys_dict:";
|
||||
|
||||
/**
|
||||
* 登录账户密码错误次数 redis key
|
||||
*/
|
||||
public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
|
||||
|
||||
/**
|
||||
* 登录IP黑名单 cache key
|
||||
*/
|
||||
public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList";
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* 通用常量信息
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class Constants {
|
||||
/**
|
||||
* UTF-8 字符集
|
||||
*/
|
||||
public static final String UTF8 = "UTF-8";
|
||||
|
||||
/**
|
||||
* GBK 字符集
|
||||
*/
|
||||
public static final String GBK = "GBK";
|
||||
|
||||
/**
|
||||
* www主域
|
||||
*/
|
||||
public static final String WWW = "www.";
|
||||
|
||||
/**
|
||||
* RMI 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_RMI = "rmi:";
|
||||
|
||||
/**
|
||||
* LDAP 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_LDAP = "ldap:";
|
||||
|
||||
/**
|
||||
* LDAPS 远程方法调用
|
||||
*/
|
||||
public static final String LOOKUP_LDAPS = "ldaps:";
|
||||
|
||||
/**
|
||||
* http请求
|
||||
*/
|
||||
public static final String HTTP = "http://";
|
||||
|
||||
/**
|
||||
* https请求
|
||||
*/
|
||||
public static final String HTTPS = "https://";
|
||||
|
||||
/**
|
||||
* 成功标记
|
||||
*/
|
||||
public static final Integer SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 失败标记
|
||||
*/
|
||||
public static final Integer FAIL = 500;
|
||||
|
||||
/**
|
||||
* 登录成功状态
|
||||
*/
|
||||
public static final String LOGIN_SUCCESS_STATUS = "0";
|
||||
|
||||
/**
|
||||
* 登录失败状态
|
||||
*/
|
||||
public static final String LOGIN_FAIL_STATUS = "1";
|
||||
|
||||
/**
|
||||
* 登录成功
|
||||
*/
|
||||
public static final String LOGIN_SUCCESS = "Success";
|
||||
|
||||
/**
|
||||
* 注销
|
||||
*/
|
||||
public static final String LOGOUT = "Logout";
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
public static final String REGISTER = "Register";
|
||||
|
||||
/**
|
||||
* 登录失败
|
||||
*/
|
||||
public static final String LOGIN_FAIL = "Error";
|
||||
|
||||
/**
|
||||
* 当前记录起始索引
|
||||
*/
|
||||
public static final String PAGE_NUM = "pageNum";
|
||||
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
public static final String PAGE_SIZE = "pageSize";
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
public static final String ORDER_BY_COLUMN = "orderByColumn";
|
||||
|
||||
/**
|
||||
* 排序的方向 "desc" 或者 "asc".
|
||||
*/
|
||||
public static final String IS_ASC = "isAsc";
|
||||
|
||||
/**
|
||||
* 验证码有效期(分钟)
|
||||
*/
|
||||
public static final long CAPTCHA_EXPIRATION = 2;
|
||||
|
||||
/**
|
||||
* 资源映射路径 前缀
|
||||
*/
|
||||
public static final String RESOURCE_PREFIX = "/profile";
|
||||
|
||||
/**
|
||||
* 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全)
|
||||
*/
|
||||
public static final String[] JSON_WHITELIST_STR = {"org.springframework", "com.muyu"};
|
||||
|
||||
/**
|
||||
* 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加)
|
||||
*/
|
||||
public static final String[] JOB_WHITELIST_STR = {"com.muyu"};
|
||||
|
||||
/**
|
||||
* 定时任务违规的字符
|
||||
*/
|
||||
public static final String[] JOB_ERROR_STR = {"java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml",
|
||||
"org.springframework", "org.apache", "com.muyu.common.core.utils.file"};
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* 代码生成通用常量
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class GenConstants {
|
||||
/**
|
||||
* 单表(增删改查)
|
||||
*/
|
||||
public static final String TPL_CRUD = "crud";
|
||||
|
||||
/**
|
||||
* 树表(增删改查)
|
||||
*/
|
||||
public static final String TPL_TREE = "tree";
|
||||
|
||||
/**
|
||||
* 主子表(增删改查)
|
||||
*/
|
||||
public static final String TPL_SUB = "sub";
|
||||
|
||||
/**
|
||||
* 树编码字段
|
||||
*/
|
||||
public static final String TREE_CODE = "treeCode";
|
||||
|
||||
/**
|
||||
* 树父编码字段
|
||||
*/
|
||||
public static final String TREE_PARENT_CODE = "treeParentCode";
|
||||
|
||||
/**
|
||||
* 树名称字段
|
||||
*/
|
||||
public static final String TREE_NAME = "treeName";
|
||||
|
||||
/**
|
||||
* 上级菜单ID字段
|
||||
*/
|
||||
public static final String PARENT_MENU_ID = "parentMenuId";
|
||||
|
||||
/**
|
||||
* 上级菜单名称字段
|
||||
*/
|
||||
public static final String PARENT_MENU_NAME = "parentMenuName";
|
||||
|
||||
/**
|
||||
* 数据库字符串类型
|
||||
*/
|
||||
public static final String[] COLUMNTYPE_STR = {"char", "varchar", "nvarchar", "varchar2"};
|
||||
|
||||
/**
|
||||
* 数据库文本类型
|
||||
*/
|
||||
public static final String[] COLUMNTYPE_TEXT = {"tinytext", "text", "mediumtext", "longtext"};
|
||||
|
||||
/**
|
||||
* 数据库时间类型
|
||||
*/
|
||||
public static final String[] COLUMNTYPE_TIME = {"datetime", "time", "date", "timestamp"};
|
||||
|
||||
/**
|
||||
* 数据库数字类型
|
||||
*/
|
||||
public static final String[] COLUMNTYPE_NUMBER = {"tinyint", "smallint", "mediumint", "int", "number", "integer",
|
||||
"bigint", "float", "double", "decimal"};
|
||||
|
||||
/**
|
||||
* 页面不需要编辑字段
|
||||
*/
|
||||
public static final String[] COLUMNNAME_NOT_EDIT = {"id", "create_by", "create_time", "del_flag"};
|
||||
|
||||
/**
|
||||
* 页面不需要显示的列表字段
|
||||
*/
|
||||
public static final String[] COLUMNNAME_NOT_LIST = {"id", "create_by", "create_time", "del_flag", "update_by",
|
||||
"update_time"};
|
||||
|
||||
/**
|
||||
* 页面不需要查询字段
|
||||
*/
|
||||
public static final String[] COLUMNNAME_NOT_QUERY = {"id", "create_by", "create_time", "del_flag", "update_by",
|
||||
"update_time", "remark"};
|
||||
|
||||
/**
|
||||
* Entity基类字段
|
||||
*/
|
||||
public static final String[] BASE_ENTITY = {"createBy", "createTime", "updateBy", "updateTime", "remark"};
|
||||
|
||||
/**
|
||||
* Tree基类字段
|
||||
*/
|
||||
public static final String[] TREE_ENTITY = {"parentName", "parentId", "orderNum", "ancestors"};
|
||||
|
||||
/**
|
||||
* 文本框
|
||||
*/
|
||||
public static final String HTML_INPUT = "input";
|
||||
|
||||
/**
|
||||
* 文本域
|
||||
*/
|
||||
public static final String HTML_TEXTAREA = "textarea";
|
||||
|
||||
/**
|
||||
* 下拉框
|
||||
*/
|
||||
public static final String HTML_SELECT = "select";
|
||||
|
||||
/**
|
||||
* 单选框
|
||||
*/
|
||||
public static final String HTML_RADIO = "radio";
|
||||
|
||||
/**
|
||||
* 复选框
|
||||
*/
|
||||
public static final String HTML_CHECKBOX = "checkbox";
|
||||
|
||||
/**
|
||||
* 日期控件
|
||||
*/
|
||||
public static final String HTML_DATETIME = "datetime";
|
||||
|
||||
/**
|
||||
* 图片上传控件
|
||||
*/
|
||||
public static final String HTML_IMAGE_UPLOAD = "imageUpload";
|
||||
|
||||
/**
|
||||
* 文件上传控件
|
||||
*/
|
||||
public static final String HTML_FILE_UPLOAD = "fileUpload";
|
||||
|
||||
/**
|
||||
* 富文本控件
|
||||
*/
|
||||
public static final String HTML_EDITOR = "editor";
|
||||
|
||||
/**
|
||||
* 字符串类型
|
||||
*/
|
||||
public static final String TYPE_STRING = "String";
|
||||
|
||||
/**
|
||||
* 整型
|
||||
*/
|
||||
public static final String TYPE_INTEGER = "Integer";
|
||||
|
||||
/**
|
||||
* 长整型
|
||||
*/
|
||||
public static final String TYPE_LONG = "Long";
|
||||
|
||||
/**
|
||||
* 浮点型
|
||||
*/
|
||||
public static final String TYPE_DOUBLE = "Double";
|
||||
|
||||
/**
|
||||
* 高精度计算类型
|
||||
*/
|
||||
public static final String TYPE_BIGDECIMAL = "BigDecimal";
|
||||
|
||||
/**
|
||||
* 时间类型
|
||||
*/
|
||||
public static final String TYPE_DATE = "Date";
|
||||
|
||||
/**
|
||||
* 模糊查询
|
||||
*/
|
||||
public static final String QUERY_LIKE = "LIKE";
|
||||
|
||||
/**
|
||||
* 相等查询
|
||||
*/
|
||||
public static final String QUERY_EQ = "EQ";
|
||||
|
||||
/**
|
||||
* 需要
|
||||
*/
|
||||
public static final String REQUIRE = "1";
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* 返回状态码
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class HttpStatus {
|
||||
/**
|
||||
* 操作成功
|
||||
*/
|
||||
public static final int SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 对象创建成功
|
||||
*/
|
||||
public static final int CREATED = 201;
|
||||
|
||||
/**
|
||||
* 请求已经被接受
|
||||
*/
|
||||
public static final int ACCEPTED = 202;
|
||||
|
||||
/**
|
||||
* 操作已经执行成功,但是没有返回数据
|
||||
*/
|
||||
public static final int NO_CONTENT = 204;
|
||||
|
||||
/**
|
||||
* 资源已被移除
|
||||
*/
|
||||
public static final int MOVED_PERM = 301;
|
||||
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
public static final int SEE_OTHER = 303;
|
||||
|
||||
/**
|
||||
* 资源没有被修改
|
||||
*/
|
||||
public static final int NOT_MODIFIED = 304;
|
||||
|
||||
/**
|
||||
* 参数列表错误(缺少,格式不匹配)
|
||||
*/
|
||||
public static final int BAD_REQUEST = 400;
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
public static final int UNAUTHORIZED = 401;
|
||||
|
||||
/**
|
||||
* 访问受限,授权过期
|
||||
*/
|
||||
public static final int FORBIDDEN = 403;
|
||||
|
||||
/**
|
||||
* 资源,服务未找到
|
||||
*/
|
||||
public static final int NOT_FOUND = 404;
|
||||
|
||||
/**
|
||||
* 不允许的http方法
|
||||
*/
|
||||
public static final int BAD_METHOD = 405;
|
||||
|
||||
/**
|
||||
* 资源冲突,或者资源被锁
|
||||
*/
|
||||
public static final int CONFLICT = 409;
|
||||
|
||||
/**
|
||||
* 不支持的数据,媒体类型
|
||||
*/
|
||||
public static final int UNSUPPORTED_TYPE = 415;
|
||||
|
||||
/**
|
||||
* 系统内部错误
|
||||
*/
|
||||
public static final int ERROR = 500;
|
||||
|
||||
/**
|
||||
* 接口未实现
|
||||
*/
|
||||
public static final int NOT_IMPLEMENTED = 501;
|
||||
|
||||
/**
|
||||
* 系统警告消息
|
||||
*/
|
||||
public static final int WARN = 601;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* 任务调度通用常量
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class ScheduleConstants {
|
||||
public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME";
|
||||
|
||||
/**
|
||||
* 执行目标key
|
||||
*/
|
||||
public static final String TASK_PROPERTIES = "TASK_PROPERTIES";
|
||||
|
||||
/**
|
||||
* 默认
|
||||
*/
|
||||
public static final String MISFIRE_DEFAULT = "0";
|
||||
|
||||
/**
|
||||
* 立即触发执行
|
||||
*/
|
||||
public static final String MISFIRE_IGNORE_MISFIRES = "1";
|
||||
|
||||
/**
|
||||
* 触发一次执行
|
||||
*/
|
||||
public static final String MISFIRE_FIRE_AND_PROCEED = "2";
|
||||
|
||||
/**
|
||||
* 不触发立即执行
|
||||
*/
|
||||
public static final String MISFIRE_DO_NOTHING = "3";
|
||||
|
||||
public enum Status {
|
||||
/**
|
||||
* 正常
|
||||
*/
|
||||
NORMAL("0"),
|
||||
/**
|
||||
* 暂停
|
||||
*/
|
||||
PAUSE("1");
|
||||
|
||||
private String value;
|
||||
|
||||
private Status (String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue () {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* 权限相关通用常量
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class SecurityConstants {
|
||||
/**
|
||||
* 用户ID字段
|
||||
*/
|
||||
public static final String DETAILS_USER_ID = "user_id";
|
||||
|
||||
/**
|
||||
* 用户名字段
|
||||
*/
|
||||
public static final String DETAILS_USERNAME = "username";
|
||||
|
||||
/**
|
||||
* 授权信息字段
|
||||
*/
|
||||
public static final String AUTHORIZATION_HEADER = "authorization";
|
||||
|
||||
/**
|
||||
* 请求来源
|
||||
*/
|
||||
public static final String FROM_SOURCE = "from-source";
|
||||
|
||||
/**
|
||||
* 内部请求
|
||||
*/
|
||||
public static final String INNER = "inner";
|
||||
|
||||
/**
|
||||
* 用户标识
|
||||
*/
|
||||
public static final String USER_KEY = "user_key";
|
||||
|
||||
/**
|
||||
* 登录用户
|
||||
*/
|
||||
public static final String LOGIN_USER = "login_user";
|
||||
|
||||
/**
|
||||
* 角色权限
|
||||
*/
|
||||
public static final String ROLE_PERMISSION = "role_permission";
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* 服务名称
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class ServiceNameConstants {
|
||||
/**
|
||||
* 认证服务的serviceid
|
||||
*/
|
||||
public static final String AUTH_SERVICE = "muyu-auth";
|
||||
|
||||
/**
|
||||
* 系统模块的serviceid
|
||||
*/
|
||||
public static final String SYSTEM_SERVICE = "muyu-system";
|
||||
|
||||
/**
|
||||
* 文件服务的serviceid
|
||||
*/
|
||||
public static final String FILE_SERVICE = "muyu-file";
|
||||
|
||||
/**
|
||||
* 商品服务
|
||||
*/
|
||||
public static final String PRODUCT_SERVICE = "muyu-product";
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* Token的Key常量
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class TokenConstants {
|
||||
/**
|
||||
* 令牌自定义标识
|
||||
*/
|
||||
public static final String AUTHENTICATION = "Authorization";
|
||||
|
||||
/**
|
||||
* 令牌前缀
|
||||
*/
|
||||
public static final String PREFIX = "Bearer ";
|
||||
|
||||
/**
|
||||
* 令牌秘钥
|
||||
*/
|
||||
public final static String SECRET = "abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
package com.muyu.common.core.constant;
|
||||
|
||||
/**
|
||||
* 用户常量信息
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class UserConstants {
|
||||
/**
|
||||
* 平台内系统用户的唯一标志
|
||||
*/
|
||||
public static final String SYS_USER = "SYS_USER";
|
||||
|
||||
/**
|
||||
* 正常状态
|
||||
*/
|
||||
public static final String NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 异常状态
|
||||
*/
|
||||
public static final String EXCEPTION = "1";
|
||||
|
||||
/**
|
||||
* 用户封禁状态
|
||||
*/
|
||||
public static final String USER_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 角色封禁状态
|
||||
*/
|
||||
public static final String ROLE_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 部门正常状态
|
||||
*/
|
||||
public static final String DEPT_NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 部门停用状态
|
||||
*/
|
||||
public static final String DEPT_DISABLE = "1";
|
||||
|
||||
/**
|
||||
* 字典正常状态
|
||||
*/
|
||||
public static final String DICT_NORMAL = "0";
|
||||
|
||||
/**
|
||||
* 是否为系统默认(是)
|
||||
*/
|
||||
public static final String YES = "Y";
|
||||
|
||||
/**
|
||||
* 是否菜单外链(是)
|
||||
*/
|
||||
public static final String YES_FRAME = "0";
|
||||
|
||||
/**
|
||||
* 是否菜单外链(否)
|
||||
*/
|
||||
public static final String NO_FRAME = "1";
|
||||
|
||||
/**
|
||||
* 菜单类型(目录)
|
||||
*/
|
||||
public static final String TYPE_DIR = "M";
|
||||
|
||||
/**
|
||||
* 菜单类型(菜单)
|
||||
*/
|
||||
public static final String TYPE_MENU = "C";
|
||||
|
||||
/**
|
||||
* 菜单类型(按钮)
|
||||
*/
|
||||
public static final String TYPE_BUTTON = "F";
|
||||
|
||||
/**
|
||||
* Layout组件标识
|
||||
*/
|
||||
public final static String LAYOUT = "Layout";
|
||||
|
||||
/**
|
||||
* ParentView组件标识
|
||||
*/
|
||||
public final static String PARENT_VIEW = "ParentView";
|
||||
|
||||
/**
|
||||
* InnerLink组件标识
|
||||
*/
|
||||
public final static String INNER_LINK = "InnerLink";
|
||||
|
||||
/**
|
||||
* 校验是否唯一的返回标识
|
||||
*/
|
||||
public final static boolean UNIQUE = true;
|
||||
public final static boolean NOT_UNIQUE = false;
|
||||
|
||||
/**
|
||||
* 用户名长度限制
|
||||
*/
|
||||
public static final int USERNAME_MIN_LENGTH = 2;
|
||||
|
||||
public static final int USERNAME_MAX_LENGTH = 20;
|
||||
|
||||
/**
|
||||
* 密码长度限制
|
||||
*/
|
||||
public static final int PASSWORD_MIN_LENGTH = 5;
|
||||
|
||||
public static final int PASSWORD_MAX_LENGTH = 20;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package com.muyu.common.core.context;
|
||||
|
||||
import com.alibaba.ttl.TransmittableThreadLocal;
|
||||
import com.muyu.common.core.constant.SecurityConstants;
|
||||
import com.muyu.common.core.text.Convert;
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 获取当前线程变量中的 用户id、用户名称、Token等信息
|
||||
* 注意: 必须在网关通过请求头的方法传入,同时在HeaderInterceptor拦截器设置值。 否则这里无法获取
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class SecurityContextHolder {
|
||||
private static final TransmittableThreadLocal<Map<String, Object>> THREAD_LOCAL = new TransmittableThreadLocal<>();
|
||||
|
||||
public static void set (String key, Object value) {
|
||||
Map<String, Object> map = getLocalMap();
|
||||
map.put(key, value == null ? StringUtils.EMPTY : value);
|
||||
}
|
||||
|
||||
public static String get (String key) {
|
||||
Map<String, Object> map = getLocalMap();
|
||||
return Convert.toStr(map.getOrDefault(key, StringUtils.EMPTY));
|
||||
}
|
||||
|
||||
public static <T> T get (String key, Class<T> clazz) {
|
||||
Map<String, Object> map = getLocalMap();
|
||||
return StringUtils.cast(map.getOrDefault(key, null));
|
||||
}
|
||||
|
||||
public static Map<String, Object> getLocalMap () {
|
||||
Map<String, Object> map = THREAD_LOCAL.get();
|
||||
if (map == null) {
|
||||
map = new ConcurrentHashMap<String, Object>();
|
||||
THREAD_LOCAL.set(map);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void setLocalMap (Map<String, Object> threadLocalMap) {
|
||||
THREAD_LOCAL.set(threadLocalMap);
|
||||
}
|
||||
|
||||
public static Long getUserId () {
|
||||
return Convert.toLong(get(SecurityConstants.DETAILS_USER_ID), 0L);
|
||||
}
|
||||
|
||||
public static void setUserId (String account) {
|
||||
set(SecurityConstants.DETAILS_USER_ID, account);
|
||||
}
|
||||
|
||||
public static String getUserName () {
|
||||
return get(SecurityConstants.DETAILS_USERNAME);
|
||||
}
|
||||
|
||||
public static void setUserName (String username) {
|
||||
set(SecurityConstants.DETAILS_USERNAME, username);
|
||||
}
|
||||
|
||||
public static String getUserKey () {
|
||||
return get(SecurityConstants.USER_KEY);
|
||||
}
|
||||
|
||||
public static void setUserKey (String userKey) {
|
||||
set(SecurityConstants.USER_KEY, userKey);
|
||||
}
|
||||
|
||||
public static String getPermission () {
|
||||
return get(SecurityConstants.ROLE_PERMISSION);
|
||||
}
|
||||
|
||||
public static void setPermission (String permissions) {
|
||||
set(SecurityConstants.ROLE_PERMISSION, permissions);
|
||||
}
|
||||
|
||||
public static void remove () {
|
||||
THREAD_LOCAL.remove();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package com.muyu.common.core.domain;
|
||||
|
||||
import com.muyu.common.core.constant.Constants;
|
||||
import com.muyu.common.core.constant.HttpStatus;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 响应信息主体
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Result<T> implements Serializable {
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
public static final int SUCCESS = Constants.SUCCESS;
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
public static final int FAIL = Constants.FAIL;
|
||||
/**
|
||||
* 警告
|
||||
*/
|
||||
public static final int WARN = HttpStatus.WARN;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private int code;
|
||||
|
||||
private String msg;
|
||||
|
||||
private T data;
|
||||
|
||||
public static <T> Result<T> buildCode(int code, String msg, T data){
|
||||
return restResult(data, code, msg);
|
||||
}
|
||||
|
||||
public static <T> Result<T> success () {
|
||||
return restResult(null, SUCCESS, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> success (T data) {
|
||||
return restResult(data, SUCCESS, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> success (T data, String msg) {
|
||||
return restResult(data, SUCCESS, msg);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error () {
|
||||
return restResult(null, FAIL, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error (String msg) {
|
||||
return restResult(null, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error (T data) {
|
||||
return restResult(data, FAIL, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error (T data, String msg) {
|
||||
return restResult(data, FAIL, msg);
|
||||
}
|
||||
|
||||
public static <T> Result<T> error (int code, String msg) {
|
||||
return restResult(null, code, msg);
|
||||
}
|
||||
|
||||
public static <T> Result<T> warn () {
|
||||
return restResult(null, WARN, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> warn (String msg) {
|
||||
return restResult(null, WARN, msg);
|
||||
}
|
||||
|
||||
public static <T> Result<T> warn (T data) {
|
||||
return restResult(data, WARN, null);
|
||||
}
|
||||
|
||||
public static <T> Result<T> warn (T data, String msg) {
|
||||
return restResult(data, WARN, msg);
|
||||
}
|
||||
|
||||
public static <T> Result<T> warn (int code, String msg) {
|
||||
return restResult(null, code, msg);
|
||||
}
|
||||
|
||||
private static <T> Result<T> restResult (T data, int code, String msg) {
|
||||
return Result.<T>builder()
|
||||
.code(code)
|
||||
.data(data)
|
||||
.msg(msg)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static <T> Boolean isError (Result<T> ret) {
|
||||
return !isSuccess(ret);
|
||||
}
|
||||
|
||||
public static <T> Boolean isSuccess (Result<T> ret) {
|
||||
return Result.SUCCESS == ret.getCode();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.muyu.common.core.enums;
|
||||
|
||||
/**
|
||||
* 用户状态
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public enum UserStatus {
|
||||
OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除");
|
||||
|
||||
private final String code;
|
||||
private final String info;
|
||||
|
||||
UserStatus (String code, String info) {
|
||||
this.code = code;
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public String getCode () {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getInfo () {
|
||||
return info;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.muyu.common.core.exception;
|
||||
|
||||
/**
|
||||
* 验证码错误异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class CaptchaException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CaptchaException (String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.muyu.common.core.exception;
|
||||
|
||||
/**
|
||||
* 检查异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class CheckedException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CheckedException (String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CheckedException (Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public CheckedException (String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public CheckedException (String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.muyu.common.core.exception;
|
||||
|
||||
/**
|
||||
* 演示模式异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class DemoModeException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DemoModeException () {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package com.muyu.common.core.exception;
|
||||
|
||||
/**
|
||||
* 全局异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class GlobalException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
* <p>
|
||||
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
|
||||
*/
|
||||
private String detailMessage;
|
||||
|
||||
/**
|
||||
* 空构造方法,避免反序列化问题
|
||||
*/
|
||||
public GlobalException () {
|
||||
}
|
||||
|
||||
public GlobalException (String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public String getDetailMessage () {
|
||||
return detailMessage;
|
||||
}
|
||||
|
||||
public GlobalException setDetailMessage (String detailMessage) {
|
||||
this.detailMessage = detailMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage () {
|
||||
return message;
|
||||
}
|
||||
|
||||
public GlobalException setMessage (String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.muyu.common.core.exception;
|
||||
|
||||
/**
|
||||
* 内部认证异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class InnerAuthException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InnerAuthException (String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package com.muyu.common.core.exception;
|
||||
|
||||
/**
|
||||
* 权限异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class PreAuthorizeException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public PreAuthorizeException () {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package com.muyu.common.core.exception;
|
||||
|
||||
/**
|
||||
* 业务异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public final class ServiceException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private Integer code;
|
||||
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private String message;
|
||||
|
||||
/**
|
||||
* 错误明细,内部调试错误
|
||||
* <p>
|
||||
* 和 {@link CommonResult#getDetailMessage()} 一致的设计
|
||||
*/
|
||||
private String detailMessage;
|
||||
|
||||
/**
|
||||
* 空构造方法,避免反序列化问题
|
||||
*/
|
||||
public ServiceException () {
|
||||
}
|
||||
|
||||
public ServiceException (String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public ServiceException (String message, Integer code) {
|
||||
this.message = message;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getDetailMessage () {
|
||||
return detailMessage;
|
||||
}
|
||||
|
||||
public ServiceException setDetailMessage (String detailMessage) {
|
||||
this.detailMessage = detailMessage;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage () {
|
||||
return message;
|
||||
}
|
||||
|
||||
public ServiceException setMessage (String message) {
|
||||
this.message = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Integer getCode () {
|
||||
return code;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package com.muyu.common.core.exception;
|
||||
|
||||
/**
|
||||
* 工具类异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class UtilException extends RuntimeException {
|
||||
private static final long serialVersionUID = 8247610319171014183L;
|
||||
|
||||
public UtilException (Throwable e) {
|
||||
super(e.getMessage(), e);
|
||||
}
|
||||
|
||||
public UtilException (String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UtilException (String message, Throwable throwable) {
|
||||
super(message, throwable);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.muyu.common.core.exception.auth;
|
||||
|
||||
/**
|
||||
* 未能通过的登录认证异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class NotLoginException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NotLoginException (String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.muyu.common.core.exception.auth;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 未能通过的权限认证异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class NotPermissionException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NotPermissionException (String permission) {
|
||||
super(permission);
|
||||
}
|
||||
|
||||
public NotPermissionException (String[] permissions) {
|
||||
super(StringUtils.join(permissions, ","));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package com.muyu.common.core.exception.auth;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* 未能通过的角色认证异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class NotRoleException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public NotRoleException (String role) {
|
||||
super(role);
|
||||
}
|
||||
|
||||
public NotRoleException (String[] roles) {
|
||||
super(StringUtils.join(roles, ","));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.muyu.common.core.exception.base;
|
||||
|
||||
/**
|
||||
* 基础异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class BaseException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 所属模块
|
||||
*/
|
||||
private String module;
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 错误码对应的参数
|
||||
*/
|
||||
private Object[] args;
|
||||
|
||||
/**
|
||||
* 错误消息
|
||||
*/
|
||||
private String defaultMessage;
|
||||
|
||||
public BaseException (String module, String code, Object[] args, String defaultMessage) {
|
||||
this.module = module;
|
||||
this.code = code;
|
||||
this.args = args;
|
||||
this.defaultMessage = defaultMessage;
|
||||
}
|
||||
|
||||
public BaseException (String module, String code, Object[] args) {
|
||||
this(module, code, args, null);
|
||||
}
|
||||
|
||||
public BaseException (String module, String defaultMessage) {
|
||||
this(module, null, null, defaultMessage);
|
||||
}
|
||||
|
||||
public BaseException (String code, Object[] args) {
|
||||
this(null, code, args, null);
|
||||
}
|
||||
|
||||
public BaseException (String defaultMessage) {
|
||||
this(null, null, null, defaultMessage);
|
||||
}
|
||||
|
||||
public String getModule () {
|
||||
return module;
|
||||
}
|
||||
|
||||
public String getCode () {
|
||||
return code;
|
||||
}
|
||||
|
||||
public Object[] getArgs () {
|
||||
return args;
|
||||
}
|
||||
|
||||
public String getDefaultMessage () {
|
||||
return defaultMessage;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.muyu.common.core.exception.file;
|
||||
|
||||
import com.muyu.common.core.exception.base.BaseException;
|
||||
|
||||
/**
|
||||
* 文件信息异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class FileException extends BaseException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public FileException (String code, Object[] args, String msg) {
|
||||
super("file", code, args, msg);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.muyu.common.core.exception.file;
|
||||
|
||||
/**
|
||||
* 文件名称超长限制异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class FileNameLengthLimitExceededException extends FileException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public FileNameLengthLimitExceededException (int defaultFileNameLength) {
|
||||
super("upload.filename.exceed.length", new Object[]{defaultFileNameLength}, "the filename is too long");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.muyu.common.core.exception.file;
|
||||
|
||||
/**
|
||||
* 文件名大小限制异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class FileSizeLimitExceededException extends FileException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public FileSizeLimitExceededException (long defaultMaxSize) {
|
||||
super("upload.exceed.maxSize", new Object[]{defaultMaxSize}, "the filesize is too large");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.muyu.common.core.exception.file;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* 文件上传异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class FileUploadException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Throwable cause;
|
||||
|
||||
public FileUploadException () {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
public FileUploadException (final String msg) {
|
||||
this(msg, null);
|
||||
}
|
||||
|
||||
public FileUploadException (String msg, Throwable cause) {
|
||||
super(msg);
|
||||
this.cause = cause;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace (PrintStream stream) {
|
||||
super.printStackTrace(stream);
|
||||
if (cause != null) {
|
||||
stream.println("Caused by:");
|
||||
cause.printStackTrace(stream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace (PrintWriter writer) {
|
||||
super.printStackTrace(writer);
|
||||
if (cause != null) {
|
||||
writer.println("Caused by:");
|
||||
cause.printStackTrace(writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable getCause () {
|
||||
return cause;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package com.muyu.common.core.exception.file;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 文件上传 误异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class InvalidExtensionException extends FileUploadException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String[] allowedExtension;
|
||||
private String extension;
|
||||
private String filename;
|
||||
|
||||
public InvalidExtensionException (String[] allowedExtension, String extension, String filename) {
|
||||
super("filename : [" + filename + "], extension : [" + extension + "], allowed extension : [" + Arrays.toString(allowedExtension) + "]");
|
||||
this.allowedExtension = allowedExtension;
|
||||
this.extension = extension;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public String[] getAllowedExtension () {
|
||||
return allowedExtension;
|
||||
}
|
||||
|
||||
public String getExtension () {
|
||||
return extension;
|
||||
}
|
||||
|
||||
public String getFilename () {
|
||||
return filename;
|
||||
}
|
||||
|
||||
public static class InvalidImageExtensionException extends InvalidExtensionException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InvalidImageExtensionException (String[] allowedExtension, String extension, String filename) {
|
||||
super(allowedExtension, extension, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidFlashExtensionException extends InvalidExtensionException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InvalidFlashExtensionException (String[] allowedExtension, String extension, String filename) {
|
||||
super(allowedExtension, extension, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidMediaExtensionException extends InvalidExtensionException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InvalidMediaExtensionException (String[] allowedExtension, String extension, String filename) {
|
||||
super(allowedExtension, extension, filename);
|
||||
}
|
||||
}
|
||||
|
||||
public static class InvalidVideoExtensionException extends InvalidExtensionException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InvalidVideoExtensionException (String[] allowedExtension, String extension, String filename) {
|
||||
super(allowedExtension, extension, filename);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.muyu.common.core.exception.job;
|
||||
|
||||
/**
|
||||
* 计划策略异常
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class TaskException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Code code;
|
||||
|
||||
public TaskException (String msg, Code code) {
|
||||
this(msg, code, null);
|
||||
}
|
||||
|
||||
public TaskException (String msg, Code code, Exception nestedEx) {
|
||||
super(msg, nestedEx);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Code getCode () {
|
||||
return code;
|
||||
}
|
||||
|
||||
public enum Code {
|
||||
TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.muyu.common.core.exception.user;
|
||||
|
||||
/**
|
||||
* 验证码失效异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class CaptchaExpireException extends UserException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public CaptchaExpireException () {
|
||||
super("user.jcaptcha.expire", null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.muyu.common.core.exception.user;
|
||||
|
||||
import com.muyu.common.core.exception.base.BaseException;
|
||||
|
||||
/**
|
||||
* 用户信息异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class UserException extends BaseException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserException (String code, Object[] args) {
|
||||
super("user", code, args, null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.muyu.common.core.exception.user;
|
||||
|
||||
/**
|
||||
* 用户密码不正确或不符合规范异常类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class UserPasswordNotMatchException extends UserException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public UserPasswordNotMatchException () {
|
||||
super("user.password.not.match", null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package com.muyu.common.core.text;
|
||||
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 字符集工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class CharsetKit {
|
||||
/**
|
||||
* ISO-8859-1
|
||||
*/
|
||||
public static final String ISO_8859_1 = "ISO-8859-1";
|
||||
/**
|
||||
* UTF-8
|
||||
*/
|
||||
public static final String UTF_8 = "UTF-8";
|
||||
/**
|
||||
* GBK
|
||||
*/
|
||||
public static final String GBK = "GBK";
|
||||
|
||||
/**
|
||||
* ISO-8859-1
|
||||
*/
|
||||
public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
|
||||
/**
|
||||
* UTF-8
|
||||
*/
|
||||
public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
|
||||
/**
|
||||
* GBK
|
||||
*/
|
||||
public static final Charset CHARSET_GBK = Charset.forName(GBK);
|
||||
|
||||
/**
|
||||
* 转换为Charset对象
|
||||
*
|
||||
* @param charset 字符集,为空则返回默认字符集
|
||||
*
|
||||
* @return Charset
|
||||
*/
|
||||
public static Charset charset (String charset) {
|
||||
return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换字符串的字符集编码
|
||||
*
|
||||
* @param source 字符串
|
||||
* @param srcCharset 源字符集,默认ISO-8859-1
|
||||
* @param destCharset 目标字符集,默认UTF-8
|
||||
*
|
||||
* @return 转换后的字符集
|
||||
*/
|
||||
public static String convert (String source, String srcCharset, String destCharset) {
|
||||
return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换字符串的字符集编码
|
||||
*
|
||||
* @param source 字符串
|
||||
* @param srcCharset 源字符集,默认ISO-8859-1
|
||||
* @param destCharset 目标字符集,默认UTF-8
|
||||
*
|
||||
* @return 转换后的字符集
|
||||
*/
|
||||
public static String convert (String source, Charset srcCharset, Charset destCharset) {
|
||||
if (null == srcCharset) {
|
||||
srcCharset = StandardCharsets.ISO_8859_1;
|
||||
}
|
||||
|
||||
if (null == destCharset) {
|
||||
destCharset = StandardCharsets.UTF_8;
|
||||
}
|
||||
|
||||
if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) {
|
||||
return source;
|
||||
}
|
||||
return new String(source.getBytes(srcCharset), destCharset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 系统字符集编码
|
||||
*/
|
||||
public static String systemCharset () {
|
||||
return Charset.defaultCharset().name();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,903 @@
|
|||
package com.muyu.common.core.text;
|
||||
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 类型转换器
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class Convert {
|
||||
/**
|
||||
* 转换为字符串<br>
|
||||
* 如果给定的值为null,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static String toStr (Object value, String defaultValue) {
|
||||
if (null == value) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return (String) value;
|
||||
}
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符串<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static String toStr (Object value) {
|
||||
return toStr(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符<br>
|
||||
* 如果给定的值为null,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Character toChar (Object value, Character defaultValue) {
|
||||
if (null == value) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Character) {
|
||||
return (Character) value;
|
||||
}
|
||||
|
||||
final String valueStr = toStr(value, null);
|
||||
return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为字符<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Character toChar (Object value) {
|
||||
return toChar(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为byte<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Byte toByte (Object value, Byte defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Byte) {
|
||||
return (Byte) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).byteValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Byte.parseByte(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为byte<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Byte toByte (Object value) {
|
||||
return toByte(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Short<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Short toShort (Object value, Short defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Short) {
|
||||
return (Short) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).shortValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Short.parseShort(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Short<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Short toShort (Object value) {
|
||||
return toShort(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Number<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Number toNumber (Object value, Number defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return (Number) value;
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return NumberFormat.getInstance().parse(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Number<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Number toNumber (Object value) {
|
||||
return toNumber(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为int<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer toInt (Object value, Integer defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为int<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer toInt (Object value) {
|
||||
return toInt(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Integer数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer[] toIntArray (String str) {
|
||||
return toIntArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Long数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long[] toLongArray (String str) {
|
||||
return toLongArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Integer数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param str 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Integer[] toIntArray (String split, String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return new Integer[]{};
|
||||
}
|
||||
String[] arr = str.split(split);
|
||||
final Integer[] ints = new Integer[arr.length];
|
||||
for (int i = 0 ; i < arr.length ; i++) {
|
||||
final Integer v = toInt(arr[i], 0);
|
||||
ints[i] = v;
|
||||
}
|
||||
return ints;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Long数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param str 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long[] toLongArray (String split, String str) {
|
||||
if (StringUtils.isEmpty(str)) {
|
||||
return new Long[]{};
|
||||
}
|
||||
String[] arr = str.split(split);
|
||||
final Long[] longs = new Long[arr.length];
|
||||
for (int i = 0 ; i < arr.length ; i++) {
|
||||
final Long v = toLong(arr[i], null);
|
||||
longs[i] = v;
|
||||
}
|
||||
return longs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为String数组<br>
|
||||
*
|
||||
* @param str 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static String[] toStrArray (String str) {
|
||||
return toStrArray(",", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为String数组<br>
|
||||
*
|
||||
* @param split 分隔符
|
||||
* @param str 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static String[] toStrArray (String split, String str) {
|
||||
return str.split(split);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为long<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long toLong (Object value, Long defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
// 支持科学计数法
|
||||
return new BigDecimal(valueStr.trim()).longValue();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为long<br>
|
||||
* 如果给定的值为<code>null</code>,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Long toLong (Object value) {
|
||||
return toLong(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为double<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Double toDouble (Object value, Double defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
return (Double) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
// 支持科学计数法
|
||||
return new BigDecimal(valueStr.trim()).doubleValue();
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为double<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Double toDouble (Object value) {
|
||||
return toDouble(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Float<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Float toFloat (Object value, Float defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Float) {
|
||||
return (Float) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).floatValue();
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Float.parseFloat(valueStr.trim());
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Float<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Float toFloat (Object value) {
|
||||
return toFloat(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为boolean<br>
|
||||
* String支持的值为:true、false、yes、ok、no,1,0 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Boolean toBool (Object value, Boolean defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
valueStr = valueStr.trim().toLowerCase();
|
||||
switch (valueStr) {
|
||||
case "true":
|
||||
case "yes":
|
||||
case "ok":
|
||||
case "1":
|
||||
return true;
|
||||
case "false":
|
||||
case "no":
|
||||
case "0":
|
||||
return false;
|
||||
default:
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为boolean<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static Boolean toBool (Object value) {
|
||||
return toBool(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Enum对象<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
*
|
||||
* @param clazz Enum的Class
|
||||
* @param value 值
|
||||
* @param defaultValue 默认值
|
||||
*
|
||||
* @return Enum
|
||||
*/
|
||||
public static <E extends Enum<E>> E toEnum (Class<E> clazz, Object value, E defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (clazz.isAssignableFrom(value.getClass())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
E myE = (E) value;
|
||||
return myE;
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return Enum.valueOf(clazz, valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为Enum对象<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
*
|
||||
* @param clazz Enum的Class
|
||||
* @param value 值
|
||||
*
|
||||
* @return Enum
|
||||
*/
|
||||
public static <E extends Enum<E>> E toEnum (Class<E> clazz, Object value) {
|
||||
return toEnum(clazz, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigInteger<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigInteger toBigInteger (Object value, BigInteger defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof BigInteger) {
|
||||
return (BigInteger) value;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return BigInteger.valueOf((Long) value);
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return new BigInteger(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigInteger<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<code>null</code><br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigInteger toBigInteger (Object value) {
|
||||
return toBigInteger(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigDecimal<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
* @param defaultValue 转换错误时的默认值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigDecimal toBigDecimal (Object value, BigDecimal defaultValue) {
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
if (value instanceof BigDecimal) {
|
||||
return (BigDecimal) value;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return new BigDecimal((Long) value);
|
||||
}
|
||||
if (value instanceof Double) {
|
||||
return BigDecimal.valueOf((Double) value);
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return new BigDecimal((Integer) value);
|
||||
}
|
||||
final String valueStr = toStr(value, null);
|
||||
if (StringUtils.isEmpty(valueStr)) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(valueStr);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigDecimal<br>
|
||||
* 如果给定的值为空,或者转换失败,返回默认值<br>
|
||||
* 转换失败不会报错
|
||||
*
|
||||
* @param value 被转换的值
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static BigDecimal toBigDecimal (Object value) {
|
||||
return toBigDecimal(value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为字符串<br>
|
||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||
*
|
||||
* @param obj 对象
|
||||
*
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String utf8Str (Object obj) {
|
||||
return str(obj, CharsetKit.CHARSET_UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为字符串<br>
|
||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param charsetName 字符集
|
||||
*
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str (Object obj, String charsetName) {
|
||||
return str(obj, Charset.forName(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将对象转为字符串<br>
|
||||
* 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法
|
||||
*
|
||||
* @param obj 对象
|
||||
* @param charset 字符集
|
||||
*
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str (Object obj, Charset charset) {
|
||||
if (null == obj) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (obj instanceof String) {
|
||||
return (String) obj;
|
||||
} else if (obj instanceof byte[] || obj instanceof Byte[]) {
|
||||
if (obj instanceof byte[]) {
|
||||
return str((byte[]) obj, charset);
|
||||
} else {
|
||||
Byte[] bytes = (Byte[]) obj;
|
||||
int length = bytes.length;
|
||||
byte[] dest = new byte[length];
|
||||
for (int i = 0 ; i < length ; i++) {
|
||||
dest[i] = bytes[i];
|
||||
}
|
||||
return str(dest, charset);
|
||||
}
|
||||
} else if (obj instanceof ByteBuffer) {
|
||||
return str((ByteBuffer) obj, charset);
|
||||
}
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将byte数组转为字符串
|
||||
*
|
||||
* @param bytes byte数组
|
||||
* @param charset 字符集
|
||||
*
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str (byte[] bytes, String charset) {
|
||||
return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码字节码
|
||||
*
|
||||
* @param data 字符串
|
||||
* @param charset 字符集,如果此字段为空,则解码的结果取决于平台
|
||||
*
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String str (byte[] data, Charset charset) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null == charset) {
|
||||
return new String(data);
|
||||
}
|
||||
return new String(data, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将编码的byteBuffer数据转换为字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @param charset 字符集,如果为空使用当前系统字符集
|
||||
*
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str (ByteBuffer data, String charset) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return str(data, Charset.forName(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将编码的byteBuffer数据转换为字符串
|
||||
*
|
||||
* @param data 数据
|
||||
* @param charset 字符集,如果为空使用当前系统字符集
|
||||
*
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str (ByteBuffer data, Charset charset) {
|
||||
if (null == charset) {
|
||||
charset = Charset.defaultCharset();
|
||||
}
|
||||
return charset.decode(data).toString();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- 全角半角转换
|
||||
|
||||
/**
|
||||
* 半角转全角
|
||||
*
|
||||
* @param input String.
|
||||
*
|
||||
* @return 全角字符串.
|
||||
*/
|
||||
public static String toSBC (String input) {
|
||||
return toSBC(input, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 半角转全角
|
||||
*
|
||||
* @param input String
|
||||
* @param notConvertSet 不替换的字符集合
|
||||
*
|
||||
* @return 全角字符串.
|
||||
*/
|
||||
public static String toSBC (String input, Set<Character> notConvertSet) {
|
||||
char[] c = input.toCharArray();
|
||||
for (int i = 0 ; i < c.length ; i++) {
|
||||
if (null != notConvertSet && notConvertSet.contains(c[i])) {
|
||||
// 跳过不替换的字符
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c[i] == ' ') {
|
||||
c[i] = '\u3000';
|
||||
} else if (c[i] < '\177') {
|
||||
c[i] = (char) (c[i] + 65248);
|
||||
|
||||
}
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 全角转半角
|
||||
*
|
||||
* @param input String.
|
||||
*
|
||||
* @return 半角字符串
|
||||
*/
|
||||
public static String toDBC (String input) {
|
||||
return toDBC(input, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换全角为半角
|
||||
*
|
||||
* @param text 文本
|
||||
* @param notConvertSet 不替换的字符集合
|
||||
*
|
||||
* @return 替换后的字符
|
||||
*/
|
||||
public static String toDBC (String text, Set<Character> notConvertSet) {
|
||||
char[] c = text.toCharArray();
|
||||
for (int i = 0 ; i < c.length ; i++) {
|
||||
if (null != notConvertSet && notConvertSet.contains(c[i])) {
|
||||
// 跳过不替换的字符
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c[i] == '\u3000') {
|
||||
c[i] = ' ';
|
||||
} else if (c[i] > '\uFF00' && c[i] < '\uFF5F') {
|
||||
c[i] = (char) (c[i] - 65248);
|
||||
}
|
||||
}
|
||||
return new String(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字金额大写转换 先写个完整的然后将如零拾替换成零
|
||||
*
|
||||
* @param n 数字
|
||||
*
|
||||
* @return 中文大写数字
|
||||
*/
|
||||
public static String digitUppercase (double n) {
|
||||
String[] fraction = {"角", "分"};
|
||||
String[] digit = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
|
||||
String[][] unit = {{"元", "万", "亿"}, {"", "拾", "佰", "仟"}};
|
||||
|
||||
String head = n < 0 ? "负" : "";
|
||||
n = Math.abs(n);
|
||||
|
||||
String s = "";
|
||||
for (int i = 0 ; i < fraction.length ; i++) {
|
||||
// 优化double计算精度丢失问题
|
||||
BigDecimal nNum = new BigDecimal(n);
|
||||
BigDecimal decimal = new BigDecimal(10);
|
||||
BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN);
|
||||
double d = scale.doubleValue();
|
||||
s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", "");
|
||||
}
|
||||
if (s.length() < 1) {
|
||||
s = "整";
|
||||
}
|
||||
int integerPart = (int) Math.floor(n);
|
||||
|
||||
for (int i = 0 ; i < unit[0].length && integerPart > 0 ; i++) {
|
||||
String p = "";
|
||||
for (int j = 0 ; j < unit[1].length && n > 0 ; j++) {
|
||||
p = digit[integerPart % 10] + unit[1][j] + p;
|
||||
integerPart = integerPart / 10;
|
||||
}
|
||||
s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s;
|
||||
}
|
||||
return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.muyu.common.core.text;
|
||||
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 字符串格式化
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class StrFormatter {
|
||||
public static final String EMPTY_JSON = "{}";
|
||||
public static final char C_BACKSLASH = '\\';
|
||||
public static final char C_DELIM_START = '{';
|
||||
public static final char C_DELIM_END = '}';
|
||||
|
||||
/**
|
||||
* 格式化字符串<br>
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||
* 例:<br>
|
||||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||
*
|
||||
* @param strPattern 字符串模板
|
||||
* @param argArray 参数列表
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static String format (final String strPattern, final Object... argArray) {
|
||||
if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) {
|
||||
return strPattern;
|
||||
}
|
||||
final int strPatternLength = strPattern.length();
|
||||
|
||||
// 初始化定义好的长度以获得更好的性能
|
||||
StringBuilder sbuf = new StringBuilder(strPatternLength + 50);
|
||||
|
||||
int handledPosition = 0;
|
||||
int delimIndex;// 占位符所在位置
|
||||
for (int argIndex = 0 ; argIndex < argArray.length ; argIndex++) {
|
||||
delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition);
|
||||
if (delimIndex == -1) {
|
||||
if (handledPosition == 0) {
|
||||
return strPattern;
|
||||
} else { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果
|
||||
sbuf.append(strPattern, handledPosition, strPatternLength);
|
||||
return sbuf.toString();
|
||||
}
|
||||
} else {
|
||||
if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) {
|
||||
if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) {
|
||||
// 转义符之前还有一个转义符,占位符依旧有效
|
||||
sbuf.append(strPattern, handledPosition, delimIndex - 1);
|
||||
sbuf.append(Convert.utf8Str(argArray[argIndex]));
|
||||
handledPosition = delimIndex + 2;
|
||||
} else {
|
||||
// 占位符被转义
|
||||
argIndex--;
|
||||
sbuf.append(strPattern, handledPosition, delimIndex - 1);
|
||||
sbuf.append(C_DELIM_START);
|
||||
handledPosition = delimIndex + 1;
|
||||
}
|
||||
} else {
|
||||
// 正常占位符
|
||||
sbuf.append(strPattern, handledPosition, delimIndex);
|
||||
sbuf.append(Convert.utf8Str(argArray[argIndex]));
|
||||
handledPosition = delimIndex + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 加入最后一个占位符后所有的字符
|
||||
sbuf.append(strPattern, handledPosition, strPattern.length());
|
||||
|
||||
return sbuf.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package com.muyu.common.core.utils;
|
||||
|
||||
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.*;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 时间工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
|
||||
public static String YYYY = "yyyy";
|
||||
|
||||
public static String YYYY_MM = "yyyy-MM";
|
||||
|
||||
public static String YYYY_MM_DD = "yyyy-MM-dd";
|
||||
|
||||
public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
|
||||
|
||||
public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
private static String[] parsePatterns = {
|
||||
"yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
|
||||
"yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
|
||||
"yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
|
||||
|
||||
/**
|
||||
* 获取当前Date型日期
|
||||
*
|
||||
* @return Date() 当前日期
|
||||
*/
|
||||
public static Date getNowDate () {
|
||||
return new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日期, 默认格式为yyyy-MM-dd
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public static String getDate () {
|
||||
return dateTimeNow(YYYY_MM_DD);
|
||||
}
|
||||
|
||||
public static final String getTime () {
|
||||
return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow () {
|
||||
return dateTimeNow(YYYYMMDDHHMMSS);
|
||||
}
|
||||
|
||||
public static final String dateTimeNow (final String format) {
|
||||
return parseDateToStr(format, new Date());
|
||||
}
|
||||
|
||||
public static final String dateTime (final Date date) {
|
||||
return parseDateToStr(YYYY_MM_DD, date);
|
||||
}
|
||||
|
||||
public static final String parseDateToStr (final String format, final Date date) {
|
||||
return new SimpleDateFormat(format).format(date);
|
||||
}
|
||||
|
||||
public static final Date dateTime (final String format, final String ts) {
|
||||
try {
|
||||
return new SimpleDateFormat(format).parse(ts);
|
||||
} catch (ParseException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如2018/08/08
|
||||
*/
|
||||
public static final String datePath () {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyy/MM/dd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期路径 即年/月/日 如20180808
|
||||
*/
|
||||
public static final String dateTime () {
|
||||
Date now = new Date();
|
||||
return DateFormatUtils.format(now, "yyyyMMdd");
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期型字符串转化为日期 格式
|
||||
*/
|
||||
public static Date parseDate (Object str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return parseDate(str.toString(), parsePatterns);
|
||||
} catch (ParseException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器启动时间
|
||||
*/
|
||||
public static Date getServerStartDate () {
|
||||
long time = ManagementFactory.getRuntimeMXBean().getStartTime();
|
||||
return new Date(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算时间差
|
||||
*
|
||||
* @param endDate 最后时间
|
||||
* @param startTime 开始时间
|
||||
*
|
||||
* @return 时间差(天/小时/分钟)
|
||||
*/
|
||||
public static String timeDistance (Date endDate, Date startTime) {
|
||||
long nd = 1000 * 24 * 60 * 60;
|
||||
long nh = 1000 * 60 * 60;
|
||||
long nm = 1000 * 60;
|
||||
// long ns = 1000;
|
||||
// 获得两个时间的毫秒时间差异
|
||||
long diff = endDate.getTime() - startTime.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 + "分钟";
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 LocalDateTime ==> Date
|
||||
*/
|
||||
public static Date toDate (LocalDateTime temporalAccessor) {
|
||||
ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zdt.toInstant());
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加 LocalDate ==> Date
|
||||
*/
|
||||
public static Date toDate (LocalDate temporalAccessor) {
|
||||
LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
|
||||
ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
|
||||
return Date.from(zdt.toInstant());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.muyu.common.core.utils;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* 错误信息处理类。
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class ExceptionUtil {
|
||||
/**
|
||||
* 获取exception的详细错误信息。
|
||||
*/
|
||||
public static String getExceptionMessage (Throwable e) {
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw, true));
|
||||
return sw.toString();
|
||||
}
|
||||
|
||||
public static String getRootErrorMessage (Exception e) {
|
||||
Throwable root = ExceptionUtils.getRootCause(e);
|
||||
root = (root == null ? e : root);
|
||||
if (root == null) {
|
||||
return "";
|
||||
}
|
||||
String msg = root.getMessage();
|
||||
if (msg == null) {
|
||||
return "null";
|
||||
}
|
||||
return StringUtils.defaultString(msg);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package com.muyu.common.core.utils;
|
||||
|
||||
import com.muyu.common.core.constant.SecurityConstants;
|
||||
import com.muyu.common.core.constant.TokenConstants;
|
||||
import com.muyu.common.core.text.Convert;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Jwt工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class JwtUtils {
|
||||
public static String secret = TokenConstants.SECRET;
|
||||
|
||||
/**
|
||||
* 从数据声明生成令牌
|
||||
*
|
||||
* @param claims 数据声明
|
||||
*
|
||||
* @return 令牌
|
||||
*/
|
||||
public static String createToken (Map<String, Object> claims) {
|
||||
String token = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从令牌中获取数据声明
|
||||
*
|
||||
* @param token 令牌
|
||||
*
|
||||
* @return 数据声明
|
||||
*/
|
||||
public static Claims parseToken (String token) {
|
||||
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据令牌获取用户标识
|
||||
*
|
||||
* @param token 令牌
|
||||
*
|
||||
* @return 用户ID
|
||||
*/
|
||||
public static String getUserKey (String token) {
|
||||
Claims claims = parseToken(token);
|
||||
return getValue(claims, SecurityConstants.USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据令牌获取用户标识
|
||||
*
|
||||
* @param claims 身份信息
|
||||
*
|
||||
* @return 用户ID
|
||||
*/
|
||||
public static String getUserKey (Claims claims) {
|
||||
return getValue(claims, SecurityConstants.USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据令牌获取用户ID
|
||||
*
|
||||
* @param token 令牌
|
||||
*
|
||||
* @return 用户ID
|
||||
*/
|
||||
public static String getUserId (String token) {
|
||||
Claims claims = parseToken(token);
|
||||
return getValue(claims, SecurityConstants.DETAILS_USER_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据身份信息获取用户ID
|
||||
*
|
||||
* @param claims 身份信息
|
||||
*
|
||||
* @return 用户ID
|
||||
*/
|
||||
public static String getUserId (Claims claims) {
|
||||
return getValue(claims, SecurityConstants.DETAILS_USER_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据令牌获取用户名
|
||||
*
|
||||
* @param token 令牌
|
||||
*
|
||||
* @return 用户名
|
||||
*/
|
||||
public static String getUserName (String token) {
|
||||
Claims claims = parseToken(token);
|
||||
return getValue(claims, SecurityConstants.DETAILS_USERNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据身份信息获取用户名
|
||||
*
|
||||
* @param claims 身份信息
|
||||
*
|
||||
* @return 用户名
|
||||
*/
|
||||
public static String getUserName (Claims claims) {
|
||||
return getValue(claims, SecurityConstants.DETAILS_USERNAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据身份信息获取键值
|
||||
*
|
||||
* @param claims 身份信息
|
||||
* @param key 键
|
||||
*
|
||||
* @return 值
|
||||
*/
|
||||
public static String getValue (Claims claims, String key) {
|
||||
return Convert.toStr(claims.get(key), "");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package com.muyu.common.core.utils;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* @author DongZl
|
||||
* @description: 对象工具类
|
||||
* @Date 2023-10-9 下午 04:56
|
||||
*/
|
||||
public class ObjUtils {
|
||||
|
||||
/**
|
||||
* 兼容
|
||||
* CharSequence: 如果长度为零,则认为为空。
|
||||
* Array: 如果长度为零,则认为为空。
|
||||
* Collection: 如果元素为零,则认为为空。
|
||||
* Map: 如果键值映射为零,则认为为空。
|
||||
* @param o 对象
|
||||
* @return 如果对象具有受支持的类型并且为空或null,则为true,否则为false
|
||||
*/
|
||||
public static boolean notNull(Object o){
|
||||
return ObjectUtils.isNotEmpty(o);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断long类型不为0
|
||||
* @param val 值
|
||||
* @return 返回值不为0
|
||||
*/
|
||||
public static boolean notNull(Long val){
|
||||
return ObjectUtils.isNotEmpty(val) && val != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Integer类型不为0
|
||||
* @param val 值
|
||||
* @return 返回值不为0
|
||||
*/
|
||||
public static boolean notNull(Integer val){
|
||||
return ObjectUtils.isNotEmpty(val) && val != 0;
|
||||
}
|
||||
/**
|
||||
* 判断BigDecimal类型不为0
|
||||
* @param val 值
|
||||
* @return 返回值不为0
|
||||
*/
|
||||
public static boolean notNull(BigDecimal val){
|
||||
return ObjectUtils.isNotEmpty(val) && val.doubleValue() == 0.00;
|
||||
}
|
||||
/**
|
||||
* 判断BigDecimal类型不为0
|
||||
* @param val 值
|
||||
* @return 返回值不为0
|
||||
*/
|
||||
public static boolean notChildNull(Object[] val){
|
||||
for (Object o : val) {
|
||||
if (!notNull(o)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package com.muyu.common.core.utils;
|
||||
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.muyu.common.core.utils.sql.SqlUtil;
|
||||
import com.muyu.common.core.web.page.PageDomain;
|
||||
import com.muyu.common.core.web.page.TableSupport;
|
||||
|
||||
/**
|
||||
* 分页工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class PageUtils extends PageHelper {
|
||||
/**
|
||||
* 设置请求分页数据
|
||||
*/
|
||||
public static void startPage () {
|
||||
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||
Integer pageNum = pageDomain.getPageNum();
|
||||
Integer pageSize = pageDomain.getPageSize();
|
||||
String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
|
||||
Boolean reasonable = pageDomain.getReasonable();
|
||||
PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理分页的线程变量
|
||||
*/
|
||||
public static void clearPage () {
|
||||
PageHelper.clearPage();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
package com.muyu.common.core.utils;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.muyu.common.core.constant.Constants;
|
||||
import com.muyu.common.core.domain.Result;
|
||||
import com.muyu.common.core.text.Convert;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpResponse;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 客户端工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class ServletUtils {
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter (String name) {
|
||||
return getRequest().getParameter(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取String参数
|
||||
*/
|
||||
public static String getParameter (String name, String defaultValue) {
|
||||
return Convert.toStr(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt (String name) {
|
||||
return Convert.toInt(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Integer参数
|
||||
*/
|
||||
public static Integer getParameterToInt (String name, Integer defaultValue) {
|
||||
return Convert.toInt(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool (String name) {
|
||||
return Convert.toBool(getRequest().getParameter(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Boolean参数
|
||||
*/
|
||||
public static Boolean getParameterToBool (String name, Boolean defaultValue) {
|
||||
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @param request 请求对象{@link ServletRequest}
|
||||
*
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String[]> getParams (ServletRequest request) {
|
||||
final Map<String, String[]> map = request.getParameterMap();
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @param request 请求对象{@link ServletRequest}
|
||||
*
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String> getParamMap (ServletRequest request) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
|
||||
params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取request
|
||||
*/
|
||||
public static HttpServletRequest getRequest () {
|
||||
try {
|
||||
return getRequestAttributes().getRequest();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取response
|
||||
*/
|
||||
public static HttpServletResponse getResponse () {
|
||||
try {
|
||||
return getRequestAttributes().getResponse();
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取session
|
||||
*/
|
||||
public static HttpSession getSession () {
|
||||
return getRequest().getSession();
|
||||
}
|
||||
|
||||
public static ServletRequestAttributes getRequestAttributes () {
|
||||
try {
|
||||
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
|
||||
return (ServletRequestAttributes) attributes;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getHeader (HttpServletRequest request, String name) {
|
||||
String value = request.getHeader(name);
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return urlDecode(value);
|
||||
}
|
||||
|
||||
public static Map<String, String> getHeaders (HttpServletRequest request) {
|
||||
Map<String, String> map = new LinkedCaseInsensitiveMap<>();
|
||||
Enumeration<String> enumeration = request.getHeaderNames();
|
||||
if (enumeration != null) {
|
||||
while (enumeration.hasMoreElements()) {
|
||||
String key = enumeration.nextElement();
|
||||
String value = request.getHeader(key);
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将字符串渲染到客户端
|
||||
*
|
||||
* @param response 渲染对象
|
||||
* @param string 待渲染的字符串
|
||||
*/
|
||||
public static void renderString (HttpServletResponse response, String string) {
|
||||
try {
|
||||
response.setStatus(200);
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.getWriter().print(string);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否是Ajax异步请求
|
||||
*
|
||||
* @param request
|
||||
*/
|
||||
public static boolean isAjaxRequest (HttpServletRequest request) {
|
||||
String accept = request.getHeader("accept");
|
||||
if (accept != null && accept.contains("application/json")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String xRequestedWith = request.getHeader("X-Requested-With");
|
||||
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String ajax = request.getParameter("__ajax");
|
||||
return StringUtils.inStringIgnoreCase(ajax, "json", "xml");
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容编码
|
||||
*
|
||||
* @param str 内容
|
||||
*
|
||||
* @return 编码后的内容
|
||||
*/
|
||||
public static String urlEncode (String str) {
|
||||
try {
|
||||
return URLEncoder.encode(str, Constants.UTF8);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 内容解码
|
||||
*
|
||||
* @param str 内容
|
||||
*
|
||||
* @return 解码后的内容
|
||||
*/
|
||||
public static String urlDecode (String str) {
|
||||
try {
|
||||
return URLDecoder.decode(str, Constants.UTF8);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置webflux模型响应
|
||||
*
|
||||
* @param response ServerHttpResponse
|
||||
* @param value 响应内容
|
||||
*
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
public static Mono<Void> webFluxResponseWriter (ServerHttpResponse response, Object value) {
|
||||
return webFluxResponseWriter(response, HttpStatus.OK, value, Result.FAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置webflux模型响应
|
||||
*
|
||||
* @param response ServerHttpResponse
|
||||
* @param code 响应状态码
|
||||
* @param value 响应内容
|
||||
*
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
public static Mono<Void> webFluxResponseWriter (ServerHttpResponse response, Object value, int code) {
|
||||
return webFluxResponseWriter(response, HttpStatus.OK, value, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置webflux模型响应
|
||||
*
|
||||
* @param response ServerHttpResponse
|
||||
* @param status http状态码
|
||||
* @param code 响应状态码
|
||||
* @param value 响应内容
|
||||
*
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
public static Mono<Void> webFluxResponseWriter (ServerHttpResponse response, HttpStatus status, Object value, int code) {
|
||||
return webFluxResponseWriter(response, MediaType.APPLICATION_JSON_VALUE, status, value, code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置webflux模型响应
|
||||
*
|
||||
* @param response ServerHttpResponse
|
||||
* @param contentType content-type
|
||||
* @param status http状态码
|
||||
* @param code 响应状态码
|
||||
* @param value 响应内容
|
||||
*
|
||||
* @return Mono<Void>
|
||||
*/
|
||||
public static Mono<Void> webFluxResponseWriter (ServerHttpResponse response, String contentType, HttpStatus status, Object value, int code) {
|
||||
response.setStatusCode(status);
|
||||
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
|
||||
Result<?> result = Result.error(code, value.toString());
|
||||
DataBuffer dataBuffer = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
|
||||
return response.writeWith(Mono.just(dataBuffer));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package com.muyu.common.core.utils;
|
||||
|
||||
import org.springframework.aop.framework.AopContext;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* spring工具类 方便在非spring管理环境中获取bean
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Component
|
||||
public final class SpringUtils implements BeanFactoryPostProcessor {
|
||||
/**
|
||||
* Spring应用上下文环境
|
||||
*/
|
||||
private static ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
/**
|
||||
* 获取对象
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return Object 一个以所给名字注册的bean的实例
|
||||
*
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getBean (String name) throws BeansException {
|
||||
return (T) beanFactory.getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型为requiredType的对象
|
||||
*
|
||||
* @param clz
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws org.springframework.beans.BeansException
|
||||
*/
|
||||
public static <T> T getBean (Class<T> clz) throws BeansException {
|
||||
T result = (T) beanFactory.getBean(clz);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static boolean containsBean (String name) {
|
||||
return beanFactory.containsBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return boolean
|
||||
*
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*/
|
||||
public static boolean isSingleton (String name) throws NoSuchBeanDefinitionException {
|
||||
return beanFactory.isSingleton(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
*
|
||||
* @return Class 注册对象的类型
|
||||
*
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*/
|
||||
public static Class<?> getType (String name) throws NoSuchBeanDefinitionException {
|
||||
return beanFactory.getType(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
|
||||
*
|
||||
* @param name
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
|
||||
*/
|
||||
public static String[] getAliases (String name) throws NoSuchBeanDefinitionException {
|
||||
return beanFactory.getAliases(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取aop代理对象
|
||||
*
|
||||
* @param invoker
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T getAopProxy (T invoker) {
|
||||
return (T) AopContext.currentProxy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory (ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
SpringUtils.beanFactory = beanFactory;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,504 @@
|
|||
package com.muyu.common.core.utils;
|
||||
|
||||
import com.muyu.common.core.constant.Constants;
|
||||
import com.muyu.common.core.text.StrFormatter;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/**
|
||||
* 空字符串
|
||||
*/
|
||||
private static final String NULLSTR = "";
|
||||
|
||||
/**
|
||||
* 下划线
|
||||
*/
|
||||
private static final char SEPARATOR = '_';
|
||||
|
||||
/**
|
||||
* 获取参数不为空值
|
||||
*
|
||||
* @param value defaultValue 要判断的value
|
||||
*
|
||||
* @return value 返回值
|
||||
*/
|
||||
public static <T> T nvl (T value, T defaultValue) {
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否为空, 包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
*
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty (Collection<?> coll) {
|
||||
return isNull(coll) || coll.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Collection是否非空,包含List,Set,Queue
|
||||
*
|
||||
* @param coll 要判断的Collection
|
||||
*
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty (Collection<?> coll) {
|
||||
return !isEmpty(coll);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否为空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
* * @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty (Object[] objects) {
|
||||
return isNull(objects) || (objects.length == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象数组是否非空
|
||||
*
|
||||
* @param objects 要判断的对象数组
|
||||
*
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty (Object[] objects) {
|
||||
return !isEmpty(objects);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
*
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty (Map<?, ?> map) {
|
||||
return isNull(map) || map.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个Map是否为空
|
||||
*
|
||||
* @param map 要判断的Map
|
||||
*
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotEmpty (Map<?, ?> map) {
|
||||
return !isEmpty(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为空串
|
||||
*
|
||||
* @param str String
|
||||
*
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isEmpty (String str) {
|
||||
return isNull(str) || NULLSTR.equals(str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个字符串是否为非空串
|
||||
*
|
||||
* @param str String
|
||||
*
|
||||
* @return true:非空串 false:空串
|
||||
*/
|
||||
public static boolean isNotEmpty (String str) {
|
||||
return !isEmpty(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否为空
|
||||
*
|
||||
* @param object Object
|
||||
*
|
||||
* @return true:为空 false:非空
|
||||
*/
|
||||
public static boolean isNull (Object object) {
|
||||
return object == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否非空
|
||||
*
|
||||
* @param object Object
|
||||
*
|
||||
* @return true:非空 false:空
|
||||
*/
|
||||
public static boolean isNotNull (Object object) {
|
||||
return !isNull(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* * 判断一个对象是否是数组类型(Java基本型别的数组)
|
||||
*
|
||||
* @param object 对象
|
||||
*
|
||||
* @return true:是数组 false:不是数组
|
||||
*/
|
||||
public static boolean isArray (Object object) {
|
||||
return isNotNull(object) && object.getClass().isArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 去空格
|
||||
*/
|
||||
public static String trim (String str) {
|
||||
return (str == null ? "" : str.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring (final String str, int start) {
|
||||
if (str == null) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (start > str.length()) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
return str.substring(start);
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param str 字符串
|
||||
* @param start 开始
|
||||
* @param end 结束
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static String substring (final String str, int start, int end) {
|
||||
if (str == null) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (end < 0) {
|
||||
end = str.length() + end;
|
||||
}
|
||||
if (start < 0) {
|
||||
start = str.length() + start;
|
||||
}
|
||||
|
||||
if (end > str.length()) {
|
||||
end = str.length();
|
||||
}
|
||||
|
||||
if (start > end) {
|
||||
return NULLSTR;
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
}
|
||||
if (end < 0) {
|
||||
end = 0;
|
||||
}
|
||||
|
||||
return str.substring(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为空,并且不是空白字符
|
||||
*
|
||||
* @param str 要判断的value
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean hasText (String str) {
|
||||
return (str != null && !str.isEmpty() && containsText(str));
|
||||
}
|
||||
|
||||
private static boolean containsText (CharSequence str) {
|
||||
int strLen = str.length();
|
||||
for (int i = 0 ; i < strLen ; i++) {
|
||||
if (!Character.isWhitespace(str.charAt(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化文本, {} 表示占位符<br>
|
||||
* 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
|
||||
* 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
|
||||
* 例:<br>
|
||||
* 通常使用:format("this is {} for {}", "a", "b") -> this is a for b<br>
|
||||
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
|
||||
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
|
||||
*
|
||||
* @param template 文本模板,被替换的部分用 {} 表示
|
||||
* @param params 参数值
|
||||
*
|
||||
* @return 格式化后的文本
|
||||
*/
|
||||
public static String format (String template, Object... params) {
|
||||
if (isEmpty(params) || isEmpty(template)) {
|
||||
return template;
|
||||
}
|
||||
return StrFormatter.format(template, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为http(s)://开头
|
||||
*
|
||||
* @param link 链接
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean ishttp (String link) {
|
||||
return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value
|
||||
*
|
||||
* @param collection 给定的集合
|
||||
* @param array 给定的数组
|
||||
*
|
||||
* @return boolean 结果
|
||||
*/
|
||||
public static boolean containsAny (Collection<String> collection, String... array) {
|
||||
if (isEmpty(collection) || isEmpty(array)) {
|
||||
return false;
|
||||
} else {
|
||||
for (String str : array) {
|
||||
if (collection.contains(str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰转下划线命名
|
||||
*/
|
||||
public static String toUnderScoreCase (String str) {
|
||||
if (str == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// 前置字符是否大写
|
||||
boolean preCharIsUpperCase = true;
|
||||
// 当前字符是否大写
|
||||
boolean curreCharIsUpperCase = true;
|
||||
// 下一字符是否大写
|
||||
boolean nexteCharIsUpperCase = true;
|
||||
for (int i = 0 ; i < str.length() ; i++) {
|
||||
char c = str.charAt(i);
|
||||
if (i > 0) {
|
||||
preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
|
||||
} else {
|
||||
preCharIsUpperCase = false;
|
||||
}
|
||||
|
||||
curreCharIsUpperCase = Character.isUpperCase(c);
|
||||
|
||||
if (i < (str.length() - 1)) {
|
||||
nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
|
||||
}
|
||||
|
||||
if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
|
||||
sb.append(SEPARATOR);
|
||||
} else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
|
||||
sb.append(SEPARATOR);
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否包含字符串
|
||||
*
|
||||
* @param str 验证字符串
|
||||
* @param strs 字符串组
|
||||
*
|
||||
* @return 包含返回true
|
||||
*/
|
||||
public static boolean inStringIgnoreCase (String str, String... strs) {
|
||||
if (str != null && strs != null) {
|
||||
for (String s : strs) {
|
||||
if (str.equalsIgnoreCase(trim(s))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld
|
||||
*
|
||||
* @param name 转换前的下划线大写方式命名的字符串
|
||||
*
|
||||
* @return 转换后的驼峰式命名的字符串
|
||||
*/
|
||||
public static String convertToCamelCase (String name) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
// 快速检查
|
||||
if (name == null || name.isEmpty()) {
|
||||
// 没必要转换
|
||||
return "";
|
||||
} else if (!name.contains("_")) {
|
||||
// 不含下划线,仅将首字母大写
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
// 用下划线将原始字符串分割
|
||||
String[] camels = name.split("_");
|
||||
for (String camel : camels) {
|
||||
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
||||
if (camel.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// 首字母大写
|
||||
result.append(camel.substring(0, 1).toUpperCase());
|
||||
result.append(camel.substring(1).toLowerCase());
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 驼峰式命名法
|
||||
* 例如:user_name->userName
|
||||
*/
|
||||
public static String toCamelCase (String s) {
|
||||
if (s == null) {
|
||||
return null;
|
||||
}
|
||||
if (s.indexOf(SEPARATOR) == -1) {
|
||||
return s;
|
||||
}
|
||||
s = s.toLowerCase();
|
||||
StringBuilder sb = new StringBuilder(s.length());
|
||||
boolean upperCase = false;
|
||||
for (int i = 0 ; i < s.length() ; i++) {
|
||||
char c = s.charAt(i);
|
||||
|
||||
if (c == SEPARATOR) {
|
||||
upperCase = true;
|
||||
} else if (upperCase) {
|
||||
sb.append(Character.toUpperCase(c));
|
||||
upperCase = false;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
|
||||
*
|
||||
* @param str 指定字符串
|
||||
* @param strs 需要检查的字符串数组
|
||||
*
|
||||
* @return 是否匹配
|
||||
*/
|
||||
public static boolean matches (String str, List<String> strs) {
|
||||
if (isEmpty(str) || isEmpty(strs)) {
|
||||
return false;
|
||||
}
|
||||
for (String pattern : strs) {
|
||||
if (isMatch(pattern, str)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断url是否与规则配置:
|
||||
* ? 表示单个字符;
|
||||
* * 表示一层路径内的任意字符串,不可跨层级;
|
||||
* ** 表示任意层路径;
|
||||
*
|
||||
* @param pattern 匹配规则
|
||||
* @param url 需要匹配的url
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatch (String pattern, String url) {
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
return matcher.match(pattern, url);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T cast (Object obj) {
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||
*
|
||||
* @param num 数字对象
|
||||
* @param size 字符串指定长度
|
||||
*
|
||||
* @return 返回数字的字符串格式,该字符串为指定长度。
|
||||
*/
|
||||
public static final String padl (final Number num, final int size) {
|
||||
return padl(num.toString(), size, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
||||
*
|
||||
* @param s 原始字符串
|
||||
* @param size 字符串指定长度
|
||||
* @param c 用于补齐的字符
|
||||
*
|
||||
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
|
||||
*/
|
||||
public static final String padl (final String s, final int size, final char c) {
|
||||
final StringBuilder sb = new StringBuilder(size);
|
||||
if (s != null) {
|
||||
final int len = s.length();
|
||||
if (s.length() <= size) {
|
||||
for (int i = size - len ; i > 0 ; i--) {
|
||||
sb.append(c);
|
||||
}
|
||||
sb.append(s);
|
||||
} else {
|
||||
return s.substring(len - size, len);
|
||||
}
|
||||
} else {
|
||||
for (int i = size ; i > 0 ; i--) {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package com.muyu.common.core.utils.bean;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Bean 工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class BeanUtils extends org.springframework.beans.BeanUtils {
|
||||
/**
|
||||
* Bean方法名中属性名开始的下标
|
||||
*/
|
||||
private static final int BEAN_METHOD_PROP_INDEX = 3;
|
||||
|
||||
/**
|
||||
* 匹配getter方法的正则表达式
|
||||
*/
|
||||
private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)");
|
||||
|
||||
/**
|
||||
* 匹配setter方法的正则表达式
|
||||
*/
|
||||
private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)");
|
||||
|
||||
/**
|
||||
* Bean属性复制工具方法。
|
||||
*
|
||||
* @param dest 目标对象
|
||||
* @param src 源对象
|
||||
*/
|
||||
public static void copyBeanProp (Object dest, Object src) {
|
||||
try {
|
||||
copyProperties(src, dest);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的setter方法。
|
||||
*
|
||||
* @param obj 对象
|
||||
*
|
||||
* @return 对象的setter方法列表
|
||||
*/
|
||||
public static List<Method> getSetterMethods (Object obj) {
|
||||
// setter方法列表
|
||||
List<Method> setterMethods = new ArrayList<Method>();
|
||||
|
||||
// 获取所有方法
|
||||
Method[] methods = obj.getClass().getMethods();
|
||||
|
||||
// 查找setter方法
|
||||
|
||||
for (Method method : methods) {
|
||||
Matcher m = SET_PATTERN.matcher(method.getName());
|
||||
if (m.matches() && (method.getParameterTypes().length == 1)) {
|
||||
setterMethods.add(method);
|
||||
}
|
||||
}
|
||||
// 返回setter方法列表
|
||||
return setterMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象的getter方法。
|
||||
*
|
||||
* @param obj 对象
|
||||
*
|
||||
* @return 对象的getter方法列表
|
||||
*/
|
||||
|
||||
public static List<Method> getGetterMethods (Object obj) {
|
||||
// getter方法列表
|
||||
List<Method> getterMethods = new ArrayList<Method>();
|
||||
// 获取所有方法
|
||||
Method[] methods = obj.getClass().getMethods();
|
||||
// 查找getter方法
|
||||
for (Method method : methods) {
|
||||
Matcher m = GET_PATTERN.matcher(method.getName());
|
||||
if (m.matches() && (method.getParameterTypes().length == 0)) {
|
||||
getterMethods.add(method);
|
||||
}
|
||||
}
|
||||
// 返回getter方法列表
|
||||
return getterMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Bean方法名中的属性名是否相等。<br>
|
||||
* 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。
|
||||
*
|
||||
* @param m1 方法名1
|
||||
* @param m2 方法名2
|
||||
*
|
||||
* @return 属性名一样返回true,否则返回false
|
||||
*/
|
||||
|
||||
public static boolean isMethodPropEquals (String m1, String m2) {
|
||||
return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package com.muyu.common.core.utils.bean;
|
||||
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import javax.validation.Validator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* bean对象属性验证
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class BeanValidators {
|
||||
public static void validateWithException (Validator validator, Object object, Class<?>... groups)
|
||||
throws ConstraintViolationException {
|
||||
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
|
||||
if (!constraintViolations.isEmpty()) {
|
||||
throw new ConstraintViolationException(constraintViolations);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package com.muyu.common.core.utils.file;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 文件类型工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class FileTypeUtils {
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: muyu.txt, 返回: txt
|
||||
*
|
||||
* @param file 文件名
|
||||
*
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType (File file) {
|
||||
if (null == file) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
return getFileType(file.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
* <p>
|
||||
* 例如: muyu.txt, 返回: txt
|
||||
*
|
||||
* @param fileName 文件名
|
||||
*
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileType (String fileName) {
|
||||
int separatorIndex = fileName.lastIndexOf(".");
|
||||
if (separatorIndex < 0) {
|
||||
return "";
|
||||
}
|
||||
return fileName.substring(separatorIndex + 1).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名的后缀
|
||||
*
|
||||
* @param file 表单文件
|
||||
*
|
||||
* @return 后缀名
|
||||
*/
|
||||
public static final String getExtension (MultipartFile file) {
|
||||
String extension = FilenameUtils.getExtension(file.getOriginalFilename());
|
||||
if (StringUtils.isEmpty(extension)) {
|
||||
extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
|
||||
}
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
*
|
||||
* @param photoByte 文件字节码
|
||||
*
|
||||
* @return 后缀(不含".")
|
||||
*/
|
||||
public static String getFileExtendName (byte[] photoByte) {
|
||||
String strFileExtendName = "JPG";
|
||||
if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56)
|
||||
&& ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) {
|
||||
strFileExtendName = "GIF";
|
||||
} else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) {
|
||||
strFileExtendName = "JPG";
|
||||
} else if ((photoByte[0] == 66) && (photoByte[1] == 77)) {
|
||||
strFileExtendName = "BMP";
|
||||
} else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) {
|
||||
strFileExtendName = "PNG";
|
||||
}
|
||||
return strFileExtendName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
package com.muyu.common.core.utils.file;
|
||||
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 文件处理工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class FileUtils {
|
||||
/**
|
||||
* 字符常量:斜杠 {@code '/'}
|
||||
*/
|
||||
public static final char SLASH = '/';
|
||||
|
||||
/**
|
||||
* 字符常量:反斜杠 {@code '\\'}
|
||||
*/
|
||||
public static final char BACKSLASH = '\\';
|
||||
|
||||
public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+";
|
||||
|
||||
/**
|
||||
* 输出指定文件的byte数组
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* @param os 输出流
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static void writeBytes (String filePath, OutputStream os) throws IOException {
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException(filePath);
|
||||
}
|
||||
fis = new FileInputStream(file);
|
||||
byte[] b = new byte[1024];
|
||||
int length;
|
||||
while ((length = fis.read(b)) > 0) {
|
||||
os.write(b, 0, length);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} finally {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (fis != null) {
|
||||
try {
|
||||
fis.close();
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param filePath 文件
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean deleteFile (String filePath) {
|
||||
boolean flag = false;
|
||||
File file = new File(filePath);
|
||||
// 路径为文件且不为空则进行删除
|
||||
if (file.isFile() && file.exists()) {
|
||||
flag = file.delete();
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件名称验证
|
||||
*
|
||||
* @param filename 文件名称
|
||||
*
|
||||
* @return true 正常 false 非法
|
||||
*/
|
||||
public static boolean isValidFilename (String filename) {
|
||||
return filename.matches(FILENAME_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否可下载
|
||||
*
|
||||
* @param resource 需要下载的文件
|
||||
*
|
||||
* @return true 正常 false 非法
|
||||
*/
|
||||
public static boolean checkAllowDownload (String resource) {
|
||||
// 禁止目录上跳级别
|
||||
if (StringUtils.contains(resource, "..")) {
|
||||
return false;
|
||||
}
|
||||
// 判断是否在允许下载的文件规则内
|
||||
return ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件名重新编码
|
||||
*
|
||||
* @param request 请求对象
|
||||
* @param fileName 文件名
|
||||
*
|
||||
* @return 编码后的文件名
|
||||
*/
|
||||
public static String setFileDownloadHeader (HttpServletRequest request, String fileName) throws UnsupportedEncodingException {
|
||||
final String agent = request.getHeader("USER-AGENT");
|
||||
String filename = fileName;
|
||||
if (agent.contains("MSIE")) {
|
||||
// IE浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
filename = filename.replace("+", " ");
|
||||
} else if (agent.contains("Firefox")) {
|
||||
// 火狐浏览器
|
||||
filename = new String(fileName.getBytes(), "ISO8859-1");
|
||||
} else if (agent.contains("Chrome")) {
|
||||
// google浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
} else {
|
||||
// 其它浏览器
|
||||
filename = URLEncoder.encode(filename, "utf-8");
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回文件名
|
||||
*
|
||||
* @param filePath 文件
|
||||
*
|
||||
* @return 文件名
|
||||
*/
|
||||
public static String getName (String filePath) {
|
||||
if (null == filePath) {
|
||||
return null;
|
||||
}
|
||||
int len = filePath.length();
|
||||
if (0 == len) {
|
||||
return filePath;
|
||||
}
|
||||
if (isFileSeparator(filePath.charAt(len - 1))) {
|
||||
// 以分隔符结尾的去掉结尾分隔符
|
||||
len--;
|
||||
}
|
||||
|
||||
int begin = 0;
|
||||
char c;
|
||||
for (int i = len - 1 ; i > -1 ; i--) {
|
||||
c = filePath.charAt(i);
|
||||
if (isFileSeparator(c)) {
|
||||
// 查找最后一个路径分隔符(/或者\)
|
||||
begin = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return filePath.substring(begin, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为Windows或者Linux(Unix)文件分隔符<br>
|
||||
* Windows平台下分隔符为\,Linux(Unix)为/
|
||||
*
|
||||
* @param c 字符
|
||||
*
|
||||
* @return 是否为Windows或者Linux(Unix)文件分隔符
|
||||
*/
|
||||
public static boolean isFileSeparator (char c) {
|
||||
return SLASH == c || BACKSLASH == c;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件名重新编码
|
||||
*
|
||||
* @param response 响应对象
|
||||
* @param realFileName 真实文件名
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static void setAttachmentResponseHeader (HttpServletResponse response, String realFileName) throws UnsupportedEncodingException {
|
||||
String percentEncodedFileName = percentEncode(realFileName);
|
||||
|
||||
StringBuilder contentDispositionValue = new StringBuilder();
|
||||
contentDispositionValue.append("attachment; filename=")
|
||||
.append(percentEncodedFileName)
|
||||
.append(";")
|
||||
.append("filename*=")
|
||||
.append("utf-8''")
|
||||
.append(percentEncodedFileName);
|
||||
|
||||
response.setHeader("Content-disposition", contentDispositionValue.toString());
|
||||
response.setHeader("download-filename", percentEncodedFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 百分号编码工具方法
|
||||
*
|
||||
* @param s 需要百分号编码的字符串
|
||||
*
|
||||
* @return 百分号编码后的字符串
|
||||
*/
|
||||
public static String percentEncode (String s) throws UnsupportedEncodingException {
|
||||
String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString());
|
||||
return encode.replaceAll("\\+", "%20");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.muyu.common.core.utils.file;
|
||||
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 图片处理工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class ImageUtils {
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageUtils.class);
|
||||
|
||||
public static byte[] getImage (String imagePath) {
|
||||
InputStream is = getFile(imagePath);
|
||||
try {
|
||||
return IOUtils.toByteArray(is);
|
||||
} catch (Exception e) {
|
||||
log.error("图片加载异常 {}", e);
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(is);
|
||||
}
|
||||
}
|
||||
|
||||
public static InputStream getFile (String imagePath) {
|
||||
try {
|
||||
byte[] result = readFile(imagePath);
|
||||
result = Arrays.copyOf(result, result.length);
|
||||
return new ByteArrayInputStream(result);
|
||||
} catch (Exception e) {
|
||||
log.error("获取图片异常 {}", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件为字节数据
|
||||
*
|
||||
* @param url 地址
|
||||
*
|
||||
* @return 字节数据
|
||||
*/
|
||||
public static byte[] readFile (String url) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
// 网络地址
|
||||
URL urlObj = new URL(url);
|
||||
URLConnection urlConnection = urlObj.openConnection();
|
||||
urlConnection.setConnectTimeout(30 * 1000);
|
||||
urlConnection.setReadTimeout(60 * 1000);
|
||||
urlConnection.setDoInput(true);
|
||||
in = urlConnection.getInputStream();
|
||||
return IOUtils.toByteArray(in);
|
||||
} catch (Exception e) {
|
||||
log.error("访问文件异常 {}", e);
|
||||
return null;
|
||||
} finally {
|
||||
IOUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.muyu.common.core.utils.file;
|
||||
|
||||
/**
|
||||
* 媒体类型工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class MimeTypeUtils {
|
||||
public static final String IMAGE_PNG = "image/png";
|
||||
|
||||
public static final String IMAGE_JPG = "image/jpg";
|
||||
|
||||
public static final String IMAGE_JPEG = "image/jpeg";
|
||||
|
||||
public static final String IMAGE_BMP = "image/bmp";
|
||||
|
||||
public static final String IMAGE_GIF = "image/gif";
|
||||
|
||||
public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
|
||||
|
||||
public static final String[] FLASH_EXTENSION = {"swf", "flv"};
|
||||
|
||||
public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
|
||||
"asf", "rm", "rmvb"};
|
||||
|
||||
public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
|
||||
|
||||
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
|
||||
// 图片
|
||||
"bmp", "gif", "jpg", "jpeg", "png",
|
||||
// word excel powerpoint
|
||||
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
|
||||
// 压缩文件
|
||||
"rar", "zip", "gz", "bz2",
|
||||
// 视频格式
|
||||
"mp4", "avi", "rmvb",
|
||||
// pdf
|
||||
"pdf"};
|
||||
|
||||
public static String getExtension (String prefix) {
|
||||
switch (prefix) {
|
||||
case IMAGE_PNG:
|
||||
return "png";
|
||||
case IMAGE_JPG:
|
||||
return "jpg";
|
||||
case IMAGE_JPEG:
|
||||
return "jpeg";
|
||||
case IMAGE_BMP:
|
||||
return "bmp";
|
||||
case IMAGE_GIF:
|
||||
return "gif";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package com.muyu.common.core.utils.html;
|
||||
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 转义和反转义工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class EscapeUtil {
|
||||
public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)";
|
||||
|
||||
private static final char[][] TEXT = new char[64][];
|
||||
|
||||
static {
|
||||
for (int i = 0 ; i < 64 ; i++) {
|
||||
TEXT[i] = new char[]{(char) i};
|
||||
}
|
||||
|
||||
// special HTML characters
|
||||
TEXT['\''] = "'".toCharArray(); // 单引号
|
||||
TEXT['"'] = """.toCharArray(); // 双引号
|
||||
TEXT['&'] = "&".toCharArray(); // &符
|
||||
TEXT['<'] = "<".toCharArray(); // 小于号
|
||||
TEXT['>'] = ">".toCharArray(); // 大于号
|
||||
}
|
||||
|
||||
/**
|
||||
* 转义文本中的HTML字符为安全的字符
|
||||
*
|
||||
* @param text 被转义的文本
|
||||
*
|
||||
* @return 转义后的文本
|
||||
*/
|
||||
public static String escape (String text) {
|
||||
return encode(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原被转义的HTML特殊字符
|
||||
*
|
||||
* @param content 包含转义符的HTML内容
|
||||
*
|
||||
* @return 转换后的字符串
|
||||
*/
|
||||
public static String unescape (String content) {
|
||||
return decode(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有HTML标签,但是不删除标签内的内容
|
||||
*
|
||||
* @param content 文本
|
||||
*
|
||||
* @return 清除标签后的文本
|
||||
*/
|
||||
public static String clean (String content) {
|
||||
return new HTMLFilter().filter(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape编码
|
||||
*
|
||||
* @param text 被编码的文本
|
||||
*
|
||||
* @return 编码后的字符
|
||||
*/
|
||||
private static String encode (String text) {
|
||||
if (StringUtils.isEmpty(text)) {
|
||||
return StringUtils.EMPTY;
|
||||
}
|
||||
|
||||
final StringBuilder tmp = new StringBuilder(text.length() * 6);
|
||||
char c;
|
||||
for (int i = 0 ; i < text.length() ; i++) {
|
||||
c = text.charAt(i);
|
||||
if (c < 256) {
|
||||
tmp.append("%");
|
||||
if (c < 16) {
|
||||
tmp.append("0");
|
||||
}
|
||||
tmp.append(Integer.toString(c, 16));
|
||||
} else {
|
||||
tmp.append("%u");
|
||||
if (c <= 0xfff) {
|
||||
// issue#I49JU8@Gitee
|
||||
tmp.append("0");
|
||||
}
|
||||
tmp.append(Integer.toString(c, 16));
|
||||
}
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape解码
|
||||
*
|
||||
* @param content 被转义的内容
|
||||
*
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String decode (String content) {
|
||||
if (StringUtils.isEmpty(content)) {
|
||||
return content;
|
||||
}
|
||||
|
||||
StringBuilder tmp = new StringBuilder(content.length());
|
||||
int lastPos = 0, pos = 0;
|
||||
char ch;
|
||||
while (lastPos < content.length()) {
|
||||
pos = content.indexOf("%", lastPos);
|
||||
if (pos == lastPos) {
|
||||
if (content.charAt(pos + 1) == 'u') {
|
||||
ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16);
|
||||
tmp.append(ch);
|
||||
lastPos = pos + 6;
|
||||
} else {
|
||||
ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16);
|
||||
tmp.append(ch);
|
||||
lastPos = pos + 3;
|
||||
}
|
||||
} else {
|
||||
if (pos == -1) {
|
||||
tmp.append(content.substring(lastPos));
|
||||
lastPos = content.length();
|
||||
} else {
|
||||
tmp.append(content.substring(lastPos, pos));
|
||||
lastPos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tmp.toString();
|
||||
}
|
||||
|
||||
public static void main (String[] args) {
|
||||
String html = "<script>alert(1);</script>";
|
||||
String escape = EscapeUtil.escape(html);
|
||||
// String html = "<scr<script>ipt>alert(\"XSS\")</scr<script>ipt>";
|
||||
// String html = "<123";
|
||||
// String html = "123>";
|
||||
System.out.println("clean: " + EscapeUtil.clean(html));
|
||||
System.out.println("escape: " + escape);
|
||||
System.out.println("unescape: " + EscapeUtil.unescape(escape));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,498 @@
|
|||
package com.muyu.common.core.utils.html;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* HTML过滤器,用于去除XSS漏洞隐患。
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public final class HTMLFilter {
|
||||
/**
|
||||
* regex flag union representing /si modifiers in php
|
||||
**/
|
||||
private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
|
||||
private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
|
||||
private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
|
||||
private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
|
||||
private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
|
||||
private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
|
||||
private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
|
||||
private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
|
||||
private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
|
||||
private static final Pattern P_END_ARROW = Pattern.compile("^>");
|
||||
private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||
private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||
private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
|
||||
private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
|
||||
private static final Pattern P_AMP = Pattern.compile("&");
|
||||
private static final Pattern P_QUOTE = Pattern.compile("\"");
|
||||
private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
|
||||
private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
|
||||
private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
|
||||
|
||||
// @xxx could grow large... maybe use sesat's ReferenceMap
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* set of allowed html elements, along with allowed attributes for each element
|
||||
**/
|
||||
private final Map<String, List<String>> vAllowed;
|
||||
/**
|
||||
* counts of open tags for each (allowable) html element
|
||||
**/
|
||||
private final Map<String, Integer> vTagCounts = new HashMap<>();
|
||||
|
||||
/**
|
||||
* html elements which must always be self-closing (e.g. "<img />")
|
||||
**/
|
||||
private final String[] vSelfClosingTags;
|
||||
/**
|
||||
* html elements which must always have separate opening and closing tags (e.g. "<b></b>")
|
||||
**/
|
||||
private final String[] vNeedClosingTags;
|
||||
/**
|
||||
* set of disallowed html elements
|
||||
**/
|
||||
private final String[] vDisallowed;
|
||||
/**
|
||||
* attributes which should be checked for valid protocols
|
||||
**/
|
||||
private final String[] vProtocolAtts;
|
||||
/**
|
||||
* allowed protocols
|
||||
**/
|
||||
private final String[] vAllowedProtocols;
|
||||
/**
|
||||
* tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
|
||||
**/
|
||||
private final String[] vRemoveBlanks;
|
||||
/**
|
||||
* entities allowed within html markup
|
||||
**/
|
||||
private final String[] vAllowedEntities;
|
||||
/**
|
||||
* flag determining whether comments are allowed in input String.
|
||||
*/
|
||||
private final boolean stripComment;
|
||||
private final boolean encodeQuotes;
|
||||
/**
|
||||
* flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "<b text </b>"
|
||||
* becomes "<b> text </b>"). If set to false, unbalanced angle brackets will be html escaped.
|
||||
*/
|
||||
private final boolean alwaysMakeTags;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public HTMLFilter () {
|
||||
vAllowed = new HashMap<>();
|
||||
|
||||
final ArrayList<String> a_atts = new ArrayList<>();
|
||||
a_atts.add("href");
|
||||
a_atts.add("target");
|
||||
vAllowed.put("a", a_atts);
|
||||
|
||||
final ArrayList<String> img_atts = new ArrayList<>();
|
||||
img_atts.add("src");
|
||||
img_atts.add("width");
|
||||
img_atts.add("height");
|
||||
img_atts.add("alt");
|
||||
vAllowed.put("img", img_atts);
|
||||
|
||||
final ArrayList<String> no_atts = new ArrayList<>();
|
||||
vAllowed.put("b", no_atts);
|
||||
vAllowed.put("strong", no_atts);
|
||||
vAllowed.put("i", no_atts);
|
||||
vAllowed.put("em", no_atts);
|
||||
|
||||
vSelfClosingTags = new String[]{"img"};
|
||||
vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
|
||||
vDisallowed = new String[]{};
|
||||
vAllowedProtocols = new String[]{"http", "mailto", "https"}; // no ftp.
|
||||
vProtocolAtts = new String[]{"src", "href"};
|
||||
vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
|
||||
vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
|
||||
stripComment = true;
|
||||
encodeQuotes = true;
|
||||
alwaysMakeTags = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map-parameter configurable constructor.
|
||||
*
|
||||
* @param conf map containing configuration. keys match field names.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public HTMLFilter (final Map<String, Object> conf) {
|
||||
|
||||
assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
|
||||
assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
|
||||
assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
|
||||
assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
|
||||
assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
|
||||
assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
|
||||
assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
|
||||
assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
|
||||
|
||||
vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
|
||||
vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
|
||||
vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
|
||||
vDisallowed = (String[]) conf.get("vDisallowed");
|
||||
vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
|
||||
vProtocolAtts = (String[]) conf.get("vProtocolAtts");
|
||||
vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
|
||||
vAllowedEntities = (String[]) conf.get("vAllowedEntities");
|
||||
stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
|
||||
encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
|
||||
alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// my versions of some PHP library functions
|
||||
public static String chr (final int decimal) {
|
||||
return String.valueOf((char) decimal);
|
||||
}
|
||||
|
||||
public static String htmlSpecialChars (final String s) {
|
||||
String result = s;
|
||||
result = regexReplace(P_AMP, "&", result);
|
||||
result = regexReplace(P_QUOTE, """, result);
|
||||
result = regexReplace(P_LEFT_ARROW, "<", result);
|
||||
result = regexReplace(P_RIGHT_ARROW, ">", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static String regexReplace (final Pattern regex_pattern, final String replacement, final String s) {
|
||||
Matcher m = regex_pattern.matcher(s);
|
||||
return m.replaceAll(replacement);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
private static boolean inArray (final String s, final String[] array) {
|
||||
for (String item : array) {
|
||||
if (item != null && item.equals(s)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void reset () {
|
||||
vTagCounts.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* given a user submitted input String, filter out any invalid or restricted html.
|
||||
*
|
||||
* @param input text (i.e. submitted by a user) than may contain html
|
||||
*
|
||||
* @return "clean" version of input, with only valid, whitelisted html elements allowed
|
||||
*/
|
||||
public String filter (final String input) {
|
||||
reset();
|
||||
String s = input;
|
||||
|
||||
s = escapeComments(s);
|
||||
|
||||
s = balanceHTML(s);
|
||||
|
||||
s = checkTags(s);
|
||||
|
||||
s = processRemoveBlanks(s);
|
||||
|
||||
// s = validateEntities(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public boolean isAlwaysMakeTags () {
|
||||
return alwaysMakeTags;
|
||||
}
|
||||
|
||||
public boolean isStripComments () {
|
||||
return stripComment;
|
||||
}
|
||||
|
||||
private String escapeComments (final String s) {
|
||||
final Matcher m = P_COMMENTS.matcher(s);
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
if (m.find()) {
|
||||
final String match = m.group(1); // (.*?)
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private String balanceHTML (String s) {
|
||||
if (alwaysMakeTags) {
|
||||
//
|
||||
// try and form html
|
||||
//
|
||||
s = regexReplace(P_END_ARROW, "", s);
|
||||
// 不追加结束标签
|
||||
s = regexReplace(P_BODY_TO_END, "<$1>", s);
|
||||
s = regexReplace(P_XML_CONTENT, "$1<$2", s);
|
||||
|
||||
} else {
|
||||
//
|
||||
// escape stray brackets
|
||||
//
|
||||
s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s);
|
||||
s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s);
|
||||
|
||||
//
|
||||
// the last regexp causes '<>' entities to appear
|
||||
// (we need to do a lookahead assertion so that the last bracket can
|
||||
// be used in the next pass of the regexp)
|
||||
//
|
||||
s = regexReplace(P_BOTH_ARROWS, "", s);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String checkTags (String s) {
|
||||
Matcher m = P_TAGS.matcher(s);
|
||||
|
||||
final StringBuffer buf = new StringBuffer();
|
||||
while (m.find()) {
|
||||
String replaceStr = m.group(1);
|
||||
replaceStr = processTag(replaceStr);
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
// these get tallied in processTag
|
||||
// (remember to reset before subsequent calls to filter method)
|
||||
final StringBuilder sBuilder = new StringBuilder(buf.toString());
|
||||
for (String key : vTagCounts.keySet()) {
|
||||
for (int ii = 0 ; ii < vTagCounts.get(key) ; ii++) {
|
||||
sBuilder.append("</").append(key).append(">");
|
||||
}
|
||||
}
|
||||
s = sBuilder.toString();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String processRemoveBlanks (final String s) {
|
||||
String result = s;
|
||||
for (String tag : vRemoveBlanks) {
|
||||
if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
|
||||
P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
|
||||
}
|
||||
result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
|
||||
if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
|
||||
P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
|
||||
}
|
||||
result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private String processTag (final String s) {
|
||||
// ending tags
|
||||
Matcher m = P_END_TAG.matcher(s);
|
||||
if (m.find()) {
|
||||
final String name = m.group(1).toLowerCase();
|
||||
if (allowed(name)) {
|
||||
if (!inArray(name, vSelfClosingTags)) {
|
||||
if (vTagCounts.containsKey(name)) {
|
||||
vTagCounts.put(name, vTagCounts.get(name) - 1);
|
||||
return "</" + name + ">";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// starting tags
|
||||
m = P_START_TAG.matcher(s);
|
||||
if (m.find()) {
|
||||
final String name = m.group(1).toLowerCase();
|
||||
final String body = m.group(2);
|
||||
String ending = m.group(3);
|
||||
|
||||
// debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" );
|
||||
if (allowed(name)) {
|
||||
final StringBuilder params = new StringBuilder();
|
||||
|
||||
final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
|
||||
final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
|
||||
final List<String> paramNames = new ArrayList<>();
|
||||
final List<String> paramValues = new ArrayList<>();
|
||||
while (m2.find()) {
|
||||
paramNames.add(m2.group(1)); // ([a-z0-9]+)
|
||||
paramValues.add(m2.group(3)); // (.*?)
|
||||
}
|
||||
while (m3.find()) {
|
||||
paramNames.add(m3.group(1)); // ([a-z0-9]+)
|
||||
paramValues.add(m3.group(3)); // ([^\"\\s']+)
|
||||
}
|
||||
|
||||
String paramName, paramValue;
|
||||
for (int ii = 0 ; ii < paramNames.size() ; ii++) {
|
||||
paramName = paramNames.get(ii).toLowerCase();
|
||||
paramValue = paramValues.get(ii);
|
||||
|
||||
// debug( "paramName='" + paramName + "'" );
|
||||
// debug( "paramValue='" + paramValue + "'" );
|
||||
// debug( "allowed? " + vAllowed.get( name ).contains( paramName ) );
|
||||
|
||||
if (allowedAttribute(name, paramName)) {
|
||||
if (inArray(paramName, vProtocolAtts)) {
|
||||
paramValue = processParamProtocol(paramValue);
|
||||
}
|
||||
params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\"");
|
||||
}
|
||||
}
|
||||
|
||||
if (inArray(name, vSelfClosingTags)) {
|
||||
ending = " /";
|
||||
}
|
||||
|
||||
if (inArray(name, vNeedClosingTags)) {
|
||||
ending = "";
|
||||
}
|
||||
|
||||
if (ending == null || ending.length() < 1) {
|
||||
if (vTagCounts.containsKey(name)) {
|
||||
vTagCounts.put(name, vTagCounts.get(name) + 1);
|
||||
} else {
|
||||
vTagCounts.put(name, 1);
|
||||
}
|
||||
} else {
|
||||
ending = " /";
|
||||
}
|
||||
return "<" + name + params + ending + ">";
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// comments
|
||||
m = P_COMMENT.matcher(s);
|
||||
if (!stripComment && m.find()) {
|
||||
return "<" + m.group() + ">";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private String processParamProtocol (String s) {
|
||||
s = decodeEntities(s);
|
||||
final Matcher m = P_PROTOCOL.matcher(s);
|
||||
if (m.find()) {
|
||||
final String protocol = m.group(1);
|
||||
if (!inArray(protocol, vAllowedProtocols)) {
|
||||
// bad protocol, turn into local anchor link instead
|
||||
s = "#" + s.substring(protocol.length() + 1);
|
||||
if (s.startsWith("#//")) {
|
||||
s = "#" + s.substring(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String decodeEntities (String s) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
Matcher m = P_ENTITY.matcher(s);
|
||||
while (m.find()) {
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.decode(match).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
buf = new StringBuffer();
|
||||
m = P_ENTITY_UNICODE.matcher(s);
|
||||
while (m.find()) {
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.valueOf(match, 16).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
buf = new StringBuffer();
|
||||
m = P_ENCODE.matcher(s);
|
||||
while (m.find()) {
|
||||
final String match = m.group(1);
|
||||
final int decimal = Integer.valueOf(match, 16).intValue();
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
s = buf.toString();
|
||||
|
||||
s = validateEntities(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
private String validateEntities (final String s) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
// validate entities throughout the string
|
||||
Matcher m = P_VALID_ENTITIES.matcher(s);
|
||||
while (m.find()) {
|
||||
final String one = m.group(1); // ([^&;]*)
|
||||
final String two = m.group(2); // (?=(;|&|$))
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
|
||||
return encodeQuotes(buf.toString());
|
||||
}
|
||||
|
||||
private String encodeQuotes (final String s) {
|
||||
if (encodeQuotes) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
Matcher m = P_VALID_QUOTES.matcher(s);
|
||||
while (m.find()) {
|
||||
final String one = m.group(1); // (>|^)
|
||||
final String two = m.group(2); // ([^<]+?)
|
||||
final String three = m.group(3); // (<|$)
|
||||
// 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two)
|
||||
m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three));
|
||||
}
|
||||
m.appendTail(buf);
|
||||
return buf.toString();
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
private String checkEntity (final String preamble, final String term) {
|
||||
|
||||
return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble;
|
||||
}
|
||||
|
||||
private boolean isValidEntity (final String entity) {
|
||||
return inArray(entity, vAllowedEntities);
|
||||
}
|
||||
|
||||
private boolean allowed (final String name) {
|
||||
return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
|
||||
}
|
||||
|
||||
private boolean allowedAttribute (final String name, final String paramName) {
|
||||
return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,331 @@
|
|||
package com.muyu.common.core.utils.ip;
|
||||
|
||||
import com.muyu.common.core.utils.ServletUtils;
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* 获取IP方法
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class IpUtils {
|
||||
public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)";
|
||||
// 匹配 ip
|
||||
public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")";
|
||||
// 匹配网段
|
||||
public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")";
|
||||
public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))";
|
||||
|
||||
/**
|
||||
* 获取客户端IP
|
||||
*
|
||||
* @return IP地址
|
||||
*/
|
||||
public static String getIpAddr () {
|
||||
return getIpAddr(ServletUtils.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取客户端IP
|
||||
*
|
||||
* @param request 请求对象
|
||||
*
|
||||
* @return IP地址
|
||||
*/
|
||||
public static String getIpAddr (HttpServletRequest request) {
|
||||
if (request == null) {
|
||||
return "unknown";
|
||||
}
|
||||
String ip = request.getHeader("x-forwarded-for");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
}
|
||||
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
|
||||
return "0:0:0:0:0:0:0:1".equals(ip) ? "10.10.10.132" : getMultistageReverseProxyIp(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为内部IP地址
|
||||
*
|
||||
* @param ip IP地址
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
public static boolean internalIp (String ip) {
|
||||
byte[] addr = textToNumericFormatV4(ip);
|
||||
return internalIp(addr) || "10.10.10.132".equals(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为内部IP地址
|
||||
*
|
||||
* @param addr byte地址
|
||||
*
|
||||
* @return 结果
|
||||
*/
|
||||
private static boolean internalIp (byte[] addr) {
|
||||
if (StringUtils.isNull(addr) || addr.length < 2) {
|
||||
return true;
|
||||
}
|
||||
final byte b0 = addr[0];
|
||||
final byte b1 = addr[1];
|
||||
// 10.x.x.x/8
|
||||
final byte SECTION_1 = 0x0A;
|
||||
// 172.16.x.x/12
|
||||
final byte SECTION_2 = (byte) 0xAC;
|
||||
final byte SECTION_3 = (byte) 0x10;
|
||||
final byte SECTION_4 = (byte) 0x1F;
|
||||
// 192.168.x.x/16
|
||||
final byte SECTION_5 = (byte) 0xC0;
|
||||
final byte SECTION_6 = (byte) 0xA8;
|
||||
switch (b0) {
|
||||
case SECTION_1:
|
||||
return true;
|
||||
case SECTION_2:
|
||||
if (b1 >= SECTION_3 && b1 <= SECTION_4) {
|
||||
return true;
|
||||
}
|
||||
case SECTION_5:
|
||||
switch (b1) {
|
||||
case SECTION_6:
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将IPv4地址转换成字节
|
||||
*
|
||||
* @param text IPv4地址
|
||||
*
|
||||
* @return byte 字节
|
||||
*/
|
||||
public static byte[] textToNumericFormatV4 (String text) {
|
||||
if (text.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] bytes = new byte[4];
|
||||
String[] elements = text.split("\\.", -1);
|
||||
try {
|
||||
long l;
|
||||
int i;
|
||||
switch (elements.length) {
|
||||
case 1:
|
||||
l = Long.parseLong(elements[0]);
|
||||
if ((l < 0L) || (l > 4294967295L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l >> 24 & 0xFF);
|
||||
bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 2:
|
||||
l = Integer.parseInt(elements[0]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[0] = (byte) (int) (l & 0xFF);
|
||||
l = Integer.parseInt(elements[1]);
|
||||
if ((l < 0L) || (l > 16777215L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[1] = (byte) (int) (l >> 16 & 0xFF);
|
||||
bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 3:
|
||||
for (i = 0; i < 2 ; ++i) {
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
l = Integer.parseInt(elements[2]);
|
||||
if ((l < 0L) || (l > 65535L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[2] = (byte) (int) (l >> 8 & 0xFF);
|
||||
bytes[3] = (byte) (int) (l & 0xFF);
|
||||
break;
|
||||
case 4:
|
||||
for (i = 0; i < 4 ; ++i) {
|
||||
l = Integer.parseInt(elements[i]);
|
||||
if ((l < 0L) || (l > 255L)) {
|
||||
return null;
|
||||
}
|
||||
bytes[i] = (byte) (int) (l & 0xFF);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取IP地址
|
||||
*
|
||||
* @return 本地IP地址
|
||||
*/
|
||||
public static String getHostIp () {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
}
|
||||
return "10.10.10.132";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机名
|
||||
*
|
||||
* @return 本地主机名
|
||||
*/
|
||||
public static String getHostName () {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
}
|
||||
return "未知";
|
||||
}
|
||||
|
||||
/**
|
||||
* 从多级反向代理中获得第一个非unknown IP地址
|
||||
*
|
||||
* @param ip 获得的IP地址
|
||||
*
|
||||
* @return 第一个非unknown IP地址
|
||||
*/
|
||||
public static String getMultistageReverseProxyIp (String ip) {
|
||||
// 多级反向代理检测
|
||||
if (ip != null && ip.indexOf(",") > 0) {
|
||||
final String[] ips = ip.trim().split(",");
|
||||
for (String subIp : ips) {
|
||||
if (false == isUnknown(subIp)) {
|
||||
ip = subIp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return StringUtils.substring(ip, 0, 255);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测给定字符串是否为未知,多用于检测HTTP请求相关
|
||||
*
|
||||
* @param checkString 被检测的字符串
|
||||
*
|
||||
* @return 是否未知
|
||||
*/
|
||||
public static boolean isUnknown (String checkString) {
|
||||
return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为IP
|
||||
*/
|
||||
public static boolean isIP (String ip) {
|
||||
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为IP,或 *为间隔的通配符地址
|
||||
*/
|
||||
public static boolean isIpWildCard (String ip) {
|
||||
return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测参数是否在ip通配符里
|
||||
*/
|
||||
public static boolean ipIsInWildCardNoCheck (String ipWildCard, String ip) {
|
||||
String[] s1 = ipWildCard.split("\\.");
|
||||
String[] s2 = ip.split("\\.");
|
||||
boolean isMatchedSeg = true;
|
||||
for (int i = 0 ; i < s1.length && !s1[i].equals("*") ; i++) {
|
||||
if (!s1[i].equals(s2[i])) {
|
||||
isMatchedSeg = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return isMatchedSeg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串
|
||||
*/
|
||||
public static boolean isIPSegment (String ipSeg) {
|
||||
return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断ip是否在指定网段中
|
||||
*/
|
||||
public static boolean ipIsInNetNoCheck (String iparea, String ip) {
|
||||
int idx = iparea.indexOf('-');
|
||||
String[] sips = iparea.substring(0, idx).split("\\.");
|
||||
String[] sipe = iparea.substring(idx + 1).split("\\.");
|
||||
String[] sipt = ip.split("\\.");
|
||||
long ips = 0L, ipe = 0L, ipt = 0L;
|
||||
for (int i = 0 ; i < 4 ; ++i) {
|
||||
ips = ips << 8 | Integer.parseInt(sips[i]);
|
||||
ipe = ipe << 8 | Integer.parseInt(sipe[i]);
|
||||
ipt = ipt << 8 | Integer.parseInt(sipt[i]);
|
||||
}
|
||||
if (ips > ipe) {
|
||||
long t = ips;
|
||||
ips = ipe;
|
||||
ipe = t;
|
||||
}
|
||||
return ips <= ipt && ipt <= ipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验ip是否符合过滤串规则
|
||||
*
|
||||
* @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99`
|
||||
* @param ip 校验IP地址
|
||||
*
|
||||
* @return boolean 结果
|
||||
*/
|
||||
public static boolean isMatchedIp (String filter, String ip) {
|
||||
if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) {
|
||||
return false;
|
||||
}
|
||||
String[] ips = filter.split(";");
|
||||
for (String iStr : ips) {
|
||||
if (isIP(iStr) && iStr.equals(ip)) {
|
||||
return true;
|
||||
} else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) {
|
||||
return true;
|
||||
} else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package com.muyu.common.core.utils.poi;
|
||||
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
|
||||
/**
|
||||
* Excel数据格式处理适配器
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public interface ExcelHandlerAdapter {
|
||||
/**
|
||||
* 格式化
|
||||
*
|
||||
* @param value 单元格数据值
|
||||
* @param args excel注解args参数组
|
||||
* @param cell 单元格对象
|
||||
* @param wb 工作簿对象
|
||||
*
|
||||
* @return 处理后的值
|
||||
*/
|
||||
Object format (Object value, String[] args, Cell cell, Workbook wb);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,324 @@
|
|||
package com.muyu.common.core.utils.reflect;
|
||||
|
||||
import com.muyu.common.core.text.Convert;
|
||||
import com.muyu.common.core.utils.DateUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.poi.ss.usermodel.DateUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数.
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ReflectUtils {
|
||||
private static final String SETTER_PREFIX = "set";
|
||||
|
||||
private static final String GETTER_PREFIX = "get";
|
||||
|
||||
private static final String CGLIB_CLASS_SEPARATOR = "$$";
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class);
|
||||
|
||||
/**
|
||||
* 调用Getter方法.
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeGetter (Object obj, String propertyName) {
|
||||
Object object = obj;
|
||||
for (String name : StringUtils.split(propertyName, ".")) {
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
|
||||
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
|
||||
}
|
||||
return (E) object;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用Setter方法, 仅匹配方法名。
|
||||
* 支持多级,如:对象名.对象名.方法
|
||||
*/
|
||||
public static <E> void invokeSetter (Object obj, String propertyName, E value) {
|
||||
Object object = obj;
|
||||
String[] names = StringUtils.split(propertyName, ".");
|
||||
for (int i = 0 ; i < names.length ; i++) {
|
||||
if (i < names.length - 1) {
|
||||
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
object = invokeMethod(object, getterMethodName, new Class[]{}, new Object[]{});
|
||||
} else {
|
||||
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
|
||||
invokeMethodByName(object, setterMethodName, new Object[]{value});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E getFieldValue (final Object obj, final String fieldName) {
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null) {
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return null;
|
||||
}
|
||||
E result = null;
|
||||
try {
|
||||
result = (E) field.get(obj);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.error("不可能抛出的异常{}", e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数.
|
||||
*/
|
||||
public static <E> void setFieldValue (final Object obj, final String fieldName, final E value) {
|
||||
Field field = getAccessibleField(obj, fieldName);
|
||||
if (field == null) {
|
||||
// throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 ");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
field.set(obj, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.error("不可能抛出的异常: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符.
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用.
|
||||
* 同时匹配方法名+参数类型,
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethod (final Object obj, final String methodName, final Class<?>[] parameterTypes,
|
||||
final Object[] args) {
|
||||
if (obj == null || methodName == null) {
|
||||
return null;
|
||||
}
|
||||
Method method = getAccessibleMethod(obj, methodName, parameterTypes);
|
||||
if (method == null) {
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return (E) method.invoke(obj, args);
|
||||
} catch (Exception e) {
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接调用对象方法, 无视private/protected修饰符,
|
||||
* 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用.
|
||||
* 只匹配函数名,如果有多个同名函数调用第一个。
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> E invokeMethodByName (final Object obj, final String methodName, final Object[] args) {
|
||||
Method method = getAccessibleMethodByName(obj, methodName, args.length);
|
||||
if (method == null) {
|
||||
// 如果为空不报错,直接返回空。
|
||||
logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 ");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 类型转换(将参数数据类型转换为目标方法参数类型)
|
||||
Class<?>[] cs = method.getParameterTypes();
|
||||
for (int i = 0 ; i < cs.length ; i++) {
|
||||
if (args[i] != null && !args[i].getClass().equals(cs[i])) {
|
||||
if (cs[i] == String.class) {
|
||||
args[i] = Convert.toStr(args[i]);
|
||||
if (StringUtils.endsWith((String) args[i], ".0")) {
|
||||
args[i] = StringUtils.substringBefore((String) args[i], ".0");
|
||||
}
|
||||
} else if (cs[i] == Integer.class) {
|
||||
args[i] = Convert.toInt(args[i]);
|
||||
} else if (cs[i] == Long.class) {
|
||||
args[i] = Convert.toLong(args[i]);
|
||||
} else if (cs[i] == Double.class) {
|
||||
args[i] = Convert.toDouble(args[i]);
|
||||
} else if (cs[i] == Float.class) {
|
||||
args[i] = Convert.toFloat(args[i]);
|
||||
} else if (cs[i] == Date.class) {
|
||||
if (args[i] instanceof String) {
|
||||
args[i] = DateUtils.parseDate(args[i]);
|
||||
} else {
|
||||
args[i] = DateUtil.getJavaDate((Double) args[i]);
|
||||
}
|
||||
} else if (cs[i] == boolean.class || cs[i] == Boolean.class) {
|
||||
args[i] = Convert.toBool(args[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (E) method.invoke(obj, args);
|
||||
} catch (Exception e) {
|
||||
String msg = "method: " + method + ", obj: " + obj + ", args: " + args + "";
|
||||
throw convertReflectionExceptionToUnchecked(msg, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
*/
|
||||
public static Field getAccessibleField (final Object obj, final String fieldName) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(fieldName, "fieldName can't be blank");
|
||||
for (Class<?> superClass = obj.getClass() ; superClass != Object.class ; superClass = superClass.getSuperclass()) {
|
||||
try {
|
||||
Field field = superClass.getDeclaredField(fieldName);
|
||||
makeAccessible(field);
|
||||
return field;
|
||||
} catch (NoSuchFieldException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 匹配函数名+参数类型。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethod (final Object obj, final String methodName,
|
||||
final Class<?>... parameterTypes) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass() ; searchType != Object.class ; searchType = searchType.getSuperclass()) {
|
||||
try {
|
||||
Method method = searchType.getDeclaredMethod(methodName, parameterTypes);
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
} catch (NoSuchMethodException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问.
|
||||
* 如向上转型到Object仍无法找到, 返回null.
|
||||
* 只匹配函数名。
|
||||
* 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args)
|
||||
*/
|
||||
public static Method getAccessibleMethodByName (final Object obj, final String methodName, int argsNum) {
|
||||
// 为空不报错。直接返回 null
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
Validate.notBlank(methodName, "methodName can't be blank");
|
||||
for (Class<?> searchType = obj.getClass() ; searchType != Object.class ; searchType = searchType.getSuperclass()) {
|
||||
Method[] methods = searchType.getDeclaredMethods();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) {
|
||||
makeAccessible(method);
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible (Method method) {
|
||||
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers()))
|
||||
&& !method.isAccessible()) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。
|
||||
*/
|
||||
public static void makeAccessible (Field field) {
|
||||
if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())
|
||||
|| Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
|
||||
field.setAccessible(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处
|
||||
* 如无法找到, 返回Object.class.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> getClassGenricType (final Class clazz) {
|
||||
return getClassGenricType(clazz, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过反射, 获得Class定义中声明的父类的泛型参数的类型.
|
||||
* 如无法找到, 返回Object.class.
|
||||
*/
|
||||
public static Class getClassGenricType (final Class clazz, final int index) {
|
||||
Type genType = clazz.getGenericSuperclass();
|
||||
|
||||
if (!(genType instanceof ParameterizedType)) {
|
||||
logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType");
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
|
||||
|
||||
if (index >= params.length || index < 0) {
|
||||
logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
|
||||
+ params.length);
|
||||
return Object.class;
|
||||
}
|
||||
if (!(params[index] instanceof Class)) {
|
||||
logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
return (Class) params[index];
|
||||
}
|
||||
|
||||
public static Class<?> getUserClass (Object instance) {
|
||||
if (instance == null) {
|
||||
throw new RuntimeException("Instance must not be null");
|
||||
}
|
||||
Class clazz = instance.getClass();
|
||||
if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) {
|
||||
Class<?> superClass = clazz.getSuperclass();
|
||||
if (superClass != null && !Object.class.equals(superClass)) {
|
||||
return superClass;
|
||||
}
|
||||
}
|
||||
return clazz;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 将反射时的checked exception转换为unchecked exception.
|
||||
*/
|
||||
public static RuntimeException convertReflectionExceptionToUnchecked (String msg, Exception e) {
|
||||
if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException
|
||||
|| e instanceof NoSuchMethodException) {
|
||||
return new IllegalArgumentException(msg, e);
|
||||
} else if (e instanceof InvocationTargetException) {
|
||||
return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException());
|
||||
}
|
||||
return new RuntimeException(msg, e);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
package com.muyu.common.core.utils.sign;
|
||||
|
||||
/**
|
||||
* Base64工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public final class Base64 {
|
||||
static private final int BASELENGTH = 128;
|
||||
static private final int LOOKUPLENGTH = 64;
|
||||
static private final int TWENTYFOURBITGROUP = 24;
|
||||
static private final int EIGHTBIT = 8;
|
||||
static private final int SIXTEENBIT = 16;
|
||||
static private final int FOURBYTE = 4;
|
||||
static private final int SIGN = -128;
|
||||
static private final char PAD = '=';
|
||||
static final private byte[] base64Alphabet = new byte[BASELENGTH];
|
||||
static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
|
||||
|
||||
static {
|
||||
for (int i = 0 ; i < BASELENGTH ; ++i) {
|
||||
base64Alphabet[i] = -1;
|
||||
}
|
||||
for (int i = 'Z' ; i >= 'A' ; i--) {
|
||||
base64Alphabet[i] = (byte) (i - 'A');
|
||||
}
|
||||
for (int i = 'z' ; i >= 'a' ; i--) {
|
||||
base64Alphabet[i] = (byte) (i - 'a' + 26);
|
||||
}
|
||||
|
||||
for (int i = '9' ; i >= '0' ; i--) {
|
||||
base64Alphabet[i] = (byte) (i - '0' + 52);
|
||||
}
|
||||
|
||||
base64Alphabet['+'] = 62;
|
||||
base64Alphabet['/'] = 63;
|
||||
|
||||
for (int i = 0 ; i <= 25 ; i++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('A' + i);
|
||||
}
|
||||
|
||||
for (int i = 26, j = 0 ; i <= 51 ; i++, j++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('a' + j);
|
||||
}
|
||||
|
||||
for (int i = 52, j = 0 ; i <= 61 ; i++, j++) {
|
||||
lookUpBase64Alphabet[i] = (char) ('0' + j);
|
||||
}
|
||||
lookUpBase64Alphabet[62] = (char) '+';
|
||||
lookUpBase64Alphabet[63] = (char) '/';
|
||||
}
|
||||
|
||||
private static boolean isWhiteSpace (char octect) {
|
||||
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
|
||||
}
|
||||
|
||||
private static boolean isPad (char octect) {
|
||||
return (octect == PAD);
|
||||
}
|
||||
|
||||
private static boolean isData (char octect) {
|
||||
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes hex octects into Base64
|
||||
*
|
||||
* @param binaryData Array containing binaryData
|
||||
*
|
||||
* @return Encoded Base64 array
|
||||
*/
|
||||
public static String encode (byte[] binaryData) {
|
||||
if (binaryData == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int lengthDataBits = binaryData.length * EIGHTBIT;
|
||||
if (lengthDataBits == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
|
||||
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
|
||||
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
|
||||
char encodedData[] = null;
|
||||
|
||||
encodedData = new char[numberQuartet * 4];
|
||||
|
||||
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
|
||||
|
||||
int encodedIndex = 0;
|
||||
int dataIndex = 0;
|
||||
|
||||
for (int i = 0 ; i < numberTriplets ; i++) {
|
||||
b1 = binaryData[dataIndex++];
|
||||
b2 = binaryData[dataIndex++];
|
||||
b3 = binaryData[dataIndex++];
|
||||
|
||||
l = (byte) (b2 & 0x0f);
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
|
||||
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
|
||||
}
|
||||
|
||||
// form integral number of 6-bit groups
|
||||
if (fewerThan24bits == EIGHTBIT) {
|
||||
b1 = binaryData[dataIndex];
|
||||
k = (byte) (b1 & 0x03);
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
} else if (fewerThan24bits == SIXTEENBIT) {
|
||||
b1 = binaryData[dataIndex];
|
||||
b2 = binaryData[dataIndex + 1];
|
||||
l = (byte) (b2 & 0x0f);
|
||||
k = (byte) (b1 & 0x03);
|
||||
|
||||
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
|
||||
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
|
||||
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
|
||||
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
|
||||
encodedData[encodedIndex++] = PAD;
|
||||
}
|
||||
return new String(encodedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes Base64 data into octects
|
||||
*
|
||||
* @param encoded string containing Base64 data
|
||||
*
|
||||
* @return Array containind decoded data.
|
||||
*/
|
||||
public static byte[] decode (String encoded) {
|
||||
if (encoded == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
char[] base64Data = encoded.toCharArray();
|
||||
// remove white spaces
|
||||
int len = removeWhiteSpace(base64Data);
|
||||
|
||||
if (len % FOURBYTE != 0) {
|
||||
return null;// should be divisible by four
|
||||
}
|
||||
|
||||
int numberQuadruple = (len / FOURBYTE);
|
||||
|
||||
if (numberQuadruple == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
byte decodedData[] = null;
|
||||
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
|
||||
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
|
||||
|
||||
int i = 0;
|
||||
int encodedIndex = 0;
|
||||
int dataIndex = 0;
|
||||
decodedData = new byte[(numberQuadruple) * 3];
|
||||
|
||||
for ( ; i < numberQuadruple - 1 ; i++) {
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
|
||||
|| !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) {
|
||||
return null;
|
||||
} // if found "no data" just return null
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||
}
|
||||
|
||||
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
|
||||
return null;// if found "no data" just return null
|
||||
}
|
||||
|
||||
b1 = base64Alphabet[d1];
|
||||
b2 = base64Alphabet[d2];
|
||||
|
||||
d3 = base64Data[dataIndex++];
|
||||
d4 = base64Data[dataIndex++];
|
||||
if (!isData((d3)) || !isData((d4))) {// Check if they are PAD characters
|
||||
if (isPad(d3) && isPad(d4)) {
|
||||
if ((b2 & 0xf) != 0)// last 4 bits should be zero
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] tmp = new byte[i * 3 + 1];
|
||||
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
|
||||
return tmp;
|
||||
} else if (!isPad(d3) && isPad(d4)) {
|
||||
b3 = base64Alphabet[d3];
|
||||
if ((b3 & 0x3) != 0)// last 2 bits should be zero
|
||||
{
|
||||
return null;
|
||||
}
|
||||
byte[] tmp = new byte[i * 3 + 2];
|
||||
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
|
||||
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
return tmp;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else { // No PAD e.g 3cQl
|
||||
b3 = base64Alphabet[d3];
|
||||
b4 = base64Alphabet[d4];
|
||||
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
|
||||
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
|
||||
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
|
||||
|
||||
}
|
||||
return decodedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* remove WhiteSpace from MIME containing encoded Base64 data.
|
||||
*
|
||||
* @param data the byte array of base64 data (with WS)
|
||||
*
|
||||
* @return the new length
|
||||
*/
|
||||
private static int removeWhiteSpace (char[] data) {
|
||||
if (data == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// count characters that's not whitespace
|
||||
int newSize = 0;
|
||||
int len = data.length;
|
||||
for (int i = 0 ; i < len ; i++) {
|
||||
if (!isWhiteSpace(data[i])) {
|
||||
data[newSize++] = data[i];
|
||||
}
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.muyu.common.core.utils.sql;
|
||||
|
||||
import com.muyu.common.core.exception.UtilException;
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* sql操作工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class SqlUtil {
|
||||
/**
|
||||
* 限制orderBy最大长度
|
||||
*/
|
||||
private static final int ORDER_BY_MAX_LENGTH = 500;
|
||||
/**
|
||||
* 定义常用的 sql关键字
|
||||
*/
|
||||
public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()";
|
||||
/**
|
||||
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
|
||||
*/
|
||||
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
|
||||
|
||||
/**
|
||||
* 检查字符,防止注入绕过
|
||||
*/
|
||||
public static String escapeOrderBySql (String value) {
|
||||
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
|
||||
throw new UtilException("参数不符合规范,不能进行查询");
|
||||
}
|
||||
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) {
|
||||
throw new UtilException("参数已超过最大限制,不能进行查询");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证 order by 语法是否符合规范
|
||||
*/
|
||||
public static boolean isValidOrderBySql (String value) {
|
||||
return value.matches(SQL_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL关键字检查
|
||||
*/
|
||||
public static void filterKeyword (String value) {
|
||||
if (StringUtils.isEmpty(value)) {
|
||||
return;
|
||||
}
|
||||
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
|
||||
for (String sqlKeyword : sqlKeywords) {
|
||||
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
|
||||
throw new UtilException("参数存在SQL注入风险");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.muyu.common.core.utils.uuid;
|
||||
|
||||
/**
|
||||
* ID生成器工具类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class IdUtils {
|
||||
/**
|
||||
* 获取随机UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String randomUUID () {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String simpleUUID () {
|
||||
return UUID.randomUUID().toString(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 随机UUID
|
||||
*/
|
||||
public static String fastUUID () {
|
||||
return UUID.fastUUID().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
|
||||
*
|
||||
* @return 简化的UUID,去掉了横线
|
||||
*/
|
||||
public static String fastSimpleUUID () {
|
||||
return UUID.fastUUID().toString(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package com.muyu.common.core.utils.uuid;
|
||||
|
||||
import com.muyu.common.core.utils.DateUtils;
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author muyu 序列生成类
|
||||
*/
|
||||
public class Seq {
|
||||
// 通用序列类型
|
||||
public static final String commSeqType = "COMMON";
|
||||
|
||||
// 上传序列类型
|
||||
public static final String uploadSeqType = "UPLOAD";
|
||||
// 机器标识
|
||||
private static final String machineCode = "A";
|
||||
// 通用接口序列数
|
||||
private static AtomicInteger commSeq = new AtomicInteger(1);
|
||||
// 上传接口序列数
|
||||
private static AtomicInteger uploadSeq = new AtomicInteger(1);
|
||||
|
||||
/**
|
||||
* 获取通用序列号
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId () {
|
||||
return getId(commSeqType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId (String type) {
|
||||
AtomicInteger atomicInt = commSeq;
|
||||
if (uploadSeqType.equals(type)) {
|
||||
atomicInt = uploadSeq;
|
||||
}
|
||||
return getId(atomicInt, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串
|
||||
*
|
||||
* @param atomicInt 序列数
|
||||
* @param length 数值长度
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
public static String getId (AtomicInteger atomicInt, int length) {
|
||||
String result = DateUtils.dateTimeNow();
|
||||
result += machineCode;
|
||||
result += getSeq(atomicInt, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数
|
||||
*
|
||||
* @return 序列值
|
||||
*/
|
||||
private synchronized static String getSeq (AtomicInteger atomicInt, int length) {
|
||||
// 先取值再+1
|
||||
int value = atomicInt.getAndIncrement();
|
||||
|
||||
// 如果更新后值>=10 的 (length)幂次方则重置为1
|
||||
int maxSeq = (int) Math.pow(10, length);
|
||||
if (atomicInt.get() >= maxSeq) {
|
||||
atomicInt.set(1);
|
||||
}
|
||||
// 转字符串,用0左补齐
|
||||
return StringUtils.padl(value, length);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,450 @@
|
|||
package com.muyu.common.core.utils.uuid;
|
||||
|
||||
import com.muyu.common.core.exception.UtilException;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
/**
|
||||
* 提供通用唯一识别码(universally unique identifier)(UUID)实现
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public final class UUID implements java.io.Serializable, Comparable<UUID> {
|
||||
private static final long serialVersionUID = -1185015143654744140L;
|
||||
/**
|
||||
* 此UUID的最高64有效位
|
||||
*/
|
||||
private final long mostSigBits;
|
||||
/**
|
||||
* 此UUID的最低64有效位
|
||||
*/
|
||||
private final long leastSigBits;
|
||||
|
||||
/**
|
||||
* 私有构造
|
||||
*
|
||||
* @param data 数据
|
||||
*/
|
||||
private UUID (byte[] data) {
|
||||
long msb = 0;
|
||||
long lsb = 0;
|
||||
assert data.length == 16 : "data must be 16 bytes in length";
|
||||
for (int i = 0 ; i < 8 ; i++) {
|
||||
msb = (msb << 8) | (data[i] & 0xff);
|
||||
}
|
||||
for (int i = 8 ; i < 16 ; i++) {
|
||||
lsb = (lsb << 8) | (data[i] & 0xff);
|
||||
}
|
||||
this.mostSigBits = msb;
|
||||
this.leastSigBits = lsb;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用指定的数据构造新的 UUID。
|
||||
*
|
||||
* @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
|
||||
* @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
|
||||
*/
|
||||
public UUID (long mostSigBits, long leastSigBits) {
|
||||
this.mostSigBits = mostSigBits;
|
||||
this.leastSigBits = leastSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID fastUUID () {
|
||||
return randomUUID(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID randomUUID () {
|
||||
return randomUUID(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
|
||||
*
|
||||
* @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
|
||||
*
|
||||
* @return 随机生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID randomUUID (boolean isSecure) {
|
||||
final Random ng = isSecure ? Holder.numberGenerator : getRandom();
|
||||
|
||||
byte[] randomBytes = new byte[16];
|
||||
ng.nextBytes(randomBytes);
|
||||
randomBytes[6] &= 0x0f; /* clear version */
|
||||
randomBytes[6] |= 0x40; /* set to version 4 */
|
||||
randomBytes[8] &= 0x3f; /* clear variant */
|
||||
randomBytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new UUID(randomBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
|
||||
*
|
||||
* @param name 用于构造 UUID 的字节数组。
|
||||
*
|
||||
* @return 根据指定数组生成的 {@code UUID}
|
||||
*/
|
||||
public static UUID nameUUIDFromBytes (byte[] name) {
|
||||
MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
throw new InternalError("MD5 not supported");
|
||||
}
|
||||
byte[] md5Bytes = md.digest(name);
|
||||
md5Bytes[6] &= 0x0f; /* clear version */
|
||||
md5Bytes[6] |= 0x30; /* set to version 3 */
|
||||
md5Bytes[8] &= 0x3f; /* clear variant */
|
||||
md5Bytes[8] |= 0x80; /* set to IETF variant */
|
||||
return new UUID(md5Bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
|
||||
*
|
||||
* @param name 指定 {@code UUID} 字符串
|
||||
*
|
||||
* @return 具有指定值的 {@code UUID}
|
||||
*
|
||||
* @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
|
||||
*/
|
||||
public static UUID fromString (String name) {
|
||||
String[] components = name.split("-");
|
||||
if (components.length != 5) {
|
||||
throw new IllegalArgumentException("Invalid UUID string: " + name);
|
||||
}
|
||||
for (int i = 0 ; i < 5 ; i++) {
|
||||
components[i] = "0x" + components[i];
|
||||
}
|
||||
|
||||
long mostSigBits = Long.decode(components[0]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[1]).longValue();
|
||||
mostSigBits <<= 16;
|
||||
mostSigBits |= Long.decode(components[2]).longValue();
|
||||
|
||||
long leastSigBits = Long.decode(components[3]).longValue();
|
||||
leastSigBits <<= 48;
|
||||
leastSigBits |= Long.decode(components[4]).longValue();
|
||||
|
||||
return new UUID(mostSigBits, leastSigBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回指定数字对应的hex值
|
||||
*
|
||||
* @param val 值
|
||||
* @param digits 位
|
||||
*
|
||||
* @return 值
|
||||
*/
|
||||
private static String digits (long val, int digits) {
|
||||
long hi = 1L << (digits * 4);
|
||||
return Long.toHexString(hi | (val & (hi - 1))).substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
|
||||
*
|
||||
* @return {@link SecureRandom}
|
||||
*/
|
||||
public static SecureRandom getSecureRandom () {
|
||||
try {
|
||||
return SecureRandom.getInstance("SHA1PRNG");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new UtilException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机数生成器对象<br>
|
||||
* ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
|
||||
*
|
||||
* @return {@link ThreadLocalRandom}
|
||||
*/
|
||||
public static ThreadLocalRandom getRandom () {
|
||||
return ThreadLocalRandom.current();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的 128 位值中的最低有效 64 位。
|
||||
*
|
||||
* @return 此 UUID 的 128 位值中的最低有效 64 位。
|
||||
*/
|
||||
public long getLeastSignificantBits () {
|
||||
return leastSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的 128 位值中的最高有效 64 位。
|
||||
*
|
||||
* @return 此 UUID 的 128 位值中最高有效 64 位。
|
||||
*/
|
||||
public long getMostSignificantBits () {
|
||||
return mostSigBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
|
||||
* <p>
|
||||
* 版本号具有以下含意:
|
||||
* <ul>
|
||||
* <li>1 基于时间的 UUID
|
||||
* <li>2 DCE 安全 UUID
|
||||
* <li>3 基于名称的 UUID
|
||||
* <li>4 随机生成的 UUID
|
||||
* </ul>
|
||||
*
|
||||
* @return 此 {@code UUID} 的版本号
|
||||
*/
|
||||
public int version () {
|
||||
// Version is bits masked by 0x000000000000F000 in MS long
|
||||
return (int) ((mostSigBits >> 12) & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
|
||||
* <p>
|
||||
* 变体号具有以下含意:
|
||||
* <ul>
|
||||
* <li>0 为 NCS 向后兼容保留
|
||||
* <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类
|
||||
* <li>6 保留,微软向后兼容
|
||||
* <li>7 保留供以后定义使用
|
||||
* </ul>
|
||||
*
|
||||
* @return 此 {@code UUID} 相关联的变体号
|
||||
*/
|
||||
public int variant () {
|
||||
// This field is composed of a varying number of bits.
|
||||
// 0 - - Reserved for NCS backward compatibility
|
||||
// 1 0 - The IETF aka Leach-Salz variant (used by this class)
|
||||
// 1 1 0 Reserved, Microsoft backward compatibility
|
||||
// 1 1 1 Reserved for future definition.
|
||||
return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关联的时间戳值。
|
||||
*
|
||||
* <p>
|
||||
* 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
|
||||
* 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
|
||||
*
|
||||
* <p>
|
||||
* 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||
* 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
|
||||
*/
|
||||
public long timestamp () throws UnsupportedOperationException {
|
||||
checkTimeBase();
|
||||
return (mostSigBits & 0x0FFFL) << 48//
|
||||
| ((mostSigBits >> 16) & 0x0FFFFL) << 32//
|
||||
| mostSigBits >>> 32;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关联的时钟序列值。
|
||||
*
|
||||
* <p>
|
||||
* 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
|
||||
* <p>
|
||||
* {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
|
||||
* UnsupportedOperationException。
|
||||
*
|
||||
* @return 此 {@code UUID} 的时钟序列
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||
*/
|
||||
public int clockSequence () throws UnsupportedOperationException {
|
||||
checkTimeBase();
|
||||
return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与此 UUID 相关的节点值。
|
||||
*
|
||||
* <p>
|
||||
* 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
|
||||
* <p>
|
||||
* 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
|
||||
* 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
|
||||
*
|
||||
* @return 此 {@code UUID} 的节点值
|
||||
*
|
||||
* @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
|
||||
*/
|
||||
public long node () throws UnsupportedOperationException {
|
||||
checkTimeBase();
|
||||
return leastSigBits & 0x0000FFFFFFFFFFFFL;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此{@code UUID} 的字符串表现形式。
|
||||
*
|
||||
* <p>
|
||||
* UUID 的字符串表示形式由此 BNF 描述:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||
* time_low = 4*<hexOctet>
|
||||
* time_mid = 2*<hexOctet>
|
||||
* time_high_and_version = 2*<hexOctet>
|
||||
* variant_and_sequence = 2*<hexOctet>
|
||||
* node = 6*<hexOctet>
|
||||
* hexOctet = <hexDigit><hexDigit>
|
||||
* hexDigit = [0-9a-fA-F]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @return 此{@code UUID} 的字符串表现形式
|
||||
*
|
||||
* @see #toString(boolean)
|
||||
*/
|
||||
@Override
|
||||
public String toString () {
|
||||
return toString(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回此{@code UUID} 的字符串表现形式。
|
||||
*
|
||||
* <p>
|
||||
* UUID 的字符串表示形式由此 BNF 描述:
|
||||
*
|
||||
* <pre>
|
||||
* {@code
|
||||
* UUID = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
|
||||
* time_low = 4*<hexOctet>
|
||||
* time_mid = 2*<hexOctet>
|
||||
* time_high_and_version = 2*<hexOctet>
|
||||
* variant_and_sequence = 2*<hexOctet>
|
||||
* node = 6*<hexOctet>
|
||||
* hexOctet = <hexDigit><hexDigit>
|
||||
* hexDigit = [0-9a-fA-F]
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* </blockquote>
|
||||
*
|
||||
* @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
|
||||
*
|
||||
* @return 此{@code UUID} 的字符串表现形式
|
||||
*/
|
||||
public String toString (boolean isSimple) {
|
||||
final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
|
||||
// time_low
|
||||
builder.append(digits(mostSigBits >> 32, 8));
|
||||
if (false == isSimple) {
|
||||
builder.append('-');
|
||||
}
|
||||
// time_mid
|
||||
builder.append(digits(mostSigBits >> 16, 4));
|
||||
if (false == isSimple) {
|
||||
builder.append('-');
|
||||
}
|
||||
// time_high_and_version
|
||||
builder.append(digits(mostSigBits, 4));
|
||||
if (false == isSimple) {
|
||||
builder.append('-');
|
||||
}
|
||||
// variant_and_sequence
|
||||
builder.append(digits(leastSigBits >> 48, 4));
|
||||
if (false == isSimple) {
|
||||
builder.append('-');
|
||||
}
|
||||
// node
|
||||
builder.append(digits(leastSigBits, 12));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
// Comparison Operations
|
||||
|
||||
/**
|
||||
* 返回此 UUID 的哈希码。
|
||||
*
|
||||
* @return UUID 的哈希码值。
|
||||
*/
|
||||
@Override
|
||||
public int hashCode () {
|
||||
long hilo = mostSigBits ^ leastSigBits;
|
||||
return ((int) (hilo >> 32)) ^ (int) hilo;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------
|
||||
// Private method start
|
||||
|
||||
/**
|
||||
* 将此对象与指定对象比较。
|
||||
* <p>
|
||||
* 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
|
||||
*
|
||||
* @param obj 要与之比较的对象
|
||||
*
|
||||
* @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals (Object obj) {
|
||||
if ((null == obj) || (obj.getClass() != UUID.class)) {
|
||||
return false;
|
||||
}
|
||||
UUID id = (UUID) obj;
|
||||
return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将此 UUID 与指定的 UUID 比较。
|
||||
*
|
||||
* <p>
|
||||
* 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
|
||||
*
|
||||
* @param val 与此 UUID 比较的 UUID
|
||||
*
|
||||
* @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
|
||||
*/
|
||||
@Override
|
||||
public int compareTo (UUID val) {
|
||||
// The ordering is intentionally set up so that the UUIDs
|
||||
// can simply be numerically compared as two numbers
|
||||
return (this.mostSigBits < val.mostSigBits ? -1 : //
|
||||
(this.mostSigBits > val.mostSigBits ? 1 : //
|
||||
(this.leastSigBits < val.leastSigBits ? -1 : //
|
||||
(this.leastSigBits > val.leastSigBits ? 1 : //
|
||||
0))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否为time-based版本UUID
|
||||
*/
|
||||
private void checkTimeBase () {
|
||||
if (version() != 1) {
|
||||
throw new UnsupportedOperationException("Not a time-based UUID");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SecureRandom 的单例
|
||||
*/
|
||||
private static class Holder {
|
||||
static final SecureRandom numberGenerator = getSecureRandom();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package com.muyu.common.core.web.controller;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.muyu.common.core.utils.DateUtils;
|
||||
import com.muyu.common.core.utils.PageUtils;
|
||||
import com.muyu.common.core.domain.Result;
|
||||
import com.muyu.common.core.web.page.TableDataInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.InitBinder;
|
||||
|
||||
import java.beans.PropertyEditorSupport;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* web层通用数据处理
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class BaseController {
|
||||
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
/**
|
||||
* 将前台传递过来的日期格式的字符串,自动转化为Date类型
|
||||
*/
|
||||
@InitBinder
|
||||
public void initBinder (WebDataBinder binder) {
|
||||
// Date 类型转换
|
||||
binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
|
||||
@Override
|
||||
public void setAsText (String text) {
|
||||
setValue(DateUtils.parseDate(text));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求分页数据
|
||||
*/
|
||||
protected void startPage () {
|
||||
PageUtils.startPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理分页的线程变量
|
||||
*/
|
||||
protected void clearPage () {
|
||||
PageUtils.clearPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应请求分页数据
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected <T> Result<TableDataInfo<T>> getDataTable (List<T> list) {
|
||||
return Result.success(
|
||||
TableDataInfo.<T>builder()
|
||||
.total(new PageInfo(list).getTotal())
|
||||
.rows(list)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功
|
||||
*/
|
||||
public Result success () {
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功消息
|
||||
*/
|
||||
public Result success (String message) {
|
||||
return Result.success(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回成功消息
|
||||
*/
|
||||
public Result success (Object data) {
|
||||
return Result.success(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回失败消息
|
||||
*/
|
||||
public Result error () {
|
||||
return Result.error();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回失败消息
|
||||
*/
|
||||
public Result error (String message) {
|
||||
return Result.error(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回警告消息
|
||||
*/
|
||||
public Result warn (String message) {
|
||||
return Result.warn(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应返回结果
|
||||
*
|
||||
* @param rows 影响行数
|
||||
*
|
||||
* @return 操作结果
|
||||
*/
|
||||
protected Result toAjax (int rows) {
|
||||
return rows > 0 ? Result.success() : Result.error();
|
||||
}
|
||||
|
||||
/**
|
||||
* 响应返回结果
|
||||
*
|
||||
* @param result 结果
|
||||
*
|
||||
* @return 操作结果
|
||||
*/
|
||||
protected Result toAjax (boolean result) {
|
||||
return result ? success() : error();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package com.muyu.common.core.web.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Entity基类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BaseEntity implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
@JsonIgnore
|
||||
@TableField(exist = false)
|
||||
private String searchValue;
|
||||
|
||||
/**
|
||||
* 创建者
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新者
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@TableField(exist = false)
|
||||
private Map<String, Object> params;
|
||||
|
||||
public Map<String, Object> getParams () {
|
||||
if (params == null) {
|
||||
params = new HashMap<>();
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
public void setParams (Map<String, Object> params) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.muyu.common.core.web.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tree基类
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TreeEntity extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 父菜单名称
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String parentName;
|
||||
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 显示顺序
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Integer orderNum;
|
||||
|
||||
/**
|
||||
* 祖级列表
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String ancestors;
|
||||
|
||||
/**
|
||||
* 子部门
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private List<?> children = new ArrayList<>();
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package com.muyu.common.core.web.page;
|
||||
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* 分页数据
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class PageDomain {
|
||||
/**
|
||||
* 当前记录起始索引
|
||||
*/
|
||||
private Integer pageNum;
|
||||
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
private Integer pageSize;
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
private String orderByColumn;
|
||||
|
||||
/**
|
||||
* 排序的方向desc或者asc
|
||||
*/
|
||||
private String isAsc = "asc";
|
||||
|
||||
/**
|
||||
* 分页参数合理化
|
||||
*/
|
||||
private Boolean reasonable = true;
|
||||
|
||||
public String getOrderBy () {
|
||||
if (StringUtils.isEmpty(orderByColumn)) {
|
||||
return "";
|
||||
}
|
||||
return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc;
|
||||
}
|
||||
|
||||
public Integer getPageNum () {
|
||||
return pageNum;
|
||||
}
|
||||
|
||||
public void setPageNum (Integer pageNum) {
|
||||
this.pageNum = pageNum;
|
||||
}
|
||||
|
||||
public Integer getPageSize () {
|
||||
return pageSize;
|
||||
}
|
||||
|
||||
public void setPageSize (Integer pageSize) {
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
public String getOrderByColumn () {
|
||||
return orderByColumn;
|
||||
}
|
||||
|
||||
public void setOrderByColumn (String orderByColumn) {
|
||||
this.orderByColumn = orderByColumn;
|
||||
}
|
||||
|
||||
public String getIsAsc () {
|
||||
return isAsc;
|
||||
}
|
||||
|
||||
public void setIsAsc (String isAsc) {
|
||||
if (StringUtils.isNotEmpty(isAsc)) {
|
||||
// 兼容前端排序类型
|
||||
if ("ascending".equals(isAsc)) {
|
||||
isAsc = "asc";
|
||||
} else if ("descending".equals(isAsc)) {
|
||||
isAsc = "desc";
|
||||
}
|
||||
this.isAsc = isAsc;
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean getReasonable () {
|
||||
if (StringUtils.isNull(reasonable)) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
return reasonable;
|
||||
}
|
||||
|
||||
public void setReasonable (Boolean reasonable) {
|
||||
this.reasonable = reasonable;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.muyu.common.core.web.page;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 表格分页数据对象
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TableDataInfo<T> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
private long total;
|
||||
|
||||
/**
|
||||
* 列表数据
|
||||
*/
|
||||
private List<T> rows;
|
||||
|
||||
/**
|
||||
* 消息状态码
|
||||
*/
|
||||
private int code;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.muyu.common.core.web.page;
|
||||
|
||||
import com.muyu.common.core.text.Convert;
|
||||
import com.muyu.common.core.utils.ServletUtils;
|
||||
|
||||
/**
|
||||
* 表格数据处理
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class TableSupport {
|
||||
/**
|
||||
* 当前记录起始索引
|
||||
*/
|
||||
public static final String PAGE_NUM = "pageNum";
|
||||
|
||||
/**
|
||||
* 每页显示记录数
|
||||
*/
|
||||
public static final String PAGE_SIZE = "pageSize";
|
||||
|
||||
/**
|
||||
* 排序列
|
||||
*/
|
||||
public static final String ORDER_BY_COLUMN = "orderByColumn";
|
||||
|
||||
/**
|
||||
* 排序的方向 "desc" 或者 "asc".
|
||||
*/
|
||||
public static final String IS_ASC = "isAsc";
|
||||
|
||||
/**
|
||||
* 分页参数合理化
|
||||
*/
|
||||
public static final String REASONABLE = "reasonable";
|
||||
|
||||
/**
|
||||
* 封装分页对象
|
||||
*/
|
||||
public static PageDomain getPageDomain () {
|
||||
PageDomain pageDomain = new PageDomain();
|
||||
pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1));
|
||||
pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10));
|
||||
pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN));
|
||||
pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC));
|
||||
pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE));
|
||||
return pageDomain;
|
||||
}
|
||||
|
||||
public static PageDomain buildPageRequest () {
|
||||
return getPageDomain();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package com.muyu.common.core.xss;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 自定义xss校验注解
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(value = {ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
|
||||
@Constraint(validatedBy = {XssValidator.class})
|
||||
public @interface Xss {
|
||||
String message ()
|
||||
|
||||
default "不允许任何脚本运行";
|
||||
|
||||
Class<?>[] groups () default {};
|
||||
|
||||
Class<? extends Payload>[] payload () default {};
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.muyu.common.core.xss;
|
||||
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 自定义xss校验注解实现
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
public class XssValidator implements ConstraintValidator<Xss, String> {
|
||||
private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />";
|
||||
|
||||
public static boolean containsHtml (String value) {
|
||||
Pattern pattern = Pattern.compile(HTML_PATTERN);
|
||||
Matcher matcher = pattern.matcher(value);
|
||||
return matcher.matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid (String value, ConstraintValidatorContext constraintValidatorContext) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return true;
|
||||
}
|
||||
return !containsHtml(value);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
com.muyu.common.core.utils.SpringUtils
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>com.muyu</groupId>
|
||||
<artifactId>muyu-common</artifactId>
|
||||
<version>3.6.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>muyu-common-datascope</artifactId>
|
||||
|
||||
<description>
|
||||
muyu-common-datascope权限范围
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- MuYu Common Security -->
|
||||
<dependency>
|
||||
<groupId>com.muyu</groupId>
|
||||
<artifactId>muyu-common-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
|
@ -0,0 +1,28 @@
|
|||
package com.muyu.common.datascope.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 数据权限过滤注解
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface DataScope {
|
||||
/**
|
||||
* 部门表的别名
|
||||
*/
|
||||
public String deptAlias () default "";
|
||||
|
||||
/**
|
||||
* 用户表的别名
|
||||
*/
|
||||
public String userAlias () default "";
|
||||
|
||||
/**
|
||||
* 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@RequiresPermissions获取,多个权限用逗号分隔开来
|
||||
*/
|
||||
public String permission () default "";
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package com.muyu.common.datascope.aspect;
|
||||
|
||||
import com.muyu.common.core.context.SecurityContextHolder;
|
||||
import com.muyu.common.core.text.Convert;
|
||||
import com.muyu.common.core.utils.StringUtils;
|
||||
import com.muyu.common.core.web.domain.BaseEntity;
|
||||
import com.muyu.common.datascope.annotation.DataScope;
|
||||
import com.muyu.common.security.utils.SecurityUtils;
|
||||
import com.muyu.common.system.domain.SysRole;
|
||||
import com.muyu.common.system.domain.SysUser;
|
||||
import com.muyu.common.system.domain.LoginUser;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Before;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 数据过滤处理
|
||||
*
|
||||
* @author muyu
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class DataScopeAspect {
|
||||
/**
|
||||
* 全部数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_ALL = "1";
|
||||
|
||||
/**
|
||||
* 自定数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_CUSTOM = "2";
|
||||
|
||||
/**
|
||||
* 部门数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT = "3";
|
||||
|
||||
/**
|
||||
* 部门及以下数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";
|
||||
|
||||
/**
|
||||
* 仅本人数据权限
|
||||
*/
|
||||
public static final String DATA_SCOPE_SELF = "5";
|
||||
|
||||
/**
|
||||
* 数据权限过滤关键字
|
||||
*/
|
||||
public static final String DATA_SCOPE = "dataScope";
|
||||
|
||||
/**
|
||||
* 数据范围过滤
|
||||
*
|
||||
* @param joinPoint 切点
|
||||
* @param user 用户
|
||||
* @param deptAlias 部门别名
|
||||
* @param userAlias 用户别名
|
||||
* @param permission 权限字符
|
||||
*/
|
||||
public static void dataScopeFilter (JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) {
|
||||
StringBuilder sqlString = new StringBuilder();
|
||||
List<String> conditions = new ArrayList<String>();
|
||||
|
||||
for (SysRole role : user.getRoles()) {
|
||||
String dataScope = role.getDataScope();
|
||||
if (!DATA_SCOPE_CUSTOM.equals(dataScope) && conditions.contains(dataScope)) {
|
||||
continue;
|
||||
}
|
||||
if (StringUtils.isNotEmpty(permission) && StringUtils.isNotEmpty(role.getPermissions())
|
||||
&& !StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) {
|
||||
continue;
|
||||
}
|
||||
if (DATA_SCOPE_ALL.equals(dataScope)) {
|
||||
sqlString = new StringBuilder();
|
||||
conditions.add(dataScope);
|
||||
break;
|
||||
} else if (DATA_SCOPE_CUSTOM.equals(dataScope)) {
|
||||
sqlString.append(StringUtils.format(
|
||||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias,
|
||||
role.getRoleId()));
|
||||
} else if (DATA_SCOPE_DEPT.equals(dataScope)) {
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId()));
|
||||
} else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) {
|
||||
sqlString.append(StringUtils.format(
|
||||
" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )",
|
||||
deptAlias, user.getDeptId(), user.getDeptId()));
|
||||
} else if (DATA_SCOPE_SELF.equals(dataScope)) {
|
||||
if (StringUtils.isNotBlank(userAlias)) {
|
||||
sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId()));
|
||||
} else {
|
||||
// 数据权限为仅本人且没有userAlias别名不查询任何数据
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
|
||||
}
|
||||
}
|
||||
conditions.add(dataScope);
|
||||
}
|
||||
|
||||
// 多角色情况下,所有角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据
|
||||
if (StringUtils.isEmpty(conditions)) {
|
||||
sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias));
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(sqlString.toString())) {
|
||||
Object params = joinPoint.getArgs()[0];
|
||||
if (StringUtils.isNotNull(params) && params instanceof BaseEntity baseEntity) {
|
||||
baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Before("@annotation(controllerDataScope)")
|
||||
public void doBefore (JoinPoint point, DataScope controllerDataScope) throws Throwable {
|
||||
clearDataScope(point);
|
||||
handleDataScope(point, controllerDataScope);
|
||||
}
|
||||
|
||||
protected void handleDataScope (final JoinPoint joinPoint, DataScope controllerDataScope) {
|
||||
// 获取当前的用户
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
if (StringUtils.isNotNull(loginUser)) {
|
||||
SysUser currentUser = loginUser.getSysUser();
|
||||
// 如果是超级管理员,则不过滤数据
|
||||
if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) {
|
||||
String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), SecurityContextHolder.getPermission());
|
||||
dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(),
|
||||
controllerDataScope.userAlias(), permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接权限sql前先清空params.dataScope参数防止注入
|
||||
*/
|
||||
private void clearDataScope (final JoinPoint joinPoint) {
|
||||
Object params = joinPoint.getArgs()[0];
|
||||
if (StringUtils.isNotNull(params) && params instanceof BaseEntity) {
|
||||
BaseEntity baseEntity = (BaseEntity) params;
|
||||
baseEntity.getParams().put(DATA_SCOPE, "");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
com.muyu.common.datascope.aspect.DataScopeAspect
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue