Commit e68b3ab2 by zwb

招聘会演示平台搭建与简历导入

parent 34583954
Showing with 4831 additions and 0 deletions
package com.bkty.system.api.model;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
/**
* 用户信息
*
* @author ruoyi
*/
@Data
@NoArgsConstructor
public class LoginUser implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 租户ID(学校ID)
*/
private String tenantId;
/**
* 租户名称(学校名称)
*/
private String tenantName;
/**
* 用户ID
*/
private Long userId;
/**
* 部门ID
*/
private Long deptId;
/**
* 部门类别编码
*/
private String deptCategory;
/**
* 部门名
*/
private String deptName;
/**
* 用户唯一标识
*/
private String token;
/**
* 用户类型
*/
private String userType;
/**
* 登录时间
*/
private Long loginTime;
/**
* 过期时间
*/
private Long expireTime;
/**
* 登录IP地址
*/
private String ipaddr;
/**
* 登录地点
*/
private String loginLocation;
/**
* 浏览器类型
*/
private String browser;
/**
* 操作系统
*/
private String os;
/**
* 菜单权限
*/
private Set<String> menuPermission;
/**
* 角色权限
*/
private Set<String> rolePermission;
/**
* 用户名
*/
private String username;
/**
* 用户昵称
*/
private String nickname;
/**
* 密码
*/
private String password;
/**
* 角色对象
*/
private List<RoleDTO> roles;
/**
* 数据权限 当前角色ID
*/
private Long roleId;
/**
* 客户端
*/
private String clientKey;
/**
* 设备类型
*/
private String deviceType;
/**
* 获取登录id
*/
public String getLoginId() {
if (userType == null) {
throw new IllegalArgumentException("用户类型不能为空");
}
if (userId == null) {
throw new IllegalArgumentException("用户ID不能为空");
}
return userType + ":" + userId;
}
}
package com.bkty.domain.vo;
import lombok.Data;
import java.util.List;
/**
* 登录租户对象
*
* @author Michelle.Chung
*/
@Data
public class LoginTenantVo {
/**
* 租户开关
*/
private Boolean tenantEnabled;
/**
* 租户对象列表
*/
private List<TenantListVo> voList;
}
package com.bkty.domain.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
* 登录验证信息
*
* @author Michelle.Chung
*/
@Data
public class LoginVo {
/**
* 授权令牌
*/
@JsonProperty("access_token")
private String accessToken;
/**
* 刷新令牌
*/
@JsonProperty("refresh_token")
private String refreshToken;
/**
* 授权令牌 access_token 的有效期
*/
@JsonProperty("expire_in")
private Long expireIn;
/**
* 刷新令牌 refresh_token 的有效期
*/
@JsonProperty("refresh_expire_in")
private Long refreshExpireIn;
/**
* 应用id
*/
@JsonProperty("client_id")
private String clientId;
/**
* 令牌权限
*/
private String scope;
/**
* 用户 openid
*/
private String openid;
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/${project.artifactId}"/>
<!-- 日志输出格式 -->
<property name="console.log.pattern"
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<include resource="logback-common.xml" />
<include resource="logback-logstash.xml" />
<!-- 开启 skywalking 日志收集 -->
<include resource="logback-skylog.xml" />
<!--系统操作日志-->
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>
package org.dromara.common.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解用于标记 JSON 字段映射
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface JxgInitField {
String value();
String format() default "";
}
package org.dromara.common.core.domain.model;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 用户登录对象
*
* @author Lion Li
*/
@Data
@NoArgsConstructor
public class LoginBody {
/**
* 客户端id
*/
//@NotBlank(message = "{auth.clientid.not.blank}")
private String clientId;
/**
* 授权类型
*/
@NotBlank(message = "{auth.grant.type.not.blank}")
private String grantType;
/**
* 租户ID
*/
private String tenantId;
/**
* 验证码
*/
private String code;
/**
* 唯一标识
*/
private String uuid;
}
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录类型
*
* @author Lion Li
*/
@Getter
@AllArgsConstructor
public enum LoginType {
/**
* 密码登录
*/
PASSWORD("user.password.retry.limit.exceed", "user.password.retry.limit.count"),
/**
* 短信登录
*/
SMS("sms.code.retry.limit.exceed", "sms.code.retry.limit.count"),
/**
* 邮箱登录
*/
EMAIL("email.code.retry.limit.exceed", "email.code.retry.limit.count"),
/**
* 小程序登录
*/
XCX("", "");
/**
* 登录重试超出限制提示
*/
final String retryLimitExceed;
/**
* 登录重试限制计数提示
*/
final String retryLimitCount;
}
package org.dromara.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.*;
/**
* 模拟面试平均分
* @author Herbert
*/
@Getter
@AllArgsConstructor
public enum MockInterviewAverageEnum {
/**
* 已作废
*/
FIRST("first", "91及以上"),
/**
* 已完成
*/
SECOND("second", "81-90"),
/**
* 待审核
*/
THIRD("third", "71-80"),
/**
* 草稿
*/
FOURTH("fourth", "61-70"),
/**
* 已撤销
*/
FIFTH("fifth", "60及以下");
/**
* code码
*/
private final String code;
/**
* 名称
*/
private final String name;
/**
*
* 将枚举值转换成列表,方便下拉框使用
* @return
* @author Herbert
*/
public static List<Map<String, Object>> getMockInterviewAverage(){
Map<String, Object> map = null;
List<Map<String, Object>> lstResult = new ArrayList<>(10);
for (MockInterviewAverageEnum typeEnum : EnumSet.allOf(MockInterviewAverageEnum.class)) {
map = new HashMap<>(16);
map.put("key", typeEnum.getCode());
map.put("value", typeEnum.getName());
lstResult.add(map);
}
return lstResult;
}
}
package org.dromara.common.core.exception;
/**
* @author jiangxiaoge
*/
public class JxgException extends RuntimeException {
private Integer code;
public JxgException() {
}
public JxgException(String message) {
super(message);
}
public JxgException(String message, int code) {
super(message);
this.code = code;
}
public Integer getCode() {
return code;
}
public JxgException(Throwable cause) {
super(cause);
}
public JxgException(String message, Throwable cause) {
super(message, cause);
}
protected JxgException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package org.dromara.common.core.utils;
import com.fasterxml.jackson.core.json.JsonWriteFeature;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class JSON {
private static final ObjectMapper objectMapper = new ObjectMapper();
static {
// 反序列时,遇到未知的字段,不报错。比如Json中有key1字段,Java的object中没有key1字段,如果不设置成false,反序列时会报错
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 数字类型统一转成String, 因为前端Js不支持long类型的数据,前端读取到long类型数据会丢失后三位数
objectMapper.configure(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS.mappedFeature(), true);
}
public static <T> T convert(Object obj, Class<T> returnType) {
if (obj == null) {
return null;
}
return parse(stringify(obj), returnType);
}
/**
* Object to json
*
* @param obj
* @return
*/
public static String stringify(Object obj) {
try {
if (obj == null) {
return null;
} else if (obj instanceof String) {
return obj.toString();
}
return objectMapper.writeValueAsString(obj);
} catch (Exception e) {
throw new IllegalArgumentException("对象转化成json字符串出错", e);
}
}
/**
* json to Object
*
* @param json
* @param targetType
* @param <T>
* @return
*/
public static <T> T parse(String json, Type targetType) {
try {
return objectMapper.readValue(json, TypeFactory.defaultInstance().constructType(targetType));
} catch (IOException e) {
throw new IllegalArgumentException("将JSON转换为对象时发生错误:" + json, e);
}
}
public static <T> T parse(String json, Class<T> targetType) {
try {
return objectMapper.readValue(json, TypeFactory.defaultInstance().constructType(targetType));
} catch (IOException e) {
throw new IllegalArgumentException("将JSON转换为对象时发生错误:" + json, e);
}
}
/**
* json to Object
*/
public static <T> T parse(String json, TypeReference<T> typeReference) {
if (json != null && !json.isEmpty()) {
try {
return objectMapper.readValue(json, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
return null;
}
}
public static Map<String, Object> parseToMap(String json) {
return parse(json, HashMap.class);
}
}
package org.dromara.common.core.utils;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.Map;
public class JWTGenerator {
/*private static final String PRIVATE_KEY_STR =
"""
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCrun+MXy7xp47k
QQ3lL8e/lkjz7H+iMKLFIgCGhN9drxcozbyzohIFiaiVz9YTggu81hj/3KB3wHrR
J8BrTrF9Cql8uPlLTyAMdas73vFNMaX9fiJAS4ve/H3a9CA12dF6c67E+tTMJ0GX
JMesxfSnyNTIiTkR5WkFbfYAbwQPSPWQYyrpmC+i1UlwIIgO0XIyk+RPR/irBYjg
iCAHdNRLG1bp2qVFS5O8xGV9arC7v4ox8AjgjUoUV6glEdRmaa2VWQ+/Hmb0Uu64
sL8VvAh/iKbqv6NJ/Pwm2BVSUizNLLS/oennHvqL6xhlkxFcGiTkMSc0PIbokE70
nBm8rhRJAgMBAAECggEAGlDjGOTBMeEGga4CbhurVY23MkIMsRMY7nKud+7Gt/mL
6AHXKAPvFRAqWILYAHdOSyrX++WYZaGGcKTR1N/jTREkNeu2sDa454/kpLEec0Dn
efgpUYcCMTKqKrJ8eSQe+M/feWoi0AI4B+xn+O8A9fFNwkldCIQKWv2l1B0vFxRL
yVDRW8/8Jw9LWXfvsvCmejxwfMZQ6eqSN0PzojZ89HoRkmbtRayYWeGc3tAL8rqo
cwJd20ipCLsPbahviKByzsLR00hQOdLhSL5y1JbzrqHLHvCTaQSiJIOz9U7YY2S8
NCd8S0zI/8S8Nx/sHdgzM80jDFVynlQuDCXmTYtBnQKBgQDdhxRXxQFyBE45WbfX
4MHloL8cTY5DeIv3RB/v/DrjL1/TfiOIHsIXJXGFIwa8FvHfizFqRM4RUl+a0ncv
A+/hqUfyFkoeykua6TgBz4j8cieHROl2wIYIcutArGyToqFb9BndOUHe/gVX/99w
G/zZVSsgcUkIA5rqe/FMHK6lhwKBgQDGc5ZidZaqRVnOlVF82G36+K/g1DRgITsj
s1gUj57Nk1u4H3X3/ucjQrjYImPioLWf5yNV5KlrWJ1g8DbyvxUzTkp0THlYPr/m
iu9OVsyYIfAhine5IHf3rPHXGEb6tjqAaPFJtWgmbLC02ovOyEAjqotlXq9PYs9M
2+CNC+jrrwKBgGJd8eWLYDntuYJz2VOtxMufVl1N/ubs51HT2U6qNMfeqHF0KHjw
F4VJph/VTs9EbQCzgSzMAqjkZ/mKjUu+Ns2AaqcPyLflYi8Wcj0zl4NpeS3ypqyJ
jrq/tMDSD6B67aW0PavjGeHX8C2EPAY/F0a2L+FHDIPXffmzMZF3hwGPAoGBAK+E
TY55Z67itWIzyCV+Gg9tYcichlI7Md4KYa2VtKMnYGiBUsnq5azW9zsZd3RaJSju
ud7e5USu1EqHSmH5xJti9BDWDkCRBxK08KA9vqgS5BoWKHtQjyo8CIzvyN82ypkF
t9o0GpBZcxQRcrOJgsjIro8SZkT1JFla4sh8rx27AoGADI7cEovOc2lC2FoAuWgY
fEsufAANC3hM5EbifYlCqvel7lOvFGRY7Aqr8YQLRB0fgWmDh0tClblXEb9pNXqs
mKGnHYNNVXCcefH5uxZLymziZcFDUWFc8u3hZ+Pk0U1mDM2HkWQ/y7dMj6GamprR
M3UzJ3TjhGUVxxaO1UX3unA=
""";*/
// 示例私钥和公钥字符串 (Base64 编码)
private static final String PRIVATE_KEY_STR = """
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQClNKJ1PjaDGyfi
FXssYUIqDPZedusSiwY569sQ+tMewM05rGP3eHe3lqdkTy1yFHI+LGe14WbFMEwA
pp7cU4bjbcvZq5qGG63IBXBSO+F293y+Ggh1t8gatxhKrU8+r+MKckqdIJFnjCzH
/ZCgzSXnXpYy4RjkW6C3eP+gN7dLgnCv9dTYyn3L7b+6cbYNJ9Xy4NSPC73n963l
8wyF7R51PYnRc1S+wdRMCruNeU7Lsayoba1T54P8aE+IFSdegbrOcKF2LQxUit7k
QV/Q/jjvS6QpFyMIRyk9CX1T+iAdlKM+sbIcRwp9dbNulNlsldZJVkPAPAKhzOC3
cJ4YtjcfAgMBAAECggEAHwZU8gambxWk9kjJcmqrZcE8bT4Q5FEWcL8xyy0Hg9F6
WPfeaCWH+gLBWc2iv7dGQxXMvjPfvG1XAJJJ6B+Jh+FhNHYNFV1SU+ttjgvvygst
MngqGY1Td2zmE3Qbmy6lyRrwlzR8CgGhkUc1p3UPRrWVKGXsakoo0vvWqax88CF4
MK5E1jTLDQpQArAqL66K7AlkB9aXz5q+Sb0K6BJnoCfwJ13ZqlwPuT+2c8whtqrm
G2/t3D/NkKXOxmJuVVJGSHz8yoYEP1sD8HmCQ0W7T8hGHnVtznQKeT5Fm2oSt/0n
S0YQU5ar2Nvf07YL2JLlaXBCxnWZjfKBc/7IQsL9dQKBgQDPKbQXa4Yqcon1qpL6
kP9qWb1qlKqUdcNhNkqWk2/Vjxugq7uZpL0Y0Wha2l2ldFVGk4x18ZfO6eCw7w3D
8tdPpJZQaq4XFwO2tw+sHr7ozImTspoeOxGgJd9rLTJNad/u0A/osHUNWexvoT/A
LAEUJKZg/LUb023LHsTMa++T/QKBgQDMJs4RqIOQSYWhYGUVkws8O/TvKwy681+J
Bz3IgwDqXuCEuji2iBbOW/nWwxdjBP/s2au+L+34b205u6WPVdAN+6Olw6/SnEdD
OkIBI/PRBlJ+UJ7QUyfvAZQygKUFdsDQq4x24d9gCzH48J9wFKqj/yY24tgttqW8
F7Ggnc4MSwKBgQCkHca1UxJ76DeRLqu340zlJOjhWZt1d4HsLoWSmoTba78YX1SE
mLTRx3vjleDgZbP08IkzGs0IN9IweYGvPWADkUEfvhPi6PGhO48n7UeSyA8pujFM
R8Fj3P0NPEYAfCLSX3RrA/gHNrkPinAlvvavzYUdirWzsKDnbJ290LiAxQKBgQCr
exjy+jJwm2E02WxvP9FbKh2mW+nYix8/UabS6z90nndG5clHgW5urhFhFQRq90Ml
pv08xrPET4xWx0jc6j7Vz/54+bORXBX74U6Z+KIPfJkEYyHTaVYMy6248hgrS8HQ
RZi6LyZbXWv/Z9b05VckY+ish8UAOQBjyx84A/WUmwKBgQCb2YKK577akcE1xRmT
l+53LjrwXClOpVd14ViRpXwHmDTQ8O46lMD9FYLkhaudBvVRkEBJs2BMdmL23m/+
cR3fwB4P+eqC5gOJDDQ0mIewJBpxO0P8ZAm+pRhF/c4OUVZ8NF99f1s5BlTbUgQH
LR7zbl/RpqAStCs3+mJFacJBkQ==
""";
private static final String PUBLIC_KEY_STR = "nE8ymC2sb8q84w6hiSijoKAuxH93wygob9BAOP18cyI";
/*private static final String PUBLIC_KEY_STR = "NILbA9z8J2CbLBnAgFMN972DYp6tCXY3FNW91RQ5bOI";*/
public static void main(String[] args) throws Exception {
// 加载 RSA 私钥
PrivateKey privateKey = loadPrivateKey(PRIVATE_KEY_STR.replace("\n",""));
// 生成 JWT
String jwt = Jwts.builder()
.setSubject("user123") // 设置 JWT 的主题
.setIssuer("myapp") // 设置发行者
.setIssuedAt(new Date())// 设置签发时间
.setExpiration(new Date(System.currentTimeMillis() + 3600_000)) // 1 小时后过期
.signWith(privateKey, SignatureAlgorithm.RS256) // 使用 RSA 私钥签名
.compact();
System.out.println("Generated JWT: " + jwt);
}
public static String generatorJwtString(Map<String, Object> header, String payload, String privateKeyStr) throws Exception {
PrivateKey privateKey = loadPrivateKey(privateKeyStr.replace("\n", ""));
// 生成 JWT
String jwt = Jwts.builder()
.setHeader(header)
.setPayload(payload)
.signWith(privateKey, SignatureAlgorithm.RS256) // 使用 RSA 私钥签名
.compact();
return jwt;
}
// 加载 Base64 编码的 RSA 私钥
private static PrivateKey loadPrivateKey(String base64PrivateKey) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(base64PrivateKey);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(keySpec);
}
}
package org.dromara.common.core.utils;
import com.alibaba.fastjson.JSONObject;
import org.dromara.common.core.annotation.JxgInitField;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import org.dromara.common.core.constant.Constants;
public class JsonToObjectMapper {
public static <T> T mapJsonToObject(JSONObject json, Class<T> clazz) {
try {
T instance = clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(JxgInitField.class)) {
JxgInitField annotation = field.getAnnotation(JxgInitField.class);
String jsonKey = annotation.value();
String format = annotation.format();
// 获取 JSON 中的值
Object value = json.get(jsonKey);
if (value != null) {
field.setAccessible(true);
if (field.getType() == LocalDateTime.class && !format.isEmpty()) {
// 格式化 LocalDateTime 类型字段
// 如果字符串缺少时间部分,补充时间部分
if (format.equals(Constants.DATE_FULL_FORMAT)){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
LocalDateTime dateTime = LocalDateTime.parse(value.toString(), formatter);
field.set(instance, dateTime);
}else {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
String valueStr = value.toString();
// 如果字符串缺少时间部分,补充时间部分
if (!valueStr.contains("T") && format.contains("HH:mm:ss")) {
valueStr += "T00:00:00"; // 补充时间部分
}
LocalDateTime dateTime = LocalDateTime.parse(valueStr, formatter);
field.set(instance, dateTime);
}
/*DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
LocalDateTime dateTime = LocalDateTime.parse(value.toString(), formatter);
field.set(instance, dateTime);*/
} else if (field.getType() == Date.class && !format.isEmpty()) {
// 格式化 Date 类型字段
SimpleDateFormat sdf = new SimpleDateFormat(format);
Date date = sdf.parse(value.toString());
field.set(instance, date);
} else if (field.getType() == Long.class && value instanceof Number) {
field.set(instance, ((Number) value).longValue());
} else {
// 直接设置非日期字段
try {
field.set(instance, value);
} catch (Exception e){
System.out.println(e.getMessage());
System.out.println("错误问题:" + value);
}
}
}
}
}
return instance;
} catch (Exception e) {
throw new RuntimeException("Failed to map JSON to Object", e);
}
}
}
package org.dromara.common.core.utils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import org.dromara.common.core.exception.JxgException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtil {
private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
private static final String PRIVATE_KEY_BASE64 = "";
private static final String PUBLIC_KEY_BASE64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7wg45uUnUgPB2/uE/hpto6pSoviXi7JzS9ip6J1+CbB/bRYydF+6XnVJ5ddw5AAXSNo51beMKUEWguKg5QVzfrYPw063ojTy/36plFmTpNs7u+2fd4fvy7SrS64NRIfahp7scp6NMMXbgDrFLFXs6KJEsG7ThlA4XS4h5BS+oJ6nSnjYz6iC8PXt4wXSoyf61uWSloihQL9fO0RuAHQtHEuwuT8oHG20sg/ylSwV1/8zF4A0MdlOtbSq5UvvDWyVoOKfmEXt8V8h7ZLFAFABW2vVref5ltY0aTTqv/sM5niCa5JLB0w0beCd8FtiWljk7AF0j1W22YqtSDy2xP58IwIDAQAB";
private static final PublicKey PUBLIC_KEY;
private static final JwtParser JWT_PARSER;
static {
try {
// 解析公钥
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Decoders.BASE64.decode(PUBLIC_KEY_BASE64));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PUBLIC_KEY = keyFactory.generatePublic(keySpec);
JWT_PARSER = Jwts.parserBuilder().setSigningKey(PUBLIC_KEY).build();
} catch (Exception e) {
throw new JxgException("初始化Jwt公钥失败", e);
}
}
private static PrivateKey getPrivateKey() {
try {
KeyFactory kf = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(Decoders.BASE64.decode(PRIVATE_KEY_BASE64));
return kf.generatePrivate(ks);
} catch (Exception e) {
logger.error(e.getMessage(), e);
throw new JxgException("获取Jwt私钥失败");
}
}
public static String createJwt(Object jwtPayload, long expiredAt) {
Map<String, Object> headMap = new HashMap<>();
headMap.put("alg", SignatureAlgorithm.RS256.getValue());
headMap.put("typ", "JWT");
Map<String, Object> body = JSON.parse(JSON.stringify(jwtPayload), HashMap.class);
return Jwts.builder()
.setHeader(headMap)
.setClaims(body)
.setExpiration(new Date(expiredAt))
.signWith(getPrivateKey())
.compact();
}
public static <T> T verifyJwt(String jwt, Class<T> jwtPayloadClass) {
if (jwt == null || jwt.isEmpty()) {
return null;
}
Jws<Claims> jws = JWT_PARSER.parseClaimsJws(jwt);
Claims jwtPayload = jws.getBody();
if (jwtPayload == null) {
return null;
}
return JSON.convert(jwtPayload, jwtPayloadClass);
}
public static <T> T getPayload(String jwt, Class<T> jwtPayloadClass) {
if (jwt == null || jwt.isEmpty()) {
return null;
}
try {
byte[] decodedBytes = Base64.getDecoder().decode(jwt.split("\\.")[1]);
return JSON.parse(new String(decodedBytes), jwtPayloadClass);
} catch (Exception e) {
return null;
}
}
}
\ No newline at end of file
package org.dromara.common.core.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import io.github.linpeilie.Converter;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
* Mapstruct 工具类
* <p>参考文档:<a href="https://mapstruct.plus/introduction/quick-start.html">mapstruct-plus</a></p>
*
* @author Michelle.Chung
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MapstructUtils {
private final static Converter CONVERTER = SpringUtils.getBean(Converter.class);
/**
* 将 T 类型对象,转换为 desc 类型的对象并返回
*
* @param source 数据来源实体
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> V convert(T source, Class<V> desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
return CONVERTER.convert(source, desc);
}
/**
* 将 T 类型对象,按照配置的映射字段规则,给 desc 类型的对象赋值并返回 desc 对象
*
* @param source 数据来源实体
* @param desc 转换后的对象
* @return desc
*/
public static <T, V> V convert(T source, V desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
return CONVERTER.convert(source, desc);
}
/**
* 将 T 类型的集合,转换为 desc 类型的集合并返回
*
* @param sourceList 数据来源实体列表
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> List<V> convert(List<T> sourceList, Class<V> desc) {
if (ObjectUtil.isNull(sourceList)) {
return null;
}
if (CollUtil.isEmpty(sourceList)) {
return CollUtil.newArrayList();
}
return CONVERTER.convert(sourceList, desc);
}
/**
* 将 Map 转换为 beanClass 类型的集合并返回
*
* @param map 数据来源
* @param beanClass bean类
* @return bean对象
*/
public static <T> T convert(Map<String, Object> map, Class<T> beanClass) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(beanClass)) {
return null;
}
return CONVERTER.convert(map, beanClass);
}
}
package org.dromara.common.core.utils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.context.NoSuchMessageException;
import org.springframework.context.i18n.LocaleContextHolder;
/**
* 获取i18n资源文件
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MessageUtils {
private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class);
/**
* 根据消息键和参数 获取消息 委托给spring messageSource
*
* @param code 消息键
* @param args 参数
* @return 获取国际化翻译值
*/
public static String message(String code, Object... args) {
try {
return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale());
} catch (NoSuchMessageException e) {
return code;
}
}
}
package org.dromara.common.core.utils.file;
/**
* 媒体类型工具类
*
* @author ruoyi
*/
public class MimeTypeUtils {
public static final String IMAGE_PNG = "image/png";
public static final String IMAGE_JPG = "image/jpg";
public static final String IMAGE_JPEG = "image/jpeg";
public static final String IMAGE_BMP = "image/bmp";
public static final String IMAGE_GIF = "image/gif";
public static final String[] IMAGE_EXTENSION = {"bmp", "gif", "jpg", "jpeg", "png"};
public static final String[] FLASH_EXTENSION = {"swf", "flv"};
public static final String[] MEDIA_EXTENSION = {"swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg",
"asf", "rm", "rmvb"};
public static final String[] VIDEO_EXTENSION = {"mp4", "avi", "rmvb"};
public static final String[] DEFAULT_ALLOWED_EXTENSION = {
// 图片
"bmp", "gif", "jpg", "jpeg", "png",
// word excel powerpoint
"doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
// 压缩文件
"rar", "zip", "gz", "bz2",
// 视频格式
"mp4", "avi", "rmvb",
// pdf
"pdf"};
}
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.register.save.error=保存用户 {0} 失败,注册账号已存在
user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
tenant.blocked=对不起,您的租户已禁用,请联系管理员
tenant.expired=对不起,您的租户已过期,请联系管理员
#错误消息
not.null=* Required fill in
user.jcaptcha.error=Captcha error
user.jcaptcha.expire=Captcha invalid
user.not.exists=Sorry, your account: {0} does not exist
user.password.not.match=User does not exist/Password error
user.password.retry.limit.count=Password input error {0} times
user.password.retry.limit.exceed=Password input error {0} times, account locked for {1} minutes
user.password.delete=Sorry, your account:{0} has been deleted
user.blocked=Sorry, your account: {0} has been disabled. Please contact the administrator
role.blocked=Role disabled,please contact administrators
user.logout.success=Exit successful
length.not.valid=The length must be between {min} and {max} characters
user.username.not.blank=Username cannot be blank
user.username.not.valid=* 2 to 20 chinese characters, letters, numbers or underscores, and must start with a non number
user.username.length.valid=Account length must be between {min} and {max} characters
user.password.not.blank=Password cannot be empty
user.password.length.valid=Password length must be between {min} and {max} characters
user.password.not.valid=* 5-50 characters
user.email.not.valid=Mailbox format error
user.email.not.blank=Mailbox cannot be blank
user.phonenumber.not.blank=Phone number cannot be blank
user.mobile.phone.number.not.valid=Phone number format error
user.login.success=Login successful
user.register.success=Register successful
user.register.save.error=Failed to save user {0}, The registered account already exists
user.register.error=Register failed, please contact system administrator
user.notfound=Please login again
user.forcelogout=The administrator is forced to exit,please login again
user.unknown.error=Unknown error, please login again
auth.grant.type.error=Auth grant type error
auth.grant.type.blocked=Auth grant type disabled
auth.grant.type.not.blank=Auth grant type cannot be blank
auth.clientid.not.blank=Auth clientid cannot be blank
##文件上传消息
upload.exceed.maxSize=The uploaded file size exceeds the limit file size!<br/>the maximum allowed file size is:{0}MB!
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
##权限
no.permission=You do not have permission to the data,please contact your administrator to add permissions [{0}]
no.create.permission=You do not have permission to create data,please contact your administrator to add permissions [{0}]
no.update.permission=You do not have permission to modify data,please contact your administrator to add permissions [{0}]
no.delete.permission=You do not have permission to delete data,please contact your administrator to add permissions [{0}]
no.export.permission=You do not have permission to export data,please contact your administrator to add permissions [{0}]
no.view.permission=You do not have permission to view data,please contact your administrator to add permissions [{0}]
repeat.submit.message=Repeat submit is not allowed, please try again later
rate.limiter.message=Visit too frequently, please try again later
sms.code.not.blank=Sms code cannot be blank
sms.code.retry.limit.count=Sms code input error {0} times
sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {1} minutes
email.code.not.blank=Email code cannot be blank
email.code.retry.limit.count=Email code input error {0} times
email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
xcx.code.not.blank=Mini program [code] cannot be blank
social.source.not.blank=Social login platform [source] cannot be blank
social.code.not.blank=Social login platform [code] cannot be blank
social.state.not.blank=Social login platform [state] cannot be blank
##租户
tenant.number.not.blank=Tenant number cannot be blank
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator
tenant.blocked=Sorry, your tenant is disabled. Please contact the administrator
tenant.expired=Sorry, your tenant has expired. Please contact the administrator.
#错误消息
not.null=* 必须填写
user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
user.logout.success=退出成功
length.not.valid=长度必须在{min}到{max}个字符之间
user.username.not.blank=用户名不能为空
user.username.not.valid=* 2到20个汉字、字母、数字或下划线组成,且必须以非数字开头
user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空
user.mobile.phone.number.not.valid=手机号格式错误
user.login.success=登录成功
user.register.success=注册成功
user.register.save.error=保存用户 {0} 失败,注册账号已存在
user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB!
upload.filename.exceed.length=上传的文件名最长{0}个字符
##权限
no.permission=您没有数据的权限,请联系管理员添加权限 [{0}]
no.create.permission=您没有创建数据的权限,请联系管理员添加权限 [{0}]
no.update.permission=您没有修改数据的权限,请联系管理员添加权限 [{0}]
no.delete.permission=您没有删除数据的权限,请联系管理员添加权限 [{0}]
no.export.permission=您没有导出数据的权限,请联系管理员添加权限 [{0}]
no.view.permission=您没有查看数据的权限,请联系管理员添加权限 [{0}]
repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员
tenant.blocked=对不起,您的租户已禁用,请联系管理员
tenant.expired=对不起,您的租户已过期,请联系管理员
package org.dromara.common.doc.handler;
import cn.hutool.core.io.IoUtil;
import io.swagger.v3.core.jackson.TypeNameResolver;
import io.swagger.v3.core.util.AnnotationsUtils;
import io.swagger.v3.oas.annotations.tags.Tags;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
import org.springdoc.core.customizers.ServerBaseUrlCustomizer;
import org.springdoc.core.properties.SpringDocConfigProperties;
import org.springdoc.core.providers.JavadocProvider;
import org.springdoc.core.service.OpenAPIService;
import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 自定义 openapi 处理器
* 对源码功能进行修改 增强使用
*/
@Slf4j
@SuppressWarnings("all")
public class OpenApiHandler extends OpenAPIService {
/**
* The Basic error controller.
*/
private static Class<?> basicErrorController;
/**
* The Security parser.
*/
private final SecurityService securityParser;
/**
* The Mappings map.
*/
private final Map<String, Object> mappingsMap = new HashMap<>();
/**
* The Springdoc tags.
*/
private final Map<HandlerMethod, Tag> springdocTags = new HashMap<>();
/**
* The Open api builder customisers.
*/
private final Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers;
/**
* The server base URL customisers.
*/
private final Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers;
/**
* The Spring doc config properties.
*/
private final SpringDocConfigProperties springDocConfigProperties;
/**
* The Cached open api map.
*/
private final Map<String, OpenAPI> cachedOpenAPI = new HashMap<>();
/**
* The Property resolver utils.
*/
private final PropertyResolverUtils propertyResolverUtils;
/**
* The javadoc provider.
*/
private final Optional<JavadocProvider> javadocProvider;
/**
* The Context.
*/
private ApplicationContext context;
/**
* The Open api.
*/
private OpenAPI openAPI;
/**
* The Is servers present.
*/
private boolean isServersPresent;
/**
* The Server base url.
*/
private String serverBaseUrl;
/**
* Instantiates a new Open api builder.
*
* @param openAPI the open api
* @param securityParser the security parser
* @param springDocConfigProperties the spring doc config properties
* @param propertyResolverUtils the property resolver utils
* @param openApiBuilderCustomizers the open api builder customisers
* @param serverBaseUrlCustomizers the server base url customizers
* @param javadocProvider the javadoc provider
*/
public OpenApiHandler(Optional<OpenAPI> openAPI, SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomizers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomizers,
Optional<JavadocProvider> javadocProvider) {
super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider);
if (openAPI.isPresent()) {
this.openAPI = openAPI.get();
if (this.openAPI.getComponents() == null)
this.openAPI.setComponents(new Components());
if (this.openAPI.getPaths() == null)
this.openAPI.setPaths(new Paths());
if (!CollectionUtils.isEmpty(this.openAPI.getServers()))
this.isServersPresent = true;
}
this.propertyResolverUtils = propertyResolverUtils;
this.securityParser = securityParser;
this.springDocConfigProperties = springDocConfigProperties;
this.openApiBuilderCustomisers = openApiBuilderCustomizers;
this.serverBaseUrlCustomizers = serverBaseUrlCustomizers;
this.javadocProvider = javadocProvider;
if (springDocConfigProperties.isUseFqn())
TypeNameResolver.std.setUseFqn(true);
}
@Override
public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {
Set<Tag> tags = new HashSet<>();
Set<String> tagsStr = new HashSet<>();
buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);
buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);
if (!CollectionUtils.isEmpty(tagsStr))
tagsStr = tagsStr.stream()
.map(str -> propertyResolverUtils.resolve(str, locale))
.collect(Collectors.toSet());
if (springdocTags.containsKey(handlerMethod)) {
Tag tag = springdocTags.get(handlerMethod);
tagsStr.add(tag.getName());
if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
openAPI.addTagsItem(tag);
}
}
if (!CollectionUtils.isEmpty(tagsStr)) {
if (CollectionUtils.isEmpty(operation.getTags()))
operation.setTags(new ArrayList<>(tagsStr));
else {
Set<String> operationTagsSet = new HashSet<>(operation.getTags());
operationTagsSet.addAll(tagsStr);
operation.getTags().clear();
operation.getTags().addAll(operationTagsSet);
}
}
if (isAutoTagClasses(operation)) {
if (javadocProvider.isPresent()) {
String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType());
if (StringUtils.isNotBlank(description)) {
Tag tag = new Tag();
// 自定义部分 修改使用java注释当tag名
List<String> list = IoUtil.readLines(new StringReader(description), new ArrayList<>());
// tag.setName(tagAutoName);
tag.setName(list.get(0));
operation.addTagsItem(list.get(0));
tag.setDescription(description);
if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) {
openAPI.addTagsItem(tag);
}
}
} else {
String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName());
operation.addTagsItem(tagAutoName);
}
}
if (!CollectionUtils.isEmpty(tags)) {
// Existing tags
List<Tag> openApiTags = openAPI.getTags();
if (!CollectionUtils.isEmpty(openApiTags))
tags.addAll(openApiTags);
openAPI.setTags(new ArrayList<>(tags));
}
// Handle SecurityRequirement at operation level
io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser
.getSecurityRequirements(handlerMethod);
if (securityRequirements != null) {
if (securityRequirements.length == 0)
operation.setSecurity(Collections.emptyList());
else
securityParser.buildSecurityRequirement(securityRequirements, operation);
}
return operation;
}
private void buildTagsFromMethod(Method method, Set<Tag> tags, Set<String> tagsStr, Locale locale) {
// method tags
Set<Tags> tagsSet = AnnotatedElementUtils
.findAllMergedAnnotations(method, Tags.class);
Set<io.swagger.v3.oas.annotations.tags.Tag> methodTags = tagsSet.stream()
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet());
methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
if (!CollectionUtils.isEmpty(methodTags)) {
tagsStr.addAll(methodTags.stream().map(tag -> propertyResolverUtils.resolve(tag.name(), locale)).collect(Collectors.toSet()));
List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
addTags(allTags, tags, locale);
}
}
private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<Tag> tags, Locale locale) {
Optional<Set<Tag>> optionalTagSet = AnnotationsUtils
.getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);
optionalTagSet.ifPresent(tagsSet -> {
tagsSet.forEach(tag -> {
tag.name(propertyResolverUtils.resolve(tag.getName(), locale));
tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale));
if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName())))
tags.add(tag);
});
});
}
}
package org.dromara.common.encrypt.core;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.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);
}
package org.dromara.common.encrypt.interceptor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.core.EncryptorManager;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import org.dromara.common.encrypt.properties.EncryptorProperties;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.*;
/**
* 出参解密拦截器
*
* @author 老马
* @version 4.6.0
*/
@Slf4j
@Intercepts({@Signature(
type = ResultSetHandler.class,
method = "handleResultSets",
args = {Statement.class})
})
@AllArgsConstructor
public class MybatisDecryptInterceptor implements Interceptor {
private final EncryptorManager encryptorManager;
private final EncryptorProperties defaultProperties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 获取执行mysql执行结果
Object result = invocation.proceed();
if (result == null) {
return null;
}
decryptHandler(result);
return result;
}
/**
* 解密对象
*
* @param sourceObject 待加密对象
*/
private void decryptHandler(Object sourceObject) {
if (ObjectUtil.isNull(sourceObject)) {
return;
}
if (sourceObject instanceof Map<?, ?> map) {
new HashSet<>(map.values()).forEach(this::decryptHandler);
return;
}
if (sourceObject instanceof List<?> list) {
if(CollUtil.isEmpty(list)) {
return;
}
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
Object firstItem = list.get(0);
if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
return;
}
list.forEach(this::decryptHandler);
return;
}
// 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
if(ObjectUtil.isNull(fields)){
return;
}
try {
for (Field field : fields) {
field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field));
}
} catch (Exception e) {
log.error("处理解密字段时出错", e);
}
}
/**
* 字段值进行加密。通过字段的批注注册新的加密算法
*
* @param value 待加密的值
* @param field 待加密字段
* @return 加密后结果
*/
private String decryptField(String value, Field field) {
if (ObjectUtil.isNull(value)) {
return null;
}
EncryptField encryptField = field.getAnnotation(EncryptField.class);
EncryptContext encryptContext = new EncryptContext();
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
return this.encryptorManager.decrypt(value, encryptContext);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
package org.dromara.common.encrypt.interceptor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.encrypt.annotation.EncryptField;
import org.dromara.common.encrypt.core.EncryptContext;
import org.dromara.common.encrypt.core.EncryptorManager;
import org.dromara.common.encrypt.enumd.AlgorithmType;
import org.dromara.common.encrypt.enumd.EncodeType;
import org.dromara.common.encrypt.properties.EncryptorProperties;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.util.*;
/**
* 入参加密拦截器
*
* @author 老马
* @version 4.6.0
*/
@Slf4j
@Intercepts({@Signature(
type = ParameterHandler.class,
method = "setParameters",
args = {PreparedStatement.class})
})
@AllArgsConstructor
public class MybatisEncryptInterceptor implements Interceptor {
private final EncryptorManager encryptorManager;
private final EncryptorProperties defaultProperties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
return invocation;
}
@Override
public Object plugin(Object target) {
if (target instanceof ParameterHandler parameterHandler) {
// 进行加密操作
Object parameterObject = parameterHandler.getParameterObject();
if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) {
this.encryptHandler(parameterObject);
}
}
return target;
}
/**
* 加密对象
*
* @param sourceObject 待加密对象
*/
private void encryptHandler(Object sourceObject) {
if (ObjectUtil.isNull(sourceObject)) {
return;
}
if (sourceObject instanceof Map<?, ?> map) {
new HashSet<>(map.values()).forEach(this::encryptHandler);
return;
}
if (sourceObject instanceof List<?> list) {
if(CollUtil.isEmpty(list)) {
return;
}
// 判断第一个元素是否含有注解。如果没有直接返回,提高效率
Object firstItem = list.get(0);
if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) {
return;
}
list.forEach(this::encryptHandler);
return;
}
// 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错)
Set<Field> fields = encryptorManager.getFieldCache(sourceObject.getClass());
if(ObjectUtil.isNull(fields)){
return;
}
try {
for (Field field : fields) {
field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field));
}
} catch (Exception e) {
log.error("处理加密字段时出错", e);
}
}
/**
* 字段值进行加密。通过字段的批注注册新的加密算法
*
* @param value 待加密的值
* @param field 待加密字段
* @return 加密后结果
*/
private String encryptField(String value, Field field) {
if (ObjectUtil.isNull(value)) {
return null;
}
EncryptField encryptField = field.getAnnotation(EncryptField.class);
EncryptContext encryptContext = new EncryptContext();
encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm());
encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode());
encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password());
encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey());
encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey());
return this.encryptorManager.encrypt(value, encryptContext);
}
@Override
public void setProperties(Properties properties) {
}
}
package org.dromara.common.json.config;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.dromara.common.json.handler.BigNumberSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.context.annotation.Bean;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
/**
* jackson 配置
*
* @author Lion Li
*/
@Slf4j
@AutoConfiguration(before = JacksonAutoConfiguration.class)
public class JacksonConfig {
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() {
return builder -> {
// 全局配置序列化返回 JSON 处理
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(Long.class, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(Long.TYPE, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(BigInteger.class, BigNumberSerializer.INSTANCE);
javaTimeModule.addSerializer(BigDecimal.class, ToStringSerializer.instance);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
builder.modules(javaTimeModule);
builder.timeZone(TimeZone.getDefault());
log.info("初始化 jackson 配置");
};
}
}
package org.dromara.common.json.utils;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* JSON 工具类
*
* @author 芋道源码
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {
private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
public static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
/**
* 将对象转换为JSON格式的字符串
*
* @param object 要转换的对象
* @return JSON格式的字符串,如果对象为null,则返回null
* @throws RuntimeException 如果转换过程中发生JSON处理异常,则抛出运行时异常
*/
public static String toJsonString(Object object) {
if (ObjectUtil.isNull(object)) {
return null;
}
try {
return OBJECT_MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/**
* 将JSON格式的字符串转换为指定类型的对象
*
* @param text JSON格式的字符串
* @param clazz 要转换的目标对象类型
* @param <T> 目标对象的泛型类型
* @return 转换后的对象,如果字符串为空则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static <T> T parseObject(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将字节数组转换为指定类型的对象
*
* @param bytes 字节数组
* @param clazz 要转换的目标对象类型
* @param <T> 目标对象的泛型类型
* @return 转换后的对象,如果字节数组为空则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
if (ArrayUtil.isEmpty(bytes)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(bytes, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将JSON格式的字符串转换为指定类型的对象,支持复杂类型
*
* @param text JSON格式的字符串
* @param typeReference 指定类型的TypeReference对象
* @param <T> 目标对象的泛型类型
* @return 转换后的对象,如果字符串为空则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将JSON格式的字符串转换为Dict对象
*
* @param text JSON格式的字符串
* @return 转换后的Dict对象,如果字符串为空或者不是JSON格式则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static Dict parseMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));
} catch (MismatchedInputException e) {
// 类型不匹配说明不是json
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将JSON格式的字符串转换为Dict对象的列表
*
* @param text JSON格式的字符串
* @return 转换后的Dict对象的列表,如果字符串为空则返回null
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static List<Dict> parseArrayMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 将JSON格式的字符串转换为指定类型对象的列表
*
* @param text JSON格式的字符串
* @param clazz 要转换的目标对象类型
* @param <T> 目标对象的泛型类型
* @return 转换后的对象的列表,如果字符串为空则返回空列表
* @throws RuntimeException 如果转换过程中发生IO异常,则抛出运行时异常
*/
public static <T> List<T> parseArray(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return new ArrayList<>();
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
package org.dromara.common.log.annotation;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.log.enums.OperatorType;
import java.lang.annotation.*;
/**
* 自定义操作日志记录注解
*
* @author ruoyi
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 模块
*/
String title() default "";
/**
* 功能
*/
BusinessType businessType() default BusinessType.OTHER;
/**
* 操作人类别
*/
OperatorType operatorType() default OperatorType.MANAGE;
/**
* 是否保存请求的参数
*/
boolean isSaveRequestData() default true;
/**
* 是否保存响应的参数
*/
boolean isSaveResponseData() default true;
/**
* 排除指定的请求参数
*/
String[] excludeParamNames() default {};
}
package org.dromara.common.log.aspect;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import com.bkty.system.api.model.LoginUser;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessStatus;
import org.dromara.common.log.event.OperLogEvent;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.http.HttpMethod;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import java.util.Collection;
import java.util.Map;
import java.util.StringJoiner;
/**
* 操作日志记录处理
*
* @author Lion Li
*/
@Slf4j
@Aspect
@AutoConfiguration
public class LogAspect {
/**
* 排除敏感属性字段
*/
public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };
/**
* 计时 key
*/
private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
/**
* 处理请求前执行
*/
@Before(value = "@annotation(controllerLog)")
public void doBefore(JoinPoint joinPoint, Log controllerLog) {
StopWatch stopWatch = new StopWatch();
KEY_CACHE.set(stopWatch);
stopWatch.start();
}
/**
* 处理完请求后执行
*
* @param joinPoint 切点
*/
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
handleLog(joinPoint, controllerLog, null, jsonResult);
}
/**
* 拦截异常操作
*
* @param joinPoint 切点
* @param e 异常
*/
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
handleLog(joinPoint, controllerLog, e, null);
}
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
try {
// *========数据库日志=========*//
OperLogEvent operLog = new OperLogEvent();
operLog.setTenantId(LoginHelper.getTenantId());
operLog.setStatus(BusinessStatus.SUCCESS.ordinal());
// 请求的地址
String ip = ServletUtils.getClientIP();
operLog.setOperIp(ip);
operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));
LoginUser loginUser = LoginHelper.getLoginUser();
operLog.setOperName(loginUser.getUsername());
operLog.setDeptName(loginUser.getDeptName());
if (e != null) {
operLog.setStatus(BusinessStatus.FAIL.ordinal());
operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));
}
// 设置方法名称
String className = joinPoint.getTarget().getClass().getName();
String methodName = joinPoint.getSignature().getName();
operLog.setMethod(className + "." + methodName + "()");
// 设置请求方式
operLog.setRequestMethod(ServletUtils.getRequest().getMethod());
// 处理设置注解上的参数
getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult);
// 设置消耗时间
StopWatch stopWatch = KEY_CACHE.get();
stopWatch.stop();
operLog.setCostTime(stopWatch.getTime());
// 发布事件保存数据库
SpringUtils.context().publishEvent(operLog);
} catch (Exception exp) {
// 记录本地异常日志
log.error("异常信息:{}", exp.getMessage());
exp.printStackTrace();
} finally {
KEY_CACHE.remove();
}
}
/**
* 获取注解中对方法的描述信息 用于Controller层注解
*
* @param log 日志
* @param operLog 操作日志
* @throws Exception
*/
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception {
// 设置action动作
operLog.setBusinessType(log.businessType().ordinal());
// 设置标题
operLog.setTitle(log.title());
// 设置操作人类别
operLog.setOperatorType(log.operatorType().ordinal());
// 是否需要保存request,参数和值
if (log.isSaveRequestData()) {
// 获取参数的信息,传入到数据库中。
setRequestValue(joinPoint, operLog, log.excludeParamNames());
}
// 是否需要保存response,参数和值
if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) {
operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 2000));
}
}
/**
* 获取请求的参数,放到log中
*
* @param operLog 操作日志
* @throws Exception 异常
*/
private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception {
Map<String, String> paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest());
String requestMethod = operLog.getRequestMethod();
if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) {
String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);
operLog.setOperParam(StringUtils.substring(params, 0, 2000));
} else {
MapUtil.removeAny(paramsMap, EXCLUDE_PROPERTIES);
MapUtil.removeAny(paramsMap, excludeParamNames);
operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 2000));
}
}
/**
* 参数拼装
*/
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {
StringJoiner params = new StringJoiner(" ");
if (ArrayUtil.isEmpty(paramsArray)) {
return params.toString();
}
for (Object o : paramsArray) {
if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) {
String str = JsonUtils.toJsonString(o);
Dict dict = JsonUtils.parseMap(str);
if (MapUtil.isNotEmpty(dict)) {
MapUtil.removeAny(dict, EXCLUDE_PROPERTIES);
MapUtil.removeAny(dict, excludeParamNames);
str = JsonUtils.toJsonString(dict);
}
params.add(str);
}
}
return params.toString();
}
/**
* 判断是否需要过滤的对象。
*
* @param o 对象信息。
* @return 如果是需要过滤的对象,则返回true;否则返回false。
*/
@SuppressWarnings("rawtypes")
public boolean isFilterObject(final Object o) {
Class<?> clazz = o.getClass();
if (clazz.isArray()) {
return MultipartFile.class.isAssignableFrom(clazz.getComponentType());
} else if (Collection.class.isAssignableFrom(clazz)) {
Collection collection = (Collection) o;
for (Object value : collection) {
return value instanceof MultipartFile;
}
} else if (Map.class.isAssignableFrom(clazz)) {
Map map = (Map) o;
for (Object value : map.values()) {
return value instanceof MultipartFile;
}
}
return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
|| o instanceof BindingResult;
}
}
package org.dromara.common.log.enums;
/**
* 操作人类别
*
* @author ruoyi
*/
public enum OperatorType {
/**
* 其它
*/
OTHER,
/**
* 后台用户
*/
MANAGE,
/**
* 手机端用户
*/
MOBILE
}
package org.dromara.common.log.event;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.utils.ServletUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.ip.AddressUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* 异步调用日志服务
*
* @author ruoyi
*/
@Component
@Slf4j
public class LogEventListener {
/*@DubboReference
private RemoteLogService remoteLogService;
@DubboReference
private RemoteClientService remoteClientService;*/
/**
* 保存系统日志记录
*/
@EventListener
public void saveLog(OperLogEvent operLogEvent) {
/*RemoteOperLogBo sysOperLog = BeanUtil.toBean(operLogEvent, RemoteOperLogBo.class);
remoteLogService.saveLog(sysOperLog);*/
}
/**
* 保存系统访问记录
*/
@EventListener
public void saveLogininfor(LogininforEvent logininforEvent) {
HttpServletRequest request = ServletUtils.getRequest();
final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
final String ip = ServletUtils.getClientIP(request);
// 客户端信息
String clientId = request.getHeader(LoginHelper.CLIENT_KEY);
/*RemoteClientVo clientVo = null;
if (StringUtils.isNotBlank(clientId)) {
clientVo = remoteClientService.queryByClientId(clientId);
}*/
String address = AddressUtils.getRealAddressByIP(ip);
StringBuilder s = new StringBuilder();
s.append(getBlock(ip));
s.append(address);
s.append(getBlock(logininforEvent.getUsername()));
s.append(getBlock(logininforEvent.getStatus()));
s.append(getBlock(logininforEvent.getMessage()));
// 打印信息到日志
log.info(s.toString(), logininforEvent.getArgs());
// 获取客户端操作系统
String os = userAgent.getOs().getName();
// 获取客户端浏览器
String browser = userAgent.getBrowser().getName();
// 封装对象
/*RemoteLogininforBo logininfor = new RemoteLogininforBo();
logininfor.setTenantId(logininforEvent.getTenantId());
logininfor.setUserName(logininforEvent.getUsername());
if (ObjectUtil.isNotNull(clientVo)) {
logininfor.setClientKey(clientVo.getClientKey());
logininfor.setDeviceType(clientVo.getDeviceType());
}
logininfor.setIpaddr(ip);
logininfor.setLoginLocation(address);
logininfor.setBrowser(browser);
logininfor.setOs(os);
logininfor.setMsg(logininforEvent.getMessage());
// 日志状态
if (StringUtils.equalsAny(logininforEvent.getStatus(), Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) {
logininfor.setStatus(Constants.SUCCESS);
} else if (Constants.LOGIN_FAIL.equals(logininforEvent.getStatus())) {
logininfor.setStatus(Constants.FAIL);
}
remoteLogService.saveLogininfor(logininfor);*/
}
private String getBlock(Object msg) {
if (msg == null) {
msg = "";
}
return "[" + msg + "]";
}
}
package org.dromara.common.log.event;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 登录事件
*
* @author Lion Li
*/
@Data
public class LogininforEvent implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 租户ID
*/
private String tenantId;
/**
* 用户账号
*/
private String username;
/**
* 登录状态 0成功 1失败
*/
private String status;
/**
* 提示消息
*/
private String message;
/**
* 其他参数
*/
private Object[] args;
}
package org.dromara.common.mybatis.config;
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.dromara.common.core.factory.YmlPropertySourceFactory;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
import org.dromara.common.mybatis.handler.MybatisExceptionHandler;
import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor;
import org.dromara.common.mybatis.service.SysDataScopeService;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* mybatis-plus配置类(下方注释有插件介绍)
*
* @author Lion Li
*/
@AutoConfiguration
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan("${mybatis-plus.mapperPackage}")
@PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class)
public class MybatisPlusConfiguration {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 多租户插件 必须放到第一位
try {
TenantLineInnerInterceptor tenant = SpringUtils.getBean(TenantLineInnerInterceptor.class);
interceptor.addInnerInterceptor(tenant);
} catch (BeansException ignore) {
}
// 数据权限处理
interceptor.addInnerInterceptor(dataPermissionInterceptor());
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
return interceptor;
}
/**
* 数据权限拦截器
*/
public PlusDataPermissionInterceptor dataPermissionInterceptor() {
return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage"));
}
/**
* 分页插件,自动识别数据库类型
*/
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 分页合理化
paginationInnerInterceptor.setOverflow(true);
return paginationInnerInterceptor;
}
/**
* 乐观锁插件
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
/**
* 元对象字段填充控制器
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new InjectionMetaObjectHandler();
}
/**
* 使用网卡信息绑定雪花生成器
* 防止集群雪花ID重复
*/
@Bean
public IdentifierGenerator idGenerator() {
return new DefaultIdentifierGenerator(NetUtil.getLocalhost());
}
/**
* 异常处理器
*/
@Bean
public MybatisExceptionHandler mybatisExceptionHandler() {
return new MybatisExceptionHandler();
}
/**
* 数据权限处理实现
*/
@Bean("sdss")
public SysDataScopeService sysDataScopeService() {
return new SysDataScopeService();
}
/**
* PaginationInnerInterceptor 分页插件,自动识别数据库类型
* https://baomidou.com/pages/97710a/
* OptimisticLockerInnerInterceptor 乐观锁插件
* https://baomidou.com/pages/0d93c0/
* MetaObjectHandler 元对象字段填充控制器
* https://baomidou.com/pages/4c6bcf/
* ISqlInjector sql注入器
* https://baomidou.com/pages/42ea4a/
* BlockAttackInnerInterceptor 如果是对全表的删除或更新操作,就会终止该操作
* https://baomidou.com/pages/f9a237/
* IllegalSQLInnerInterceptor sql性能规范插件(垃圾SQL拦截)
* IdentifierGenerator 自定义主键策略
* https://baomidou.com/pages/568eb2/
* TenantLineInnerInterceptor 多租户插件
* https://baomidou.com/pages/aef2f2/
* DynamicTableNameInnerInterceptor 动态表名插件
* https://baomidou.com/pages/2a45ff/
*/
}
package org.dromara.common.mybatis.handler;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.bkty.system.api.model.LoginUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.satoken.utils.LoginHelper;
import java.util.Date;
/**
* MP注入处理器
*
* @author Lion Li
*/
@Slf4j
public class InjectionMetaObjectHandler implements MetaObjectHandler {
/**
* 插入填充方法,用于在插入数据时自动填充实体对象中的创建时间、更新时间、创建人、更新人等信息
*
* @param metaObject 元对象,用于获取原始对象并进行填充
*/
@Override
public void insertFill(MetaObject metaObject) {
try {
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
// 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间
Date current = ObjectUtil.isNotNull(baseEntity.getCreateTime())
? baseEntity.getCreateTime() : new Date();
baseEntity.setCreateTime(current);
baseEntity.setUpdateTime(current);
// 如果创建人为空,则填充当前登录用户的信息
if (ObjectUtil.isNull(baseEntity.getCreateBy())) {
LoginUser loginUser = getLoginUser();
if (ObjectUtil.isNotNull(loginUser)) {
Long userId = loginUser.getUserId();
// 填充创建人、更新人和创建部门信息
baseEntity.setCreateBy(String.valueOf(userId));
baseEntity.setUpdateBy(String.valueOf(userId));
}
}
} else {
Date date = new Date();
this.strictInsertFill(metaObject, "createTime", Date.class, date);
this.strictInsertFill(metaObject, "updateTime", Date.class, date);
}
} catch (Exception e) {
throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
}
}
/**
* 更新填充方法,用于在更新数据时自动填充实体对象中的更新时间和更新人信息
*
* @param metaObject 元对象,用于获取原始对象并进行填充
*/
@Override
public void updateFill(MetaObject metaObject) {
try {
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
// 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充
Date current = new Date();
baseEntity.setUpdateTime(current);
// 获取当前登录用户的ID,并填充更新人信息
Long userId = LoginHelper.getUserId();
if (ObjectUtil.isNotNull(userId)) {
baseEntity.setUpdateBy(String.valueOf(userId));
}
} else {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
}
} catch (Exception e) {
throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
}
}
/**
* 获取当前登录用户信息
*
* @return 当前登录用户的信息,如果用户未登录则返回 null
*/
private LoginUser getLoginUser() {
LoginUser loginUser;
try {
loginUser = LoginHelper.getLoginUser();
} catch (Exception e) {
log.warn("自动注入警告 => 用户未登录");
return null;
}
return loginUser;
}
}
package org.dromara.common.mybatis.handler;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.StringUtils;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* Mybatis异常处理器
*
* @author Lion Li
*/
@Slf4j
@RestControllerAdvice
public class MybatisExceptionHandler {
/**
* 主键或UNIQUE索引,数据重复异常
*/
@ExceptionHandler(DuplicateKeyException.class)
public R<Void> handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage());
return R.fail("数据库中已存在该记录,请联系管理员确认");
}
/**
* Mybatis系统异常 通用处理
*/
@ExceptionHandler(MyBatisSystemException.class)
public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {
String requestURI = request.getRequestURI();
String message = e.getMessage();
if (StringUtils.contains("CannotFindDataSourceException", message)) {
log.error("请求地址'{}', 未找到数据源", requestURI);
return R.fail("未找到数据源,请联系管理员确认");
}
log.error("请求地址'{}', Mybatis系统异常", requestURI, e);
return R.fail(message);
}
}
package org.dromara.common.redis.handler;
import org.dromara.common.core.utils.StringUtils;
import org.redisson.api.NameMapper;
/**
* redis缓存key前缀处理
*
* @author ye
* @date 2022/7/14 17:44
* @since 4.3.0
*/
public class KeyPrefixHandler implements NameMapper {
private final String keyPrefix;
public KeyPrefixHandler(String keyPrefix) {
//前缀为空 则返回空前缀
this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":";
}
/**
* 增加前缀
*/
@Override
public String map(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
if (StringUtils.isNotBlank(keyPrefix) && !name.startsWith(keyPrefix)) {
return keyPrefix + name;
}
return name;
}
/**
* 去除前缀
*/
@Override
public String unmap(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
if (StringUtils.isNotBlank(keyPrefix) && name.startsWith(keyPrefix)) {
return name.substring(keyPrefix.length());
}
return name;
}
}
package org.dromara.common.satoken.utils;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import com.bkty.system.api.model.LoginUser;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.constant.TenantConstants;
import org.dromara.common.core.constant.UserConstants;
import org.dromara.common.core.enums.UserType;
import java.util.Set;
/**
* 登录鉴权助手
* <p>
* user_type 为 用户类型 同一个用户表 可以有多种用户类型 例如 pc,app
* deivce 为 设备类型 同一个用户类型 可以有 多种设备类型 例如 web,ios
* 可以组成 用户类型与设备类型多对多的 权限灵活控制
* <p>
* 多用户体系 针对 多种用户类型 但权限控制不一致
* 可以组成 多用户类型表与多设备类型 分别控制权限
*
* @author Lion Li
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginHelper {
public static final String LOGIN_USER_KEY = "loginUser";
public static final String TENANT_KEY = "tenantId";
public static final String TENANT_NAME_KEY = "tenantName";
public static final String USER_KEY = "userId";
public static final String USER_NAME_KEY = "userName";
public static final String NICK_NAME_KEY = "nickName";
public static final String DEPT_KEY = "deptId";
public static final String DEPT_NAME_KEY = "deptName";
public static final String DEPT_CATEGORY_KEY = "deptCategory";
public static final String CLIENT_KEY = "clientid";
/**
* 登录系统 基于 设备类型
* 针对相同用户体系不同设备
*
* @param loginUser 登录用户信息
* @param model 配置参数
*/
public static void login(LoginUser loginUser, SaLoginModel model) {
model = ObjectUtil.defaultIfNull(model, new SaLoginModel());
StpUtil.login(loginUser.getLoginId(),
model.setExtra(TENANT_KEY, loginUser.getTenantId())
.setExtra(TENANT_NAME_KEY, loginUser.getTenantName())
.setExtra(USER_KEY, loginUser.getUserId())
.setExtra(USER_NAME_KEY, loginUser.getUsername())
.setExtra(NICK_NAME_KEY, loginUser.getNickname())
.setExtra(DEPT_KEY, loginUser.getDeptId())
.setExtra(DEPT_NAME_KEY, loginUser.getDeptName())
.setExtra(DEPT_CATEGORY_KEY, loginUser.getDeptCategory())
);
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
}
/**
* 获取用户(多级缓存)
*/
public static LoginUser getLoginUser() {
SaSession session = StpUtil.getTokenSession();
if (ObjectUtil.isNull(session)) {
return null;
}
return (LoginUser) session.get(LOGIN_USER_KEY);
}
/**
* 获取用户基于token
*/
public static LoginUser getLoginUser(String token) {
SaSession session = StpUtil.getTokenSessionByToken(token);
if (ObjectUtil.isNull(session)) {
return null;
}
return (LoginUser) session.get(LOGIN_USER_KEY);
}
/**
* 获取用户id
*/
public static Long getUserId() {
return Convert.toLong(getExtra(USER_KEY));
}
/**
* 获取用户账户
*/
public static String getUsername() {
return Convert.toStr(getExtra(USER_NAME_KEY));
}
/**
* 获取用户昵称
*/
public static String getNickName() {
return Convert.toStr(getExtra(NICK_NAME_KEY));
}
/**
* 获取租户ID(学校Id)
*/
public static String getTenantId() {
return Convert.toStr(getExtra(TENANT_KEY));
}
/**
* 获取租户名称(学校名称)
*/
public static String getTenantName() {
return Convert.toStr(getExtra(TENANT_NAME_KEY));
}
/**
* 获取部门ID
*/
public static Long getDeptId() {
return Convert.toLong(getExtra(DEPT_KEY));
}
/**
* 获取部门名
*/
public static String getDeptName() {
return Convert.toStr(getExtra(DEPT_NAME_KEY));
}
/**
* 获取部门类别编码
*/
public static String getDeptCategory() {
return Convert.toStr(getExtra(DEPT_CATEGORY_KEY));
}
/**
* 获取当前 Token 的扩展信息
*
* @param key 键值
* @return 对应的扩展数据
*/
private static Object getExtra(String key) {
try {
return StpUtil.getExtra(key);
} catch (Exception e) {
return null;
}
}
/**
* 获取用户类型
*/
public static UserType getUserType() {
String loginType = StpUtil.getLoginIdAsString();
return UserType.getUserType(loginType);
}
/**
* 是否为超级管理员
*
* @param userId 用户ID
* @return 结果
*/
public static boolean isSuperAdmin(Long userId) {
return UserConstants.SUPER_ADMIN_ID.equals(userId);
}
/**
* 是否为超级管理员
*
* @return 结果
*/
public static boolean isSuperAdmin() {
return isSuperAdmin(getUserId());
}
/**
* 是否为租户管理员
*
* @param rolePermission 角色权限标识组
* @return 结果
*/
public static boolean isTenantAdmin(Set<String> rolePermission) {
if (CollUtil.isEmpty(rolePermission)) {
return false;
}
return rolePermission.contains(TenantConstants.TENANT_ADMIN_ROLE_KEY);
}
/**
* 是否为租户管理员
*
* @return 结果
*/
public static boolean isTenantAdmin() {
return Convert.toBool(isTenantAdmin(getLoginUser().getRolePermission()));
}
/**
* 检查当前用户是否已登录
*
* @return 结果
*/
public static boolean isLogin() {
try {
return getLoginUser() != null;
} catch (Exception e) {
return false;
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<included>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/console.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/console.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
</filter>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- info异步输出 -->
<appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_info"/>
</appender>
<!-- error异步输出 -->
<appender name="async_error" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_error"/>
</appender>
<!--系统操作日志-->
<root level="info">
<appender-ref ref="async_info"/>
<appender-ref ref="async_error"/>
<appender-ref ref="file_console"/>
</root>
</included>
package com.bkty.gateway.config.properties;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* 放行白名单配置
*
* @author ruoyi
*/
@Data
@NoArgsConstructor
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.ignore")
public class IgnoreWhiteProperties {
/**
* 放行白名单配置,网关不校验此处的白名单
*/
private List<String> whites = new ArrayList<>();
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/${project.artifactId}"/>
<!-- 日志输出格式 -->
<property name="console.log.pattern"
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/console.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/console.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
</filter>
</appender>
<!-- 系统日志输出 -->
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<!-- 过滤的级别 -->
<level>ERROR</level>
<!-- 匹配时的操作:接收(记录) -->
<onMatch>ACCEPT</onMatch>
<!-- 不匹配时的操作:拒绝(不记录) -->
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- info异步输出 -->
<appender name="async_info" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_info"/>
</appender>
<!-- error异步输出 -->
<appender name="async_error" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACT、DEBUG、INFO级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
<queueSize>512</queueSize>
<!-- 添加附加的appender,最多只能添加一个 -->
<appender-ref ref="file_error"/>
</appender>
<include resource="logback-logstash.xml" />
<!-- 开启 skywalking 日志收集 -->
<include resource="logback-skylog.xml" />
<!--系统操作日志-->
<root level="info">
<appender-ref ref="console"/>
<appender-ref ref="async_info"/>
<appender-ref ref="async_error"/>
<appender-ref ref="file_console"/>
</root>
</configuration>
package com.bkty.system.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* minio 配置信息
*
* @author jiangxiaoge
*/
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
/**
* minio 服务地址 http://ip:port
*/
private String url;
/**
* 用户名
*/
private String accessKey;
/**
* 密码
*/
private String secretKey;
/**
* 端口
*/
private int port;
/**
* 桶名
*/
private String bucket;
/**
* 请求方式 http-false https-true
*/
private Boolean secure;
}
package com.bkty.system.config;
import org.springframework.core.io.InputStreamResource;
import java.io.InputStream;
public class MultipartInputStreamFileResource extends InputStreamResource {
private final String filename;
public MultipartInputStreamFileResource(InputStream inputStream, String filename) {
super(inputStream);
this.filename = filename;
}
@Override
public String getFilename() {
return this.filename;
}
@Override
public long contentLength() {
return -1; // 不能提前获取长度
}
}
package com.bkty.system.controller;
import com.bkty.system.domain.dto.ResumeListItemCache;
import com.bkty.system.domain.dto.ResumeMakeDto;
import com.bkty.system.domain.vo.ResumeVo;
import com.bkty.system.service.resume.NewEditionResumeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.annotation.RepeatSubmit;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.WarnException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
/**
* @author Herbert
* @description 新版简历
* @data 2025/04/08
**/
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping(value = "/newEditionResume", method = RequestMethod.POST)
@Tag(description = "newEditionResume", name = "新版简历")
public class NewEditionResumeController {
private final NewEditionResumeService newEditionResumeService;
/**
* 新版导入简历
* @param file 文件
* type 类型(1-文件,2图片)
*/
@RequestMapping(value = "/insertNewEditionResume")
@Operation(description = "新版导入简历")
@RepeatSubmit(interval = 1000)
public R insertNewEditionResume(@RequestParam("file") MultipartFile file, @RequestParam(value = "type", required = false) String type) {
this.newEditionResumeService.insertNewEditionResume(file,type);
return new R<>(Boolean.TRUE);
}
/**
* 根据简历id查询个人简历信息
* @param resumeId
* @return
*/
@GetMapping("/queryNewEditionResumeId")
public R<ResumeVo> queryNewEditionResumeId(@RequestParam("resumeId") String resumeId){
ResumeVo vo = this.newEditionResumeService.queryNewEditionResumeId(resumeId).join();
return new R<>(vo);
}
/**
* 修改简历各个模块信息
* @return
*/
@PostMapping("/updateModule/{modelName}")
public R<Object> updateModule(@PathVariable("modelName") String modelName,@RequestBody ResumeMakeDto resumeMakeDto) throws Exception {
if (null == resumeMakeDto.getResumeId()) {
throw new WarnException("简历id不能为空");
}
Object result = this.newEditionResumeService.updateModule(modelName, resumeMakeDto);
return new R<>(result);
}
/**
* 查询简历列表
* @param request
* @return
*/
@PostMapping("/resumeList")
public R<List<ResumeListItemCache>> resumeList(@RequestBody Map<String, Long> request){
Long userId = request.get("userId");
List<ResumeListItemCache> result = this.newEditionResumeService.resumeList(userId);
return new R<>(result);
}
}
package com.bkty.system.domain.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import lombok.Data;
/**
* @Desc: 代码表字段表
* @author: Herbert
* @date: 2024/07/18 09:49
* @since: 17
*/
@Data
@TableName("mock_interview_results_report")
public class MockInterviewResultsReport extends BaseEntity {
/**
* 主键id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 报告ID(第三方)
*/
private String reportId;
/**
* 内容
*/
private String content;
/**
* 面试分数
*/
private Integer score;
/**
* 训练职业
*/
private String major;
private static final long serialVersionUID = 1L;
}
\ No newline at end of file
package com.bkty.system.domain.vo;
import lombok.Data;
import org.dromara.common.core.utils.StringUtils;
import java.io.Serial;
import java.io.Serializable;
/**
* 路由显示信息
*
* @author ruoyi
*/
@Data
public class MetaVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 设置该路由在侧边栏和面包屑中展示的名字
*/
private String title;
/**
* 设置该路由的图标,对应路径src/assets/icons/svg
*/
private String icon;
/**
* 设置为true,则不会被 <keep-alive>缓存
*/
private boolean noCache;
/**
* 内链地址(http(s)://开头)
*/
private String link;
/**
* 菜单ID
*/
private Long menuId;
public MetaVo(String title, String icon) {
this.title = title;
this.icon = icon;
}
public MetaVo(String title, String icon, boolean noCache) {
this.title = title;
this.icon = icon;
this.noCache = noCache;
}
public MetaVo(String title, String icon, String link) {
this.title = title;
this.icon = icon;
this.link = link;
}
public MetaVo(String title, String icon, boolean noCache, String link) {
this.title = title;
this.icon = icon;
this.noCache = noCache;
if (StringUtils.ishttp(link)) {
this.link = link;
}
}
public MetaVo(String title, String icon, boolean noCache, String link, Long menuId) {
this.title = title;
this.icon = icon;
this.noCache = noCache;
this.menuId = menuId;
if (StringUtils.ishttp(link)) {
this.link = link;
}
}
public MetaVo(String title, String icon, String link, Long menuId) {
this.title = title;
this.icon = icon;
this.link = link;
this.menuId = menuId;
}
public MetaVo(String title, String icon, Long menuId) {
this.title = title;
this.icon = icon;
this.menuId = menuId;
}
}
package com.bkty.system.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author Herbert
* @description 基础个人信息模块
* @data 2024/12/17
**/
@Data
public class NewEditionBase {
@Schema(description = "头像URL")
private String avatar;
@Schema(description = "出生日期")
private String birthday;
@Schema(description = "电子邮箱")
private String email;
@Schema(description = "民族")
private String ethnic;
@Schema(description = "求职意向")
private String jobInt;
@Schema(description = "手机号")
private String phone;
@Schema(description = "政治面貌")
private String polStatus;
@Schema(description = "简历名称")
private String resumeName;
@Schema(description = "性别")
private String sex;
@Schema(description = "用户真实姓名")
private String username;
@Schema(description = "微信号")
private String wechat;
@Schema(description = "工作状态")
private String workStatus;
@Schema(description = "参加工作时间")
private String workTime;
}
package com.bkty.system.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author Herbert
* @description 社团经历模块
* @data 2024/12/17
**/
@Data
public class NewEditionClubs {
@Schema(description = "社团/学生组织名称")
private String clubName;
@Schema(description = "参与活动详情")
private String description;
@Schema(description = "担任职务")
private String roleName;
@Schema(description = "加入时间")
private String startTime;
@Schema(description = "退出时间")
private String endTime;
}
package com.bkty.system.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author Herbert
* @description 教育背景模块
* @data 2024/12/17
**/
@Data
public class NewEditionEdus {
@Schema(description = "学历(本科/硕士等)")
private String degree;
@Schema(description = "HTML格式的课程/研究方向描述")
private String description;
@Schema(description = "专业名称")
private String major;
@Schema(description = "学校名称")
private String school;
@Schema(description = "入学时间")
private String startTime;
@Schema(description = "结束时间('至今'表示在读)")
private String endTime;
}
package com.bkty.system.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author Herbert
* @description 实习经历模块
* @data 2024/12/17
**/
@Data
public class NewEditionInternships {
@Schema(description = "HTML格式的工作描述")
private String description;
@Schema(description = "实习结束时间")
private String endTime;
@Schema(description = "实习公司名称")
private String internshipCorp;
@Schema(description = "实习岗位")
private String internshipPost;
@Schema(description = "量化成果说明")
private String performance;
@Schema(description = "实习开始时间")
private String startTime;
}
package com.bkty.system.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author Herbert
* @description 项目经历模块
* @data 2024/12/17
**/
@Data
public class NewEditionProjects {
@Schema(description = "项目技术细节")
private String description;
@Schema(description = "项目结束时间")
private String endTime;
@Schema(description = "量化成果说明")
private String performance;
@Schema(description = "项目标题")
private String projectName;
@Schema(description = "担任角色(如负责人)")
private String roleName;
@Schema(description = "项目开始时间")
private String startTime;
}
package com.bkty.system.domain.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* @author Herbert
* @description 工作经历模块
* @data 2024/12/17
**/
@Data
public class NewEditionWorkExps {
@Schema(description = "企业全称")
private String corpName;
@Schema(description = "离职时间")
private String endTime;
@Schema(description = "HTML格式的工作业绩")
private String performance;
@Schema(description = "岗位名称")
private String postName;
@Schema(description = "入职时间")
private String startTime;
@Schema(description = "HTML格式的职责描述")
private String workContent;
}
package com.bkty.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bkty.system.domain.entity.MockInterviewResultsReport;
import org.apache.ibatis.annotations.Mapper;
/**
* @author Herbert
* @description 模拟面试结果报告
* @data 2025/3/13
**/
@Mapper
public interface MockInterviewResultsReportMapper extends BaseMapper<MockInterviewResultsReport> {
}
package com.bkty.system.service;
import cn.hutool.core.lang.tree.Tree;
import com.bkty.system.domain.dto.SysMenuDto;
import com.bkty.system.domain.entity.SysMenu;
import com.bkty.system.domain.vo.RouterVo;
import com.bkty.system.domain.vo.SysMenuVo;
import java.util.List;
import java.util.Set;
/**
* 菜单 业务层
*
* @author Lion Li
*/
public interface ISysMenuService {
/**
* 根据用户查询系统菜单列表
*
* @param userId 用户ID
* @return 菜单列表
*/
List<SysMenuVo> selectMenuList(Long userId);
/**
* 根据用户查询系统菜单列表
*
* @param menu 菜单信息
* @param userId 用户ID
* @return 菜单列表
*/
List<SysMenuVo> selectMenuList(SysMenuDto menu, Long userId);
/**
* 根据用户ID查询权限
*
* @param userId 用户ID
* @return 权限列表
*/
Set<String> selectMenuPermsByUserId(Long userId);
/**
* 根据角色ID查询权限
*
* @param roleId 角色ID
* @return 权限列表
*/
Set<String> selectMenuPermsByRoleId(Long roleId);
/**
* 根据用户ID查询菜单树信息
*
* @param userId 用户ID
* @return 菜单列表
*/
List<SysMenu> selectMenuTreeByUserId(Long userId);
/**
* 根据角色ID查询菜单树信息
*
* @param roleId 角色ID
* @return 选中菜单列表
*/
List<Long> selectMenuListByRoleId(Long roleId);
/**
* 构建前端路由所需要的菜单
*
* @param menus 菜单列表
* @return 路由列表
*/
List<RouterVo> buildMenus(List<SysMenu> menus);
/**
* 构建前端所需要下拉树结构
*
* @param menus 菜单列表
* @return 下拉树结构列表
*/
List<Tree<String>> buildMenuTreeSelect(List<SysMenuVo> menus);
/**
* 根据菜单ID查询信息
*
* @param menuId 菜单ID
* @return 菜单信息
*/
SysMenuVo selectMenuById(Long menuId);
/**
* 是否存在菜单子节点
*
* @param menuId 菜单ID
* @return 结果 true 存在 false 不存在
*/
boolean hasChildByMenuId(Long menuId);
/**
* 查询菜单是否存在角色
*
* @param menuId 菜单ID
* @return 结果 true 存在 false 不存在
*/
boolean checkMenuExistRole(Long menuId);
/**
* 新增保存菜单信息
*
* @param bo 菜单信息
* @return 结果
*/
int insertMenu(SysMenuDto bo);
/**
* 修改保存菜单信息
*
* @param bo 菜单信息
* @return 结果
*/
int updateMenu(SysMenuDto bo);
/**
* 删除菜单管理信息
*
* @param menuId 菜单ID
* @return 结果
*/
int deleteMenuById(Long menuId);
/**
* 校验菜单名称是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
boolean checkMenuNameUnique(SysMenuDto menu);
}
package com.bkty.system.service;
import java.util.Set;
/**
* 用户权限处理
*
* @author Lion Li
*/
public interface ISysPermissionService {
/**
* 获取角色数据权限
*
* @param userId 用户id
* @return 角色权限信息
*/
Set<String> getRolePermission(Long userId);
/**
* 获取菜单数据权限
*
* @param userId 用户id
* @return 菜单权限信息
*/
Set<String> getMenuPermission(Long userId);
}
package com.bkty.system.service;
import com.bkty.system.domain.dto.SysRoleDto;
import com.bkty.system.domain.entity.SysUserRole;
import com.bkty.system.domain.vo.SysRoleVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.List;
import java.util.Set;
/**
* 角色业务层
*
* @author Lion Li
*/
public interface ISysRoleService {
TableDataInfo<SysRoleVo> selectPageRoleList(SysRoleDto role, PageQuery pageQuery);
/**
* 根据条件分页查询角色数据
*
* @param role 角色信息
* @return 角色数据集合信息
*/
List<SysRoleVo> selectRoleList(SysRoleDto role);
/**
* 根据用户ID查询角色列表
*
* @param userId 用户ID
* @return 角色列表
*/
List<SysRoleVo> selectRolesByUserId(Long userId);
/**
* 根据用户ID查询角色列表(包含被授权状态)
*
* @param userId 用户ID
* @return 角色列表
*/
List<SysRoleVo> selectRolesAuthByUserId(Long userId);
/**
* 根据用户ID查询角色权限
*
* @param userId 用户ID
* @return 权限列表
*/
Set<String> selectRolePermissionByUserId(Long userId);
/**
* 查询所有角色
*
* @return 角色列表
*/
List<SysRoleVo> selectRoleAll();
/**
* 根据用户ID获取角色选择框列表
*
* @param userId 用户ID
* @return 选中角色ID列表
*/
List<Long> selectRoleListByUserId(Long userId);
/**
* 通过角色ID查询角色
*
* @param roleId 角色ID
* @return 角色对象信息
*/
SysRoleVo selectRoleById(Long roleId);
/**
* 通过角色ID串查询角色
*
* @param roleIds 角色ID串
* @return 角色列表信息
*/
List<SysRoleVo> selectRoleByIds(List<Long> roleIds);
/**
* 校验角色名称是否唯一
*
* @param role 角色信息
* @return 结果
*/
boolean checkRoleNameUnique(SysRoleDto role);
/**
* 校验角色权限是否唯一
*
* @param role 角色信息
* @return 结果
*/
boolean checkRoleKeyUnique(SysRoleDto role);
/**
* 校验角色是否允许操作
*
* @param role 角色信息
*/
void checkRoleAllowed(SysRoleDto role);
/**
* 校验角色是否有数据权限
*
* @param roleId 角色id
*/
void checkRoleDataScope(Long roleId);
/**
* 通过角色ID查询角色使用数量
*
* @param roleId 角色ID
* @return 结果
*/
long countUserRoleByRoleId(Long roleId);
/**
* 新增保存角色信息
*
* @param bo 角色信息
* @return 结果
*/
int insertRole(SysRoleDto bo);
/**
* 修改保存角色信息
*
* @param bo 角色信息
* @return 结果
*/
int updateRole(SysRoleDto bo);
/**
* 修改角色状态
*
* @param roleId 角色ID
* @param status 角色状态
* @return 结果
*/
int updateRoleStatus(Long roleId, String status);
/**
* 修改数据权限信息
*
* @param bo 角色信息
* @return 结果
*/
int authDataScope(SysRoleDto bo);
/**
* 通过角色ID删除角色
*
* @param roleId 角色ID
* @return 结果
*/
int deleteRoleById(Long roleId);
/**
* 批量删除角色信息
*
* @param roleIds 需要删除的角色ID
* @return 结果
*/
int deleteRoleByIds(Long[] roleIds);
/**
* 取消授权用户角色
*
* @param userRole 用户和角色关联信息
* @return 结果
*/
int deleteAuthUser(SysUserRole userRole);
/**
* 批量取消授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要取消授权的用户数据ID
* @return 结果
*/
int deleteAuthUsers(Long roleId, Long[] userIds);
/**
* 批量选择授权用户角色
*
* @param roleId 角色ID
* @param userIds 需要删除的用户数据ID
* @return 结果
*/
int insertAuthUsers(Long roleId, Long[] userIds);
void cleanOnlineUserByRole(Long roleId);
}
package com.bkty.system.service;
import com.bkty.system.domain.dto.EditPasswordDto;
import com.bkty.system.domain.dto.SysUserDto;
import com.bkty.system.domain.entity.SysUser;
import com.bkty.system.domain.vo.SysUserVo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.List;
/**
* 用户 业务层
*
* @author Lion Li
*/
public interface ISysUserService {
TableDataInfo<SysUserVo> selectPageUserList(SysUserDto user, PageQuery pageQuery);
/**
* 根据条件分页查询已分配用户角色列表
*
* @param user 用户信息
* @return 用户信息集合信息
*/
TableDataInfo<SysUserVo> selectAllocatedList(SysUserDto user, PageQuery pageQuery);
/**
* 通过用户ID查询用户
*
* @param userId 用户ID
* @return 用户对象信息
*/
SysUserVo selectUserById(Long userId);
/**
* 通过用户ID串查询用户
*
* @param userIds 用户ID串
* @param deptId 部门id
* @return 用户列表信息
*/
List<SysUserVo> selectUserByIds(List<Long> userIds, Long deptId);
/**
* 校验用户名称是否唯一
*
* @param user 用户信息
* @return 结果
*/
boolean checkUserNameUnique(SysUserDto user);
/**
* 校验手机号码是否唯一
*
* @param user 用户信息
* @return 结果
*/
boolean checkPhoneUnique(SysUserDto user);
/**
* 校验email是否唯一
*
* @param user 用户信息
* @return 结果
*/
boolean checkEmailUnique(SysUserDto user);
/**
* 校验用户是否允许操作
*
* @param userId 用户ID
*/
void checkUserAllowed(Long userId);
/**
* 校验用户是否有数据权限
*
* @param userId 用户id
*/
void checkUserDataScope(Long userId);
/**
* 新增用户信息
*
* @param user 用户信息
* @return 结果
*/
int insertUser(SysUserDto user);
/**
* 注册用户信息
*
* @param user 用户信息
* @return 结果
*/
boolean registerUser(SysUserDto user, String tenantId);
/**
* 修改用户信息
*
* @param user 用户信息
* @return 结果
*/
int updateUser(SysUserDto user);
/**
* 通过用户ID查询用户账户
*
* @param userId 用户ID
* @return 用户账户
*/
String selectUserNameById(Long userId);
/**
* 通过用户ID查询用户账户
*
* @param userId 用户ID
* @return 用户账户
*/
String selectNicknameById(Long userId);
/**
* 通过用户ID查询用户账户
*
* @param userIds 用户ID
* @return 用户账户
*/
String selectNicknameByIds(String userIds);
/**
* 通过用户ID查询用户手机号
*
* @param userId 用户id
* @return 用户手机号
*/
String selectPhonenumberById(Long userId);
/**
* 通过用户ID查询用户邮箱
*
* @param userId 用户id
* @return 用户邮箱
*/
String selectEmailById(Long userId);
/**
* 通过角色ID查询用户ID
*
* @param roleIds 角色ids
* @return 用户ids
*/
List<Long> selectUserIdsByRoleIds(List<Long> roleIds);
/**
* 批量删除用户信息
*
* @param userIds 需要删除的用户ID
* @return 结果
*/
int deleteUserByIds(Long[] userIds);
/**
* 查询用户信息下拉框使用
* @return list
*/
List<SysUser> queryUserPullDown();
/**
* 重置用户密码
*
* @param userId 用户ID
* @return 结果
*/
int resetUserPwd(Long userId);
/**
* 修改用户密码
*
* @param user 修改用户密码
* @return 结果
*/
int editPassword(EditPasswordDto user);
}
package com.bkty.system.service.resume;
import com.bkty.system.domain.dto.ResumeListItemCache;
import com.bkty.system.domain.dto.ResumeMakeDto;
import com.bkty.system.domain.vo.ResumeVo;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* @author Herbert
* @description 新版简历
* @data 2025/04/08
**/
public interface NewEditionResumeService {
/**
* 新版导入简历
*
* @param file 文件
* type 类型(1-文件,2图片)
*/
void insertNewEditionResume(MultipartFile file, String type);
/**
* 查询简历vo
*
* @param resumeId
* @return
*/
CompletableFuture<ResumeVo> queryNewEditionResumeId(String resumeId);
/**
* 修改简历各个模块信息
*
* @param modelName
* @param resumeMakeDto
*/
Object updateModule(String modelName, ResumeMakeDto resumeMakeDto) throws Exception;
/**
* 查询简历列表
* @param userId
* @return
*/
List<ResumeListItemCache> resumeList(Long userId);
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/${project.artifactId}" />
<!-- 日志输出格式 -->
<property name="console.log.pattern"
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<include resource="logback-common.xml" />
<include resource="logback-logstash.xml" />
<!-- 开启 skywalking 日志收集 -->
<include resource="logback-skylog.xml" />
<!--系统操作日志-->
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos;
import com.alibaba.nacos.sys.filter.NacosTypeExcludeFilter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.FilterType;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* Nacos starter.
* <p>
* Use @SpringBootApplication and @ComponentScan at the same time, using CUSTOM type filter to control module enabled.
* </p>
*
* @author nacos
*/
@SpringBootApplication
@ComponentScan(basePackages = "com.alibaba.nacos", excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = {NacosTypeExcludeFilter.class}),
@Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}),
@Filter(type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})})
@ServletComponentScan
@EnableScheduling
public class Nacos {
public static void main(String[] args) {
// true 单机模式 false 为集群模式 集群模式需搭配 cluster.conf 使用 使用方法请查看文档
System.setProperty("nacos.standalone", "true");
System.setProperty("server.tomcat.accesslog.enabled", "false");
// 本地集群搭建使用 分别在所有 nacos 目录下创建 conf/cluster.conf 文件用于编写集群ip端口
// 注意 如果本地启动多个 nacos 此目录不能相同 例如 nacos1 nacos2 nacos3 对应三个nacos服务
// System.setProperty("nacos.home", "D:/nacos");
SpringApplication.run(Nacos.class, args);
System.out.println("(♥◠‿◠)ノ゙ NACOS服务启动成功 ლ(´ڡ`ლ)゙ ");
}
}
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.console.controller;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.common.model.RestResult;
import com.alibaba.nacos.common.model.RestResultUtils;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor;
import com.alibaba.nacos.core.namespace.model.Namespace;
import com.alibaba.nacos.core.namespace.repository.NamespacePersistService;
import com.alibaba.nacos.core.paramcheck.ExtractorManager;
import com.alibaba.nacos.core.service.NamespaceOperationService;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
/**
* namespace service.
*
* @author Nacos
*/
@RestController
@RequestMapping("/v1/console/namespaces")
@ExtractorManager.Extractor(httpExtractor = ConsoleDefaultHttpParamExtractor.class)
public class NamespaceController {
@Autowired
private NamespacePersistService namespacePersistService;
@Autowired
private NamespaceOperationService namespaceOperationService;
private final Pattern namespaceIdCheckPattern = Pattern.compile("^[\\w-]+");
private final Pattern namespaceNameCheckPattern = Pattern.compile("^[^@#$%^&*]+$");
private static final int NAMESPACE_ID_MAX_LENGTH = 128;
/**
* Get namespace list.
*
* @return namespace list
*/
@GetMapping
public RestResult<List<Namespace>> getNamespaces() {
return RestResultUtils.success(namespaceOperationService.getNamespaceList());
}
/**
* get namespace all info by namespace id.
*
* @param namespaceId namespaceId
* @return namespace all info
*/
@GetMapping(params = "show=all")
public Namespace getNamespace(@RequestParam("namespaceId") String namespaceId) throws NacosException {
return namespaceOperationService.getNamespace(namespaceId);
}
/**
* create namespace.
*
* @param namespaceName namespace Name
* @param namespaceDesc namespace Desc
* @return whether create ok
*/
@PostMapping
@Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
public Boolean createNamespace(@RequestParam("customNamespaceId") String namespaceId,
@RequestParam("namespaceName") String namespaceName,
@RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) {
if (StringUtils.isBlank(namespaceId)) {
namespaceId = UUID.randomUUID().toString();
} else {
namespaceId = namespaceId.trim();
if (!namespaceIdCheckPattern.matcher(namespaceId).matches()) {
return false;
}
if (namespaceId.length() > NAMESPACE_ID_MAX_LENGTH) {
return false;
}
// check unique
if (namespacePersistService.tenantInfoCountByTenantId(namespaceId) > 0) {
return false;
}
}
// contains illegal chars
if (!namespaceNameCheckPattern.matcher(namespaceName).matches()) {
return false;
}
try {
return namespaceOperationService.createNamespace(namespaceId, namespaceName, namespaceDesc);
} catch (NacosException e) {
return false;
}
}
/**
* check namespaceId exist.
*
* @param namespaceId namespace id
* @return true if exist, otherwise false
*/
@GetMapping(params = "checkNamespaceIdExist=true")
public Boolean checkNamespaceIdExist(@RequestParam("customNamespaceId") String namespaceId) {
if (StringUtils.isBlank(namespaceId)) {
return false;
}
return (namespacePersistService.tenantInfoCountByTenantId(namespaceId) > 0);
}
/**
* edit namespace.
*
* @param namespace namespace
* @param namespaceShowName namespace ShowName
* @param namespaceDesc namespace Desc
* @return whether edit ok
*/
@PutMapping
@Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
public Boolean editNamespace(@RequestParam("namespace") String namespace,
@RequestParam("namespaceShowName") String namespaceShowName,
@RequestParam(value = "namespaceDesc", required = false) String namespaceDesc) {
// contains illegal chars
if (!namespaceNameCheckPattern.matcher(namespaceShowName).matches()) {
return false;
}
return namespaceOperationService.editNamespace(namespace, namespaceShowName, namespaceDesc);
}
/**
* del namespace by id.
*
* @param namespaceId namespace Id
* @return whether del ok
*/
@DeleteMapping
@Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE)
public Boolean deleteNamespace(@RequestParam("namespaceId") String namespaceId) {
return namespaceOperationService.removeNamespace(namespaceId);
}
}
/*
* Copyright 1999-2022 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.console.controller.v2;
import com.alibaba.nacos.api.annotation.NacosApi;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.exception.api.NacosApiException;
import com.alibaba.nacos.api.model.v2.ErrorCode;
import com.alibaba.nacos.api.model.v2.Result;
import com.alibaba.nacos.auth.annotation.Secured;
import com.alibaba.nacos.common.utils.StringUtils;
import com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor;
import com.alibaba.nacos.core.namespace.model.Namespace;
import com.alibaba.nacos.core.namespace.model.form.NamespaceForm;
import com.alibaba.nacos.core.namespace.repository.NamespacePersistService;
import com.alibaba.nacos.core.paramcheck.ExtractorManager;
import com.alibaba.nacos.core.service.NamespaceOperationService;
import com.alibaba.nacos.plugin.auth.constant.ActionTypes;
import com.alibaba.nacos.plugin.auth.constant.SignType;
import com.alibaba.nacos.plugin.auth.impl.constant.AuthConstants;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.UUID;
import java.util.regex.Pattern;
/**
* NamespaceControllerV2.
*
* @author dongyafei
* @date 2022/8/16
*/
@NacosApi
@RestController
@RequestMapping("/v2/console/namespace")
@ExtractorManager.Extractor(httpExtractor = ConsoleDefaultHttpParamExtractor.class)
public class NamespaceControllerV2 {
private final NamespaceOperationService namespaceOperationService;
private NamespacePersistService namespacePersistService;
public NamespaceControllerV2(NamespaceOperationService namespaceOperationService, NamespacePersistService namespacePersistService) {
this.namespaceOperationService = namespaceOperationService;
this.namespacePersistService = namespacePersistService;
}
private final Pattern namespaceIdCheckPattern = Pattern.compile("^[\\w-]+");
private final Pattern namespaceNameCheckPattern = Pattern.compile("^[^@#$%^&*]+$");
private static final int NAMESPACE_ID_MAX_LENGTH = 128;
/**
* Get namespace list.
*
* @return namespace list
*/
@GetMapping("/list")
public Result<List<Namespace>> getNamespaceList() {
return Result.success(namespaceOperationService.getNamespaceList());
}
/**
* get namespace all info by namespace id.
*
* @param namespaceId namespaceId
* @return namespace all info
*/
@GetMapping()
@Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX
+ "namespaces", action = ActionTypes.READ, signType = SignType.CONSOLE)
public Result<Namespace> getNamespace(@RequestParam("namespaceId") String namespaceId) throws NacosException {
return Result.success(namespaceOperationService.getNamespace(namespaceId));
}
/**
* create namespace.
*
* @param namespaceForm namespaceForm.
* @return whether create ok
*/
@PostMapping
@Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX
+ "namespaces", action = ActionTypes.WRITE, signType = SignType.CONSOLE)
public Result<Boolean> createNamespace(NamespaceForm namespaceForm) throws NacosException {
namespaceForm.validate();
String namespaceId = namespaceForm.getNamespaceId();
String namespaceName = namespaceForm.getNamespaceName();
String namespaceDesc = namespaceForm.getNamespaceDesc();
if (StringUtils.isBlank(namespaceId)) {
namespaceId = UUID.randomUUID().toString();
} else {
namespaceId = namespaceId.trim();
if (!namespaceIdCheckPattern.matcher(namespaceId).matches()) {
throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.ILLEGAL_NAMESPACE,
"namespaceId [" + namespaceId + "] mismatch the pattern");
}
if (namespaceId.length() > NAMESPACE_ID_MAX_LENGTH) {
throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.ILLEGAL_NAMESPACE,
"too long namespaceId, over " + NAMESPACE_ID_MAX_LENGTH);
}
// check unique
if (namespacePersistService.tenantInfoCountByTenantId(namespaceId) > 0) {
throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.ILLEGAL_NAMESPACE,
"the namespaceId is existed, namespaceId: " + namespaceForm.getNamespaceId());
}
}
// contains illegal chars
if (!namespaceNameCheckPattern.matcher(namespaceName).matches()) {
throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.ILLEGAL_NAMESPACE,
"namespaceName [" + namespaceName + "] contains illegal char");
}
return Result.success(namespaceOperationService.createNamespace(namespaceId, namespaceName, namespaceDesc));
}
/**
* edit namespace.
*
* @param namespaceForm namespace params
* @return whether edit ok
*/
@PutMapping
@Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX
+ "namespaces", action = ActionTypes.WRITE, signType = SignType.CONSOLE)
public Result<Boolean> editNamespace(NamespaceForm namespaceForm) throws NacosException {
namespaceForm.validate();
// contains illegal chars
if (!namespaceNameCheckPattern.matcher(namespaceForm.getNamespaceName()).matches()) {
throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.ILLEGAL_NAMESPACE,
"namespaceName [" + namespaceForm.getNamespaceName() + "] contains illegal char");
}
return Result.success(namespaceOperationService
.editNamespace(namespaceForm.getNamespaceId(), namespaceForm.getNamespaceName(),
namespaceForm.getNamespaceDesc()));
}
/**
* delete namespace by id.
*
* @param namespaceId namespace ID
* @return whether delete ok
*/
@DeleteMapping
@Secured(resource = AuthConstants.CONSOLE_RESOURCE_NAME_PREFIX
+ "namespaces", action = ActionTypes.WRITE, signType = SignType.CONSOLE)
public Result<Boolean> deleteNamespace(@RequestParam("namespaceId") String namespaceId) {
return Result.success(namespaceOperationService.removeNamespace(namespaceId));
}
}
/*
* Copyright 1999-2022 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.console.exception;
import com.alibaba.nacos.api.annotation.NacosApi;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.exception.api.NacosApiException;
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.api.model.v2.ErrorCode;
import com.alibaba.nacos.api.model.v2.Result;
import com.alibaba.nacos.common.utils.ExceptionUtil;
import com.alibaba.nacos.plugin.auth.exception.AccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import javax.servlet.ServletException;
import java.io.IOException;
/**
* Exception Handler for Nacos API.
* @author dongyafei
* @date 2022/7/22
*/
@Order(-1)
@ControllerAdvice(annotations = {NacosApi.class})
@ResponseBody
public class NacosApiExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(NacosApiExceptionHandler.class);
@ExceptionHandler(NacosApiException.class)
public ResponseEntity<Result<String>> handleNacosApiException(NacosApiException e) {
LOGGER.error("got exception. {} {}", e.getErrAbstract(), e.getErrMsg());
return ResponseEntity.status(e.getErrCode()).body(new Result<>(e.getDetailErrCode(), e.getErrAbstract(), e.getErrMsg()));
}
@ExceptionHandler(NacosException.class)
public ResponseEntity<Result<String>> handleNacosException(NacosException e) {
LOGGER.error("got exception. {}", e.getErrMsg());
return ResponseEntity.status(e.getErrCode()).body(Result.failure(ErrorCode.SERVER_ERROR, e.getErrMsg()));
}
@ExceptionHandler(NacosRuntimeException.class)
public ResponseEntity<Result<String>> handleNacosRuntimeException(NacosRuntimeException e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return ResponseEntity.status(e.getErrCode()).body(Result.failure(ErrorCode.SERVER_ERROR, e.getMessage()));
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public Result<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(ErrorCode.PARAMETER_MISSING, e.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageConversionException.class)
public Result<String> handleHttpMessageConversionException(HttpMessageConversionException e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(ErrorCode.PARAMETER_VALIDATE_ERROR, e.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(NumberFormatException.class)
public Result<String> handleNumberFormatException(NumberFormatException e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(ErrorCode.PARAMETER_VALIDATE_ERROR, e.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(IllegalArgumentException.class)
public Result<String> handleIllegalArgumentException(IllegalArgumentException e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(ErrorCode.PARAMETER_VALIDATE_ERROR, e.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MissingServletRequestParameterException.class)
public Result<String> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(ErrorCode.PARAMETER_MISSING, e.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMediaTypeException.class)
public Result<String> handleHttpMediaTypeException(HttpMediaTypeException e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(ErrorCode.MEDIA_TYPE_ERROR, e.getMessage());
}
@ResponseStatus(HttpStatus.FORBIDDEN)
@ExceptionHandler(AccessException.class)
public Result<String> handleAccessException(AccessException e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(ErrorCode.ACCESS_DENIED, e.getErrMsg());
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(value = {DataAccessException.class, ServletException.class, IOException.class})
public Result<String> handleDataAccessException(Exception e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(ErrorCode.DATA_ACCESS_ERROR, e.getMessage());
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public Result<String> handleOtherException(Exception e) {
LOGGER.error("got exception. {} {}", e.getMessage(), ExceptionUtil.getAllExceptionMsg(e));
return Result.failure(e.getMessage());
}
}
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment