对齐 boot 与 cloud 的代码
This commit is contained in:
parent
1475e9a507
commit
0feb865ef0
@ -29,6 +29,7 @@
|
||||
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
|
||||
<mybatis-plus-generator.version>3.5.3.1</mybatis-plus-generator.version>
|
||||
<dynamic-datasource.version>3.6.1</dynamic-datasource.version>
|
||||
<mybatis-plus-join-boot-starter.version>1.4.5</mybatis-plus-join-boot-starter.version>
|
||||
<redisson.version>3.18.0</redisson.version>
|
||||
<dm8.jdbc.version>8.1.2.141</dm8.jdbc.version>
|
||||
<!-- RPC 相关 -->
|
||||
@ -248,7 +249,7 @@
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency> <!-- TODO 芋艿:说不清楚 -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-webflux-ui</artifactId>
|
||||
<version>${springdoc.version}</version>
|
||||
@ -274,6 +275,11 @@
|
||||
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
|
||||
<version>${dynamic-datasource.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.yulichang</groupId>
|
||||
<artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
|
||||
<version>${mybatis-plus-join-boot-starter.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
|
@ -4,6 +4,7 @@ import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
|
||||
/**
|
||||
* 时间工具类,用于 {@link java.time.LocalDateTime}
|
||||
@ -60,4 +61,24 @@ public class LocalDateTimeUtils {
|
||||
return LocalDateTimeUtil.isIn(LocalDateTime.now(), startTime, endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查时间重叠 不包含日期
|
||||
*
|
||||
* @param startTime1 需要校验的开始时间
|
||||
* @param endTime1 需要校验的结束时间
|
||||
* @param startTime2 校验所需的开始时间
|
||||
* @param endTime2 校验所需的结束时间
|
||||
* @return 是否重叠
|
||||
*/
|
||||
// TODO @puhui999:LocalDateTimeUtil.isOverlap() 是不是可以满足呀?
|
||||
public static boolean checkTimeOverlap(LocalTime startTime1, LocalTime endTime1, LocalTime startTime2, LocalTime endTime2) {
|
||||
// 判断时间是否重叠
|
||||
// 开始时间在已配置时段的结束时间之前 且 结束时间在已配置时段的开始时间之后 []
|
||||
return startTime1.isBefore(endTime2) && endTime1.isAfter(startTime2)
|
||||
// 开始时间在已配置时段的开始时间之前 且 结束时间在已配置时段的开始时间之后 (] 或 ()
|
||||
|| startTime1.isBefore(startTime2) && endTime1.isAfter(startTime2)
|
||||
// 开始时间在已配置时段的结束时间之前 且 结束时间在已配值时段的结束时间之后 [) 或 ()
|
||||
|| startTime1.isBefore(endTime2) && endTime1.isAfter(endTime2);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.spring;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
@ -24,7 +25,13 @@ import java.util.Map;
|
||||
*/
|
||||
public class SpringExpressionUtils {
|
||||
|
||||
/**
|
||||
* spel表达式解析器
|
||||
*/
|
||||
private static final ExpressionParser EXPRESSION_PARSER = new SpelExpressionParser();
|
||||
/**
|
||||
* 参数名发现器
|
||||
*/
|
||||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||
|
||||
private SpringExpressionUtils() {
|
||||
@ -79,4 +86,48 @@ public class SpringExpressionUtils {
|
||||
});
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -63,6 +63,10 @@
|
||||
<artifactId>dynamic-datasource-spring-boot-starter</artifactId> <!-- 多数据源 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.yulichang</groupId>
|
||||
<artifactId>mybatis-plus-join-boot-starter</artifactId> <!-- MyBatis 联表查询 -->
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@ -60,7 +60,7 @@ public class YudaoWebSecurityConfigurerAdapter {
|
||||
/**
|
||||
* 自定义的权限映射 Bean 们
|
||||
*
|
||||
* @see #configure(HttpSecurity)
|
||||
* @see #filterChain(HttpSecurity)
|
||||
*/
|
||||
@Resource
|
||||
private List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers;
|
||||
@ -95,7 +95,7 @@ public class YudaoWebSecurityConfigurerAdapter {
|
||||
* authenticated | 用户登录后可访问
|
||||
*/
|
||||
@Bean
|
||||
protected SecurityFilterChain configure(HttpSecurity httpSecurity) throws Exception {
|
||||
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
// 登出
|
||||
httpSecurity
|
||||
// 开启跨域
|
||||
@ -116,16 +116,16 @@ public class YudaoWebSecurityConfigurerAdapter {
|
||||
httpSecurity
|
||||
// ①:全局共享规则
|
||||
.authorizeRequests()
|
||||
// 静态资源,可匿名访问
|
||||
// 1.1 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/*.html", "/**/*.html", "/**/*.css", "/**/*.js").permitAll()
|
||||
// 设置 @PermitAll 无需认证
|
||||
// 1.2 设置 @PermitAll 无需认证
|
||||
.antMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
|
||||
.antMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
|
||||
.antMatchers(HttpMethod.PUT, permitAllUrls.get(HttpMethod.PUT).toArray(new String[0])).permitAll()
|
||||
.antMatchers(HttpMethod.DELETE, permitAllUrls.get(HttpMethod.DELETE).toArray(new String[0])).permitAll()
|
||||
// 基于 yudao.security.permit-all-urls 无需认证
|
||||
// 1.3 基于 yudao.security.permit-all-urls 无需认证
|
||||
.antMatchers(securityProperties.getPermitAllUrls().toArray(new String[0])).permitAll()
|
||||
// 设置 App API 无需认证
|
||||
// 1.4 设置 App API 无需认证
|
||||
.antMatchers(buildAppApi("/**")).permitAll()
|
||||
// ②:每个项目的自定义规则
|
||||
.and().authorizeRequests(registry -> // 下面,循环设置自定义规则
|
||||
@ -135,7 +135,7 @@ public class YudaoWebSecurityConfigurerAdapter {
|
||||
.anyRequest().authenticated()
|
||||
;
|
||||
|
||||
// 添加 JWT Filter
|
||||
// 添加 Token Filter
|
||||
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
return httpSecurity.build();
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
|
||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
||||
import org.redisson.spring.starter.RedissonAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
@ -41,7 +40,7 @@ public class BaseDbAndRedisUnitTest {
|
||||
|
||||
// Redis 配置类
|
||||
RedisTestConfiguration.class, // Redis 测试配置类,用于启动 RedisServer
|
||||
RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
||||
// RedisAutoConfiguration.class, // Spring Redis 自动配置类
|
||||
YudaoRedisAutoConfiguration.class, // 自己的 Redis 配置类
|
||||
RedissonAutoConfiguration.class, // Redisson 自动高配置类
|
||||
})
|
||||
|
@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.mybatis.config.YudaoMybatisAutoConfiguration;
|
||||
import cn.iocoder.yudao.framework.test.config.SqlInitializationTestConfiguration;
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
|
||||
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration;
|
||||
import com.github.yulichang.autoconfigure.MybatisPlusJoinAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
@ -34,6 +35,7 @@ public class BaseDbUnitTest {
|
||||
// MyBatis 配置类
|
||||
YudaoMybatisAutoConfiguration.class, // 自己的 MyBatis 配置类
|
||||
MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类
|
||||
MybatisPlusJoinAutoConfiguration.class, // MyBatis 的Join配置类
|
||||
})
|
||||
public static class Application {
|
||||
}
|
||||
|
@ -12,15 +12,13 @@ import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
|
||||
import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
||||
@AutoConfiguration
|
||||
@AutoConfigureAfter(YudaoWebAutoConfiguration.class)
|
||||
@AutoConfiguration(after = YudaoWebAutoConfiguration.class)
|
||||
public class YudaoApiLogAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.apilog.core.filter;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.extra.servlet.ServletUtil;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLog;
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
@ -50,8 +49,8 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
|
||||
// 获得开始时间
|
||||
LocalDateTime beginTime = LocalDateTime.now();
|
||||
// 提前获得参数,避免 XssFilter 过滤处理
|
||||
Map<String, String> queryString = ServletUtil.getParamMap(request);
|
||||
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtil.getBody(request) : null;
|
||||
Map<String, String> queryString = ServletUtils.getParamMap(request);
|
||||
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;
|
||||
|
||||
try {
|
||||
// 继续过滤器
|
||||
@ -101,7 +100,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
|
||||
accessLog.setRequestParams(toJsonString(requestParams));
|
||||
accessLog.setRequestMethod(request.getMethod());
|
||||
accessLog.setUserAgent(ServletUtils.getUserAgent(request));
|
||||
accessLog.setUserIp(ServletUtil.getClientIP(request));
|
||||
accessLog.setUserIp(ServletUtils.getClientIP(request));
|
||||
// 持续时间
|
||||
accessLog.setBeginTime(beginTime);
|
||||
accessLog.setEndTime(LocalDateTime.now());
|
||||
|
@ -1 +1,4 @@
|
||||
/**
|
||||
* Web 框架,全局异常、API 日志等
|
||||
*/
|
||||
package cn.iocoder.yudao.framework;
|
||||
|
@ -2,28 +2,22 @@ package cn.iocoder.yudao.framework.web.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
|
||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||
import cn.iocoder.yudao.framework.web.core.clean.JsoupXssCleaner;
|
||||
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
|
||||
import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter;
|
||||
import cn.iocoder.yudao.framework.web.core.filter.DemoFilter;
|
||||
import cn.iocoder.yudao.framework.web.core.filter.XssFilter;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
|
||||
import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
|
||||
import cn.iocoder.yudao.framework.web.core.json.XssStringJsonDeserializer;
|
||||
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
@ -34,7 +28,7 @@ import javax.annotation.Resource;
|
||||
import javax.servlet.Filter;
|
||||
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties({WebProperties.class, XssProperties.class})
|
||||
@EnableConfigurationProperties(WebProperties.class)
|
||||
public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Resource
|
||||
@ -107,15 +101,6 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
|
||||
return createFilterBean(new CacheRequestBodyFilter(), WebFilterOrderEnum.REQUEST_BODY_CACHE_FILTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 XssFilter Bean,解决 Xss 安全问题
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(XssCleaner.class)
|
||||
public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher, XssCleaner xssCleaner) {
|
||||
return createFilterBean(new XssFilter(properties, pathMatcher, xssCleaner), WebFilterOrderEnum.XSS_FILTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 DemoFilter Bean,演示模式
|
||||
*/
|
||||
@ -125,37 +110,19 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
|
||||
return createFilterBean(new DemoFilter(), WebFilterOrderEnum.DEMO_FILTER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Xss 清理者
|
||||
*
|
||||
* @return XssCleaner
|
||||
*/
|
||||
@ConditionalOnMissingBean(XssCleaner.class)
|
||||
@Bean
|
||||
public XssCleaner xssCleaner() {
|
||||
return new JsoupXssCleaner();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 Jackson 的序列化器,用于处理 json 类型参数的 xss 过滤
|
||||
*
|
||||
* @return Jackson2ObjectMapperBuilderCustomizer
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "xssJacksonCustomizer")
|
||||
@ConditionalOnBean(ObjectMapper.class)
|
||||
@ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true")
|
||||
public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssCleaner xssCleaner) {
|
||||
// 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理
|
||||
return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(xssCleaner));
|
||||
}
|
||||
|
||||
|
||||
private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
|
||||
public static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
|
||||
FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
|
||||
bean.setOrder(order);
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 RestTemplate 实例
|
||||
*
|
||||
* @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder}
|
||||
*/
|
||||
@Bean
|
||||
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
|
||||
return restTemplateBuilder.build();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.framework.web.config;
|
||||
package cn.iocoder.yudao.framework.xss.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
@ -0,0 +1,60 @@
|
||||
package cn.iocoder.yudao.framework.xss.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
|
||||
import cn.iocoder.yudao.framework.xss.core.clean.JsoupXssCleaner;
|
||||
import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
|
||||
import cn.iocoder.yudao.framework.xss.core.filter.XssFilter;
|
||||
import cn.iocoder.yudao.framework.xss.core.json.XssStringJsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
import static cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration.createFilterBean;
|
||||
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(XssProperties.class)
|
||||
public class YudaoXssAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* Xss 清理者
|
||||
*
|
||||
* @return XssCleaner
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(XssCleaner.class)
|
||||
public XssCleaner xssCleaner() {
|
||||
return new JsoupXssCleaner();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 Jackson 的序列化器,用于处理 json 类型参数的 xss 过滤
|
||||
*
|
||||
* @return Jackson2ObjectMapperBuilderCustomizer
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = "xssJacksonCustomizer")
|
||||
@ConditionalOnBean(ObjectMapper.class)
|
||||
@ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true")
|
||||
public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssCleaner xssCleaner) {
|
||||
// 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理
|
||||
return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(xssCleaner));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 XssFilter Bean,解决 Xss 安全问题
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnBean(XssCleaner.class)
|
||||
public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher, XssCleaner xssCleaner) {
|
||||
return createFilterBean(new XssFilter(properties, pathMatcher, xssCleaner), WebFilterOrderEnum.XSS_FILTER);
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
package cn.iocoder.yudao.framework.web.core.clean;
|
||||
package cn.iocoder.yudao.framework.xss.core.clean;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.safety.Safelist;
|
||||
|
||||
/**
|
||||
* jsonp 过滤字符串
|
||||
* 基于 JSONP 实现 XSS 过滤字符串
|
||||
*/
|
||||
public class JsoupXssCleaner implements XssCleaner {
|
||||
|
||||
@ -24,21 +24,6 @@ public class JsoupXssCleaner implements XssCleaner {
|
||||
this.baseUri = "";
|
||||
}
|
||||
|
||||
public JsoupXssCleaner(Safelist safelist) {
|
||||
this.safelist = safelist;
|
||||
this.baseUri = "";
|
||||
}
|
||||
|
||||
public JsoupXssCleaner(String baseUri) {
|
||||
this.safelist = buildSafelist();
|
||||
this.baseUri = baseUri;
|
||||
}
|
||||
|
||||
public JsoupXssCleaner(Safelist safelist, String baseUri) {
|
||||
this.safelist = safelist;
|
||||
this.baseUri = baseUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一个 Xss 清理的 Safelist 规则。
|
||||
* 基于 Safelist#relaxed() 的基础上:
|
||||
@ -67,7 +52,6 @@ public class JsoupXssCleaner implements XssCleaner {
|
||||
// 虽然可以重写 WhiteList#isSafeAttribute 来处理,但是有隐患,所以暂时不支持相对路径
|
||||
// WHITELIST.removeProtocols("a", "href", "ftp", "http", "https", "mailto");
|
||||
// WHITELIST.removeProtocols("img", "src", "http", "https");
|
||||
|
||||
return relaxedSafelist;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.yudao.framework.web.core.clean;
|
||||
package cn.iocoder.yudao.framework.xss.core.clean;
|
||||
|
||||
/**
|
||||
* 对 html 文本中的有 Xss 风险的数据进行清理
|
||||
@ -12,4 +12,5 @@ public interface XssCleaner {
|
||||
* @return 清理后的 html
|
||||
*/
|
||||
String clean(String html);
|
||||
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package cn.iocoder.yudao.framework.web.core.filter;
|
||||
package cn.iocoder.yudao.framework.xss.core.filter;
|
||||
|
||||
import cn.iocoder.yudao.framework.web.config.XssProperties;
|
||||
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
|
||||
import cn.iocoder.yudao.framework.xss.config.XssProperties;
|
||||
import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
@ -14,8 +14,6 @@ import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Xss 过滤器
|
||||
* <p>
|
||||
* 对 Xss 不了解的胖友,可以看看 http://www.iocoder.cn/Fight/The-new-girl-asked-me-why-AJAX-requests-are-not-secure-I-did-not-answer/
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.framework.web.core.filter;
|
||||
package cn.iocoder.yudao.framework.xss.core.filter;
|
||||
|
||||
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
|
||||
import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
@ -13,6 +13,7 @@ import java.util.Map;
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class XssRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
private final XssCleaner xssCleaner;
|
||||
|
||||
public XssRequestWrapper(HttpServletRequest request, XssCleaner xssCleaner) {
|
@ -1,6 +1,6 @@
|
||||
package cn.iocoder.yudao.framework.web.core.json;
|
||||
package cn.iocoder.yudao.framework.xss.core.json;
|
||||
|
||||
import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
|
||||
import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 针对 XSS 的基础封装
|
||||
*
|
||||
* XSS 说明:https://tech.meituan.com/2018/09/27/fe-security.html
|
||||
*/
|
||||
package cn.iocoder.yudao.framework.xss;
|
@ -4,16 +4,15 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
|
||||
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
@ -1,5 +1,5 @@
|
||||
### 请求 /infra/file-config/create 接口 => 成功
|
||||
POST {{infaBaseUrl}}/infra/file-config/create
|
||||
POST {{baseUrl}}/infra/file-config/create
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
Authorization: Bearer {{token}}
|
||||
@ -19,7 +19,7 @@ Authorization: Bearer {{token}}
|
||||
}
|
||||
|
||||
### 请求 /infra/file-config/update 接口 => 成功
|
||||
PUT {{infaBaseUrl}}/infra/file-config/update
|
||||
PUT {{baseUrl}}/infra/file-config/update
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
Authorization: Bearer {{token}}
|
||||
@ -39,7 +39,7 @@ Authorization: Bearer {{token}}
|
||||
}
|
||||
|
||||
### 请求 /infra/file-config/test 接口 => 成功
|
||||
GET {{infaBaseUrl}}/infra/file-config/test?id=2
|
||||
GET {{baseUrl}}/infra/file-config/test?id=2
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.infra.controller.admin.logger.vo.apierrorlog;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
@ -9,7 +8,7 @@ import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - API 错误日志 Excel 导出 Request VO,参数和 ApiErrorLogPageReqVO 是一致的")
|
||||
@Schema(description = "管理后台 - API 错误日志 Excel 导出 Request VO,参数和 ApiErrorLogPageReqVO 是一致的")
|
||||
@Data
|
||||
public class ApiErrorLogExportReqVO {
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
### 请求 /infra/redis/get-monitor-info 接口 => 成功
|
||||
GET {{infaBaseUrl}}/infra/redis/get-monitor-info
|
||||
GET {{baseUrl}}/infra/redis/get-monitor-info
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 请求 /infra/redis/get-key-list 接口 => 成功
|
||||
GET {{infaBaseUrl}}/infra/redis/get-key-list
|
||||
GET {{baseUrl}}/infra/redis/get-key-list
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
@ -1,45 +1,19 @@
|
||||
### 请求 /infra/file-config/create 接口 => 成功
|
||||
POST {{baseUrl}}/infra/file-config/create
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
### 请求 /infra/test-demo/get 接口 => 成功
|
||||
GET {{baseUrl}}/infra/test-demo/get?id=106
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 请求 /infra/test-demo/update 接口 => 成功
|
||||
PUT {{baseUrl}}/infra/test-demo/update
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
Content-Type: application/json
|
||||
|
||||
|
||||
{
|
||||
"name": "S3 - 七牛云",
|
||||
"remark": "",
|
||||
"storage": 20,
|
||||
"config": {
|
||||
"accessKey": "b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8",
|
||||
"accessSecret": "kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP",
|
||||
"bucket": "ruoyi-vue-pro",
|
||||
"endpoint": "s3-cn-south-1.qiniucs.com",
|
||||
"domain": "http://test.yudao.iocoder.cn",
|
||||
"region": "oss-cn-beijing"
|
||||
}
|
||||
"id": 106,
|
||||
"name": "测试",
|
||||
"status": "0",
|
||||
"type": 1,
|
||||
"category": 1
|
||||
}
|
||||
|
||||
### 请求 /infra/file-config/update 接口 => 成功
|
||||
PUT {{baseUrl}}/infra/file-config/update
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"name": "S3 - 七牛云",
|
||||
"remark": "",
|
||||
"config": {
|
||||
"accessKey": "b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8",
|
||||
"accessSecret": "kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP",
|
||||
"bucket": "ruoyi-vue-pro",
|
||||
"endpoint": "s3-cn-south-1.qiniucs.com",
|
||||
"domain": "http://test.yudao.iocoder.cn",
|
||||
"region": "oss-cn-beijing"
|
||||
}
|
||||
}
|
||||
|
||||
### 请求 /infra/file-config/test 接口 => 成功
|
||||
GET {{baseUrl}}/infra/file-config/test?id=2
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
@ -1,8 +1,7 @@
|
||||
### 请求 /login 接口 => 成功
|
||||
POST {{systemBaseUrl}}/system/auth/login
|
||||
POST {{baseUrl}}/system/auth/login
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
tag: {{tag}}
|
||||
|
||||
{
|
||||
"username": "admin",
|
||||
@ -12,7 +11,7 @@ tag: {{tag}}
|
||||
}
|
||||
|
||||
### 请求 /login 接口 => 成功(无验证码)
|
||||
POST {{systemBaseUrl}}/system/auth/login
|
||||
POST {{baseUrl}}/system/auth/login
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
@ -22,12 +21,12 @@ tenant-id: {{adminTenentId}}
|
||||
}
|
||||
|
||||
### 请求 /get-permission-info 接口 => 成功
|
||||
GET {{systemBaseUrl}}/system/auth/get-permission-info
|
||||
GET {{baseUrl}}/system/auth/get-permission-info
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
### 请求 /list-menus 接口 => 成功
|
||||
GET {{systemBaseUrl}}/system/auth/list-menus
|
||||
#Authorization: Bearer {{token}}
|
||||
Authorization: Bearer c347026e805e4d99b0d116eae66eda8c
|
||||
GET {{baseUrl}}/system/list-menus
|
||||
Authorization: Bearer {{token}}
|
||||
#Authorization: Bearer a6aa7714a2e44c95aaa8a2c5adc2a67a
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
@ -14,7 +14,7 @@ import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
@Schema(description = "管理后台 - 账号密码登录 Request VO,如果登录并绑定社交用户,需要传递 social 开头的参数")
|
||||
@Schema(description = "管理后台 - 账号密码登录 Request VO,如果登录并绑定社交用户,需要传递 social 开头的参数")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ -34,7 +34,7 @@ public class AuthLoginReqVO {
|
||||
|
||||
// ========== 图片验证码相关 ==========
|
||||
|
||||
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
@Schema(description = "验证码,验证码开启时,需要传递", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "PfcH6mgr8tpXuMWFjvW6YVaqrswIuwmWI5dsVZSg7sGpWtDCUbHuDEXl3cFB1+VvCC/rAkSwK8Fad52FSuncVg==")
|
||||
@NotEmpty(message = "验证码不能为空", groups = CodeEnableGroup.class)
|
||||
private String captchaVerification;
|
||||
@ -54,8 +54,7 @@ public class AuthLoginReqVO {
|
||||
/**
|
||||
* 开启验证码的 Group
|
||||
*/
|
||||
public interface CodeEnableGroup {
|
||||
}
|
||||
public interface CodeEnableGroup {}
|
||||
|
||||
@AssertTrue(message = "授权码不能为空")
|
||||
public boolean isSocialCodeValid() {
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.auth.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
@ -38,7 +37,7 @@ public class AuthPermissionInfoRespVO {
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://www.iocoder.cn/xx.jpg")
|
||||
@Schema(description = "用户头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/xx.jpg")
|
||||
private String avatar;
|
||||
|
||||
}
|
||||
|
@ -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.IPUtils;
|
||||
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 io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
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 org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@ -34,6 +34,28 @@ public class AreaController {
|
||||
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")
|
||||
@Operation(summary = "获得 IP 对应的地区名")
|
||||
@Parameter(name = "ip", description = "IP", required = true)
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.ip.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
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;
|
||||
|
||||
}
|
@ -9,9 +9,9 @@ import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 站内信模版 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
* 站内信模版 Base VO,提供给添加、修改、详细的子 VO 使用
|
||||
* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
|
||||
*/
|
||||
@Data
|
||||
public class NotifyTemplateBaseVO {
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@ -17,7 +16,7 @@ public class OAuth2UserInfoRespVO {
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
||||
@Schema(description = "用户账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
|
||||
private String username;
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
|
||||
|
@ -31,7 +31,7 @@ tenant-id: {{adminTenentId}}
|
||||
roleId=14
|
||||
|
||||
### /role/get 成功
|
||||
GET {{systemBaseUrl}}/system/role/get?id=100
|
||||
GET {{baseUrl}}/system/role/get?id=100
|
||||
Content-Type: application/x-www-form-urlencoded
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
@ -40,6 +40,3 @@ tenant-id: {{adminTenentId}}
|
||||
GET {{baseUrl}}/system/role/page?pageNo=1&pageSize=10
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
||||
###
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.permission.vo.permission;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@ -12,8 +11,8 @@ import java.util.Set;
|
||||
@Data
|
||||
public class PermissionAssignUserRoleReqVO {
|
||||
|
||||
@Schema(description = "角色编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "角色编号不能为空")
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "角色编号列表", example = "1,3,5")
|
||||
|
@ -1,5 +1,5 @@
|
||||
### 请求 /system/sms-template/send-sms 接口 => 成功
|
||||
POST {{systemBaseUrl}}/system/sms-template/send-sms
|
||||
POST {{baseUrl}}/system/sms-template/send-sms
|
||||
Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenentId}}
|
||||
|
@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.controller.admin.sms.vo.log;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
@ -31,14 +30,14 @@ public class SmsLogPageReqVO extends PageParam {
|
||||
private Integer sendStatus;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "开始发送时间")
|
||||
@Schema(description = "发送时间")
|
||||
private LocalDateTime[] sendTime;
|
||||
|
||||
@Schema(description = "接收状态,参见 SmsSendStatusEnum 枚举类", example = "0")
|
||||
@Schema(description = "接收状态,参见 SmsReceiveStatusEnum 枚举类", example = "0")
|
||||
private Integer receiveStatus;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
@Schema(description = "开始接收时间")
|
||||
@Schema(description = "接收时间")
|
||||
private LocalDateTime[] receiveTime;
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.user.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@ -26,7 +25,7 @@ public class UserExportReqVO {
|
||||
@Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
|
||||
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00,2022-07-01 23:59:59]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.user.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
@ -1,12 +1,11 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.user.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Schema(description = "用户精简信息 Response VO")
|
||||
@Schema(description = "管理后台 - 用户精简信息 Response VO")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
|
@ -1,6 +1,7 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.user.vo.user;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@ -16,7 +17,7 @@ public class UserUpdateStatusReqVO {
|
||||
|
||||
@Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "状态不能为空")
|
||||
// @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
|
||||
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
|
||||
private Integer status;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package cn.iocoder.yudao.module.system.controller.app.ip;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.system.controller.app.ip.vo.AppAreaNodeRespVO;
|
||||
import cn.iocoder.yudao.module.system.convert.ip.AreaConvert;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
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.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "用户 App - 地区")
|
||||
@RestController
|
||||
@RequestMapping("/system/area")
|
||||
@Validated
|
||||
public class AppAreaController {
|
||||
|
||||
@GetMapping("/tree")
|
||||
@Operation(summary = "获得地区树")
|
||||
public CommonResult<List<AppAreaNodeRespVO>> getAreaTree() {
|
||||
Area area = AreaUtils.getArea(Area.ID_CHINA);
|
||||
Assert.notNull(area, "获取不到中国");
|
||||
return success(AreaConvert.INSTANCE.convertList3(area.getChildren()));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package cn.iocoder.yudao.module.system.controller.app.ip.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 地区节点 Response VO")
|
||||
@Data
|
||||
public class AppAreaNodeRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "110000")
|
||||
private Integer id;
|
||||
|
||||
@Schema(description = "名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "北京")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 子节点
|
||||
*/
|
||||
private List<AppAreaNodeRespVO> children;
|
||||
|
||||
}
|
@ -1,11 +1,16 @@
|
||||
package cn.iocoder.yudao.module.system.convert.ip;
|
||||
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
|
||||
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.controller.app.ip.vo.AppAreaNodeRespVO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
@Mapper
|
||||
public interface AreaConvert {
|
||||
@ -14,4 +19,15 @@ public interface AreaConvert {
|
||||
|
||||
List<AreaNodeRespVO> convertList(List<Area> list);
|
||||
|
||||
List<AreaNodeSimpleRespVO> convertList2(List<Area> list);
|
||||
|
||||
@Mapping(source = "type", target = "leaf")
|
||||
AreaNodeSimpleRespVO convert(Area area);
|
||||
|
||||
default Boolean convertAreaType(Integer type) {
|
||||
return Objects.equals(AreaTypeEnum.DISTRICT.getType(), type);
|
||||
}
|
||||
|
||||
List<AppAreaNodeRespVO> convertList3(List<Area> list);
|
||||
|
||||
}
|
||||
|
@ -349,7 +349,8 @@ public class AdminAuthServiceImplTest extends BaseDbUnitTest {
|
||||
// 调用
|
||||
authService.logout(token, LoginLogTypeEnum.LOGOUT_SELF.getType());
|
||||
// 校验调用参数
|
||||
verify(loginLogService).createLoginLog(argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
|
||||
verify(loginLogService).createLoginLog(
|
||||
argThat(o -> o.getLogType().equals(LoginLogTypeEnum.LOGOUT_SELF.getType())
|
||||
&& o.getResult().equals(LoginResultEnum.SUCCESS.getResult()))
|
||||
);
|
||||
// 调用,并校验
|
||||
|
@ -33,7 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
public class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private MailSendServiceImpl mailSendService;
|
||||
@ -278,7 +278,8 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest {
|
||||
|
||||
// mock 方法(发送邮件)
|
||||
String messageId = randomString();
|
||||
mailUtilMock.when(() -> MailUtil.send(argThat(mailAccount -> {
|
||||
mailUtilMock.when(() -> MailUtil.send(
|
||||
argThat(mailAccount -> {
|
||||
assertEquals("芋艿 <7685@qq.com>", mailAccount.getFrom());
|
||||
assertTrue(mailAccount.isAuth());
|
||||
assertEquals(account.getUsername(), mailAccount.getUser());
|
||||
|
Loading…
Reference in New Issue
Block a user