update: 同步 RuoYi-Vue-Plus(v4.6.0) 更新
This commit is contained in:
@@ -13,7 +13,9 @@ import java.lang.annotation.Target;
|
||||
* 字典数据映射注解
|
||||
*
|
||||
* @author itino
|
||||
* @deprecated 建议使用通用翻译注解
|
||||
*/
|
||||
@Deprecated
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@JacksonAnnotationsInside
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import com.ruoyi.common.enums.EncodeType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 字段加密注解
|
||||
*
|
||||
* @author 老马
|
||||
*/
|
||||
@Documented
|
||||
@Inherited
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface EncryptField {
|
||||
|
||||
/**
|
||||
* 加密算法
|
||||
*/
|
||||
AlgorithmType algorithm() default AlgorithmType.DEFAULT;
|
||||
|
||||
/**
|
||||
* 秘钥。AES、SM4需要
|
||||
*/
|
||||
String password() default "";
|
||||
|
||||
/**
|
||||
* 公钥。RSA、SM2需要
|
||||
*/
|
||||
String publicKey() default "";
|
||||
|
||||
/**
|
||||
* 公钥。RSA、SM2需要
|
||||
*/
|
||||
String privateKey() default "";
|
||||
|
||||
/**
|
||||
* 编码方式。对加密算法为BASE64的不起作用
|
||||
*/
|
||||
EncodeType encode() default EncodeType.DEFAULT;
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
@@ -25,6 +27,6 @@ public @interface ExcelDictFormat {
|
||||
/**
|
||||
* 分隔符,读取字符串组内容
|
||||
*/
|
||||
String separator() default ",";
|
||||
String separator() default StringUtils.SEPARATOR;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 枚举格式化
|
||||
*
|
||||
* @author Liang
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
public @interface ExcelEnumFormat {
|
||||
|
||||
/**
|
||||
* 字典枚举类型
|
||||
*/
|
||||
Class<? extends Enum<?>> enumClass();
|
||||
|
||||
/**
|
||||
* 字典枚举类中对应的code属性名称,默认为code
|
||||
*/
|
||||
String codeField() default "code";
|
||||
|
||||
/**
|
||||
* 字典枚举类中对应的text属性名称,默认为text
|
||||
*/
|
||||
String textField() default "text";
|
||||
|
||||
}
|
||||
@@ -38,4 +38,10 @@ public @interface Log {
|
||||
* 是否保存响应的参数
|
||||
*/
|
||||
boolean isSaveResponseData() default true;
|
||||
|
||||
/**
|
||||
* 排除指定的请求参数
|
||||
*/
|
||||
String[] excludeParamNames() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.enums.LimitType;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
@@ -15,9 +14,10 @@ import java.lang.annotation.*;
|
||||
@Documented
|
||||
public @interface RateLimiter {
|
||||
/**
|
||||
* 限流key
|
||||
* 限流key,支持使用Spring el表达式来动态获取方法上的参数值
|
||||
* 格式类似于 #code.id #{#code}
|
||||
*/
|
||||
String key() default CacheConstants.RATE_LIMIT_KEY;
|
||||
String key() default "";
|
||||
|
||||
/**
|
||||
* 限流时间,单位秒
|
||||
@@ -33,4 +33,9 @@ public @interface RateLimiter {
|
||||
* 限流类型
|
||||
*/
|
||||
LimitType limitType() default LimitType.DEFAULT;
|
||||
|
||||
/**
|
||||
* 提示消息 支持国际化 格式为 {code}
|
||||
*/
|
||||
String message() default "{rate.limiter.message}";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.ruoyi.common.translation.handler.TranslationHandler;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 通用翻译注解
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||
@Documented
|
||||
@JacksonAnnotationsInside
|
||||
@JsonSerialize(using = TranslationHandler.class)
|
||||
public @interface Translation {
|
||||
|
||||
/**
|
||||
* 类型 (需与实现类上的 {@link com.ruoyi.common.annotation.TranslationType} 注解type对应)
|
||||
* <p>
|
||||
* 默认取当前字段的值 如果设置了 @{@link Translation#mapper()} 则取映射字段的值
|
||||
*/
|
||||
String type();
|
||||
|
||||
/**
|
||||
* 映射字段 (如果不为空则取此字段的值)
|
||||
*/
|
||||
String mapper() default "";
|
||||
|
||||
/**
|
||||
* 其他条件 例如: 字典type(sys_user_sex)
|
||||
*/
|
||||
String other() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ruoyi.common.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 翻译类型注解 (标注到{@link com.ruoyi.common.translation.TranslationInterface} 的实现类)
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Inherited
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE})
|
||||
@Documented
|
||||
public @interface TranslationType {
|
||||
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
String type();
|
||||
|
||||
}
|
||||
@@ -30,6 +30,16 @@ public interface CacheNames {
|
||||
*/
|
||||
String SYS_DICT = "sys_dict";
|
||||
|
||||
/**
|
||||
* 用户账户
|
||||
*/
|
||||
String SYS_USER_NAME = "sys_user_name#30d";
|
||||
|
||||
/**
|
||||
* 部门
|
||||
*/
|
||||
String SYS_DEPT = "sys_dept#30d";
|
||||
|
||||
/**
|
||||
* OSS内容
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.ruoyi.common.constant;
|
||||
|
||||
/**
|
||||
* 翻译常量
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface TransConstant {
|
||||
|
||||
/**
|
||||
* 用户id转账号
|
||||
*/
|
||||
String USER_ID_TO_NAME = "user_id_to_name";
|
||||
|
||||
/**
|
||||
* 部门id转名称
|
||||
*/
|
||||
String DEPT_ID_TO_NAME = "dept_id_to_name";
|
||||
|
||||
/**
|
||||
* 字典type转label
|
||||
*/
|
||||
String DICT_TYPE_TO_LABEL = "dict_type_to_label";
|
||||
|
||||
/**
|
||||
* ossId转url
|
||||
*/
|
||||
String OSS_ID_TO_URL = "oss_id_to_url";
|
||||
|
||||
}
|
||||
@@ -112,12 +112,6 @@ public interface UserConstants {
|
||||
*/
|
||||
String INNER_LINK = "InnerLink";
|
||||
|
||||
/**
|
||||
* 校验返回结果码
|
||||
*/
|
||||
String UNIQUE = "0";
|
||||
String NOT_UNIQUE = "1";
|
||||
|
||||
/**
|
||||
* 用户名长度限制
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.ruoyi.common.convert;
|
||||
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||
import com.alibaba.excel.metadata.data.ReadCellData;
|
||||
import com.alibaba.excel.metadata.data.WriteCellData;
|
||||
import com.alibaba.excel.metadata.property.ExcelContentProperty;
|
||||
import com.ruoyi.common.annotation.ExcelEnumFormat;
|
||||
import com.ruoyi.common.utils.reflect.ReflectUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 枚举格式化转换处理
|
||||
*
|
||||
* @author Liang
|
||||
*/
|
||||
@Slf4j
|
||||
public class ExcelEnumConvert implements Converter<Object> {
|
||||
|
||||
@Override
|
||||
public Class<Object> supportJavaTypeKey() {
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CellDataTypeEnum supportExcelTypeKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
|
||||
Object codeValue = cellData.getData();
|
||||
// 如果是空值
|
||||
if (ObjectUtil.isNull(codeValue)) {
|
||||
return null;
|
||||
}
|
||||
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
|
||||
String textValue = enumValueMap.get(codeValue);
|
||||
return Convert.convert(contentProperty.getField().getType(), textValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WriteCellData<String> convertToExcelData(Object object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
|
||||
if (ObjectUtil.isNull(object)) {
|
||||
return new WriteCellData<>("");
|
||||
}
|
||||
Map<Object, String> enumValueMap = beforeConvert(contentProperty);
|
||||
String value = Convert.toStr(enumValueMap.get(object), "");
|
||||
return new WriteCellData<>(value);
|
||||
}
|
||||
|
||||
private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {
|
||||
ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
|
||||
Map<Object, String> enumValueMap = new HashMap<>();
|
||||
Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
|
||||
for (Enum<?> enumConstant : enumConstants) {
|
||||
Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());
|
||||
String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());
|
||||
enumValueMap.put(codeValue, textValue);
|
||||
}
|
||||
return enumValueMap;
|
||||
}
|
||||
|
||||
private ExcelEnumFormat getAnnotation(Field field) {
|
||||
return AnnotationUtil.getAnnotation(field, ExcelEnumFormat.class);
|
||||
}
|
||||
}
|
||||
@@ -87,8 +87,8 @@ public class PageQuery implements Serializable {
|
||||
// 兼容前端排序类型
|
||||
isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"});
|
||||
|
||||
String[] orderByArr = orderBy.split(",");
|
||||
String[] isAscArr = isAsc.split(",");
|
||||
String[] orderByArr = orderBy.split(StringUtils.SEPARATOR);
|
||||
String[] isAscArr = isAsc.split(StringUtils.SEPARATOR);
|
||||
if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) {
|
||||
throw new ServiceException("排序参数有误");
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class SysDept extends TreeEntity<SysDept> {
|
||||
* 部门名称
|
||||
*/
|
||||
@NotBlank(message = "部门名称不能为空")
|
||||
@Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符")
|
||||
@Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符")
|
||||
private String deptName;
|
||||
|
||||
/**
|
||||
@@ -51,14 +51,14 @@ public class SysDept extends TreeEntity<SysDept> {
|
||||
/**
|
||||
* 联系电话
|
||||
*/
|
||||
@Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符")
|
||||
@Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符")
|
||||
private String phone;
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,7 +44,7 @@ public class SysDictData extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典标签")
|
||||
@NotBlank(message = "字典标签不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典标签长度不能超过{max}个字符")
|
||||
private String dictLabel;
|
||||
|
||||
/**
|
||||
@@ -52,7 +52,7 @@ public class SysDictData extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典键值")
|
||||
@NotBlank(message = "字典键值不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典键值长度不能超过{max}个字符")
|
||||
private String dictValue;
|
||||
|
||||
/**
|
||||
@@ -60,13 +60,13 @@ public class SysDictData extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典类型")
|
||||
@NotBlank(message = "字典类型不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典类型长度不能超过{max}个字符")
|
||||
private String dictType;
|
||||
|
||||
/**
|
||||
* 样式属性(其他样式扩展)
|
||||
*/
|
||||
@Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "样式属性长度不能超过{max}个字符")
|
||||
private String cssClass;
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,7 @@ public class SysDictType extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典名称")
|
||||
@NotBlank(message = "字典名称不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典类型名称长度不能超过{max}个字符")
|
||||
private String dictName;
|
||||
|
||||
/**
|
||||
@@ -46,7 +46,7 @@ public class SysDictType extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "字典类型")
|
||||
@NotBlank(message = "字典类型不能为空")
|
||||
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "字典类型类型长度不能超过{max}个字符")
|
||||
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)")
|
||||
private String dictType;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class SysMenu extends TreeEntity<SysMenu> {
|
||||
* 菜单名称
|
||||
*/
|
||||
@NotBlank(message = "菜单名称不能为空")
|
||||
@Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符")
|
||||
@Size(min = 0, max = 50, message = "菜单名称长度不能超过{max}个字符")
|
||||
private String menuName;
|
||||
|
||||
/**
|
||||
@@ -44,13 +44,13 @@ public class SysMenu extends TreeEntity<SysMenu> {
|
||||
/**
|
||||
* 路由地址
|
||||
*/
|
||||
@Size(min = 0, max = 200, message = "路由地址不能超过200个字符")
|
||||
@Size(min = 0, max = 200, message = "路由地址不能超过{max}个字符")
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 组件路径
|
||||
*/
|
||||
@Size(min = 0, max = 200, message = "组件路径不能超过255个字符")
|
||||
@Size(min = 0, max = 200, message = "组件路径不能超过{max}个字符")
|
||||
private String component;
|
||||
|
||||
/**
|
||||
@@ -88,7 +88,7 @@ public class SysMenu extends TreeEntity<SysMenu> {
|
||||
* 权限字符串
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "权限标识长度不能超过{max}个字符")
|
||||
private String perms;
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,6 @@ import lombok.NoArgsConstructor;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 角色表 sys_role
|
||||
@@ -44,7 +43,7 @@ public class SysRole extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "角色名称")
|
||||
@NotBlank(message = "角色名称不能为空")
|
||||
@Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符")
|
||||
@Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符")
|
||||
private String roleName;
|
||||
|
||||
/**
|
||||
@@ -52,7 +51,7 @@ public class SysRole extends BaseEntity {
|
||||
*/
|
||||
@ExcelProperty(value = "角色权限")
|
||||
@NotBlank(message = "权限字符不能为空")
|
||||
@Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符")
|
||||
@Size(min = 0, max = 100, message = "权限字符长度不能超过{max}个字符")
|
||||
private String roleKey;
|
||||
|
||||
/**
|
||||
@@ -115,12 +114,6 @@ public class SysRole extends BaseEntity {
|
||||
@TableField(exist = false)
|
||||
private Long[] deptIds;
|
||||
|
||||
/**
|
||||
* 角色菜单权限
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private Set<String> permissions;
|
||||
|
||||
public SysRole(Long roleId) {
|
||||
this.roleId = roleId;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.ruoyi.common.core.domain.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.ruoyi.common.annotation.Sensitive;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
@@ -44,14 +46,14 @@ public class SysUser extends BaseEntity {
|
||||
*/
|
||||
@Xss(message = "用户账号不能包含脚本字符")
|
||||
@NotBlank(message = "用户账号不能为空")
|
||||
@Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符")
|
||||
@Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符")
|
||||
private String userName;
|
||||
|
||||
/**
|
||||
* 用户昵称
|
||||
*/
|
||||
@Xss(message = "用户昵称不能包含脚本字符")
|
||||
@Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符")
|
||||
@Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符")
|
||||
private String nickName;
|
||||
|
||||
/**
|
||||
@@ -64,7 +66,7 @@ public class SysUser extends BaseEntity {
|
||||
*/
|
||||
@Sensitive(strategy = SensitiveStrategy.EMAIL)
|
||||
@Email(message = "邮箱格式不正确")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符")
|
||||
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
|
||||
private String email;
|
||||
|
||||
/**
|
||||
@@ -93,6 +95,12 @@ public class SysUser extends BaseEntity {
|
||||
)
|
||||
private String password;
|
||||
|
||||
@JsonIgnore
|
||||
@JsonProperty
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* 帐号状态(0正常 1停用)
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.ruoyi.common.core.domain.model;
|
||||
|
||||
import com.ruoyi.common.core.domain.dto.RoleDTO;
|
||||
import com.ruoyi.common.helper.LoginHelper;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@@ -116,7 +115,7 @@ public class LoginUser implements Serializable {
|
||||
if (userId == null) {
|
||||
throw new IllegalArgumentException("用户ID不能为空");
|
||||
}
|
||||
return userType + LoginHelper.JOIN_CODE + userId;
|
||||
return userType + ":" + userId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ruoyi.common.core.service;
|
||||
|
||||
/**
|
||||
* 通用 部门服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface DeptService {
|
||||
|
||||
/**
|
||||
* 通过部门ID查询部门名称
|
||||
*
|
||||
* @param deptIds 部门ID串逗号分隔
|
||||
* @return 部门名称串逗号分隔
|
||||
*/
|
||||
String selectDeptNameByIds(String deptIds);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ruoyi.common.core.service;
|
||||
|
||||
/**
|
||||
* 通用 OSS服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface OssService {
|
||||
|
||||
/**
|
||||
* 通过ossId查询对应的url
|
||||
*
|
||||
* @param ossIds ossId串逗号分隔
|
||||
* @return url串逗号分隔
|
||||
*/
|
||||
String selectUrlByIds(String ossIds);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ruoyi.common.core.service;
|
||||
|
||||
/**
|
||||
* 通用 用户服务
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface UserService {
|
||||
|
||||
/**
|
||||
* 通过用户ID查询用户账户
|
||||
*
|
||||
* @param userId 用户ID
|
||||
* @return 用户账户
|
||||
*/
|
||||
String selectUserNameById(Long userId);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.ruoyi.common.encrypt;
|
||||
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import com.ruoyi.common.enums.EncodeType;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 加密上下文 用于encryptor传递必要的参数。
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
@Data
|
||||
public class EncryptContext {
|
||||
|
||||
/**
|
||||
* 默认算法
|
||||
*/
|
||||
private AlgorithmType algorithm;
|
||||
|
||||
/**
|
||||
* 安全秘钥
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 公钥
|
||||
*/
|
||||
private String publicKey;
|
||||
|
||||
/**
|
||||
* 私钥
|
||||
*/
|
||||
private String privateKey;
|
||||
|
||||
/**
|
||||
* 编码方式,base64/hex
|
||||
*/
|
||||
private EncodeType encode;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.ruoyi.common.encrypt;
|
||||
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import com.ruoyi.common.enums.EncodeType;
|
||||
|
||||
/**
|
||||
* 加解者
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public interface IEncryptor {
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
AlgorithmType algorithm();
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
* @return 加密后的字符串
|
||||
*/
|
||||
String encrypt(String value, EncodeType encodeType);
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @return 解密后的字符串
|
||||
*/
|
||||
String decrypt(String value);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ruoyi.common.encrypt.encryptor;
|
||||
|
||||
import com.ruoyi.common.encrypt.EncryptContext;
|
||||
import com.ruoyi.common.encrypt.IEncryptor;
|
||||
|
||||
/**
|
||||
* 所有加密执行者的基类
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public abstract class AbstractEncryptor implements IEncryptor {
|
||||
|
||||
public AbstractEncryptor(EncryptContext context) {
|
||||
// 用户配置校验与配置注入
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.ruoyi.common.encrypt.encryptor;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.symmetric.AES;
|
||||
import com.ruoyi.common.encrypt.EncryptContext;
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import com.ruoyi.common.enums.EncodeType;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* AES算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class AesEncryptor extends AbstractEncryptor {
|
||||
|
||||
private final AES aes;
|
||||
|
||||
public AesEncryptor(EncryptContext context) {
|
||||
super(context);
|
||||
String password = context.getPassword();
|
||||
if (StrUtil.isBlank(password)) {
|
||||
throw new IllegalArgumentException("AES没有获得秘钥信息");
|
||||
}
|
||||
// aes算法的秘钥要求是16位、24位、32位
|
||||
int[] array = {16, 24, 32};
|
||||
if (!ArrayUtil.contains(array, password.length())) {
|
||||
throw new IllegalArgumentException("AES秘钥长度应该为16位、24位、32位,实际为" + password.length() + "位");
|
||||
}
|
||||
aes = SecureUtil.aes(context.getPassword().getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.AES;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
if (encodeType == EncodeType.HEX) {
|
||||
return aes.encryptHex(value);
|
||||
} else {
|
||||
return aes.encryptBase64(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return this.aes.decryptStr(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.ruoyi.common.encrypt.encryptor;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import com.ruoyi.common.encrypt.EncryptContext;
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import com.ruoyi.common.enums.EncodeType;
|
||||
|
||||
/**
|
||||
* Base64算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class Base64Encryptor extends AbstractEncryptor {
|
||||
|
||||
public Base64Encryptor(EncryptContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.BASE64;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
return Base64.encode(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return Base64.decodeStr(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.ruoyi.common.encrypt.encryptor;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.RSA;
|
||||
import com.ruoyi.common.encrypt.EncryptContext;
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import com.ruoyi.common.enums.EncodeType;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* RSA算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class RsaEncryptor extends AbstractEncryptor {
|
||||
|
||||
private final RSA rsa;
|
||||
|
||||
public RsaEncryptor(EncryptContext context) {
|
||||
super(context);
|
||||
String privateKey = context.getPrivateKey();
|
||||
String publicKey = context.getPublicKey();
|
||||
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
|
||||
throw new IllegalArgumentException("RSA公私钥均需要提供,公钥加密,私钥解密。");
|
||||
}
|
||||
this.rsa = SecureUtil.rsa(Base64.decode(privateKey), Base64.decode(publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.RSA;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
if (encodeType == EncodeType.HEX) {
|
||||
return rsa.encryptHex(value, KeyType.PublicKey);
|
||||
} else {
|
||||
return rsa.encryptBase64(value, KeyType.PublicKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return this.rsa.decryptStr(value, KeyType.PrivateKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.ruoyi.common.encrypt.encryptor;
|
||||
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.crypto.SmUtil;
|
||||
import cn.hutool.crypto.asymmetric.KeyType;
|
||||
import cn.hutool.crypto.asymmetric.SM2;
|
||||
import com.ruoyi.common.encrypt.EncryptContext;
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import com.ruoyi.common.enums.EncodeType;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* sm2算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class Sm2Encryptor extends AbstractEncryptor {
|
||||
|
||||
private final SM2 sm2;
|
||||
|
||||
public Sm2Encryptor(EncryptContext context) {
|
||||
super(context);
|
||||
String privateKey = context.getPrivateKey();
|
||||
String publicKey = context.getPublicKey();
|
||||
if (StringUtils.isAnyEmpty(privateKey, publicKey)) {
|
||||
throw new IllegalArgumentException("SM2公私钥均需要提供,公钥加密,私钥解密。");
|
||||
}
|
||||
this.sm2 = SmUtil.sm2(Base64.decode(privateKey), Base64.decode(publicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.SM2;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
if (encodeType == EncodeType.HEX) {
|
||||
return sm2.encryptHex(value, KeyType.PublicKey);
|
||||
} else {
|
||||
return sm2.encryptBase64(value, KeyType.PublicKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return this.sm2.decryptStr(value, KeyType.PrivateKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.ruoyi.common.encrypt.encryptor;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SmUtil;
|
||||
import cn.hutool.crypto.symmetric.SM4;
|
||||
import com.ruoyi.common.encrypt.EncryptContext;
|
||||
import com.ruoyi.common.enums.AlgorithmType;
|
||||
import com.ruoyi.common.enums.EncodeType;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* sm4算法实现
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public class Sm4Encryptor extends AbstractEncryptor {
|
||||
|
||||
private final SM4 sm4;
|
||||
|
||||
public Sm4Encryptor(EncryptContext context) {
|
||||
super(context);
|
||||
String password = context.getPassword();
|
||||
if (StrUtil.isBlank(password)) {
|
||||
throw new IllegalArgumentException("SM4没有获得秘钥信息");
|
||||
}
|
||||
// sm4算法的秘钥要求是16位长度
|
||||
if (16 != password.length()) {
|
||||
throw new IllegalArgumentException("SM4秘钥长度应该为16位,实际为" + password.length() + "位");
|
||||
}
|
||||
this.sm4 = SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前算法
|
||||
*/
|
||||
@Override
|
||||
public AlgorithmType algorithm() {
|
||||
return AlgorithmType.SM4;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
* @param encodeType 加密后的编码格式
|
||||
*/
|
||||
@Override
|
||||
public String encrypt(String value, EncodeType encodeType) {
|
||||
if (encodeType == EncodeType.HEX) {
|
||||
return sm4.encryptHex(value);
|
||||
} else {
|
||||
return sm4.encryptBase64(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密
|
||||
*
|
||||
* @param value 待加密字符串
|
||||
*/
|
||||
@Override
|
||||
public String decrypt(String value) {
|
||||
return this.sm4.decryptStr(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.ruoyi.common.enums;
|
||||
|
||||
import com.ruoyi.common.encrypt.encryptor.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 算法名称
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum AlgorithmType {
|
||||
|
||||
/**
|
||||
* 默认走yml配置
|
||||
*/
|
||||
DEFAULT(null),
|
||||
|
||||
/**
|
||||
* base64
|
||||
*/
|
||||
BASE64(Base64Encryptor.class),
|
||||
|
||||
/**
|
||||
* aes
|
||||
*/
|
||||
AES(AesEncryptor.class),
|
||||
|
||||
/**
|
||||
* rsa
|
||||
*/
|
||||
RSA(RsaEncryptor.class),
|
||||
|
||||
/**
|
||||
* sm2
|
||||
*/
|
||||
SM2(Sm2Encryptor.class),
|
||||
|
||||
/**
|
||||
* sm4
|
||||
*/
|
||||
SM4(Sm4Encryptor.class);
|
||||
|
||||
private final Class<? extends AbstractEncryptor> clazz;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.ruoyi.common.enums;
|
||||
|
||||
/**
|
||||
* 编码类型
|
||||
*
|
||||
* @author 老马
|
||||
* @version 4.6.0
|
||||
*/
|
||||
public enum EncodeType {
|
||||
|
||||
/**
|
||||
* 默认使用yml配置
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* base64编码
|
||||
*/
|
||||
BASE64,
|
||||
|
||||
/**
|
||||
* 16进制编码
|
||||
*/
|
||||
HEX;
|
||||
|
||||
}
|
||||
@@ -25,7 +25,7 @@ public class XssFilter implements Filter {
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
String tempExcludes = filterConfig.getInitParameter("excludes");
|
||||
if (StringUtils.isNotEmpty(tempExcludes)) {
|
||||
String[] url = tempExcludes.split(",");
|
||||
String[] url = tempExcludes.split(StringUtils.SEPARATOR);
|
||||
for (int i = 0; url != null && i < url.length; i++) {
|
||||
excludes.add(url[i]);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.ruoyi.common.helper;
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy;
|
||||
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@@ -44,4 +46,19 @@ public class DataPermissionHelper {
|
||||
}
|
||||
throw new NullPointerException("data permission context type exception");
|
||||
}
|
||||
|
||||
/**
|
||||
* 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭)
|
||||
*/
|
||||
public static void enableIgnore() {
|
||||
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭忽略数据权限
|
||||
*/
|
||||
public static void disableIgnore() {
|
||||
InterceptorIgnoreHelper.clearIgnoreStrategy();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
package com.ruoyi.common.helper;
|
||||
|
||||
import cn.dev33.satoken.context.SaHolder;
|
||||
import cn.dev33.satoken.context.model.SaStorage;
|
||||
import cn.dev33.satoken.stp.SaLoginModel;
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.core.domain.model.LoginUser;
|
||||
import com.ruoyi.common.enums.DeviceType;
|
||||
import com.ruoyi.common.enums.UserType;
|
||||
import com.ruoyi.common.exception.UtilException;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 登录鉴权助手
|
||||
*
|
||||
* <p>
|
||||
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
|
||||
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
|
||||
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
|
||||
*
|
||||
* <p>
|
||||
* 多用户体系 针对 多种用户类型 但权限控制不一致
|
||||
* 可以组成 多用户类型表与多设备类型 分别控制权限
|
||||
*
|
||||
@@ -28,8 +28,8 @@ import lombok.NoArgsConstructor;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class LoginHelper {
|
||||
|
||||
public static final String JOIN_CODE = ":";
|
||||
public static final String LOGIN_USER_KEY = "loginUser";
|
||||
public static final String USER_KEY = "userId";
|
||||
|
||||
/**
|
||||
* 登录系统
|
||||
@@ -37,9 +37,7 @@ public class LoginHelper {
|
||||
* @param loginUser 登录用户信息
|
||||
*/
|
||||
public static void login(LoginUser loginUser) {
|
||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
||||
StpUtil.login(loginUser.getLoginId());
|
||||
setLoginUser(loginUser);
|
||||
loginByDevice(loginUser, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,15 +47,14 @@ public class LoginHelper {
|
||||
* @param loginUser 登录用户信息
|
||||
*/
|
||||
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
|
||||
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
|
||||
StpUtil.login(loginUser.getLoginId(), deviceType.getDevice());
|
||||
setLoginUser(loginUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户数据(多级缓存)
|
||||
*/
|
||||
public static void setLoginUser(LoginUser loginUser) {
|
||||
SaStorage storage = SaHolder.getStorage();
|
||||
storage.set(LOGIN_USER_KEY, loginUser);
|
||||
storage.set(USER_KEY, loginUser.getUserId());
|
||||
SaLoginModel model = new SaLoginModel();
|
||||
if (ObjectUtil.isNotNull(deviceType)) {
|
||||
model.setDevice(deviceType.getDevice());
|
||||
}
|
||||
StpUtil.login(loginUser.getLoginId(), model.setExtra(USER_KEY, loginUser.getUserId()));
|
||||
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
|
||||
}
|
||||
|
||||
@@ -74,21 +71,28 @@ public class LoginHelper {
|
||||
return loginUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户基于token
|
||||
*/
|
||||
public static LoginUser getLoginUser(String token) {
|
||||
return (LoginUser) StpUtil.getTokenSessionByToken(token).get(LOGIN_USER_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户id
|
||||
*/
|
||||
public static Long getUserId() {
|
||||
LoginUser loginUser = getLoginUser();
|
||||
if (ObjectUtil.isNull(loginUser)) {
|
||||
String loginId = StpUtil.getLoginIdAsString();
|
||||
String[] strs = StringUtils.split(loginId, JOIN_CODE);
|
||||
if (!ArrayUtil.containsAny(strs, UserType.values())) {
|
||||
throw new UtilException("登录用户: LoginId异常 => " + loginId);
|
||||
Long userId;
|
||||
try {
|
||||
userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));
|
||||
if (ObjectUtil.isNull(userId)) {
|
||||
userId = Convert.toLong(StpUtil.getExtra(USER_KEY));
|
||||
SaHolder.getStorage().set(USER_KEY, userId);
|
||||
}
|
||||
// 用户id在总是在最后
|
||||
return Long.parseLong(strs[strs.length - 1]);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
return loginUser.getUserId();
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,9 @@ import java.util.Objects;
|
||||
* 字典数据json序列化工具
|
||||
*
|
||||
* @author itino
|
||||
* @deprecated 建议使用通用翻译注解
|
||||
*/
|
||||
@Deprecated
|
||||
@Slf4j
|
||||
public class DictDataJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.ruoyi.common.translation;
|
||||
|
||||
/**
|
||||
* 翻译接口 (实现类需标注 {@link com.ruoyi.common.annotation.TranslationType} 注解标明翻译类型)
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public interface TranslationInterface<T> {
|
||||
|
||||
/**
|
||||
* 翻译
|
||||
*
|
||||
* @param key 需要被翻译的键(不为空)
|
||||
* @return 返回键对应的值
|
||||
*/
|
||||
T translation(Object key, String other);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ruoyi.common.translation.handler;
|
||||
|
||||
import com.fasterxml.jackson.databind.BeanDescription;
|
||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
|
||||
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bean 序列化修改器 解决 Null 被单独处理问题
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
public class TranslationBeanSerializerModifier extends BeanSerializerModifier {
|
||||
|
||||
@Override
|
||||
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
|
||||
List<BeanPropertyWriter> beanProperties) {
|
||||
for (BeanPropertyWriter writer : beanProperties) {
|
||||
// 如果序列化器为 TranslationHandler 的话 将 Null 值也交给他处理
|
||||
if (writer.getSerializer() instanceof TranslationHandler) {
|
||||
writer.assignNullSerializer(writer.getSerializer());
|
||||
}
|
||||
}
|
||||
return beanProperties;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.ruoyi.common.translation.handler;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
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.Translation;
|
||||
import com.ruoyi.common.translation.TranslationInterface;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.reflect.ReflectUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 翻译处理器
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
public class TranslationHandler extends JsonSerializer<Object> implements ContextualSerializer {
|
||||
|
||||
/**
|
||||
* 全局翻译实现类映射器
|
||||
*/
|
||||
public static final Map<String, TranslationInterface<?>> TRANSLATION_MAPPER = new ConcurrentHashMap<>();
|
||||
|
||||
private Translation translation;
|
||||
|
||||
@Override
|
||||
public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
TranslationInterface<?> trans = TRANSLATION_MAPPER.get(translation.type());
|
||||
if (ObjectUtil.isNotNull(trans)) {
|
||||
// 如果映射字段不为空 则取映射字段的值
|
||||
if (StringUtils.isNotBlank(translation.mapper())) {
|
||||
value = ReflectUtils.invokeGetter(gen.getCurrentValue(), translation.mapper());
|
||||
}
|
||||
// 如果为 null 直接写出
|
||||
if (ObjectUtil.isNull(value)) {
|
||||
gen.writeNull();
|
||||
return;
|
||||
}
|
||||
Object result = trans.translation(value, translation.other());
|
||||
gen.writeObject(result);
|
||||
} else {
|
||||
gen.writeObject(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
|
||||
Translation translation = property.getAnnotation(Translation.class);
|
||||
if (Objects.nonNull(translation)) {
|
||||
this.translation = translation;
|
||||
return this;
|
||||
}
|
||||
return prov.findValueSerializer(property.getType(), property);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ruoyi.common.translation.impl;
|
||||
|
||||
import com.ruoyi.common.annotation.TranslationType;
|
||||
import com.ruoyi.common.constant.TransConstant;
|
||||
import com.ruoyi.common.core.service.DeptService;
|
||||
import com.ruoyi.common.translation.TranslationInterface;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 部门翻译实现
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
@TranslationType(type = TransConstant.DEPT_ID_TO_NAME)
|
||||
public class DeptNameTranslationImpl implements TranslationInterface<String> {
|
||||
|
||||
private final DeptService deptService;
|
||||
|
||||
public String translation(Object key, String other) {
|
||||
return deptService.selectDeptNameByIds(key.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.ruoyi.common.translation.impl;
|
||||
|
||||
import com.ruoyi.common.annotation.TranslationType;
|
||||
import com.ruoyi.common.constant.TransConstant;
|
||||
import com.ruoyi.common.core.service.DictService;
|
||||
import com.ruoyi.common.translation.TranslationInterface;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 字典翻译实现
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
@TranslationType(type = TransConstant.DICT_TYPE_TO_LABEL)
|
||||
public class DictTypeTranslationImpl implements TranslationInterface<String> {
|
||||
|
||||
private final DictService dictService;
|
||||
|
||||
public String translation(Object key, String other) {
|
||||
if (key instanceof String && StringUtils.isNotBlank(other)) {
|
||||
return dictService.getDictLabel(other, key.toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.ruoyi.common.translation.impl;
|
||||
|
||||
import com.ruoyi.common.annotation.TranslationType;
|
||||
import com.ruoyi.common.constant.TransConstant;
|
||||
import com.ruoyi.common.core.service.OssService;
|
||||
import com.ruoyi.common.translation.TranslationInterface;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* OSS翻译实现
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
@TranslationType(type = TransConstant.OSS_ID_TO_URL)
|
||||
public class OssUrlTranslationImpl implements TranslationInterface<String> {
|
||||
|
||||
private final OssService ossService;
|
||||
|
||||
public String translation(Object key, String other) {
|
||||
return ossService.selectUrlByIds(key.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ruoyi.common.translation.impl;
|
||||
|
||||
import com.ruoyi.common.annotation.TranslationType;
|
||||
import com.ruoyi.common.constant.TransConstant;
|
||||
import com.ruoyi.common.core.service.UserService;
|
||||
import com.ruoyi.common.translation.TranslationInterface;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 用户名翻译实现
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Component
|
||||
@AllArgsConstructor
|
||||
@TranslationType(type = TransConstant.USER_ID_TO_NAME)
|
||||
public class UserNameTranslationImpl implements TranslationInterface<String> {
|
||||
|
||||
private final UserService userService;
|
||||
|
||||
public String translation(Object key, String other) {
|
||||
if (key instanceof Long) {
|
||||
return userService.selectUserNameById((Long) key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import org.springframework.cglib.beans.BeanCopier;
|
||||
import org.springframework.cglib.beans.BeanMap;
|
||||
import org.springframework.cglib.core.Converter;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -135,6 +136,25 @@ public class BeanCopyUtils {
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* map拷贝到map
|
||||
*
|
||||
* @param map 数据来源
|
||||
* @param clazz 返回的对象类型
|
||||
* @return map对象
|
||||
*/
|
||||
public static <T, V> Map<String, V> mapToMap(Map<String, T> map, Class<V> clazz) {
|
||||
if (MapUtil.isEmpty(map)) {
|
||||
return null;
|
||||
}
|
||||
if (ObjectUtil.isNull(clazz)) {
|
||||
return null;
|
||||
}
|
||||
Map<String, V> copyMap = new LinkedHashMap<>(map.size());
|
||||
map.forEach((k, v) -> copyMap.put(k, copy(v, clazz)));
|
||||
return copyMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* BeanCopier属性缓存<br>
|
||||
* 缓存用于防止多次反射造成的性能问题
|
||||
@@ -174,7 +194,7 @@ public class BeanCopyUtils {
|
||||
private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) {
|
||||
final StringBuilder key = StrUtil.builder()
|
||||
.append(srcClass.getName()).append('#').append(targetClass.getName());
|
||||
if(null != converter){
|
||||
if (null != converter) {
|
||||
key.append('#').append(converter.getClass().getName());
|
||||
}
|
||||
return key.toString();
|
||||
|
||||
@@ -94,7 +94,7 @@ public class ServletUtils extends ServletUtil {
|
||||
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(), ","));
|
||||
params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR));
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class StreamUtils {
|
||||
* @return 拼接后的list
|
||||
*/
|
||||
public static <E> String join(Collection<E> collection, Function<E, String> function) {
|
||||
return join(collection, function, ",");
|
||||
return join(collection, function, StringUtils.SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.ruoyi.common.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Validator;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 字符串工具类
|
||||
@@ -20,6 +20,8 @@ import java.util.Set;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
|
||||
public static final String SEPARATOR = ",";
|
||||
|
||||
/**
|
||||
* 获取参数不为空值
|
||||
*
|
||||
@@ -224,7 +226,6 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
*
|
||||
* @param pattern 匹配规则
|
||||
* @param url 需要匹配的url
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatch(String pattern, String url) {
|
||||
AntPathMatcher matcher = new AntPathMatcher();
|
||||
@@ -234,23 +235,23 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
/**
|
||||
* 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。
|
||||
*
|
||||
* @param num 数字对象
|
||||
* @param num 数字对象
|
||||
* @param size 字符串指定长度
|
||||
* @return 返回数字的字符串格式,该字符串为指定长度。
|
||||
*/
|
||||
public static final String padl(final Number num, final int size) {
|
||||
public static String padl(final Number num, final int size) {
|
||||
return padl(num.toString(), size, '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。
|
||||
*
|
||||
* @param s 原始字符串
|
||||
* @param s 原始字符串
|
||||
* @param size 字符串指定长度
|
||||
* @param c 用于补齐的字符
|
||||
* @param c 用于补齐的字符
|
||||
* @return 返回指定长度的字符串,由原字符串左补齐或截取得到。
|
||||
*/
|
||||
public static final String padl(final String s, final int size, final char c) {
|
||||
public static String padl(final String s, final int size, final char c) {
|
||||
final StringBuilder sb = new StringBuilder(size);
|
||||
if (s != null) {
|
||||
final int len = s.length();
|
||||
@@ -270,4 +271,55 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串(分隔符默认逗号)
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @return 分割后的数据列表
|
||||
*/
|
||||
public static List<String> splitList(String str) {
|
||||
return splitTo(str, Convert::toStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符
|
||||
* @return 分割后的数据列表
|
||||
*/
|
||||
public static List<String> splitList(String str, String separator) {
|
||||
return splitTo(str, separator, Convert::toStr);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串自定义转换(分隔符默认逗号)
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param mapper 自定义转换
|
||||
* @return 分割后的数据列表
|
||||
*/
|
||||
public static <T> List<T> splitTo(String str, Function<? super Object, T> mapper) {
|
||||
return splitTo(str, SEPARATOR, mapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切分字符串自定义转换
|
||||
*
|
||||
* @param str 被切分的字符串
|
||||
* @param separator 分隔符
|
||||
* @param mapper 自定义转换
|
||||
* @return 分割后的数据列表
|
||||
*/
|
||||
public static <T> List<T> splitTo(String str, String separator, Function<? super Object, T> mapper) {
|
||||
if (isBlank(str)) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
return StrUtil.split(str, separator)
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(mapper)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ public class ExcelUtil {
|
||||
*/
|
||||
public static String convertByExp(String propertyValue, String converterExp, String separator) {
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
String[] convertSource = converterExp.split(",");
|
||||
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
|
||||
for (String item : convertSource) {
|
||||
String[] itemArray = item.split("=");
|
||||
if (StringUtils.containsAny(propertyValue, separator)) {
|
||||
@@ -299,7 +299,7 @@ public class ExcelUtil {
|
||||
*/
|
||||
public static String reverseByExp(String propertyValue, String converterExp, String separator) {
|
||||
StringBuilder propertyString = new StringBuilder();
|
||||
String[] convertSource = converterExp.split(",");
|
||||
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
|
||||
for (String item : convertSource) {
|
||||
String[] itemArray = item.split("=");
|
||||
if (StringUtils.containsAny(propertyValue, separator)) {
|
||||
|
||||
Reference in New Issue
Block a user