完成 TenantApi 的 feign 支持
parent
71930d492e
commit
5598f62456
|
@ -11,9 +11,11 @@ import cn.iocoder.yudao.framework.tenant.core.job.TenantJobHandlerDecorator;
|
|||
import cn.iocoder.yudao.framework.tenant.core.mq.TenantRedisMessageInterceptor;
|
||||
import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkServiceImpl;
|
||||
import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
|
||||
import cn.iocoder.yudao.framework.web.config.WebProperties;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
|
||||
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
|
||||
import org.springframework.beans.BeansException;
|
||||
|
@ -29,6 +31,11 @@ import org.springframework.context.annotation.Configuration;
|
|||
@EnableConfigurationProperties(TenantProperties.class)
|
||||
public class YudaoTenantAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) {
|
||||
return new TenantFrameworkServiceImpl(tenantApi);
|
||||
}
|
||||
|
||||
// ========== AOP ==========
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
package cn.iocoder.yudao.framework.tenant.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.rpc.TenantRequestInterceptor;
|
||||
import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户
|
||||
@EnableFeignClients(clients = TenantApi.class) // 主要是引入相关的 API 服务
|
||||
public class YudaoTenantRpcAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package cn.iocoder.yudao.framework.tenant.core.service;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tenant 框架 Service 实现类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class TenantFrameworkServiceImpl implements TenantFrameworkService {
|
||||
|
||||
private final TenantApi tenantApi;
|
||||
|
||||
@Override
|
||||
public List<Long> getTenantIds() {
|
||||
CommonResult<List<Long>> tenantIdsResult = tenantApi.getTenantIds();
|
||||
tenantIdsResult.checkError();
|
||||
return tenantIdsResult.getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validTenant(Long id) {
|
||||
tenantApi.validTenant(id).checkError();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package cn.iocoder.yudao.module.infra.framework;
|
||||
|
||||
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Configuration
|
||||
public class TmpConfiguration {
|
||||
|
||||
@Bean
|
||||
public TenantFrameworkService tenantFrameworkService() {
|
||||
return new TenantFrameworkService() {
|
||||
@Override
|
||||
public List<Long> getTenantIds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validTenant(Long id) {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -33,6 +33,7 @@ public interface DeptApi {
|
|||
CommonResult<List<DeptRespDTO>> getDepts(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
@GetMapping(PREFIX + "/valid")
|
||||
@ApiOperation("校验部门是否合法")
|
||||
@ApiImplicitParam(name = "ids", value = "部门编号数组", example = "1,2", required = true, allowMultiple = true)
|
||||
CommonResult<Boolean> validDepts(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|||
import cn.iocoder.yudao.module.system.enums.ApiConstants;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
@ -17,7 +18,8 @@ public interface PostApi {
|
|||
String PREFIX = ApiConstants.PREFIX + "/post";
|
||||
|
||||
@GetMapping(PREFIX + "/valid")
|
||||
@ApiImplicitParam(name = "ids", value = "部门编号数组", example = "1,2", required = true, allowMultiple = true)
|
||||
@ApiOperation("校验岗位是否合法")
|
||||
@ApiImplicitParam(name = "ids", value = "岗位编号数组", example = "1,2", required = true, allowMultiple = true)
|
||||
CommonResult<Boolean> validPosts(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|||
import cn.iocoder.yudao.module.system.enums.ApiConstants;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
@ -17,6 +18,7 @@ public interface RoleApi {
|
|||
String PREFIX = ApiConstants.PREFIX + "/role";
|
||||
|
||||
@GetMapping(PREFIX + "/valid")
|
||||
@ApiOperation("校验角色是否合法")
|
||||
@ApiImplicitParam(name = "ids", value = "角色编号数组", example = "1,2", required = true, allowMultiple = true)
|
||||
CommonResult<Boolean> validRoles(@RequestParam("ids") Collection<Long> ids);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import org.springframework.web.bind.annotation.*;
|
|||
import javax.validation.Valid;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory =
|
||||
@Api(tags = "RPC 服务 - 社交用户的")
|
||||
@Api(tags = "RPC 服务 - 社交用户")
|
||||
public interface SocialUserApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/social-user";
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package cn.iocoder.yudao.module.system.api.tenant;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.system.enums.ApiConstants;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory =
|
||||
@Api(tags = "RPC 服务 - 多租户")
|
||||
public interface TenantApi {
|
||||
|
||||
String PREFIX = ApiConstants.PREFIX + "/tenant";
|
||||
|
||||
@GetMapping(PREFIX + "/id-list")
|
||||
@ApiOperation("获得所有租户编号")
|
||||
CommonResult<List<Long>> getTenantIds();
|
||||
|
||||
@GetMapping(PREFIX + "/valid")
|
||||
@ApiOperation("校验租户是否合法")
|
||||
@ApiImplicitParam(name = "id", value = "租户编号", required = true, example = "1024", dataTypeClass = Long.class)
|
||||
CommonResult<Boolean> validTenant(@RequestParam("id") Long id);
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package cn.iocoder.yudao.module.system.api.tenant;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
|
||||
import org.apache.dubbo.config.annotation.DubboService;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.module.system.enums.ApiConstants.VERSION;
|
||||
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@DubboService(version = VERSION) // 提供 Dubbo RPC 接口,给 Dubbo Consumer 调用
|
||||
@Validated
|
||||
public class TenantApiImpl implements TenantApi {
|
||||
|
||||
@Resource
|
||||
private TenantService tenantService;
|
||||
|
||||
@Override
|
||||
public CommonResult<List<Long>> getTenantIds() {
|
||||
return success(tenantService.getTenantIds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> validTenant(Long id) {
|
||||
tenantService.validTenant(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -20,7 +20,7 @@ import java.util.Set;
|
|||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface TenantService extends TenantFrameworkService {
|
||||
public interface TenantService {
|
||||
|
||||
/**
|
||||
* 初始化租户的本地缓存
|
||||
|
@ -120,4 +120,18 @@ public interface TenantService extends TenantFrameworkService {
|
|||
* @param handler 处理器
|
||||
*/
|
||||
void handleTenantMenu(TenantMenuHandler handler);
|
||||
|
||||
/**
|
||||
* 获得所有租户
|
||||
*
|
||||
* @return 租户编号数组
|
||||
*/
|
||||
List<Long> getTenantIds();
|
||||
|
||||
/**
|
||||
* 校验租户是否合法
|
||||
*
|
||||
* @param id 租户编号
|
||||
*/
|
||||
void validTenant(Long id);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ yudao:
|
|||
- /admin-api/system/tenant/get-id-by-name # 基于名字获取租户,不许带租户编号
|
||||
- /admin-api/system/captcha/get-image # 获取图片验证码,和租户无关
|
||||
- /admin-api/system/sms/callback/* # 短信回调接口,无法带上租户编号
|
||||
- /rpc-api/system/tenant/valid # 防止递归。避免调用 /rpc-api/system/tenant/valid 接口时,又去触发 /rpc-api/system/tenant/valid 去校验
|
||||
ignore-tables:
|
||||
- system_tenant
|
||||
- system_tenant_package
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>common</artifactId>
|
||||
<groupId>cn.iocoder.mall</groupId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>common-framework</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.skywalking</groupId>
|
||||
<artifactId>apm-toolkit-trace</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 日志相关 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.8.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具相关 -->
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
|
@ -1,13 +0,0 @@
|
|||
package cn.iocoder.common.framework.core;
|
||||
|
||||
/**
|
||||
* 可生成 Int 数组的接口
|
||||
*/
|
||||
public interface IntArrayValuable {
|
||||
|
||||
/**
|
||||
* @return int 数组
|
||||
*/
|
||||
int[] array();
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package cn.iocoder.common.framework.enums;
|
||||
|
||||
import cn.iocoder.common.framework.core.IntArrayValuable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 通用状态枚举
|
||||
*/
|
||||
public enum CommonStatusEnum implements IntArrayValuable {
|
||||
|
||||
ENABLE(1, "开启"),
|
||||
DISABLE(2, "关闭");
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getValue).toArray();
|
||||
|
||||
/**
|
||||
* 状态值
|
||||
*/
|
||||
private final Integer value;
|
||||
/**
|
||||
* 状态名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
CommonStatusEnum(Integer value, String name) {
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package cn.iocoder.common.framework.enums;
|
||||
|
||||
import cn.iocoder.common.framework.core.IntArrayValuable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* 全局用户类型枚举
|
||||
*/
|
||||
public enum UserTypeEnum implements IntArrayValuable {
|
||||
|
||||
USER(1, "用户"),
|
||||
ADMIN(2, "管理员");
|
||||
|
||||
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray();
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
private final Integer value;
|
||||
/**
|
||||
* 类型名
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
UserTypeEnum(Integer value, String name) {
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] array() {
|
||||
return ARRAYS;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
package cn.iocoder.common.framework.exception;
|
||||
|
||||
import cn.iocoder.common.framework.exception.enums.ServiceErrorCodeRange;
|
||||
|
||||
/**
|
||||
* 错误码对象
|
||||
*
|
||||
* 全局错误码,占用 [0, 999],参见 {@link GlobalException}
|
||||
* 业务异常错误码,占用 [1 000 000 000, +∞),参见 {@link ServiceErrorCodeRange}
|
||||
*
|
||||
* TODO 错误码设计成对象的原因,为未来的 i18 国际化做准备
|
||||
*/
|
||||
public class ErrorCode {
|
||||
|
||||
/**
|
||||
* 错误码
|
||||
*/
|
||||
private final Integer code;
|
||||
/**
|
||||
* 错误提示
|
||||
*/
|
||||
private final String message;
|
||||
|
||||
public ErrorCode(Integer code, String message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Integer getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
package cn.iocoder.common.framework.exception.util;
|
||||
|
||||
import cn.iocoder.common.framework.exception.ErrorCode;
|
||||
import cn.iocoder.common.framework.exception.ServiceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
/**
|
||||
* {@link ServiceException} 工具类
|
||||
*
|
||||
* 目的在于,格式化异常信息提示。
|
||||
* 考虑到 String.format 在参数不正确时会报错,因此使用 {} 作为占位符,并使用 {@link #doFormat(int, String, Object...)} 方法来格式化
|
||||
*
|
||||
* 因为 {@link #MESSAGES} 里面默认是没有异常信息提示的模板的,所以需要使用方自己初始化进去。目前想到的有几种方式:
|
||||
*
|
||||
* 1. 异常提示信息,写在枚举类中,例如说,cn.iocoder.oceans.user.api.constants.ErrorCodeEnum 类 + ServiceExceptionConfiguration
|
||||
* 2. 异常提示信息,写在 .properties 等等配置文件
|
||||
* 3. 异常提示信息,写在 Apollo 等等配置中心中,从而实现可动态刷新
|
||||
* 4. 异常提示信息,存储在 db 等等数据库中,从而实现可动态刷新
|
||||
*/
|
||||
public class ServiceExceptionUtil {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceExceptionUtil.class);
|
||||
|
||||
/**
|
||||
* 错误码提示模板
|
||||
*/
|
||||
private static final ConcurrentMap<Integer, String> MESSAGES = new ConcurrentHashMap<>();
|
||||
|
||||
public static void putAll(Map<Integer, String> messages) {
|
||||
ServiceExceptionUtil.MESSAGES.putAll(messages);
|
||||
}
|
||||
|
||||
public static void put(Integer code, String message) {
|
||||
ServiceExceptionUtil.MESSAGES.put(code, message);
|
||||
}
|
||||
|
||||
public static void delete(Integer code, String message) {
|
||||
ServiceExceptionUtil.MESSAGES.remove(code, message);
|
||||
}
|
||||
|
||||
// ========== 和 ServiceException 的集成 ==========
|
||||
|
||||
public static ServiceException exception(ErrorCode errorCode) {
|
||||
String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMessage());
|
||||
return exception0(errorCode.getCode(), messagePattern);
|
||||
}
|
||||
|
||||
public static ServiceException exception(ErrorCode errorCode, Object... params) {
|
||||
String messagePattern = MESSAGES.getOrDefault(errorCode.getCode(), errorCode.getMessage());
|
||||
return exception0(errorCode.getCode(), messagePattern, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定编号的 ServiceException 的异常
|
||||
*
|
||||
* @param code 编号
|
||||
* @return 异常
|
||||
*/
|
||||
public static ServiceException exception(Integer code) {
|
||||
return exception0(code, MESSAGES.get(code));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建指定编号的 ServiceException 的异常
|
||||
*
|
||||
* @param code 编号
|
||||
* @param params 消息提示的占位符对应的参数
|
||||
* @return 异常
|
||||
*/
|
||||
public static ServiceException exception(Integer code, Object... params) {
|
||||
return exception0(code, MESSAGES.get(code), params);
|
||||
}
|
||||
|
||||
public static ServiceException exception0(Integer code, String messagePattern, Object... params) {
|
||||
String message = doFormat(code, messagePattern, params);
|
||||
return new ServiceException(code, message);
|
||||
}
|
||||
|
||||
// ========== 格式化方法 ==========
|
||||
|
||||
/**
|
||||
* 将错误编号对应的消息使用 params 进行格式化。
|
||||
*
|
||||
* @param code 错误编号
|
||||
* @param messagePattern 消息模版
|
||||
* @param params 参数
|
||||
* @return 格式化后的提示
|
||||
*/
|
||||
private static String doFormat(int code, String messagePattern, Object... params) {
|
||||
StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50);
|
||||
int i = 0;
|
||||
int j;
|
||||
int l;
|
||||
for (l = 0; l < params.length; l++) {
|
||||
j = messagePattern.indexOf("{}", i);
|
||||
if (j == -1) {
|
||||
LOGGER.error("[doFormat][参数过多:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
|
||||
if (i == 0) {
|
||||
return messagePattern;
|
||||
} else {
|
||||
sbuf.append(messagePattern.substring(i, messagePattern.length()));
|
||||
return sbuf.toString();
|
||||
}
|
||||
} else {
|
||||
sbuf.append(messagePattern.substring(i, j));
|
||||
sbuf.append(params[l]);
|
||||
i = j + 2;
|
||||
}
|
||||
}
|
||||
if (messagePattern.indexOf("{}", i) != -1) {
|
||||
LOGGER.error("[doFormat][参数过少:错误码({})|错误内容({})|参数({})", code, messagePattern, params);
|
||||
}
|
||||
sbuf.append(messagePattern.substring(i, messagePattern.length()));
|
||||
return sbuf.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class CollectionUtils {
|
||||
|
||||
public static boolean isEmpty(Collection collection) {
|
||||
return collection == null || collection.isEmpty();
|
||||
}
|
||||
|
||||
public static boolean isEmpty(Object[] arrays) {
|
||||
return arrays == null || arrays.length == 0;
|
||||
}
|
||||
|
||||
public static <T> Set<T> asSet(T... objs) {
|
||||
return new HashSet<>(Arrays.asList(objs));
|
||||
}
|
||||
|
||||
public static <T, U> List<U> convertList(List<T> from, Function<T, U> func) {
|
||||
return from.stream().map(func).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertSet(List<T> from, Function<T, U> func) {
|
||||
return from.stream().map(func).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMap(List<T> from, Function<T, K> keyFunc) {
|
||||
return from.stream().collect(Collectors.toMap(keyFunc, item -> item));
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, V> convertMap(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
return from.stream().collect(Collectors.toMap(keyFunc, valueFunc));
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, List<T>> convertMultiMap(List<T> from, Function<T, K> keyFunc) {
|
||||
return from.stream().collect(Collectors.groupingBy(keyFunc,
|
||||
Collectors.mapping(t -> t, Collectors.toList())));
|
||||
}
|
||||
|
||||
public static <T, K, V> Map<K, List<V>> convertMultiMap(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
return from.stream().collect(Collectors.groupingBy(keyFunc,
|
||||
Collectors.mapping(valueFunc, Collectors.toList())));
|
||||
}
|
||||
|
||||
// 暂时没想好名字,先以 2 结尾噶
|
||||
public static <T, K, V> Map<K, Set<V>> convertMultiMap2(List<T> from, Function<T, K> keyFunc, Function<T, V> valueFunc) {
|
||||
return from.stream().collect(Collectors.groupingBy(keyFunc, Collectors.mapping(valueFunc, Collectors.toSet())));
|
||||
}
|
||||
|
||||
public static boolean containsAny(Collection<?> source, Collection<?> candidates) {
|
||||
return org.springframework.util.CollectionUtils.containsAny(source, candidates);
|
||||
}
|
||||
|
||||
public static <T> T getFirst(List<T> from) {
|
||||
return !isEmpty(from) ? from.get(0) : null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
public class DateUtil {
|
||||
|
||||
/**
|
||||
* 计算当期时间相差的日期
|
||||
*
|
||||
* @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.
|
||||
* @param amount 相差的数值
|
||||
* @return 计算后的日志
|
||||
*/
|
||||
public static Date addDate(int field, int amount) {
|
||||
return addDate(null, field, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算当期时间相差的日期
|
||||
*
|
||||
* @param date 设置时间
|
||||
* @param field 日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.
|
||||
* @param amount 相差的数值
|
||||
* @return 计算后的日志
|
||||
*/
|
||||
public static Date addDate(Date date, int field, int amount) {
|
||||
if (amount == 0) {
|
||||
return date;
|
||||
}
|
||||
Calendar c = Calendar.getInstance();
|
||||
if (date != null) {
|
||||
c.setTime(date);
|
||||
}
|
||||
c.add(field, amount);
|
||||
return c.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param date 时间。若为空,则返回空串
|
||||
* @param pattern 时间格式化
|
||||
* @return 格式化后的时间字符串.
|
||||
*/
|
||||
public static String format(Date date, String pattern) {
|
||||
if (date == null) {
|
||||
return "";
|
||||
}
|
||||
// TODO 芋艿,后面改成缓存
|
||||
return new SimpleDateFormat(pattern).format(date);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定天结束时间
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 获得该日期的开始
|
||||
*/
|
||||
public static Date getDayBegin(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
setCalender(calendar, 0, 0, 0, 0);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当天开始时间
|
||||
*
|
||||
* @return 获得该日期的开始
|
||||
*/
|
||||
public static Date getDayBegin() {
|
||||
return getDayBegin(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定天结束时间
|
||||
*
|
||||
* @param date 日期
|
||||
* @return 获得该日期的结束
|
||||
*/
|
||||
public static Date getDayEnd(Date date) {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
setCalender(calendar, 23, 59, 59, 999);
|
||||
return calendar.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当天结束时间
|
||||
*
|
||||
* @return 获得该日期的开始
|
||||
*/
|
||||
public static Date getDayEnd() {
|
||||
return getDayEnd(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Calendar的小时、分钟、秒、毫秒
|
||||
*
|
||||
* @param calendar 日历
|
||||
* @param hour 小时
|
||||
* @param minute 分钟
|
||||
* @param second 秒
|
||||
* @param milliSecond 毫秒
|
||||
*/
|
||||
public static void setCalender(Calendar calendar, int hour, int minute, int second, int milliSecond) {
|
||||
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
||||
calendar.set(Calendar.MINUTE, minute);
|
||||
calendar.set(Calendar.SECOND, second);
|
||||
calendar.set(Calendar.MILLISECOND, milliSecond);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前时间,是否在该时间范围内
|
||||
*
|
||||
* @param beginTime 开始时间
|
||||
* @param endTime 结束时间
|
||||
* @return 是否在
|
||||
*/
|
||||
public static boolean isBetween(Date beginTime, Date endTime) {
|
||||
Assert.notNull(beginTime, "开始时间不能为空");
|
||||
Assert.notNull(endTime, "结束时间不能为空");
|
||||
Date now = new Date();
|
||||
return beginTime.getTime() <= now.getTime()
|
||||
&& now.getTime() <= endTime.getTime();
|
||||
}
|
||||
|
||||
public static Date max(Date a, Date b) {
|
||||
if (a == null) {
|
||||
return b;
|
||||
}
|
||||
if (b == null) {
|
||||
return a;
|
||||
}
|
||||
return a.compareTo(b) > 0 ? a : b;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import cn.hutool.crypto.digest.BCrypt;
|
||||
|
||||
/**
|
||||
* 加解密工具类
|
||||
*/
|
||||
public class DigestUtils {
|
||||
|
||||
public static String genBcryptSalt() {
|
||||
return BCrypt.gensalt();
|
||||
}
|
||||
|
||||
public static String bcrypt(String key, String salt) {
|
||||
return BCrypt.hashpw(key, salt);
|
||||
}
|
||||
|
||||
// TODO 稍后移到单元测试
|
||||
public static void main(String[] args) {
|
||||
String salt = genBcryptSalt();
|
||||
String password = "buzhidao";
|
||||
System.out.println(salt);
|
||||
System.out.println(bcrypt(password, salt));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
public class ExceptionUtil {
|
||||
|
||||
public static String getMessage(Throwable th) {
|
||||
return ExceptionUtils.getMessage(th);
|
||||
}
|
||||
|
||||
public static String getRootCauseMessage(Throwable th) {
|
||||
return ExceptionUtils.getRootCauseMessage(th);
|
||||
}
|
||||
|
||||
public static String getStackTrace(Throwable th) {
|
||||
return ExceptionUtils.getStackTrace(th);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,319 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class HttpUtil {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(HttpUtil.class);
|
||||
|
||||
/**
|
||||
* Standard Servlet 2.3+ spec request attributes for include URI and paths.
|
||||
* <p>If included via a RequestDispatcher, the current resource will see the
|
||||
* originating request. Its own URI and paths are exposed as request attributes.
|
||||
*/
|
||||
public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
|
||||
public static final String INCLUDE_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.include.context_path";
|
||||
// public static final String INCLUDE_SERVLET_PATH_ATTRIBUTE = "javax.servlet.include.servlet_path";
|
||||
// public static final String INCLUDE_PATH_INFO_ATTRIBUTE = "javax.servlet.include.path_info";
|
||||
// public static final String INCLUDE_QUERY_STRING_ATTRIBUTE = "javax.servlet.include.query_string";
|
||||
//
|
||||
// /**
|
||||
// * Standard Servlet 2.4+ spec request attributes for forward URI and paths.
|
||||
// * <p>If forwarded to via a RequestDispatcher, the current resource will see its
|
||||
// * own URI and paths. The originating URI and paths are exposed as request attributes.
|
||||
// */
|
||||
// public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri";
|
||||
// public static final String FORWARD_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.forward.context_path";
|
||||
// public static final String FORWARD_SERVLET_PATH_ATTRIBUTE = "javax.servlet.forward.servlet_path";
|
||||
// public static final String FORWARD_PATH_INFO_ATTRIBUTE = "javax.servlet.forward.path_info";
|
||||
// public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string";
|
||||
|
||||
/**
|
||||
* Default character encoding to use when <code>request.getCharacterEncoding</code>
|
||||
* returns <code>null</code>, according to the Servlet spec.
|
||||
*
|
||||
* @see javax.servlet.ServletRequest#getCharacterEncoding
|
||||
*/
|
||||
public static final String DEFAULT_CHARACTER_ENCODING = "ISO-8859-1";
|
||||
|
||||
public static String obtainAuthorization(HttpServletRequest request) {
|
||||
String authorization = request.getHeader("Authorization");
|
||||
if (!StringUtils.hasText(authorization)) {
|
||||
return null;
|
||||
}
|
||||
int index = authorization.indexOf("Bearer ");
|
||||
if (index == -1) { // 未找到
|
||||
return null;
|
||||
}
|
||||
return authorization.substring(index + 7).trim();
|
||||
}
|
||||
|
||||
public static String getIp(HttpServletRequest request) {
|
||||
// 基于 X-Forwarded-For 获得
|
||||
String ip = request.getHeader("X-Forwarded-For");
|
||||
if (!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
|
||||
// 多次反向代理后会有多个ip值,第一个 ip 才是真实 ip
|
||||
int index = ip.indexOf(",");
|
||||
if (index != -1) {
|
||||
return ip.substring(0, index);
|
||||
} else {
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
// 基于 X-Real-IP 获得
|
||||
ip = request.getHeader("X-Real-IP");
|
||||
if (!StringUtils.isEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
|
||||
return ip;
|
||||
}
|
||||
// 默认方式
|
||||
return request.getRemoteAddr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request 请求
|
||||
* @return ua
|
||||
*/
|
||||
public static String getUserAgent(HttpServletRequest request) {
|
||||
String ua = request.getHeader("User-Agent");
|
||||
return ua != null ? ua : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据request拼接queryString
|
||||
*
|
||||
* @return queryString
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static String buildQueryString(HttpServletRequest request) {
|
||||
Enumeration<String> es = request.getParameterNames();
|
||||
if (!es.hasMoreElements()) {
|
||||
return "";
|
||||
}
|
||||
String parameterName, parameterValue;
|
||||
StringBuilder params = new StringBuilder();
|
||||
while (es.hasMoreElements()) {
|
||||
parameterName = es.nextElement();
|
||||
parameterValue = request.getParameter(parameterName);
|
||||
params.append(parameterName).append("=").append(parameterValue).append("&");
|
||||
}
|
||||
return params.deleteCharAt(params.length() - 1).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path within the web application for the given request.
|
||||
* Detects include request URL if called within a RequestDispatcher include.
|
||||
* <p/>
|
||||
* For example, for a request to URL
|
||||
* <p/>
|
||||
* <code>http://www.somehost.com/myapp/my/url.jsp</code>,
|
||||
* <p/>
|
||||
* for an application deployed to <code>/mayapp</code> (the application's context path), this method would return
|
||||
* <p/>
|
||||
* <code>/my/url.jsp</code>.
|
||||
*
|
||||
* 该方法,是从 Shiro 源码中,扣出来。add by 芋艿
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the path within the web application
|
||||
*/
|
||||
public static String getPathWithinApplication(HttpServletRequest request) {
|
||||
String contextPath = getContextPath(request);
|
||||
String requestUri = getRequestUri(request);
|
||||
if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
|
||||
// Normal case: URI contains context path.
|
||||
String path = requestUri.substring(contextPath.length());
|
||||
return (StringUtils.hasText(path) ? path : "/");
|
||||
} else {
|
||||
// Special case: rather unusual.
|
||||
return requestUri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the request URI for the given request, detecting an include request
|
||||
* URL if called within a RequestDispatcher include.
|
||||
* <p>As the value returned by <code>request.getRequestURI()</code> is <i>not</i>
|
||||
* decoded by the servlet container, this method will decode it.
|
||||
* <p>The URI that the web container resolves <i>should</i> be correct, but some
|
||||
* containers like JBoss/Jetty incorrectly include ";" strings like ";jsessionid"
|
||||
* in the URI. This method cuts off such incorrect appendices.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the request URI
|
||||
*/
|
||||
public static String getRequestUri(HttpServletRequest request) {
|
||||
String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
|
||||
if (uri == null) {
|
||||
uri = request.getRequestURI();
|
||||
}
|
||||
return normalize(decodeAndCleanUriString(request, uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a relative URI path that may have relative values ("/./",
|
||||
* "/../", and so on ) it it. <strong>WARNING</strong> - This method is
|
||||
* useful only for normalizing application-generated paths. It does not
|
||||
* try to perform security checks for malicious input.
|
||||
* Normalize operations were was happily taken from org.apache.catalina.util.RequestUtil in
|
||||
* Tomcat trunk, r939305
|
||||
*
|
||||
* @param path Relative path to be normalized
|
||||
* @return normalized path
|
||||
*/
|
||||
public static String normalize(String path) {
|
||||
return normalize(path, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a relative URI path that may have relative values ("/./",
|
||||
* "/../", and so on ) it it. <strong>WARNING</strong> - This method is
|
||||
* useful only for normalizing application-generated paths. It does not
|
||||
* try to perform security checks for malicious input.
|
||||
* Normalize operations were was happily taken from org.apache.catalina.util.RequestUtil in
|
||||
* Tomcat trunk, r939305
|
||||
*
|
||||
* @param path Relative path to be normalized
|
||||
* @param replaceBackSlash Should '\\' be replaced with '/'
|
||||
* @return normalized path
|
||||
*/
|
||||
private static String normalize(String path, boolean replaceBackSlash) {
|
||||
|
||||
if (path == null)
|
||||
return null;
|
||||
|
||||
// Create a place for the normalized path
|
||||
String normalized = path;
|
||||
|
||||
if (replaceBackSlash && normalized.indexOf('\\') >= 0)
|
||||
normalized = normalized.replace('\\', '/');
|
||||
|
||||
if (normalized.equals("/."))
|
||||
return "/";
|
||||
|
||||
// Add a leading "/" if necessary
|
||||
if (!normalized.startsWith("/"))
|
||||
normalized = "/" + normalized;
|
||||
|
||||
// Resolve occurrences of "//" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("//");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index) +
|
||||
normalized.substring(index + 1);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/./" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/./");
|
||||
if (index < 0)
|
||||
break;
|
||||
normalized = normalized.substring(0, index) +
|
||||
normalized.substring(index + 2);
|
||||
}
|
||||
|
||||
// Resolve occurrences of "/../" in the normalized path
|
||||
while (true) {
|
||||
int index = normalized.indexOf("/../");
|
||||
if (index < 0)
|
||||
break;
|
||||
if (index == 0)
|
||||
return (null); // Trying to go outside our context
|
||||
int index2 = normalized.lastIndexOf('/', index - 1);
|
||||
normalized = normalized.substring(0, index2) +
|
||||
normalized.substring(index + 3);
|
||||
}
|
||||
|
||||
// Return the normalized path that we have completed
|
||||
return (normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the supplied URI string and strips any extraneous portion after a ';'.
|
||||
*
|
||||
* @param request the incoming HttpServletRequest
|
||||
* @param uri the application's URI string
|
||||
* @return the supplied URI string stripped of any extraneous portion after a ';'.
|
||||
*/
|
||||
private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
|
||||
uri = decodeRequestString(request, uri);
|
||||
int semicolonIndex = uri.indexOf(';');
|
||||
return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the context path for the given request, detecting an include request
|
||||
* URL if called within a RequestDispatcher include.
|
||||
* <p>As the value returned by <code>request.getContextPath()</code> is <i>not</i>
|
||||
* decoded by the servlet container, this method will decode it.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the context path
|
||||
*/
|
||||
public static String getContextPath(HttpServletRequest request) {
|
||||
String contextPath = (String) request.getAttribute(INCLUDE_CONTEXT_PATH_ATTRIBUTE);
|
||||
if (contextPath == null) {
|
||||
contextPath = request.getContextPath();
|
||||
}
|
||||
if ("/".equals(contextPath)) {
|
||||
// Invalid case, but happens for includes on Jetty: silently adapt it.
|
||||
contextPath = "";
|
||||
}
|
||||
return decodeRequestString(request, contextPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given source string with a URLDecoder. The encoding will be taken
|
||||
* from the request, falling back to the default "ISO-8859-1".
|
||||
* <p>The default implementation uses <code>URLDecoder.decode(input, enc)</code>.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @param source the String to decode
|
||||
* @return the decoded String
|
||||
* @see #DEFAULT_CHARACTER_ENCODING
|
||||
* @see javax.servlet.ServletRequest#getCharacterEncoding
|
||||
* @see java.net.URLDecoder#decode(String, String)
|
||||
* @see java.net.URLDecoder#decode(String)
|
||||
*/
|
||||
@SuppressWarnings({"deprecation"})
|
||||
public static String decodeRequestString(HttpServletRequest request, String source) {
|
||||
String enc = determineEncoding(request);
|
||||
try {
|
||||
return URLDecoder.decode(source, enc);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Could not decode request string [" + source + "] with encoding '" + enc +
|
||||
"': falling back to platform default encoding; exception message: " + ex.getMessage());
|
||||
}
|
||||
return URLDecoder.decode(source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the encoding for the given request.
|
||||
* Can be overridden in subclasses.
|
||||
* <p>The default implementation checks the request's
|
||||
* {@link ServletRequest#getCharacterEncoding() character encoding}, and if that
|
||||
* <code>null</code>, falls back to the {@link #DEFAULT_CHARACTER_ENCODING}.
|
||||
*
|
||||
* @param request current HTTP request
|
||||
* @return the encoding for the request (never <code>null</code>)
|
||||
* @see javax.servlet.ServletRequest#getCharacterEncoding()
|
||||
*/
|
||||
protected static String determineEncoding(HttpServletRequest request) {
|
||||
String enc = request.getCharacterEncoding();
|
||||
if (enc == null) {
|
||||
enc = DEFAULT_CHARACTER_ENCODING;
|
||||
}
|
||||
return enc;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class MallUtils {
|
||||
|
||||
/**
|
||||
* 获得链路追踪编号
|
||||
*
|
||||
* 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
|
||||
*
|
||||
* 默认情况下,我们使用 Apache SkyWalking 的 traceId 作为链路追踪编号。当然,可能会存在并未引入 Skywalking 的情况,此时使用 UUID 。
|
||||
*
|
||||
* @return 链路追踪编号
|
||||
*/
|
||||
public static String getTraceId() {
|
||||
// 通过 SkyWalking 获取链路编号
|
||||
try {
|
||||
String traceId = TraceContext.traceId();
|
||||
if (StringUtils.hasText(traceId)) {
|
||||
return traceId;
|
||||
}
|
||||
} catch (Throwable ignore) {}
|
||||
// TODO 芋艿 多次调用会问题
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import cn.hutool.core.lang.UUID;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class StringUtils {
|
||||
|
||||
public static boolean hasText(String str) {
|
||||
return org.springframework.util.StringUtils.hasText(str);
|
||||
}
|
||||
|
||||
public static String join(Collection<?> coll, String delim) {
|
||||
return org.springframework.util.StringUtils.collectionToDelimitedString(coll, delim);
|
||||
}
|
||||
|
||||
public static List<String> split(String toSplit, String delim) {
|
||||
String[] stringArray = org.springframework.util.StringUtils.tokenizeToStringArray(toSplit, delim);
|
||||
return Arrays.asList(stringArray);
|
||||
}
|
||||
|
||||
public static List<Integer> splitToInt(String toSplit, String delim) {
|
||||
String[] stringArray = org.springframework.util.StringUtils.tokenizeToStringArray(toSplit, delim);
|
||||
List<Integer> array = new ArrayList<>(stringArray.length);
|
||||
for (String string : stringArray) {
|
||||
array.add(Integer.valueOf(string));
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public static String substring(String str, int start) {
|
||||
return org.apache.commons.lang3.StringUtils.substring(str, start);
|
||||
}
|
||||
|
||||
public static String uuid(boolean isSimple) {
|
||||
return UUID.fastUUID().toString(isSimple);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 校验工具类
|
||||
*/
|
||||
public class ValidationUtil {
|
||||
|
||||
private static Pattern PATTERN_URL = Pattern.compile("^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
|
||||
|
||||
public static boolean isMobile(String mobile) {
|
||||
if (mobile == null || mobile.length() != 11) {
|
||||
return false;
|
||||
}
|
||||
// TODO 芋艿,后面完善手机校验
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isURL(String url) {
|
||||
return StringUtils.hasText(url)
|
||||
&& PATTERN_URL.matcher(url).matches();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(isURL("http://www.iocoder.cn"));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package cn.iocoder.common.framework.validator;
|
||||
|
||||
import cn.iocoder.common.framework.core.IntArrayValuable;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({
|
||||
ElementType.METHOD,
|
||||
ElementType.FIELD,
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.CONSTRUCTOR,
|
||||
ElementType.PARAMETER,
|
||||
ElementType.TYPE_USE
|
||||
})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Constraint(
|
||||
validatedBy = InEnumValidator.class
|
||||
)
|
||||
public @interface InEnum {
|
||||
|
||||
/**
|
||||
* @return 实现 EnumValuable 接口的
|
||||
*/
|
||||
Class<? extends IntArrayValuable> value();
|
||||
|
||||
String message() default "必须在指定范围 {value}";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package cn.iocoder.common.framework.validator;
|
||||
|
||||
import cn.iocoder.common.framework.core.IntArrayValuable;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
|
||||
|
||||
private List<Integer> values;
|
||||
|
||||
@Override
|
||||
public void initialize(InEnum annotation) {
|
||||
IntArrayValuable[] values = annotation.value().getEnumConstants();
|
||||
if (values.length == 0) {
|
||||
this.values = Collections.emptyList();
|
||||
} else {
|
||||
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(Integer value, ConstraintValidatorContext context) {
|
||||
// 为空时,默认不校验,即认为通过
|
||||
if (value == null) {
|
||||
return true;
|
||||
}
|
||||
// 校验通过
|
||||
if (values.contains(value)) {
|
||||
return true;
|
||||
}
|
||||
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
|
||||
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
|
||||
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
|
||||
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package cn.iocoder.common.framework.validator;
|
||||
|
||||
import javax.validation.Constraint;
|
||||
import javax.validation.Payload;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Target({
|
||||
ElementType.METHOD,
|
||||
ElementType.FIELD,
|
||||
ElementType.ANNOTATION_TYPE,
|
||||
ElementType.CONSTRUCTOR,
|
||||
ElementType.PARAMETER,
|
||||
ElementType.TYPE_USE
|
||||
})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Constraint(
|
||||
validatedBy = MobileValidator.class
|
||||
)
|
||||
public @interface Mobile {
|
||||
|
||||
String message() default "手机号格式不正确";
|
||||
|
||||
Class<?>[] groups() default {};
|
||||
|
||||
Class<? extends Payload>[] payload() default {};
|
||||
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package cn.iocoder.common.framework.validator;
|
||||
|
||||
import cn.iocoder.common.framework.util.StringUtils;
|
||||
import cn.iocoder.common.framework.util.ValidationUtil;
|
||||
|
||||
import javax.validation.ConstraintValidator;
|
||||
import javax.validation.ConstraintValidatorContext;
|
||||
|
||||
public class MobileValidator implements ConstraintValidator<Mobile, String> {
|
||||
|
||||
@Override
|
||||
public void initialize(Mobile annotation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(String value, ConstraintValidatorContext context) {
|
||||
// 如果手机号为空,默认不校验,即校验通过
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return true;
|
||||
}
|
||||
// 校验手机
|
||||
return ValidationUtil.isMobile(value);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package cn.iocoder.common.framework.util;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
public class DateUtilTest {
|
||||
|
||||
@Test
|
||||
@Ignore // 暂时忽略,测试不通过,add by 芋艿
|
||||
public void testAddDate() {
|
||||
Assert.assertNull(DateUtil.addDate(0, 0));
|
||||
Assert.assertEquals(new Date(1_778_410_800_000L), DateUtil.addDate(
|
||||
new Date(1_515_585_600_000L), 2, 100));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // 暂时忽略,测试不通过,add by 芋艿
|
||||
public void testFormat() {
|
||||
Assert.assertEquals("", DateUtil.format(null, null));
|
||||
Assert.assertEquals("2018-01-10:12:00:00", DateUtil.format(
|
||||
new Date(1_515_585_600_000L), "yyyy-MM-dd:HH:mm:ss"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // 暂时忽略,测试不通过,add by 芋艿
|
||||
public void testGetDayBegin() {
|
||||
Assert.assertNull(DateUtil.getDayBegin(null));
|
||||
Assert.assertEquals(new Date(1_515_542_400_000L),
|
||||
DateUtil.getDayBegin(new Date(1_515_585_600_000L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // 暂时忽略,测试不通过,add by 芋艿
|
||||
public void testGetDayEnd() {
|
||||
Assert.assertNull(DateUtil.getDayEnd(null));
|
||||
Assert.assertEquals(new Date(1_515_628_799_999L), DateUtil.getDayEnd(
|
||||
new Date(1_515_585_600_000L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsBetween() {
|
||||
Assert.assertTrue(DateUtil.isBetween(DateUtil.getDayBegin(),
|
||||
DateUtil.getDayEnd()));
|
||||
Assert.assertFalse(DateUtil.isBetween(
|
||||
DateUtil.getDayBegin(new Date(0L)),
|
||||
DateUtil.getDayEnd(new Date(100_000L))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetCalender() {
|
||||
final GregorianCalendar calendar = new GregorianCalendar();
|
||||
DateUtil.setCalender(calendar, 2, 30, 50, 0);
|
||||
|
||||
Assert.assertEquals(2, calendar.getTime().getHours());
|
||||
Assert.assertEquals(30, calendar.getTime().getMinutes());
|
||||
Assert.assertEquals(50, calendar.getTime().getSeconds());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue