统一 boot 和 cloud 代码
This commit is contained in:
parent
51f96686f8
commit
57330054de
@ -138,6 +138,12 @@
|
|||||||
<artifactId>jsoup</artifactId>
|
<artifactId>jsoup</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test 测试相关 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -41,7 +41,7 @@ public class CommonResult<T> implements Serializable {
|
|||||||
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
|
* 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
|
||||||
*
|
*
|
||||||
* @param result 传入的 result 对象
|
* @param result 传入的 result 对象
|
||||||
* @param <T> 返回的泛型
|
* @param <T> 返回的泛型
|
||||||
* @return 新的 CommonResult 对象
|
* @return 新的 CommonResult 对象
|
||||||
*/
|
*/
|
||||||
public static <T> CommonResult<T> error(CommonResult<?> result) {
|
public static <T> CommonResult<T> error(CommonResult<?> result) {
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package cn.iocoder.yudao.framework.common.pojo;
|
package cn.iocoder.yudao.framework.common.pojo;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,6 +11,9 @@ import java.io.Serializable;
|
|||||||
*
|
*
|
||||||
* 类名加了 ing 的原因是,避免和 ES SortField 重名。
|
* 类名加了 ing 的原因是,避免和 ES SortField 重名。
|
||||||
*/
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class SortingField implements Serializable {
|
public class SortingField implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,30 +34,4 @@ public class SortingField implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private String order;
|
private String order;
|
||||||
|
|
||||||
// 空构造方法,解决反序列化
|
|
||||||
public SortingField() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public SortingField(String field, String order) {
|
|
||||||
this.field = field;
|
|
||||||
this.order = order;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getField() {
|
|
||||||
return field;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SortingField setField(String field) {
|
|
||||||
this.field = field;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getOrder() {
|
|
||||||
return order;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SortingField setOrder(String order) {
|
|
||||||
this.order = order;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public class ServletUtils {
|
|||||||
* 返回 JSON 字符串
|
* 返回 JSON 字符串
|
||||||
*
|
*
|
||||||
* @param response 响应
|
* @param response 响应
|
||||||
* @param object 对象,会序列化成 JSON 字符串
|
* @param object 对象,会序列化成 JSON 字符串
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
|
@SuppressWarnings("deprecation") // 必须使用 APPLICATION_JSON_UTF8_VALUE,否则会乱码
|
||||||
public static void writeJSON(HttpServletResponse response, Object object) {
|
public static void writeJSON(HttpServletResponse response, Object object) {
|
||||||
@ -40,7 +40,7 @@ public class ServletUtils {
|
|||||||
*
|
*
|
||||||
* @param response 响应
|
* @param response 响应
|
||||||
* @param filename 文件名
|
* @param filename 文件名
|
||||||
* @param content 附件内容
|
* @param content 附件内容
|
||||||
*/
|
*/
|
||||||
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
|
public static void writeAttachment(HttpServletResponse response, String filename, byte[] content) throws IOException {
|
||||||
// 设置 header 和 contentType
|
// 设置 header 和 contentType
|
||||||
@ -88,6 +88,8 @@ public class ServletUtils {
|
|||||||
return ServletUtil.getClientIP(request);
|
return ServletUtil.getClientIP(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO @疯狂:terminal 还是从 ServletUtils 里拿,更容易全局治理;
|
||||||
|
|
||||||
public static boolean isJsonRequest(ServletRequest request) {
|
public static boolean isJsonRequest(ServletRequest request) {
|
||||||
return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
|
return StrUtil.startWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE);
|
||||||
}
|
}
|
||||||
@ -107,4 +109,5 @@ public class ServletUtils {
|
|||||||
public static Map<String, String> getParamMap(HttpServletRequest request) {
|
public static Map<String, String> getParamMap(HttpServletRequest request) {
|
||||||
return ServletUtil.getParamMap(request);
|
return ServletUtil.getParamMap(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.common.util.spring;
|
|||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import org.aspectj.lang.JoinPoint;
|
|
||||||
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
import org.aspectj.lang.reflect.MethodSignature;
|
import org.aspectj.lang.reflect.MethodSignature;
|
||||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
@ -87,47 +86,4 @@ public class SpringExpressionUtils {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* JoinPoint 切面 批量解析 EL 表达式,转换 jspl参数
|
|
||||||
*
|
|
||||||
* @param joinPoint 切面点
|
|
||||||
* @param info 返回值
|
|
||||||
* @param expressionStrings EL 表达式数组
|
|
||||||
* @return Map<String, Object> 结果
|
|
||||||
* @author 陈賝
|
|
||||||
* @since 2023/6/18 11:20
|
|
||||||
*/
|
|
||||||
// TODO @chenchen: 这个方法,和 parseExpressions 比较接近,是不是可以合并下;
|
|
||||||
public static Map<String, Object> parseExpression(JoinPoint joinPoint, Object info, List<String> expressionStrings) {
|
|
||||||
// 如果为空,则不进行解析
|
|
||||||
if (CollUtil.isEmpty(expressionStrings)) {
|
|
||||||
return MapUtil.newHashMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第一步,构建解析的上下文 EvaluationContext
|
|
||||||
// 通过 joinPoint 获取被注解方法
|
|
||||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
|
||||||
Method method = signature.getMethod();
|
|
||||||
// 使用 spring 的 ParameterNameDiscoverer 获取方法形参名数组
|
|
||||||
String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
|
|
||||||
// Spring 的表达式上下文对象
|
|
||||||
EvaluationContext context = new StandardEvaluationContext();
|
|
||||||
if (ArrayUtil.isNotEmpty(parameterNames)) {
|
|
||||||
//获取方法参数值
|
|
||||||
Object[] args = joinPoint.getArgs();
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
|
||||||
// 替换 SP EL 里的变量值为实际值, 比如 #user --> user对象
|
|
||||||
context.setVariable(parameterNames[i], args[i]);
|
|
||||||
}
|
|
||||||
context.setVariable("info", info);
|
|
||||||
}
|
|
||||||
// 第二步,逐个参数解析
|
|
||||||
Map<String, Object> result = MapUtil.newHashMap(expressionStrings.size(), true);
|
|
||||||
expressionStrings.forEach(key -> {
|
|
||||||
Object value = EXPRESSION_PARSER.parseExpression(key).getValue(context);
|
|
||||||
result.put(key, value);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import java.lang.annotation.*;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Documented
|
@Documented
|
||||||
@Constraint(
|
@Constraint(
|
||||||
validatedBy = InEnumValidator.class
|
validatedBy = {InEnumValidator.class, InEnumCollectionValidator.class}
|
||||||
)
|
)
|
||||||
public @interface InEnum {
|
public @interface InEnum {
|
||||||
|
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
package cn.iocoder.yudao.framework.common.validation;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintValidator;
|
||||||
|
import javax.validation.ConstraintValidatorContext;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<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(Collection<Integer> list, ConstraintValidatorContext context) {
|
||||||
|
// 校验通过
|
||||||
|
if (CollUtil.containsAll(values, list)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
|
||||||
|
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
|
||||||
|
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
|
||||||
|
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,64 @@
|
|||||||
|
package cn.iocoder.yudao.framework.common.util.collection;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link CollectionUtils} 的单元测试
|
||||||
|
*/
|
||||||
|
public class CollectionUtilsTest {
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
private static class Dog {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
private String name;
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDiffList() {
|
||||||
|
// 准备参数
|
||||||
|
Collection<Dog> oldList = Arrays.asList(
|
||||||
|
new Dog(1, "花花", "hh"),
|
||||||
|
new Dog(2, "旺财", "wc")
|
||||||
|
);
|
||||||
|
Collection<Dog> newList = Arrays.asList(
|
||||||
|
new Dog(null, "花花2", "hh"),
|
||||||
|
new Dog(null, "小白", "xb")
|
||||||
|
);
|
||||||
|
BiFunction<Dog, Dog, Boolean> sameFunc = (oldObj, newObj) -> {
|
||||||
|
boolean same = oldObj.getCode().equals(newObj.getCode());
|
||||||
|
// 如果相等的情况下,需要设置下 id,后续好更新
|
||||||
|
if (same) {
|
||||||
|
newObj.setId(oldObj.getId());
|
||||||
|
}
|
||||||
|
return same;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
List<List<Dog>> result = CollectionUtils.diffList(oldList, newList, sameFunc);
|
||||||
|
// 断言
|
||||||
|
assertEquals(result.size(), 3);
|
||||||
|
// 断言 create
|
||||||
|
assertEquals(result.get(0).size(), 1);
|
||||||
|
assertEquals(result.get(0).get(0), new Dog(null, "小白", "xb"));
|
||||||
|
// 断言 update
|
||||||
|
assertEquals(result.get(1).size(), 1);
|
||||||
|
assertEquals(result.get(1).get(0), new Dog(1, "花花2", "hh"));
|
||||||
|
// 断言 delete
|
||||||
|
assertEquals(result.get(2).size(), 1);
|
||||||
|
assertEquals(result.get(2).get(0), new Dog(2, "旺财", "wc"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -507,7 +507,7 @@ public class DataPermissionDatabaseInterceptor extends JsqlParserSupport impleme
|
|||||||
// 单条规则的条件
|
// 单条规则的条件
|
||||||
String tableName = MyBatisUtils.getTableName(table);
|
String tableName = MyBatisUtils.getTableName(table);
|
||||||
Expression oneExpress = rule.getExpression(tableName, table.getAlias());
|
Expression oneExpress = rule.getExpression(tableName, table.getAlias());
|
||||||
if(oneExpress == null){
|
if (oneExpress == null){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 拼接到 allExpression 中
|
// 拼接到 allExpression 中
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package cn.iocoder.yudao.framework.datapermission.core.util;
|
package cn.iocoder.yudao.framework.dict.core.util;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
|
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||||
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
|
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
|
||||||
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
|
import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO;
|
@ -11,40 +11,40 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
|||||||
*/
|
*/
|
||||||
public interface SmsFrameworkErrorCodeConstants {
|
public interface SmsFrameworkErrorCodeConstants {
|
||||||
|
|
||||||
ErrorCode SMS_UNKNOWN = new ErrorCode(2001000000, "未知错误,需要解析");
|
ErrorCode SMS_UNKNOWN = new ErrorCode(2_001_000_000, "未知错误,需要解析");
|
||||||
|
|
||||||
// ========== 权限 / 限流等相关 2001000100 ==========
|
// ========== 权限 / 限流等相关 2-001-000-100 ==========
|
||||||
|
|
||||||
ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2001000100, "没有发送短信的权限");
|
ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2_001_000_100, "没有发送短信的权限");
|
||||||
ErrorCode SMS_IP_DENY = new ErrorCode(2001000100, "IP 不允许发送短信");
|
ErrorCode SMS_IP_DENY = new ErrorCode(2_001_000_100, "IP 不允许发送短信");
|
||||||
|
|
||||||
// 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
|
// 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
|
||||||
ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2001000102, "指定手机的发送限流");
|
ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2_001_000_102, "指定手机的发送限流");
|
||||||
// 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。
|
// 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。
|
||||||
ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2001000103, "每天的发送限流");
|
ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2_001_000_103, "每天的发送限流");
|
||||||
|
|
||||||
ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2001000104, "短信内容有敏感词");
|
ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2_001_000_104, "短信内容有敏感词");
|
||||||
|
|
||||||
// 腾讯云:为避免骚扰用户,营销短信只允许在8点到22点发送。
|
// 腾讯云:为避免骚扰用户,营销短信只允许在8点到22点发送。
|
||||||
ErrorCode SMS_SEND_MARKET_LIMIT_CONTROL = new ErrorCode(2001000105, "营销短信发送时间限制");
|
ErrorCode SMS_SEND_MARKET_LIMIT_CONTROL = new ErrorCode(2_001_000_105, "营销短信发送时间限制");
|
||||||
|
|
||||||
// ========== 模板相关 2001000200 ==========
|
// ========== 模板相关 2-001-000-200 ==========
|
||||||
ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2001000200, "短信模板不合法"); // 包括短信模板不存在
|
ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2_001_000_200, "短信模板不合法"); // 包括短信模板不存在
|
||||||
ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2001000201, "模板参数不正确");
|
ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2_001_000_201, "模板参数不正确");
|
||||||
|
|
||||||
// ========== 签名相关 2001000300 ==========
|
// ========== 签名相关 2-001-000-300 ==========
|
||||||
ErrorCode SMS_SIGN_INVALID = new ErrorCode(2001000300, "短信签名不可用");
|
ErrorCode SMS_SIGN_INVALID = new ErrorCode(2_001_000_300, "短信签名不可用");
|
||||||
|
|
||||||
// ========== 账户相关 2001000400 ==========
|
// ========== 账户相关 2-001-000-400 ==========
|
||||||
ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2001000400, "账户余额不足");
|
ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2_001_000_400, "账户余额不足");
|
||||||
ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2001000401, "apiKey 不存在");
|
ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2_001_000_401, "apiKey 不存在");
|
||||||
|
|
||||||
// ========== 其它相关 2001000900 开头 ==========
|
// ========== 其它相关 2-001-000-900 开头 ==========
|
||||||
ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2001000900, "请求参数缺失");
|
ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2_001_000_900, "请求参数缺失");
|
||||||
ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2001000901, "手机格式不正确");
|
ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2_001_000_901, "手机格式不正确");
|
||||||
ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2001000902, "手机号在黑名单中");
|
ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2_001_000_902, "手机号在黑名单中");
|
||||||
ErrorCode SMS_APP_ID_INVALID = new ErrorCode(2001000903, "SdkAppId不合法");
|
ErrorCode SMS_APP_ID_INVALID = new ErrorCode(2_001_000_903, "SdkAppId不合法");
|
||||||
|
|
||||||
ErrorCode EXCEPTION = new ErrorCode(2001000999, "调用异常");
|
ErrorCode EXCEPTION = new ErrorCode(2_001_000_999, "调用异常");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.cache.annotation.EnableCaching;
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.data.redis.cache.BatchStrategies;
|
||||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||||
@ -62,10 +63,12 @@ public class YudaoCacheAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate,
|
public RedisCacheManager redisCacheManager(RedisTemplate<String, Object> redisTemplate,
|
||||||
RedisCacheConfiguration redisCacheConfiguration) {
|
RedisCacheConfiguration redisCacheConfiguration,
|
||||||
|
YudaoCacheProperties yudaoCacheProperties) {
|
||||||
// 创建 RedisCacheWriter 对象
|
// 创建 RedisCacheWriter 对象
|
||||||
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
|
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
|
||||||
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
|
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
|
||||||
|
BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));
|
||||||
// 创建 TenantRedisCacheManager 对象
|
// 创建 TenantRedisCacheManager 对象
|
||||||
return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);
|
return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,17 @@ import cn.iocoder.yudao.framework.jackson.core.databind.LocalDateTimeSerializer;
|
|||||||
import cn.iocoder.yudao.framework.jackson.core.databind.NumberSerializer;
|
import cn.iocoder.yudao.framework.jackson.core.databind.NumberSerializer;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@ -27,6 +33,10 @@ public class YudaoJacksonAutoConfiguration {
|
|||||||
// 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型
|
// 新增 Long 类型序列化规则,数值超过 2^53-1,在 JS 会出现精度丢失问题,因此 Long 自动序列化为字符串类型
|
||||||
.addSerializer(Long.class, NumberSerializer.INSTANCE)
|
.addSerializer(Long.class, NumberSerializer.INSTANCE)
|
||||||
.addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
|
.addSerializer(Long.TYPE, NumberSerializer.INSTANCE)
|
||||||
|
.addSerializer(LocalDate.class, LocalDateSerializer.INSTANCE)
|
||||||
|
.addDeserializer(LocalDate.class, LocalDateDeserializer.INSTANCE)
|
||||||
|
.addSerializer(LocalTime.class, LocalTimeSerializer.INSTANCE)
|
||||||
|
.addDeserializer(LocalTime.class, LocalTimeDeserializer.INSTANCE)
|
||||||
// 新增 LocalDateTime 序列化、反序列化规则
|
// 新增 LocalDateTime 序列化、反序列化规则
|
||||||
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
|
.addSerializer(LocalDateTime.class, LocalDateTimeSerializer.INSTANCE)
|
||||||
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
|
.addDeserializer(LocalDateTime.class, LocalDateTimeDeserializer.INSTANCE);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package cn.iocoder.yudao.framework.jackson.core.databind;
|
package cn.iocoder.yudao.framework.jackson.core.databind;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.fasterxml.jackson.core.JsonParser;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||||
|
|
||||||
@ -20,7 +19,7 @@ public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
|
|||||||
public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer();
|
public static final LocalDateTimeDeserializer INSTANCE = new LocalDateTimeDeserializer();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
|
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
||||||
return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault());
|
return LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getValueAsLong()), ZoneId.systemDefault());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,12 +258,12 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
|
|||||||
Long id = fileConfig.getId();
|
Long id = fileConfig.getId();
|
||||||
// mock 获得 Client
|
// mock 获得 Client
|
||||||
FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());
|
FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());
|
||||||
when(fileClientFactory.getFileClient(eq(0L))).thenReturn(fileClient);
|
when(fileClientFactory.getFileClient(eq(fileConfig.getId()))).thenReturn(fileClient);
|
||||||
|
|
||||||
// 调用,并断言
|
// 调用,并断言
|
||||||
assertSame(fileClient, fileConfigService.getMasterFileClient());
|
assertSame(fileClient, fileConfigService.getMasterFileClient());
|
||||||
// 断言缓存
|
// 断言缓存
|
||||||
verify(fileClientFactory).createOrUpdateFileClient(eq(0L), eq(fileConfig.getStorage()),
|
verify(fileClientFactory).createOrUpdateFileClient(eq(fileConfig.getId()), eq(fileConfig.getStorage()),
|
||||||
eq(fileConfig.getConfig()));
|
eq(fileConfig.getConfig()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,33 +18,39 @@ public enum SocialTypeEnum implements IntArrayValuable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gitee
|
* Gitee
|
||||||
* 文档链接:https://gitee.com/api/v5/oauth_doc#/
|
*
|
||||||
|
* @see <a href="https://gitee.com/api/v5/oauth_doc#/">接入文档</a>
|
||||||
*/
|
*/
|
||||||
GITEE(10, "GITEE"),
|
GITEE(10, "GITEE"),
|
||||||
/**
|
/**
|
||||||
* 钉钉
|
* 钉钉
|
||||||
* 文档链接:https://developers.dingtalk.com/document/app/obtain-identity-credentials
|
*
|
||||||
|
* @see <a href="https://developers.dingtalk.com/document/app/obtain-identity-credentials">接入文档</a>
|
||||||
*/
|
*/
|
||||||
DINGTALK(20, "DINGTALK"),
|
DINGTALK(20, "DINGTALK"),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 企业微信
|
* 企业微信
|
||||||
* 文档链接:https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html
|
*
|
||||||
|
* @see <a href="https://xkcoding.com/2019/08/06/use-justauth-integration-wechat-enterprise.html">接入文档</a>
|
||||||
*/
|
*/
|
||||||
WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"),
|
WECHAT_ENTERPRISE(30, "WECHAT_ENTERPRISE"),
|
||||||
/**
|
/**
|
||||||
* 微信公众平台 - 移动端 H5
|
* 微信公众平台 - 移动端 H5
|
||||||
* 文档链接:https://www.cnblogs.com/juewuzhe/p/11905461.html
|
*
|
||||||
|
* @see <a href="https://www.cnblogs.com/juewuzhe/p/11905461.html">接入文档</a>
|
||||||
*/
|
*/
|
||||||
WECHAT_MP(31, "WECHAT_MP"),
|
WECHAT_MP(31, "WECHAT_MP"),
|
||||||
/**
|
/**
|
||||||
* 微信开放平台 - 网站应用 PC 端扫码授权登录
|
* 微信开放平台 - 网站应用 PC 端扫码授权登录
|
||||||
* 文档链接:https://justauth.wiki/guide/oauth/wechat_open/#_2-申请开发者资质认证
|
*
|
||||||
|
* @see <a href="https://justauth.wiki/guide/oauth/wechat_open/#_2-申请开发者资质认证">接入文档</a>
|
||||||
*/
|
*/
|
||||||
WECHAT_OPEN(32, "WECHAT_OPEN"),
|
WECHAT_OPEN(32, "WECHAT_OPEN"),
|
||||||
/**
|
/**
|
||||||
* 微信小程序
|
* 微信小程序
|
||||||
* 文档链接:https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html
|
*
|
||||||
|
* @see <a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html">接入文档</a>
|
||||||
*/
|
*/
|
||||||
WECHAT_MINI_APP(34, "WECHAT_MINI_APP"),
|
WECHAT_MINI_APP(34, "WECHAT_MINI_APP"),
|
||||||
;
|
;
|
||||||
|
@ -6,17 +6,17 @@ import cn.iocoder.yudao.framework.ip.core.Area;
|
|||||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||||
import cn.iocoder.yudao.framework.ip.core.utils.IPUtils;
|
import cn.iocoder.yudao.framework.ip.core.utils.IPUtils;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO;
|
import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeRespVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.ip.vo.AreaNodeSimpleRespVO;
|
|
||||||
import cn.iocoder.yudao.module.system.convert.ip.AreaConvert;
|
import cn.iocoder.yudao.module.system.convert.ip.AreaConvert;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
|
|
||||||
@ -34,28 +34,6 @@ public class AreaController {
|
|||||||
return success(AreaConvert.INSTANCE.convertList(area.getChildren()));
|
return success(AreaConvert.INSTANCE.convertList(area.getChildren()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/get-children")
|
|
||||||
@Operation(summary = "获得地区的下级区域")
|
|
||||||
@Parameter(name = "id", description = "区域编号", required = true, example = "150000")
|
|
||||||
public CommonResult<List<AreaNodeSimpleRespVO>> getChildren(@RequestParam("id") Integer id) {
|
|
||||||
Area area = AreaUtils.getArea(id);
|
|
||||||
Assert.notNull(area, String.format("获取不到 id : %d 的区域", id));
|
|
||||||
return success(AreaConvert.INSTANCE.convertList2(area.getChildren()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4)方法改成 getAreaChildrenList 获得子节点们;5)url 可以已改成 children-list
|
|
||||||
//@芋艿 是不是叫 getAreaListByIds 更合适。 因为不一定是子节点。 用于前端树选择获取缓存数据。 见 <el-tree-select :cache-data="areaCache">
|
|
||||||
@GetMapping("/get-by-ids")
|
|
||||||
@Operation(summary = "通过区域 ids 获得地区列表")
|
|
||||||
@Parameter(name = "ids", description = "区域编号 ids", required = true, example = "1,150000")
|
|
||||||
public CommonResult<List<AreaNodeSimpleRespVO>> getAreaListByIds(@RequestParam("ids") Set<Integer> ids) {
|
|
||||||
List<Area> areaList = new ArrayList<>(ids.size());
|
|
||||||
for (Integer areaId : ids) {
|
|
||||||
areaList.add(AreaUtils.getArea(areaId));
|
|
||||||
}
|
|
||||||
return success(AreaConvert.INSTANCE.convertList2(areaList));
|
|
||||||
}
|
|
||||||
|
|
||||||
@GetMapping("/get-by-ip")
|
@GetMapping("/get-by-ip")
|
||||||
@Operation(summary = "获得 IP 对应的地区名")
|
@Operation(summary = "获得 IP 对应的地区名")
|
||||||
@Parameter(name = "ip", description = "IP", required = true)
|
@Parameter(name = "ip", description = "IP", required = true)
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
package cn.iocoder.yudao.module.system.controller.admin.ip.vo;
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 简洁的地区节点 Response VO")
|
|
||||||
@Data
|
|
||||||
public class AreaNodeSimpleRespVO {
|
|
||||||
|
|
||||||
@Schema(description = "编号", required = true, example = "110000")
|
|
||||||
private Integer id;
|
|
||||||
|
|
||||||
@Schema(description = "名字", required = true, example = "北京")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Schema(description = "是否叶子节点", required = false, example = "false")
|
|
||||||
private Boolean leaf;
|
|
||||||
|
|
||||||
}
|
|
@ -1,4 +1,39 @@
|
|||||||
package cn.iocoder.yudao.module.system.controller.app.dict;
|
package cn.iocoder.yudao.module.system.controller.app.dict;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.module.system.controller.app.dict.vo.AppDictDataRespVO;
|
||||||
|
import cn.iocoder.yudao.module.system.convert.dict.DictDataConvert;
|
||||||
|
import cn.iocoder.yudao.module.system.dal.dataobject.dict.DictDataDO;
|
||||||
|
import cn.iocoder.yudao.module.system.service.dict.DictDataService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Tag(name = "用户 App - 字典数据")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/system/dict-data")
|
||||||
|
@Validated
|
||||||
public class AppDictDataController {
|
public class AppDictDataController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DictDataService dictDataService;
|
||||||
|
|
||||||
|
@GetMapping("/type")
|
||||||
|
@Operation(summary = "根据字典类型查询字典数据信息")
|
||||||
|
@Parameter(name = "type", description = "字典类型", required = true, example = "common_status")
|
||||||
|
public CommonResult<List<AppDictDataRespVO>> getDictDataListByType(@RequestParam("type") String type) {
|
||||||
|
List<DictDataDO> list = dictDataService.getEnabledDictDataListByType(type);
|
||||||
|
return success(DictDataConvert.INSTANCE.convertList03(list));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
package cn.iocoder.yudao.module.system.controller.app.dict.vo;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Schema(description = "用户 App - 字典数据信息 Response VO")
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class AppDictDataRespVO {
|
||||||
|
|
||||||
|
@Schema(description = "字典数据编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "字典标签", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
|
||||||
|
private String label;
|
||||||
|
|
||||||
|
@Schema(description = "字典值", requiredMode = Schema.RequiredMode.REQUIRED, example = "iocoder")
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
@Schema(description = "字典类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "sys_common_sex")
|
||||||
|
private String dictType;
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user