mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2025-09-24 07:19:46 +08:00
⛄发布 4.4.0 正式版
This commit is contained in:
@ -1,22 +0,0 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 匿名访问不鉴权注解
|
||||
*
|
||||
* @author ruoyi
|
||||
* @deprecated 将在后续版本使用Sa-Token注解 {@link SaIgnore} 代替
|
||||
*/
|
||||
@Deprecated
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Anonymous {
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.ruoyi.common.jackson.DictDataJsonSerializer;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 字典数据映射注解
|
||||
*
|
||||
* @author itino
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@JacksonAnnotationsInside
|
||||
@JsonSerialize(using = DictDataJsonSerializer.class)
|
||||
public @interface DictDataMapper {
|
||||
|
||||
/**
|
||||
* 设置字典的type值 (如: sys_user_sex)
|
||||
*/
|
||||
String dictType() default "";
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package com.ruoyi.common.constant;
|
||||
|
||||
/**
|
||||
* 返回状态码
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface HttpStatus {
|
||||
/**
|
||||
* 操作成功
|
||||
*/
|
||||
int SUCCESS = 200;
|
||||
|
||||
/**
|
||||
* 对象创建成功
|
||||
*/
|
||||
int CREATED = 201;
|
||||
|
||||
/**
|
||||
* 请求已经被接受
|
||||
*/
|
||||
int ACCEPTED = 202;
|
||||
|
||||
/**
|
||||
* 操作已经执行成功,但是没有返回数据
|
||||
*/
|
||||
int NO_CONTENT = 204;
|
||||
|
||||
/**
|
||||
* 资源已被移除
|
||||
*/
|
||||
int MOVED_PERM = 301;
|
||||
|
||||
/**
|
||||
* 重定向
|
||||
*/
|
||||
int SEE_OTHER = 303;
|
||||
|
||||
/**
|
||||
* 资源没有被修改
|
||||
*/
|
||||
int NOT_MODIFIED = 304;
|
||||
|
||||
/**
|
||||
* 参数列表错误(缺少,格式不匹配)
|
||||
*/
|
||||
int BAD_REQUEST = 400;
|
||||
|
||||
/**
|
||||
* 未授权
|
||||
*/
|
||||
int UNAUTHORIZED = 401;
|
||||
|
||||
/**
|
||||
* 访问受限,授权过期
|
||||
*/
|
||||
int FORBIDDEN = 403;
|
||||
|
||||
/**
|
||||
* 资源,服务未找到
|
||||
*/
|
||||
int NOT_FOUND = 404;
|
||||
|
||||
/**
|
||||
* 不允许的http方法
|
||||
*/
|
||||
int BAD_METHOD = 405;
|
||||
|
||||
/**
|
||||
* 资源冲突,或者资源被锁
|
||||
*/
|
||||
int CONFLICT = 409;
|
||||
|
||||
/**
|
||||
* 不支持的数据,媒体类型
|
||||
*/
|
||||
int UNSUPPORTED_TYPE = 415;
|
||||
|
||||
/**
|
||||
* 系统内部错误
|
||||
*/
|
||||
int ERROR = 500;
|
||||
|
||||
/**
|
||||
* 接口未实现
|
||||
*/
|
||||
int NOT_IMPLEMENTED = 501;
|
||||
|
||||
/**
|
||||
* 系统警告消息
|
||||
*/
|
||||
int WARN = 601;
|
||||
}
|
@ -2,6 +2,8 @@ package com.ruoyi.common.core.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -23,6 +25,7 @@ public class BaseEntity implements Serializable {
|
||||
/**
|
||||
* 搜索值
|
||||
*/
|
||||
@JsonIgnore
|
||||
@TableField(exist = false)
|
||||
private String searchValue;
|
||||
|
||||
@ -53,6 +56,7 @@ public class BaseEntity implements Serializable {
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
@TableField(exist = false)
|
||||
private Map<String, Object> params = new HashMap<>();
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.ruoyi.common.core.domain;
|
||||
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@ -67,6 +68,27 @@ public class R<T> implements Serializable {
|
||||
return restResult(null, code, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回警告消息
|
||||
*
|
||||
* @param msg 返回内容
|
||||
* @return 警告消息
|
||||
*/
|
||||
public static <T> R<T> warn(String msg) {
|
||||
return restResult(null, HttpStatus.WARN, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回警告消息
|
||||
*
|
||||
* @param msg 返回内容
|
||||
* @param data 数据对象
|
||||
* @return 警告消息
|
||||
*/
|
||||
public static <T> R<T> warn(String msg, T data) {
|
||||
return restResult(data, HttpStatus.WARN, msg);
|
||||
}
|
||||
|
||||
private static <T> R<T> restResult(T data, int code, String msg) {
|
||||
R<T> r = new R<>();
|
||||
r.setCode(code);
|
||||
|
@ -12,7 +12,6 @@ import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 构建可重复读取inputStream的request
|
||||
@ -27,7 +26,7 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
|
||||
request.setCharacterEncoding(Constants.UTF8);
|
||||
response.setCharacterEncoding(Constants.UTF8);
|
||||
|
||||
body = IoUtil.readUtf8(request.getInputStream()).getBytes(StandardCharsets.UTF_8);
|
||||
body = IoUtil.readBytes(request.getInputStream(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.ruoyi.common.filter;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HtmlUtil;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@ -50,7 +51,7 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
}
|
||||
|
||||
// 为空,直接返回
|
||||
String json = IoUtil.read(super.getInputStream(), StandardCharsets.UTF_8);
|
||||
String json = StrUtil.str(IoUtil.readBytes(super.getInputStream(), false), StandardCharsets.UTF_8);
|
||||
if (StringUtils.isEmpty(json)) {
|
||||
return super.getInputStream();
|
||||
}
|
||||
@ -88,7 +89,6 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
/**
|
||||
* 是否是Json请求
|
||||
*
|
||||
*/
|
||||
public boolean isJsonRequest() {
|
||||
String header = super.getHeader(HttpHeaders.CONTENT_TYPE);
|
||||
|
@ -2,6 +2,7 @@ package com.ruoyi.common.helper;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
@ -80,18 +81,12 @@ public class LoginHelper {
|
||||
LoginUser loginUser = getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
String loginId = StpUtil.getLoginIdAsString();
|
||||
String userId = null;
|
||||
for (UserType value : UserType.values()) {
|
||||
if (StringUtils.contains(loginId, value.getUserType())) {
|
||||
String[] strs = StringUtils.split(loginId, JOIN_CODE);
|
||||
// 用户id在总是在最后
|
||||
userId = strs[strs.length - 1];
|
||||
}
|
||||
}
|
||||
if (StringUtils.isBlank(userId)) {
|
||||
String[] strs = StringUtils.split(loginId, JOIN_CODE);
|
||||
if (!ArrayUtil.containsAny(strs, UserType.values())) {
|
||||
throw new UtilException("登录用户: LoginId异常 => " + loginId);
|
||||
}
|
||||
return Long.parseLong(userId);
|
||||
// 用户id在总是在最后
|
||||
return Long.parseLong(strs[strs.length - 1]);
|
||||
}
|
||||
return loginUser.getUserId();
|
||||
}
|
||||
|
@ -0,0 +1,56 @@
|
||||
package com.ruoyi.common.jackson;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.BeanProperty;
|
||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
|
||||
import com.ruoyi.common.annotation.DictDataMapper;
|
||||
import com.ruoyi.common.core.service.DictService;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeansException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 字典数据json序列化工具
|
||||
*
|
||||
* @author itino
|
||||
*/
|
||||
@Slf4j
|
||||
public class DictDataJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
|
||||
|
||||
private String dictType;
|
||||
|
||||
@Override
|
||||
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
try {
|
||||
DictService dictService = SpringUtils.getBean(DictService.class);
|
||||
if (ObjectUtil.isNotNull(dictService)) {
|
||||
String label = dictService.getDictLabel(dictType, value);
|
||||
gen.writeString(StringUtils.isNotBlank(label) ? label : value);
|
||||
} else {
|
||||
gen.writeString(value);
|
||||
}
|
||||
} catch (BeansException e) {
|
||||
log.error("字典数据未查到, 采用默认处理 => {}", e.getMessage());
|
||||
gen.writeString(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
|
||||
DictDataMapper anno = property.getAnnotation(DictDataMapper.class);
|
||||
if (Objects.nonNull(anno) && StrUtil.isNotBlank(anno.dictType())) {
|
||||
this.dictType = anno.dictType();
|
||||
return this;
|
||||
}
|
||||
return prov.findValueSerializer(property.getType(), property);
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
@ -19,6 +20,9 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 客户端工具类
|
||||
@ -70,6 +74,31 @@ public class ServletUtils extends ServletUtil {
|
||||
return Convert.toBool(getRequest().getParameter(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @param request 请求对象{@link ServletRequest}
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String[]> getParams(ServletRequest request) {
|
||||
final Map<String, String[]> map = request.getParameterMap();
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得所有请求参数
|
||||
*
|
||||
* @param request 请求对象{@link ServletRequest}
|
||||
* @return Map
|
||||
*/
|
||||
public static Map<String, String> getParamMap(ServletRequest request) {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
|
||||
params.put(entry.getKey(), StringUtils.join(entry.getValue(), ","));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取request
|
||||
*/
|
||||
|
@ -327,6 +327,17 @@ public class RedisUtils {
|
||||
return rMap.getAll(rMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存Map的key列表
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @return key列表
|
||||
*/
|
||||
public static <T> Set<String> getCacheMapKeySet(final String key) {
|
||||
RMap<String, T> rMap = CLIENT.getMap(key);
|
||||
return rMap.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 往Hash中存入数据
|
||||
*
|
||||
|
@ -46,7 +46,7 @@ public class TestBatchController extends BaseController {
|
||||
testDemo.setValue("测试新增");
|
||||
list.add(testDemo);
|
||||
}
|
||||
return toAjax(testDemoMapper.insertBatch(list) ? 1 : 0);
|
||||
return toAjax(testDemoMapper.insertBatch(list));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,7 +74,7 @@ public class TestBatchController extends BaseController {
|
||||
testDemo.setId(null);
|
||||
}
|
||||
}
|
||||
return toAjax(testDemoMapper.insertOrUpdateBatch(list) ? 1 : 0);
|
||||
return toAjax(testDemoMapper.insertOrUpdateBatch(list));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,7 +119,7 @@ public class TestDemoController extends BaseController {
|
||||
// 使用校验工具对标 @Validated(AddGroup.class) 注解
|
||||
// 用于在非 Controller 的地方校验对象
|
||||
ValidatorUtils.validate(bo, AddGroup.class);
|
||||
return toAjax(iTestDemoService.insertByBo(bo) ? 1 : 0);
|
||||
return toAjax(iTestDemoService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,7 +130,7 @@ public class TestDemoController extends BaseController {
|
||||
@RepeatSubmit
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) {
|
||||
return toAjax(iTestDemoService.updateByBo(bo) ? 1 : 0);
|
||||
return toAjax(iTestDemoService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -143,6 +143,6 @@ public class TestDemoController extends BaseController {
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
|
||||
return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ public class TestTreeController extends BaseController {
|
||||
@RepeatSubmit
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) {
|
||||
return toAjax(iTestTreeService.insertByBo(bo) ? 1 : 0);
|
||||
return toAjax(iTestTreeService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,7 +89,7 @@ public class TestTreeController extends BaseController {
|
||||
@RepeatSubmit
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) {
|
||||
return toAjax(iTestTreeService.updateByBo(bo) ? 1 : 0);
|
||||
return toAjax(iTestTreeService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,6 +102,6 @@ public class TestTreeController extends BaseController {
|
||||
@DeleteMapping("/{ids}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ids) {
|
||||
return toAjax(iTestTreeService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0);
|
||||
return toAjax(iTestTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import org.aspectj.lang.annotation.Aspect;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.HandlerMapping;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@ -92,7 +91,6 @@ public class LogAspect {
|
||||
SpringUtils.getBean(OperLogService.class).recordOper(operLog);
|
||||
} catch (Exception exp) {
|
||||
// 记录本地异常日志
|
||||
log.error("==前置通知异常==");
|
||||
log.error("异常信息:{}", exp.getMessage());
|
||||
exp.printStackTrace();
|
||||
}
|
||||
@ -135,8 +133,9 @@ public class LogAspect {
|
||||
String params = argsArrayToString(joinPoint.getArgs());
|
||||
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
|
||||
} else {
|
||||
Map<?, ?> paramsMap = (Map<?, ?>) ServletUtils.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
|
||||
operLog.setOperParam(StringUtils.substring(paramsMap.toString(), 0, 2000));
|
||||
Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
|
||||
MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);
|
||||
operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,63 +0,0 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
|
||||
import com.alibaba.druid.util.Utils;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.servlet.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* druid 配置多数据源
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Configuration
|
||||
public class DruidConfig {
|
||||
|
||||
/**
|
||||
* 去除监控页面底部的广告
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
|
||||
public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) {
|
||||
// 获取web监控页面的参数
|
||||
DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
|
||||
// 提取common.js的配置路径
|
||||
String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
|
||||
String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
|
||||
final String filePath = "support/http/resources/js/common.js";
|
||||
// 创建filter进行过滤
|
||||
Filter filter = new Filter() {
|
||||
@Override
|
||||
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
chain.doFilter(request, response);
|
||||
// 重置缓冲区,响应头不会被重置
|
||||
// response.resetBuffer();
|
||||
// 获取common.js
|
||||
String text = Utils.readFromResource(filePath);
|
||||
// 正则替换banner, 除去底部的广告信息
|
||||
text = text.replaceAll("<a.*?banner\"></a><br/>", "");
|
||||
text = text.replaceAll("powered.*?shrek.wang</a>", "");
|
||||
response.getWriter().write(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
};
|
||||
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
|
||||
registrationBean.setFilter(filter);
|
||||
registrationBean.addUrlPatterns(commonJsPattern);
|
||||
return registrationBean;
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import com.ruoyi.framework.interceptor.PlusWebInvokeTimeInterceptor;
|
||||
import com.yomahub.tlog.web.interceptor.TLogWebInterceptor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
@ -22,8 +20,6 @@ public class ResourcesConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 全局链路跟踪拦截器
|
||||
registry.addInterceptor(new TLogWebInterceptor());
|
||||
// 全局访问性能拦截
|
||||
registry.addInterceptor(new PlusWebInvokeTimeInterceptor());
|
||||
}
|
||||
|
@ -5,8 +5,6 @@ import cn.dev33.satoken.jwt.StpLogicJwtForSimple;
|
||||
import cn.dev33.satoken.router.SaRouter;
|
||||
import cn.dev33.satoken.stp.StpLogic;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import com.ruoyi.framework.config.properties.ExcludeUrlProperties;
|
||||
import com.ruoyi.framework.config.properties.SecurityProperties;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -34,14 +32,10 @@ public class SaTokenConfig implements WebMvcConfigurer {
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册路由拦截器,自定义验证规则
|
||||
registry.addInterceptor(new SaInterceptor(handler -> {
|
||||
// 自定义注解 @Anonymous 匿名访问配置,后续版本将删除
|
||||
ExcludeUrlProperties excludeUrlProperties = SpringUtils.getBean(ExcludeUrlProperties.class);
|
||||
// 登录验证 -- 排除多个路径
|
||||
SaRouter
|
||||
// 获取所有的
|
||||
.match("/**")
|
||||
// 排除下不需要拦截的(每次匹配)
|
||||
.notMatch(excludeUrlProperties.getExcludes())
|
||||
// 对未排除的路径进行检查
|
||||
.check(() -> {
|
||||
// 检查是否登录 是否有token
|
||||
|
@ -1,18 +0,0 @@
|
||||
package com.ruoyi.framework.config;
|
||||
|
||||
import com.yomahub.tlog.springboot.TLogWebAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 整合 TLog 框架配置
|
||||
*
|
||||
* @author Lion Li
|
||||
* @since 3.3.0
|
||||
*/
|
||||
@Configuration
|
||||
// 排除 web 自动配置 自定义实现
|
||||
@EnableAutoConfiguration(exclude = TLogWebAutoConfiguration.class)
|
||||
public class TLogConfig {
|
||||
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
package com.ruoyi.framework.config.properties;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import cn.dev33.satoken.interceptor.SaInterceptor;
|
||||
import cn.hutool.core.util.ReUtil;
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import lombok.Getter;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.util.pattern.PathPattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 设置注解允许匿名访问的url
|
||||
*
|
||||
* @author Lion Li
|
||||
* @deprecated 将在后续版本使用Sa-Token注解 {@link SaIgnore} 代替,
|
||||
* 底层过滤方法详见 {@link SaInterceptor#preHandle(HttpServletRequest, HttpServletResponse, Object)}
|
||||
*/
|
||||
@Deprecated
|
||||
@Lazy
|
||||
@Component
|
||||
public class ExcludeUrlProperties implements InitializingBean {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}");
|
||||
|
||||
@Getter
|
||||
private final List<String> excludes = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
String asterisk = "*";
|
||||
RequestMappingHandlerMapping mapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
|
||||
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
|
||||
|
||||
map.keySet().forEach(info -> {
|
||||
HandlerMethod handlerMethod = map.get(info);
|
||||
|
||||
// 获取方法上边的注解 替代path variable 为 *
|
||||
Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class);
|
||||
Optional.ofNullable(method).ifPresent(anonymous -> {
|
||||
Set<PathPattern> patterns = info.getPathPatternsCondition().getPatterns();
|
||||
patterns.forEach(url -> {
|
||||
excludes.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, asterisk));
|
||||
});
|
||||
});
|
||||
|
||||
// 获取类上边的注解, 替代path variable 为 *
|
||||
Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class);
|
||||
Optional.ofNullable(controller).ifPresent(anonymous -> {
|
||||
Set<PathPattern> patterns = info.getPathPatternsCondition().getPatterns();
|
||||
patterns.forEach(url -> {
|
||||
excludes.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, asterisk));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -102,6 +102,20 @@ public class UserActionListener implements SaTokenListener {
|
||||
public void doUntieDisable(String loginType, Object loginId, String service) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次打开二级认证时触发
|
||||
*/
|
||||
@Override
|
||||
public void doOpenSafe(String loginType, String tokenValue, String service, long safeTime) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次创建Session时触发
|
||||
*/
|
||||
@Override
|
||||
public void doCloseSafe(String loginType, String tokenValue, String service) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 每次创建Session时触发
|
||||
*/
|
||||
|
@ -105,7 +105,7 @@ public class GlobalExceptionHandler {
|
||||
public R<Void> handleServiceException(ServiceException e, HttpServletRequest request) {
|
||||
log.error(e.getMessage(), e);
|
||||
Integer code = e.getCode();
|
||||
return ObjectUtil.isNotNull(code) ? R.fail(code.intValue(), e.getMessage()) : R.fail(e.getMessage());
|
||||
return ObjectUtil.isNotNull(code) ? R.fail(code, e.getMessage()) : R.fail(e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,7 +12,6 @@ import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* XxlJob开发示例(Bean模式)
|
||||
@ -39,7 +38,6 @@ public class SampleService {
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
XxlJobHelper.log("beat at:" + i);
|
||||
TimeUnit.SECONDS.sleep(2);
|
||||
}
|
||||
// default success
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ public interface OssConstant {
|
||||
/**
|
||||
* 云服务商
|
||||
*/
|
||||
String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu"};
|
||||
String[] CLOUD_SERVICE = new String[] {"aliyun", "qcloud", "qiniu", "obs"};
|
||||
|
||||
/**
|
||||
* https 状态
|
||||
|
@ -1,7 +1,9 @@
|
||||
package com.ruoyi.oss.core;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.amazonaws.ClientConfiguration;
|
||||
import com.amazonaws.HttpMethod;
|
||||
import com.amazonaws.Protocol;
|
||||
import com.amazonaws.auth.AWSCredentials;
|
||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||
@ -16,12 +18,15 @@ import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.oss.constant.OssConstant;
|
||||
import com.ruoyi.oss.entity.UploadResult;
|
||||
import com.ruoyi.oss.enumd.AccessPolicyType;
|
||||
import com.ruoyi.oss.enumd.PolicyType;
|
||||
import com.ruoyi.oss.exception.OssException;
|
||||
import com.ruoyi.oss.properties.OssProperties;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* S3 存储协议 所有兼容S3协议的云厂商均支持
|
||||
@ -57,7 +62,7 @@ public class OssClient {
|
||||
.withClientConfiguration(clientConfig)
|
||||
.withCredentials(credentialsProvider)
|
||||
.disableChunkedEncoding();
|
||||
if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)){
|
||||
if (!StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE)) {
|
||||
// minio 使用https限制使用域名访问 需要此配置 站点填域名
|
||||
build.enablePathStyleAccess();
|
||||
}
|
||||
@ -79,9 +84,10 @@ public class OssClient {
|
||||
return;
|
||||
}
|
||||
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
|
||||
createBucketRequest.setCannedAcl(CannedAccessControlList.PublicRead);
|
||||
AccessPolicyType accessPolicy = getAccessPolicy();
|
||||
createBucketRequest.setCannedAcl(accessPolicy.getAcl());
|
||||
client.createBucket(createBucketRequest);
|
||||
client.setBucketPolicy(bucketName, getPolicy(bucketName, PolicyType.READ));
|
||||
client.setBucketPolicy(bucketName, getPolicy(bucketName, accessPolicy.getPolicyType()));
|
||||
} catch (Exception e) {
|
||||
throw new OssException("创建Bucket失败, 请核对配置信息:[" + e.getMessage() + "]");
|
||||
}
|
||||
@ -92,13 +98,16 @@ public class OssClient {
|
||||
}
|
||||
|
||||
public UploadResult upload(InputStream inputStream, String path, String contentType) {
|
||||
if (!(inputStream instanceof ByteArrayInputStream)) {
|
||||
inputStream = new ByteArrayInputStream(IoUtil.readBytes(inputStream));
|
||||
}
|
||||
try {
|
||||
ObjectMetadata metadata = new ObjectMetadata();
|
||||
metadata.setContentType(contentType);
|
||||
metadata.setContentLength(inputStream.available());
|
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), path, inputStream, metadata);
|
||||
// 设置上传对象的 Acl 为公共读
|
||||
putObjectRequest.setCannedAcl(CannedAccessControlList.PublicRead);
|
||||
putObjectRequest.setCannedAcl(getAccessPolicy().getAcl());
|
||||
client.putObject(putObjectRequest);
|
||||
} catch (Exception e) {
|
||||
throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
|
||||
@ -111,7 +120,7 @@ public class OssClient {
|
||||
try {
|
||||
client.deleteObject(properties.getBucketName(), path);
|
||||
} catch (Exception e) {
|
||||
throw new OssException("上传文件失败,请检查配置信息:[" + e.getMessage() + "]");
|
||||
throw new OssException("删除文件失败,请检查配置信息:[" + e.getMessage() + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,16 +138,23 @@ public class OssClient {
|
||||
* @param path 完整文件路径
|
||||
*/
|
||||
public ObjectMetadata getObjectMetadata(String path) {
|
||||
path = path.replace(getUrl() + "/", "");
|
||||
S3Object object = client.getObject(properties.getBucketName(), path);
|
||||
return object.getObjectMetadata();
|
||||
}
|
||||
|
||||
public InputStream getObjectContent(String path) {
|
||||
path = path.replace(getUrl() + "/", "");
|
||||
S3Object object = client.getObject(properties.getBucketName(), path);
|
||||
return object.getObjectContent();
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
String domain = properties.getDomain();
|
||||
String endpoint = properties.getEndpoint();
|
||||
String header = OssConstant.IS_HTTPS.equals(properties.getIsHttps()) ? "https://" : "http://";
|
||||
// 云服务商直接返回
|
||||
if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)){
|
||||
if (StringUtils.containsAny(endpoint, OssConstant.CLOUD_SERVICE)) {
|
||||
if (StringUtils.isNotBlank(domain)) {
|
||||
return header + domain;
|
||||
}
|
||||
@ -167,6 +183,24 @@ public class OssClient {
|
||||
return configKey;
|
||||
}
|
||||
|
||||
public String getPrivateUrl(String objectKey, Integer second) {
|
||||
GeneratePresignedUrlRequest generatePresignedUrlRequest =
|
||||
new GeneratePresignedUrlRequest(properties.getBucketName(), objectKey)
|
||||
.withMethod(HttpMethod.GET)
|
||||
.withExpiration(new Date(System.currentTimeMillis() + 1000L * second));
|
||||
URL url = client.generatePresignedUrl(generatePresignedUrlRequest);
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前桶权限类型
|
||||
*
|
||||
* @return 当前桶权限类型code
|
||||
*/
|
||||
public AccessPolicyType getAccessPolicy() {
|
||||
return AccessPolicyType.getByType(properties.getAccessPolicy());
|
||||
}
|
||||
|
||||
private static String getPolicy(String bucketName, PolicyType policyType) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("{\n\"Statement\": [\n{\n\"Action\": [\n");
|
||||
|
@ -0,0 +1,55 @@
|
||||
package com.ruoyi.oss.enumd;
|
||||
|
||||
import com.amazonaws.services.s3.model.CannedAccessControlList;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 桶访问策略配置
|
||||
*
|
||||
* @author 陈賝
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AccessPolicyType {
|
||||
|
||||
/**
|
||||
* private
|
||||
*/
|
||||
PRIVATE("0", CannedAccessControlList.Private, PolicyType.WRITE),
|
||||
|
||||
/**
|
||||
* public
|
||||
*/
|
||||
PUBLIC("1", CannedAccessControlList.PublicRead, PolicyType.READ),
|
||||
|
||||
/**
|
||||
* custom
|
||||
*/
|
||||
CUSTOM("2",CannedAccessControlList.PublicRead, PolicyType.READ);
|
||||
|
||||
/**
|
||||
* 桶 权限类型
|
||||
*/
|
||||
private final String type;
|
||||
|
||||
/**
|
||||
* 文件对象 权限类型
|
||||
*/
|
||||
private final CannedAccessControlList acl;
|
||||
|
||||
/**
|
||||
* 桶策略类型
|
||||
*/
|
||||
private final PolicyType policyType;
|
||||
|
||||
public static AccessPolicyType getByType(String type) {
|
||||
for (AccessPolicyType value : values()) {
|
||||
if (value.getType().equals(type)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("'type' not found By " + type);
|
||||
}
|
||||
|
||||
}
|
@ -50,4 +50,9 @@ public class OssProperties {
|
||||
*/
|
||||
private String isHttps;
|
||||
|
||||
/**
|
||||
* 桶权限类型(0private 1public 2custom)
|
||||
*/
|
||||
private String accessPolicy;
|
||||
|
||||
}
|
||||
|
@ -82,4 +82,8 @@ public class SysOssConfig extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 桶权限类型(0private 1public 2custom)
|
||||
*/
|
||||
private String accessPolicy;
|
||||
}
|
||||
|
@ -98,4 +98,10 @@ public class SysOssConfigBo extends BaseEntity {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 桶权限类型(0private 1public 2custom)
|
||||
*/
|
||||
@NotBlank(message = "桶权限类型不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String accessPolicy;
|
||||
|
||||
}
|
||||
|
@ -82,4 +82,9 @@ public class SysOssConfigVo {
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 桶权限类型(0private 1public 2custom)
|
||||
*/
|
||||
private String accessPolicy;
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import com.ruoyi.system.domain.bo.SysOssBo;
|
||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@ -23,7 +25,9 @@ public interface ISysOssService {
|
||||
|
||||
SysOssVo getById(Long ossId);
|
||||
|
||||
SysOss upload(MultipartFile file);
|
||||
SysOssVo upload(MultipartFile file);
|
||||
|
||||
void download(Long ossId, HttpServletResponse response) throws IOException;
|
||||
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
|
@ -128,7 +128,7 @@ public class SysLoginService {
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
StpUtil.logout();
|
||||
asyncService.recordLogininfor(loginUser.getUsername(), Constants.LOGOUT, MessageUtils.message("user.logout.success"), ServletUtils.getRequest());
|
||||
} catch (NotLoginException e) {
|
||||
} catch (NotLoginException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
package com.ruoyi.system.service.impl;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.constant.CacheNames;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
@ -27,6 +29,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 字典 业务层处理
|
||||
@ -231,28 +234,24 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
|
||||
* @param separator 分隔符
|
||||
* @return 字典标签
|
||||
*/
|
||||
@SuppressWarnings("unchecked cast")
|
||||
@Override
|
||||
public String getDictLabel(String dictType, String dictValue, String separator) {
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
|
||||
|
||||
if (StringUtils.containsAny(dictValue, separator) && CollUtil.isNotEmpty(datas)) {
|
||||
for (SysDictData dict : datas) {
|
||||
for (String value : dictValue.split(separator)) {
|
||||
if (value.equals(dict.getDictValue())) {
|
||||
propertyString.append(dict.getDictLabel() + separator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (SysDictData dict : datas) {
|
||||
if (dictValue.equals(dict.getDictValue())) {
|
||||
return dict.getDictLabel();
|
||||
}
|
||||
}
|
||||
// 优先从本地缓存获取
|
||||
List<SysDictData> datas = (List<SysDictData>) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType);
|
||||
if (ObjectUtil.isNull(datas)) {
|
||||
datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
|
||||
SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas);
|
||||
}
|
||||
|
||||
Map<String, String> map = StreamUtils.toMap(datas, SysDictData::getDictValue, SysDictData::getDictLabel);
|
||||
if (StringUtils.containsAny(dictValue, separator)) {
|
||||
return Arrays.stream(dictValue.split(separator))
|
||||
.map(v -> map.getOrDefault(v, StringUtils.EMPTY))
|
||||
.collect(Collectors.joining(separator));
|
||||
} else {
|
||||
return map.getOrDefault(dictValue, StringUtils.EMPTY);
|
||||
}
|
||||
return StringUtils.stripEnd(propertyString.toString(), separator);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -263,28 +262,24 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
|
||||
* @param separator 分隔符
|
||||
* @return 字典值
|
||||
*/
|
||||
@SuppressWarnings("unchecked cast")
|
||||
@Override
|
||||
public String getDictValue(String dictType, String dictLabel, String separator) {
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
List<SysDictData> datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
|
||||
|
||||
if (StringUtils.containsAny(dictLabel, separator) && CollUtil.isNotEmpty(datas)) {
|
||||
for (SysDictData dict : datas) {
|
||||
for (String label : dictLabel.split(separator)) {
|
||||
if (label.equals(dict.getDictLabel())) {
|
||||
propertyString.append(dict.getDictValue() + separator);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (SysDictData dict : datas) {
|
||||
if (dictLabel.equals(dict.getDictLabel())) {
|
||||
return dict.getDictValue();
|
||||
}
|
||||
}
|
||||
// 优先从本地缓存获取
|
||||
List<SysDictData> datas = (List<SysDictData>) SaHolder.getStorage().get(CacheConstants.SYS_DICT_KEY + dictType);
|
||||
if (ObjectUtil.isNull(datas)) {
|
||||
datas = SpringUtils.getAopProxy(this).selectDictDataByType(dictType);
|
||||
SaHolder.getStorage().set(CacheConstants.SYS_DICT_KEY + dictType, datas);
|
||||
}
|
||||
|
||||
Map<String, String> map = StreamUtils.toMap(datas, SysDictData::getDictLabel, SysDictData::getDictValue);
|
||||
if (StringUtils.containsAny(dictLabel, separator)) {
|
||||
return Arrays.stream(dictLabel.split(separator))
|
||||
.map(l -> map.getOrDefault(l, StringUtils.EMPTY))
|
||||
.collect(Collectors.joining(separator));
|
||||
} else {
|
||||
return map.getOrDefault(dictLabel, StringUtils.EMPTY);
|
||||
}
|
||||
return StringUtils.stripEnd(propertyString.toString(), separator);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.entity.SysMenu;
|
||||
import com.ruoyi.common.core.domain.entity.SysRole;
|
||||
import com.ruoyi.common.helper.LoginHelper;
|
||||
import com.ruoyi.common.utils.StreamUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.TreeBuildUtils;
|
||||
import com.ruoyi.system.domain.SysRoleMenu;
|
||||
@ -425,13 +426,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
|
||||
* 得到子节点列表
|
||||
*/
|
||||
private List<SysMenu> getChildList(List<SysMenu> list, SysMenu t) {
|
||||
List<SysMenu> tlist = new ArrayList<SysMenu>();
|
||||
for (SysMenu n : list) {
|
||||
if (n.getParentId().longValue() == t.getMenuId().longValue()) {
|
||||
tlist.add(n);
|
||||
}
|
||||
}
|
||||
return tlist;
|
||||
return StreamUtils.filter(list, n -> n.getParentId().equals(t.getMenuId()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.ruoyi.system.service.impl;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
@ -8,10 +9,13 @@ import com.ruoyi.common.constant.CacheNames;
|
||||
import com.ruoyi.common.core.domain.PageQuery;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.BeanCopyUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.file.FileUtils;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import com.ruoyi.oss.core.OssClient;
|
||||
import com.ruoyi.oss.entity.UploadResult;
|
||||
import com.ruoyi.oss.enumd.AccessPolicyType;
|
||||
import com.ruoyi.oss.factory.OssFactory;
|
||||
import com.ruoyi.system.domain.SysOss;
|
||||
import com.ruoyi.system.domain.bo.SysOssBo;
|
||||
@ -20,14 +24,18 @@ import com.ruoyi.system.mapper.SysOssMapper;
|
||||
import com.ruoyi.system.service.ISysOssService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 文件上传 服务层实现
|
||||
@ -44,6 +52,8 @@ public class SysOssServiceImpl implements ISysOssService {
|
||||
public TableDataInfo<SysOssVo> queryPageList(SysOssBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<SysOss> lqw = buildQueryWrapper(bo);
|
||||
Page<SysOssVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
List<SysOssVo> filterResult = result.getRecords().stream().map(this::matchingUrl).collect(Collectors.toList());
|
||||
result.setRecords(filterResult);
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
@ -53,7 +63,7 @@ public class SysOssServiceImpl implements ISysOssService {
|
||||
for (Long id : ossIds) {
|
||||
SysOssVo vo = SpringUtils.getAopProxy(this).getById(id);
|
||||
if (ObjectUtil.isNotNull(vo)) {
|
||||
list.add(vo);
|
||||
list.add(this.matchingUrl(vo));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
@ -80,7 +90,25 @@ public class SysOssServiceImpl implements ISysOssService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysOss upload(MultipartFile file) {
|
||||
public void download(Long ossId, HttpServletResponse response) throws IOException {
|
||||
SysOssVo sysOss = SpringUtils.getAopProxy(this).getById(ossId);
|
||||
if (ObjectUtil.isNull(sysOss)) {
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
|
||||
OssClient storage = OssFactory.instance();
|
||||
try(InputStream inputStream = storage.getObjectContent(sysOss.getUrl())) {
|
||||
int available = inputStream.available();
|
||||
IoUtil.copy(inputStream, response.getOutputStream(), available);
|
||||
response.setContentLength(available);
|
||||
} catch (Exception e) {
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SysOssVo upload(MultipartFile file) {
|
||||
String originalfileName = file.getOriginalFilename();
|
||||
String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
|
||||
OssClient storage = OssFactory.instance();
|
||||
@ -98,7 +126,9 @@ public class SysOssServiceImpl implements ISysOssService {
|
||||
oss.setOriginalName(originalfileName);
|
||||
oss.setService(storage.getConfigKey());
|
||||
baseMapper.insert(oss);
|
||||
return oss;
|
||||
SysOssVo sysOssVo = new SysOssVo();
|
||||
BeanCopyUtils.copy(oss, sysOssVo);
|
||||
return this.matchingUrl(sysOssVo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -114,4 +144,18 @@ public class SysOssServiceImpl implements ISysOssService {
|
||||
return baseMapper.deleteBatchIds(ids) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配Url
|
||||
*
|
||||
* @param oss OSS对象
|
||||
* @return oss 匹配Url的OSS对象
|
||||
*/
|
||||
private SysOssVo matchingUrl(SysOssVo oss) {
|
||||
OssClient storage = OssFactory.instance(oss.getService());
|
||||
// 仅修改桶类型为 private 的URL,临时URL时长为120s
|
||||
if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
|
||||
oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
|
||||
}
|
||||
return oss;
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ public class SysUserOnlineController extends BaseController {
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo<SysUserOnline> list(String ipaddr, String userName) {
|
||||
// 获取所有未过期的 token
|
||||
List<String> keys = StpUtil.searchTokenValue("", -1, 0, false);
|
||||
List<String> keys = StpUtil.searchTokenValue("", 0, -1, false);
|
||||
List<UserOnlineDTO> userOnlineDTOList = new ArrayList<>();
|
||||
for (String key : keys) {
|
||||
String token = key.replace(CacheConstants.LOGIN_TOKEN_KEY, "");
|
||||
@ -83,7 +83,7 @@ public class SysUserOnlineController extends BaseController {
|
||||
public R<Void> forceLogout(@PathVariable String tokenId) {
|
||||
try {
|
||||
StpUtil.kickoutByTokenValue(tokenId);
|
||||
} catch (NotLoginException e) {
|
||||
} catch (NotLoginException ignored) {
|
||||
}
|
||||
return R.ok();
|
||||
}
|
||||
|
@ -108,10 +108,10 @@ public class SysDeptController extends BaseController {
|
||||
@DeleteMapping("/{deptId}")
|
||||
public R<Void> remove(@PathVariable Long deptId) {
|
||||
if (deptService.hasChildByDeptId(deptId)) {
|
||||
return R.fail("存在下级部门,不允许删除");
|
||||
return R.warn("存在下级部门,不允许删除");
|
||||
}
|
||||
if (deptService.checkDeptExistUser(deptId)) {
|
||||
return R.fail("部门存在用户,不允许删除");
|
||||
return R.warn("部门存在用户,不允许删除");
|
||||
}
|
||||
deptService.checkDeptDataScope(deptId);
|
||||
return toAjax(deptService.deleteDeptById(deptId));
|
||||
|
@ -117,10 +117,10 @@ public class SysMenuController extends BaseController {
|
||||
@DeleteMapping("/{menuId}")
|
||||
public R<Void> remove(@PathVariable("menuId") Long menuId) {
|
||||
if (menuService.hasChildByMenuId(menuId)) {
|
||||
return R.fail("存在子菜单,不允许删除");
|
||||
return R.warn("存在子菜单,不允许删除");
|
||||
}
|
||||
if (menuService.checkMenuExistRole(menuId)) {
|
||||
return R.fail("菜单已分配,不允许删除");
|
||||
return R.warn("菜单已分配,不允许删除");
|
||||
}
|
||||
return toAjax(menuService.deleteMenuById(menuId));
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class SysOssConfigController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody SysOssConfigBo bo) {
|
||||
return toAjax(iSysOssConfigService.insertByBo(bo) ? 1 : 0);
|
||||
return toAjax(iSysOssConfigService.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,7 +77,7 @@ public class SysOssConfigController extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody SysOssConfigBo bo) {
|
||||
return toAjax(iSysOssConfigService.updateByBo(bo) ? 1 : 0);
|
||||
return toAjax(iSysOssConfigService.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,7 +90,7 @@ public class SysOssConfigController extends BaseController {
|
||||
@DeleteMapping("/{ossConfigIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ossConfigIds) {
|
||||
return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true) ? 1 : 0);
|
||||
return toAjax(iSysOssConfigService.deleteWithValidByIds(Arrays.asList(ossConfigIds), true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -15,6 +15,8 @@ import com.ruoyi.common.core.validate.QueryGroup;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.file.FileUtils;
|
||||
import com.ruoyi.oss.core.OssClient;
|
||||
import com.ruoyi.oss.factory.OssFactory;
|
||||
import com.ruoyi.system.domain.SysOss;
|
||||
import com.ruoyi.system.domain.bo.SysOssBo;
|
||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||
@ -80,7 +82,7 @@ public class SysOssController extends BaseController {
|
||||
if (ObjectUtil.isNull(file)) {
|
||||
throw new ServiceException("上传文件不能为空");
|
||||
}
|
||||
SysOss oss = iSysOssService.upload(file);
|
||||
SysOssVo oss = iSysOssService.upload(file);
|
||||
Map<String, String> map = new HashMap<>(2);
|
||||
map.put("url", oss.getUrl());
|
||||
map.put("fileName", oss.getOriginalName());
|
||||
@ -96,23 +98,7 @@ public class SysOssController extends BaseController {
|
||||
@SaCheckPermission("system:oss:download")
|
||||
@GetMapping("/download/{ossId}")
|
||||
public void download(@PathVariable Long ossId, HttpServletResponse response) throws IOException {
|
||||
SysOssVo sysOss = iSysOssService.getById(ossId);
|
||||
if (ObjectUtil.isNull(sysOss)) {
|
||||
throw new ServiceException("文件数据不存在!");
|
||||
}
|
||||
FileUtils.setAttachmentResponseHeader(response, sysOss.getOriginalName());
|
||||
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
|
||||
long data;
|
||||
try {
|
||||
data = HttpUtil.download(sysOss.getUrl(), response.getOutputStream(), false);
|
||||
} catch (HttpException e) {
|
||||
if (e.getMessage().contains("403")) {
|
||||
throw new ServiceException("无读取权限, 请在对应的OSS开启'公有读'权限!");
|
||||
} else {
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
response.setContentLength(Convert.toInt(data));
|
||||
iSysOssService.download(ossId,response);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -125,7 +111,7 @@ public class SysOssController extends BaseController {
|
||||
@DeleteMapping("/{ossIds}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable Long[] ossIds) {
|
||||
return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true) ? 1 : 0);
|
||||
return toAjax(iSysOssService.deleteWithValidByIds(Arrays.asList(ossIds), true));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import com.ruoyi.common.helper.LoginHelper;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.file.MimeTypeUtils;
|
||||
import com.ruoyi.system.domain.SysOss;
|
||||
import com.ruoyi.system.domain.vo.SysOssVo;
|
||||
import com.ruoyi.system.service.ISysOssService;
|
||||
import com.ruoyi.system.service.ISysUserService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@ -115,7 +116,7 @@ public class SysProfileController extends BaseController {
|
||||
if (!StringUtils.equalsAnyIgnoreCase(extension, MimeTypeUtils.IMAGE_EXTENSION)) {
|
||||
return R.fail("文件格式不正确,请上传" + Arrays.toString(MimeTypeUtils.IMAGE_EXTENSION) + "格式");
|
||||
}
|
||||
SysOss oss = iSysOssService.upload(avatarfile);
|
||||
SysOssVo oss = iSysOssService.upload(avatarfile);
|
||||
String avatar = oss.getUrl();
|
||||
if (userService.updateUserAvatar(getUsername(), avatar)) {
|
||||
ajax.put("imgUrl", avatar);
|
||||
|
@ -33,7 +33,7 @@ xxl.job:
|
||||
--- # 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
||||
dynamic:
|
||||
# 性能分析插件(有性能损耗 不建议生产环境使用)
|
||||
@ -45,6 +45,7 @@ spring:
|
||||
datasource:
|
||||
# 主库数据源
|
||||
master:
|
||||
type: ${spring.datasource.type}
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
@ -54,72 +55,46 @@ spring:
|
||||
# 从库数据源
|
||||
slave:
|
||||
lazy: true
|
||||
type: ${spring.datasource.type}
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url:
|
||||
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
username:
|
||||
password:
|
||||
# oracle:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: oracle.jdbc.OracleDriver
|
||||
# url: jdbc:oracle:thin:@//localhost:1521/XE
|
||||
# username: ROOT
|
||||
# password: root
|
||||
# druid:
|
||||
# validationQuery: SELECT 1 FROM DUAL
|
||||
# hikari:
|
||||
# connectionTestQuery: SELECT 1 FROM DUAL
|
||||
# postgres:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: org.postgresql.Driver
|
||||
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
|
||||
# username: root
|
||||
# password: root
|
||||
# sqlserver:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
|
||||
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;rewriteBatchedStatements=true
|
||||
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
|
||||
# username: SA
|
||||
# password: root
|
||||
druid:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
hikari:
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
maxPoolSize: 20
|
||||
# 最小空闲线程数量
|
||||
minIdle: 10
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
# 注意这个值和druid原生不一致,默认启动了stat
|
||||
filters: stat
|
||||
|
||||
--- # druid 配置
|
||||
spring.datasource.druid:
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: ruoyi
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
connectionTimeout: 10000
|
||||
# 校验超时时间
|
||||
validationTimeout: 5000
|
||||
# 空闲连接存活最大时间,默认10分钟
|
||||
idleTimeout: 60000
|
||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||
maxLifetime: 900000
|
||||
# 连接测试query(配置检测连接是否有效)
|
||||
connectionTestQuery: SELECT 1
|
||||
|
||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||
spring:
|
||||
|
@ -36,7 +36,7 @@ xxl.job:
|
||||
--- # 数据源配置
|
||||
spring:
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
type: com.zaxxer.hikari.HikariDataSource
|
||||
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
||||
dynamic:
|
||||
# 性能分析插件(有性能损耗 不建议生产环境使用)
|
||||
@ -48,6 +48,7 @@ spring:
|
||||
datasource:
|
||||
# 主库数据源
|
||||
master:
|
||||
type: ${spring.datasource.type}
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
@ -57,72 +58,46 @@ spring:
|
||||
# 从库数据源
|
||||
slave:
|
||||
lazy: true
|
||||
type: ${spring.datasource.type}
|
||||
driverClassName: com.mysql.cj.jdbc.Driver
|
||||
url:
|
||||
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
username:
|
||||
password:
|
||||
# oracle:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: oracle.jdbc.OracleDriver
|
||||
# url: jdbc:oracle:thin:@//localhost:1521/XE
|
||||
# username: ROOT
|
||||
# password: root
|
||||
# druid:
|
||||
# validationQuery: SELECT 1 FROM DUAL
|
||||
# hikari:
|
||||
# connectionTestQuery: SELECT 1 FROM DUAL
|
||||
# postgres:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: org.postgresql.Driver
|
||||
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
|
||||
# username: root
|
||||
# password: root
|
||||
# sqlserver:
|
||||
# type: ${spring.datasource.type}
|
||||
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
|
||||
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;rewriteBatchedStatements=true
|
||||
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
|
||||
# username: SA
|
||||
# password: root
|
||||
druid:
|
||||
# 初始连接数
|
||||
initialSize: 5
|
||||
# 最小连接池数量
|
||||
minIdle: 10
|
||||
hikari:
|
||||
# 最大连接池数量
|
||||
maxActive: 20
|
||||
maxPoolSize: 20
|
||||
# 最小空闲线程数量
|
||||
minIdle: 10
|
||||
# 配置获取连接等待超时的时间
|
||||
maxWait: 60000
|
||||
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
# 配置一个连接在池中最小生存的时间,单位是毫秒
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
# 配置一个连接在池中最大生存的时间,单位是毫秒
|
||||
maxEvictableIdleTimeMillis: 900000
|
||||
# 配置检测连接是否有效
|
||||
validationQuery: SELECT 1
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
# 注意这个值和druid原生不一致,默认启动了stat
|
||||
filters: stat
|
||||
|
||||
--- # druid 配置
|
||||
spring.datasource.druid:
|
||||
webStatFilter:
|
||||
enabled: true
|
||||
statViewServlet:
|
||||
enabled: true
|
||||
# 设置白名单,不填则允许所有访问
|
||||
allow:
|
||||
url-pattern: /druid/*
|
||||
# 控制台管理用户名和密码
|
||||
login-username: ruoyi
|
||||
login-password: 123456
|
||||
filter:
|
||||
stat:
|
||||
enabled: true
|
||||
# 慢SQL记录
|
||||
log-slow-sql: true
|
||||
slow-sql-millis: 1000
|
||||
merge-sql: true
|
||||
wall:
|
||||
config:
|
||||
multi-statement-allow: true
|
||||
connectionTimeout: 10000
|
||||
# 校验超时时间
|
||||
validationTimeout: 5000
|
||||
# 空闲连接存活最大时间,默认10分钟
|
||||
idleTimeout: 60000
|
||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
||||
maxLifetime: 900000
|
||||
# 连接测试query(配置检测连接是否有效)
|
||||
connectionTestQuery: SELECT 1
|
||||
|
||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||
spring:
|
||||
|
@ -115,8 +115,6 @@ sa-token:
|
||||
token-prefix: "Bearer"
|
||||
# jwt秘钥
|
||||
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
|
||||
# 是否输出操作日志
|
||||
is-log: true
|
||||
|
||||
# security配置
|
||||
security:
|
||||
@ -131,8 +129,6 @@ security:
|
||||
- /favicon.ico
|
||||
- /*/api-docs
|
||||
- /*/api-docs/**
|
||||
# druid 监控配置
|
||||
- /druid/**
|
||||
# actuator 监控配置
|
||||
- /actuator
|
||||
- /actuator/**
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
|
||||
<encoder>
|
||||
<pattern>${console.log.pattern}</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
@ -22,7 +22,7 @@
|
||||
<!-- 日志最大 1天 -->
|
||||
<maxHistory>1</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
@ -42,7 +42,7 @@
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
@ -64,7 +64,7 @@
|
||||
<!-- 日志最大的历史 60天 -->
|
||||
<maxHistory>60</maxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder class="com.yomahub.tlog.core.enhance.logback.AspectLogbackEncoder">
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
@ -78,7 +78,7 @@
|
||||
</appender>
|
||||
|
||||
<!-- info异步输出 -->
|
||||
<appender name="async_info" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
|
||||
<appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
|
||||
@ -88,7 +88,7 @@
|
||||
</appender>
|
||||
|
||||
<!-- error异步输出 -->
|
||||
<appender name="async_error" class="com.yomahub.tlog.core.enhance.logback.async.AspectLogbackAsyncAppender">
|
||||
<appender name="async_error" class="ch.qos.logback.classic.AsyncAppender">
|
||||
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
|
||||
<discardingThreshold>0</discardingThreshold>
|
||||
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
|
||||
@ -97,12 +97,33 @@
|
||||
<appender-ref ref="file_error"/>
|
||||
</appender>
|
||||
|
||||
<!-- 整合 skywalking 控制台输出 tid -->
|
||||
<!-- <appender name="console" class="ch.qos.logback.core.ConsoleAppender">-->
|
||||
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
|
||||
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
|
||||
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
|
||||
<!-- </layout>-->
|
||||
<!-- <charset>utf-8</charset>-->
|
||||
<!-- </encoder>-->
|
||||
<!-- </appender>-->
|
||||
|
||||
<!-- 整合 skywalking 推送采集日志 -->
|
||||
<!-- <appender name="sky_log" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">-->
|
||||
<!-- <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">-->
|
||||
<!-- <layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">-->
|
||||
<!-- <pattern>[%tid] ${console.log.pattern}</pattern>-->
|
||||
<!-- </layout>-->
|
||||
<!-- <charset>utf-8</charset>-->
|
||||
<!-- </encoder>-->
|
||||
<!-- </appender>-->
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
<appender-ref ref="async_info" />
|
||||
<appender-ref ref="async_error" />
|
||||
<appender-ref ref="file_console" />
|
||||
<!-- <appender-ref ref="sky_log"/>-->
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
|
@ -24,3 +24,5 @@ outagedetection=true
|
||||
outagedetectioninterval=2
|
||||
# 是否过滤 Log
|
||||
filter=true
|
||||
# 过滤 Log 时所排除的 sql 关键字,以逗号分隔
|
||||
exclude=SELECT 1
|
||||
|
@ -89,7 +89,7 @@ public class ${ClassName}Controller extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PostMapping()
|
||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody ${ClassName}Bo bo) {
|
||||
return toAjax(i${ClassName}Service.insertByBo(bo) ? 1 : 0);
|
||||
return toAjax(i${ClassName}Service.insertByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,7 +100,7 @@ public class ${ClassName}Controller extends BaseController {
|
||||
@RepeatSubmit()
|
||||
@PutMapping()
|
||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ${ClassName}Bo bo) {
|
||||
return toAjax(i${ClassName}Service.updateByBo(bo) ? 1 : 0);
|
||||
return toAjax(i${ClassName}Service.updateByBo(bo));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,6 +113,6 @@ public class ${ClassName}Controller extends BaseController {
|
||||
@DeleteMapping("/{${pkColumn.javaField}s}")
|
||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||
@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) {
|
||||
return toAjax(i${ClassName}Service.deleteWithValidByIds(Arrays.asList(${pkColumn.javaField}s), true) ? 1 : 0);
|
||||
return toAjax(i${ClassName}Service.deleteWithValidByIds(Arrays.asList(${pkColumn.javaField}s), true));
|
||||
}
|
||||
}
|
||||
|
@ -136,24 +136,9 @@
|
||||
#end
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
icon="Plus"
|
||||
@click="handleAdd(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
||||
>新增</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
||||
>删除</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
@ -148,18 +148,8 @@
|
||||
#end
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
icon="Edit"
|
||||
@click="handleUpdate(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
||||
>修改</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
||||
>删除</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
45
ruoyi/src/test/java/com/ruoyi/test/AssertUnitTest.java
Normal file
45
ruoyi/src/test/java/com/ruoyi/test/AssertUnitTest.java
Normal file
@ -0,0 +1,45 @@
|
||||
package com.ruoyi.test;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* 断言单元测试案例
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@DisplayName("断言单元测试案例")
|
||||
public class AssertUnitTest {
|
||||
|
||||
@DisplayName("测试 assertEquals 方法")
|
||||
@Test
|
||||
public void testAssertEquals() {
|
||||
Assertions.assertEquals("666", new String("666"));
|
||||
Assertions.assertNotEquals("666", new String("666"));
|
||||
}
|
||||
|
||||
@DisplayName("测试 assertSame 方法")
|
||||
@Test
|
||||
public void testAssertSame() {
|
||||
Object obj = new Object();
|
||||
Object obj1 = obj;
|
||||
Assertions.assertSame(obj, obj1);
|
||||
Assertions.assertNotSame(obj, obj1);
|
||||
}
|
||||
|
||||
@DisplayName("测试 assertTrue 方法")
|
||||
@Test
|
||||
public void testAssertTrue() {
|
||||
Assertions.assertTrue(true);
|
||||
Assertions.assertFalse(true);
|
||||
}
|
||||
|
||||
@DisplayName("测试 assertNull 方法")
|
||||
@Test
|
||||
public void testAssertNull() {
|
||||
Assertions.assertNull(null);
|
||||
Assertions.assertNotNull(null);
|
||||
}
|
||||
|
||||
}
|
70
ruoyi/src/test/java/com/ruoyi/test/DemoUnitTest.java
Normal file
70
ruoyi/src/test/java/com/ruoyi/test/DemoUnitTest.java
Normal file
@ -0,0 +1,70 @@
|
||||
package com.ruoyi.test;
|
||||
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 单元测试案例
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@SpringBootTest // 此注解只能在 springboot 主包下使用 需包含 main 方法与 yml 配置文件
|
||||
@DisplayName("单元测试案例")
|
||||
public class DemoUnitTest {
|
||||
|
||||
@Autowired
|
||||
private RuoYiConfig ruoYiConfig;
|
||||
|
||||
@DisplayName("测试 @SpringBootTest @Test @DisplayName 注解")
|
||||
@Test
|
||||
public void testTest() {
|
||||
System.out.println(ruoYiConfig);
|
||||
}
|
||||
|
||||
@Disabled
|
||||
@DisplayName("测试 @Disabled 注解")
|
||||
@Test
|
||||
public void testDisabled() {
|
||||
System.out.println(ruoYiConfig);
|
||||
}
|
||||
|
||||
@Timeout(value = 2L, unit = TimeUnit.SECONDS)
|
||||
@DisplayName("测试 @Timeout 注解")
|
||||
@Test
|
||||
public void testTimeout() throws InterruptedException {
|
||||
Thread.sleep(3000);
|
||||
System.out.println(ruoYiConfig);
|
||||
}
|
||||
|
||||
|
||||
@DisplayName("测试 @RepeatedTest 注解")
|
||||
@RepeatedTest(3)
|
||||
public void testRepeatedTest() {
|
||||
System.out.println(666);
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void testBeforeAll() {
|
||||
System.out.println("@BeforeAll ==================");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void testBeforeEach() {
|
||||
System.out.println("@BeforeEach ==================");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void testAfterEach() {
|
||||
System.out.println("@AfterEach ==================");
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void testAfterAll() {
|
||||
System.out.println("@AfterAll ==================");
|
||||
}
|
||||
|
||||
}
|
72
ruoyi/src/test/java/com/ruoyi/test/ParamUnitTest.java
Normal file
72
ruoyi/src/test/java/com/ruoyi/test/ParamUnitTest.java
Normal file
@ -0,0 +1,72 @@
|
||||
package com.ruoyi.test;
|
||||
|
||||
import com.ruoyi.common.enums.UserType;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.junit.jupiter.params.provider.NullSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 带参数单元测试案例
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@DisplayName("带参数单元测试案例")
|
||||
public class ParamUnitTest {
|
||||
|
||||
@DisplayName("测试 @ValueSource 注解")
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"t1", "t2", "t3"})
|
||||
public void testValueSource(String str) {
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
@DisplayName("测试 @NullSource 注解")
|
||||
@ParameterizedTest
|
||||
@NullSource
|
||||
public void testNullSource(String str) {
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
@DisplayName("测试 @EnumSource 注解")
|
||||
@ParameterizedTest
|
||||
@EnumSource(UserType.class)
|
||||
public void testEnumSource(UserType type) {
|
||||
System.out.println(type.getUserType());
|
||||
}
|
||||
|
||||
@DisplayName("测试 @MethodSource 注解")
|
||||
@ParameterizedTest
|
||||
@MethodSource("getParam")
|
||||
public void testMethodSource(String str) {
|
||||
System.out.println(str);
|
||||
}
|
||||
|
||||
public static Stream<String> getParam() {
|
||||
List<String> list = new ArrayList<>();
|
||||
list.add("t1");
|
||||
list.add("t2");
|
||||
list.add("t3");
|
||||
return list.stream();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void testBeforeEach() {
|
||||
System.out.println("@BeforeEach ==================");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void testAfterEach() {
|
||||
System.out.println("@AfterEach ==================");
|
||||
}
|
||||
|
||||
|
||||
}
|
54
ruoyi/src/test/java/com/ruoyi/test/TagUnitTest.java
Normal file
54
ruoyi/src/test/java/com/ruoyi/test/TagUnitTest.java
Normal file
@ -0,0 +1,54 @@
|
||||
package com.ruoyi.test;
|
||||
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
* 标签单元测试案例
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@SpringBootTest
|
||||
@DisplayName("标签单元测试案例")
|
||||
public class TagUnitTest {
|
||||
|
||||
@Tag("dev")
|
||||
@DisplayName("测试 @Tag dev")
|
||||
@Test
|
||||
public void testTagDev() {
|
||||
System.out.println("dev");
|
||||
}
|
||||
|
||||
@Tag("prod")
|
||||
@DisplayName("测试 @Tag prod")
|
||||
@Test
|
||||
public void testTagProd() {
|
||||
System.out.println("prod");
|
||||
}
|
||||
|
||||
@Tag("local")
|
||||
@DisplayName("测试 @Tag local")
|
||||
@Test
|
||||
public void testTagLocal() {
|
||||
System.out.println("local");
|
||||
}
|
||||
|
||||
@Tag("exclude")
|
||||
@DisplayName("测试 @Tag exclude")
|
||||
@Test
|
||||
public void testTagExclude() {
|
||||
System.out.println("exclude");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void testBeforeEach() {
|
||||
System.out.println("@BeforeEach ==================");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void testAfterEach() {
|
||||
System.out.println("@AfterEach ==================");
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user