chore(build): 更新项目依赖版本并优化代码格式

- 升级 Spring Boot 父项目版本至 3.5.9
- 更新 MySQL 连接器版本至 9.4.0
- 更新 MyBatis-Plus 版本至 3.5.14
- 更新 Fastjson2 版本至 2.0.59
- 更新 Hutool 版本至 5.8.41
- 更新 JJWT 版本至 0.12.6
- 更新 Knife4j 版本至 4.5.0
- 更新 Undertow 版本至 2.3.20.Final
- 更新 Oshi 版本至 6.6.5
- 更新 Guava 版本至 33.5.0-jre
- 格式化注解文件中的代码缩进
- 移除多余的空行和注释格式调整
This commit is contained in:
2025-12-30 12:53:51 +08:00
parent 377b0989f8
commit 83f75831f0
207 changed files with 7424 additions and 6977 deletions
@@ -24,37 +24,37 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum SystemCaptchaBizEnum {
/* 通过手机号码重置邮箱 */
PHONE_RESET_EMAIL_CODE("10000", "phone_reset_email_code_", "SystemEmailCaptchaTemplate.ftl",
"通过手机号码重置邮箱"), /* 通过旧邮箱重置邮箱 */
EMAIL_RESET_EMAIL_CODE("10001", "email_reset_email_code_", "SystemEmailCaptchaTemplate.ftl", "通过旧邮箱重置邮箱"),
/* 通过手机号码重置密码 */
PHONE_RESET_PWD_CODE("10002", "phone_reset_pwd_code_", "SystemEmailCaptchaTemplate.ftl", "通过手机号码重置密码"),
/* 通过邮箱重置密码 */
EMAIL_RESET_PWD_CODE("10003", "email_reset_pwd_code_", "SystemEmailCaptchaTemplate.ftl", "通过邮箱重置密码");
/**
* 业务编码
*/
private final String bizCode;
/**
* redis中的key
*/
private final String redisKey;
/**
* resources文件夹,template中的模版名称
*/
private final String templateName;
/**
* 业务描述
*/
private final String description;
/* 通过手机号码重置邮箱 */
PHONE_RESET_EMAIL_CODE("10000", "phone_reset_email_code_", "SystemEmailCaptchaTemplate.ftl",
"通过手机号码重置邮箱"), /* 通过旧邮箱重置邮箱 */
EMAIL_RESET_EMAIL_CODE("10001", "email_reset_email_code_", "SystemEmailCaptchaTemplate.ftl", "通过旧邮箱重置邮箱"),
/* 通过手机号码重置密码 */
PHONE_RESET_PWD_CODE("10002", "phone_reset_pwd_code_", "SystemEmailCaptchaTemplate.ftl", "通过手机号码重置密码"),
/* 通过邮箱重置密码 */
EMAIL_RESET_PWD_CODE("10003", "email_reset_pwd_code_", "SystemEmailCaptchaTemplate.ftl", "通过邮箱重置密码");
/**
* 业务编码
*/
private final String bizCode;
/**
* redis中的key
*/
private final String redisKey;
/**
* resources文件夹,template中的模版名称
*/
private final String templateName;
/**
* 业务描述
*/
private final String description;
public static SystemCaptchaBizEnum getByBizCode(String bizCode) {
for (SystemCaptchaBizEnum value : SystemCaptchaBizEnum.values()) {
if (value.getBizCode().equals(bizCode)) {
return value;
}
}
return null;
public static SystemCaptchaBizEnum getByBizCode(String bizCode) {
for (SystemCaptchaBizEnum value : SystemCaptchaBizEnum.values()) {
if (value.getBizCode().equals(bizCode)) {
return value;
}
}
return null;
}
}
@@ -24,19 +24,19 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum SystemDataScopeEnum {
/* 全部的数据权限 */
ALL("全部", "全部的数据权限"), /* 自己部门的数据权限 */
THIS_LEVEL("本级", "自己部门的数据权限"), /* 自定义的数据权限 */
CUSTOMIZE("自定义", "自定义的数据权限");
private final String value;
private final String description;
/* 全部的数据权限 */
ALL("全部", "全部的数据权限"), /* 自己部门的数据权限 */
THIS_LEVEL("本级", "自己部门的数据权限"), /* 自定义的数据权限 */
CUSTOMIZE("自定义", "自定义的数据权限");
private final String value;
private final String description;
public static SystemDataScopeEnum find(String val) {
for (SystemDataScopeEnum dataScopeEnum : SystemDataScopeEnum.values()) {
if (dataScopeEnum.getValue().equals(val)) {
return dataScopeEnum;
}
}
return null;
public static SystemDataScopeEnum find(String val) {
for (SystemDataScopeEnum dataScopeEnum : SystemDataScopeEnum.values()) {
if (dataScopeEnum.getValue().equals(val)) {
return dataScopeEnum;
}
}
return null;
}
}
@@ -17,7 +17,7 @@ package cn.odboy.system.constant;
public interface TransferProtocolConst {
String PREFIX_HTTP = "http://";
String PREFIX_HTTPS = "https://";
String PREFIX_HTTPS_BAD_REQUEST = "外链必须以http://或者https://开头";
String PREFIX_HTTP = "http://";
String PREFIX_HTTPS = "https://";
String PREFIX_HTTPS_BAD_REQUEST = "外链必须以http://或者https://开头";
}
@@ -21,8 +21,8 @@ import cn.odboy.system.dal.model.SystemUserLoginArgs;
import cn.odboy.system.service.SystemAuthService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.util.Map;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
@@ -24,9 +24,9 @@ import cn.odboy.system.service.SystemDeptService;
import cn.odboy.util.KitPageUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -24,10 +24,10 @@ import cn.odboy.system.service.SystemDictService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -24,9 +24,9 @@ import cn.odboy.system.service.SystemJobService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -23,8 +23,8 @@ import cn.odboy.system.service.SystemLocalStorageService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.io.IOException;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -21,7 +21,6 @@ import cn.odboy.framework.exception.BadRequestException;
import cn.odboy.system.dal.dataobject.SystemMenuTb;
import cn.odboy.system.dal.model.SystemMenuVo;
import cn.odboy.system.dal.model.SystemQueryMenuArgs;
import cn.odboy.system.dal.mysql.SystemMenuMapper;
import cn.odboy.system.service.SystemMenuService;
import cn.odboy.util.KitPageUtil;
import io.swagger.annotations.Api;
@@ -47,8 +46,6 @@ public class SystemMenuController {
@Autowired
private SystemMenuService systemMenuService;
@Autowired
private SystemMenuMapper systemMenuMapper;
@ApiOperation("导出菜单数据")
@GetMapping(value = "/download")
@@ -23,10 +23,10 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -24,8 +24,8 @@ import cn.odboy.system.service.SystemOssStorageService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import java.io.IOException;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
@@ -25,9 +25,9 @@ import cn.odboy.system.service.SystemQuartzJobService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
@@ -26,10 +26,10 @@ import cn.odboy.system.service.SystemRoleService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -23,16 +23,14 @@ import cn.odboy.system.dal.model.SystemQueryUserArgs;
import cn.odboy.system.dal.model.SystemUpdateUserPasswordArgs;
import cn.odboy.system.dal.model.SystemUserVo;
import cn.odboy.system.framework.permission.core.KitSecurityHelper;
import cn.odboy.system.service.SystemDataService;
import cn.odboy.system.service.SystemDeptService;
import cn.odboy.system.service.SystemUserService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -53,10 +51,6 @@ public class SystemUserController {
@Autowired
private SystemUserService systemUserService;
@Autowired
private SystemDataService systemDataService;
@Autowired
private SystemDeptService systemDeptService;
@ApiOperation("导出用户数据")
@@ -35,46 +35,60 @@ import lombok.Setter;
@TableName("system_dept")
public class SystemDeptTb extends KitBaseUserTimeTb {
@NotNull(groups = Update.class) @TableId(value = "dept_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true) private Long id;
@TableField(exist = false) @JSONField(serialize = false) @ApiModelProperty(value = "角色") private Set<SystemRoleTb>
roles;
@TableField(exist = false) @ApiModelProperty(value = "子部门") private List<SystemDeptTb> children;
@ApiModelProperty(value = "排序") private Integer deptSort;
@NotBlank @ApiModelProperty(value = "部门名称") private String name;
@NotNull @ApiModelProperty(value = "是否启用") private Boolean enabled;
@ApiModelProperty(value = "上级部门") private Long pid;
@ApiModelProperty(value = "子节点数目", hidden = true) private Integer subCount = 0;
@NotNull(groups = Update.class)
@TableId(value = "dept_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@TableField(exist = false)
@JSONField(serialize = false)
@ApiModelProperty(value = "角色")
private Set<SystemRoleTb>
roles;
@TableField(exist = false)
@ApiModelProperty(value = "子部门")
private List<SystemDeptTb> children;
@ApiModelProperty(value = "排序")
private Integer deptSort;
@NotBlank
@ApiModelProperty(value = "部门名称")
private String name;
@NotNull
@ApiModelProperty(value = "是否启用")
private Boolean enabled;
@ApiModelProperty(value = "上级部门")
private Long pid;
@ApiModelProperty(value = "子节点数目", hidden = true)
private Integer subCount = 0;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemDeptTb dept = (SystemDeptTb)o;
return Objects.equals(id, dept.id) && Objects.equals(name, dept.name);
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemDeptTb dept = (SystemDeptTb) o;
return Objects.equals(id, dept.id) && Objects.equals(name, dept.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@ApiModelProperty(value = "是否有子节点")
public Boolean getHasChildren() {
return subCount > 0;
}
@ApiModelProperty(value = "是否有子节点")
public Boolean getHasChildren() {
return subCount > 0;
}
@ApiModelProperty(value = "是否为叶子")
public Boolean getLeaf() {
return subCount <= 0;
}
@ApiModelProperty(value = "是否为叶子")
public Boolean getLeaf() {
return subCount <= 0;
}
@ApiModelProperty(value = "标签名称")
public String getLabel() {
return name;
}
@ApiModelProperty(value = "标签名称")
public String getLabel() {
return name;
}
}
@@ -30,10 +30,17 @@ import lombok.Setter;
@TableName("system_dict_detail")
public class SystemDictDetailTb extends KitBaseUserTimeTb {
@NotNull(groups = Update.class) @ApiModelProperty(value = "ID", hidden = true)
@TableId(value = "id", type = IdType.AUTO) private Long id;
@TableField(value = "dict_id") @ApiModelProperty(hidden = true) private Long dictId;
@ApiModelProperty(value = "字典标签") private String label;
@ApiModelProperty(value = "字典值") private String value;
@ApiModelProperty(value = "排序") private Integer dictSort = 999;
@NotNull(groups = Update.class)
@ApiModelProperty(value = "ID", hidden = true)
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField(value = "dict_id")
@ApiModelProperty(hidden = true)
private Long dictId;
@ApiModelProperty(value = "字典标签")
private String label;
@ApiModelProperty(value = "字典值")
private String value;
@ApiModelProperty(value = "排序")
private Integer dictSort = 999;
}
@@ -32,8 +32,14 @@ import lombok.Setter;
@TableName("system_dict")
public class SystemDictTb extends KitBaseUserTimeTb {
@NotNull(groups = Update.class) @ApiModelProperty(value = "ID", hidden = true)
@TableId(value = "id", type = IdType.AUTO) private Long id;
@NotBlank @ApiModelProperty(value = "名称") private String name;
@ApiModelProperty(value = "描述") @TableField(updateStrategy = FieldStrategy.ALWAYS) private String description;
@NotNull(groups = Update.class)
@ApiModelProperty(value = "ID", hidden = true)
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@NotBlank
@ApiModelProperty(value = "名称")
private String name;
@ApiModelProperty(value = "描述")
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String description;
}
@@ -31,10 +31,21 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemEmailConfigTb extends KitObject {
@TableId("config_id") private Long id;
@NotBlank @ApiModelProperty(value = "邮件服务器SMTP地址") private String host;
@NotBlank @ApiModelProperty(value = "邮件服务器 SMTP 端口") private String port;
@NotBlank @ApiModelProperty(value = "发件者用户名") private String user;
@NotBlank @ApiModelProperty(value = "密码") private String password;
@NotBlank @ApiModelProperty(value = "收件人") private String fromUser;
@TableId("config_id")
private Long id;
@NotBlank
@ApiModelProperty(value = "邮件服务器SMTP地址")
private String host;
@NotBlank
@ApiModelProperty(value = "邮件服务器 SMTP 端口")
private String port;
@NotBlank
@ApiModelProperty(value = "发件者用户名")
private String user;
@NotBlank
@ApiModelProperty(value = "密码")
private String password;
@NotBlank
@ApiModelProperty(value = "收件人")
private String fromUser;
}
@@ -31,26 +31,34 @@ import lombok.Setter;
@TableName("system_job")
public class SystemJobTb extends KitBaseUserTimeTb {
@NotNull(groups = Update.class) @TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true) private Long id;
@NotBlank @ApiModelProperty(value = "岗位名称") private String name;
@NotNull @ApiModelProperty(value = "岗位排序") private Long jobSort;
@NotNull @ApiModelProperty(value = "是否启用") private Boolean enabled;
@NotNull(groups = Update.class)
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@NotBlank
@ApiModelProperty(value = "岗位名称")
private String name;
@NotNull
@ApiModelProperty(value = "岗位排序")
private Long jobSort;
@NotNull
@ApiModelProperty(value = "是否启用")
private Boolean enabled;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemJobTb job = (SystemJobTb)o;
return Objects.equals(id, job.id);
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemJobTb job = (SystemJobTb) o;
return Objects.equals(id, job.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
@@ -30,16 +30,25 @@ import lombok.Setter;
@TableName("system_local_storage")
public class SystemLocalStorageTb extends KitBaseUserTimeTb {
@TableId(value = "id", type = IdType.AUTO) @ApiModelProperty(value = "ID", hidden = true) private Long id;
@ApiModelProperty(value = "真实文件名") private String realName;
@ApiModelProperty(value = "文件名") private String name;
@ApiModelProperty(value = "后缀") private String suffix;
@ApiModelProperty(value = "路径") private String path;
@ApiModelProperty(value = "类型") private String type;
@ApiModelProperty(value = "大小") private String size;
@ApiModelProperty(value = "日期分组") private String dateGroup;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@ApiModelProperty(value = "真实文件名")
private String realName;
@ApiModelProperty(value = "文件名")
private String name;
@ApiModelProperty(value = "后缀")
private String suffix;
@ApiModelProperty(value = "路径")
private String path;
@ApiModelProperty(value = "类型")
private String type;
@ApiModelProperty(value = "大小")
private String size;
@ApiModelProperty(value = "日期分组")
private String dateGroup;
public void copy(SystemLocalStorageTb source) {
BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true));
}
public void copy(SystemLocalStorageTb source) {
BeanUtil.copyProperties(source, this, CopyOptions.create().setIgnoreNullValue(true));
}
}
@@ -34,54 +34,73 @@ import lombok.Setter;
@TableName("system_menu")
public class SystemMenuTb extends KitBaseUserTimeTb {
@NotNull(groups = {Update.class}) @TableId(value = "menu_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true) private Long id;
@TableField(exist = false) @JSONField(serialize = false) @ApiModelProperty(value = "菜单角色")
private Set<SystemRoleTb> roles;
@TableField(exist = false) private List<SystemMenuTb> children;
@ApiModelProperty(value = "菜单标题") private String title;
@TableField(value = "name") @ApiModelProperty(value = "菜单组件名称") private String componentName;
@ApiModelProperty(value = "排序") private Integer menuSort = 999;
@ApiModelProperty(value = "组件路径") private String component;
@ApiModelProperty(value = "路由地址") private String path;
@ApiModelProperty(value = "菜单类型,目录、菜单、按钮") private Integer type;
@ApiModelProperty(value = "权限标识") private String permission;
@ApiModelProperty(value = "菜单图标") private String icon;
@ApiModelProperty(value = "缓存") private Boolean cache;
@ApiModelProperty(value = "是否隐藏") private Boolean hidden;
@ApiModelProperty(value = "上级菜单") private Long pid;
@ApiModelProperty(value = "子节点数目", hidden = true) private Integer subCount = 0;
@ApiModelProperty(value = "外链菜单") private Boolean iFrame;
@NotNull(groups = {Update.class})
@TableId(value = "menu_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@TableField(exist = false)
@JSONField(serialize = false)
@ApiModelProperty(value = "菜单角色")
private Set<SystemRoleTb> roles;
@TableField(exist = false)
private List<SystemMenuTb> children;
@ApiModelProperty(value = "菜单标题")
private String title;
@TableField(value = "name")
@ApiModelProperty(value = "菜单组件名称")
private String componentName;
@ApiModelProperty(value = "排序")
private Integer menuSort = 999;
@ApiModelProperty(value = "组件路径")
private String component;
@ApiModelProperty(value = "路由地址")
private String path;
@ApiModelProperty(value = "菜单类型,目录、菜单、按钮")
private Integer type;
@ApiModelProperty(value = "权限标识")
private String permission;
@ApiModelProperty(value = "菜单图标")
private String icon;
@ApiModelProperty(value = "缓存")
private Boolean cache;
@ApiModelProperty(value = "是否隐藏")
private Boolean hidden;
@ApiModelProperty(value = "上级菜单")
private Long pid;
@ApiModelProperty(value = "子节点数目", hidden = true)
private Integer subCount = 0;
@ApiModelProperty(value = "外链菜单")
private Boolean iFrame;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemMenuTb menu = (SystemMenuTb)o;
return Objects.equals(id, menu.id);
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemMenuTb menu = (SystemMenuTb) o;
return Objects.equals(id, menu.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@ApiModelProperty(value = "是否有子节点")
public Boolean getHasChildren() {
return subCount > 0;
}
@ApiModelProperty(value = "是否有子节点")
public Boolean getHasChildren() {
return subCount > 0;
}
@ApiModelProperty(value = "是否为叶子")
public Boolean getLeaf() {
return subCount <= 0;
}
@ApiModelProperty(value = "是否为叶子")
public Boolean getLeaf() {
return subCount <= 0;
}
@ApiModelProperty(value = "标签名称")
public String getLabel() {
return title;
}
@ApiModelProperty(value = "标签名称")
public String getLabel() {
return title;
}
}
@@ -43,48 +43,60 @@ import lombok.ToString;
@ApiModel(value = "SystemOperationLog对象", description = "审计日志")
public class SystemOperationLogTb extends KitObject {
/**
* id
*/
@ApiModelProperty("id") @TableId(value = "id", type = IdType.AUTO) private Long id;
/**
* 业务名称
*/
@TableField("biz_name") private String bizName;
/**
* 业务方法
*/
@TableField("method") private String method;
/**
* 业务参数
*/
@TableField("params") private String params;
/**
* 访问来源IP
*/
@TableField("request_ip") private String requestIp;
/**
* 执行时间
*/
@TableField("execute_time") private Long executeTime;
/**
* 用户名
*/
@TableField("username") private String username;
/**
* 访问来源地址
*/
@TableField("address") private String address;
/**
* 浏览器信息
*/
@TableField("browser_info") private String browserInfo;
/**
* 异常详情
*/
@TableField("exception_detail") private String exceptionDetail;
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT) private Date createTime;
/**
* id
*/
@ApiModelProperty("id")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 业务名称
*/
@TableField("biz_name")
private String bizName;
/**
* 业务方法
*/
@TableField("method")
private String method;
/**
* 业务参数
*/
@TableField("params")
private String params;
/**
* 访问来源IP
*/
@TableField("request_ip")
private String requestIp;
/**
* 执行时间
*/
@TableField("execute_time")
private Long executeTime;
/**
* 用户名
*/
@TableField("username")
private String username;
/**
* 访问来源地址
*/
@TableField("address")
private String address;
/**
* 浏览器信息
*/
@TableField("browser_info")
private String browserInfo;
/**
* 异常详情
*/
@TableField("exception_detail")
private String exceptionDetail;
/**
* 创建时间
*/
@TableField(value = "create_time", fill = FieldFill.INSERT)
private Date createTime;
}
@@ -39,17 +39,43 @@ import lombok.Setter;
@ApiModel(value = "SystemOssStorage对象", description = "OSS存储")
public class SystemOssStorageTb extends KitBaseUserTimeTb {
@ApiModelProperty("ID") @TableId(value = "id", type = IdType.AUTO) private Long id;
@TableField("service_type") @ApiModelProperty("类型,比如minio") private String serviceType;
@TableField("endpoint") @ApiModelProperty("服务地址") private String endpoint;
@TableField("bucket_name") @ApiModelProperty("存储桶名称") private String bucketName;
@TableField("file_name") @ApiModelProperty("完整文件名称") private String fileName;
@TableField("file_size") @ApiModelProperty("文件大小, 单位:字节") private Long fileSize;
@TableField("file_mime") @ApiModelProperty("文件类型") private String fileMime;
@ApiModelProperty("短文件名") @TableField("file_prefix") private String filePrefix;
@ApiModelProperty("文件后缀") @TableField("file_suffix") private String fileSuffix;
@TableField("file_md5") @ApiModelProperty("文件md5") private String fileMd5;
@TableField("file_url") @ApiModelProperty("文件链接") private String fileUrl;
@TableField("file_code") @ApiModelProperty("文件编码") private String fileCode;
@TableField("object_name") @ApiModelProperty("对象路径") private String objectName;
@ApiModelProperty("ID")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@TableField("service_type")
@ApiModelProperty("类型,比如minio")
private String serviceType;
@TableField("endpoint")
@ApiModelProperty("服务地址")
private String endpoint;
@TableField("bucket_name")
@ApiModelProperty("存储桶名称")
private String bucketName;
@TableField("file_name")
@ApiModelProperty("完整文件名称")
private String fileName;
@TableField("file_size")
@ApiModelProperty("文件大小, 单位:字节")
private Long fileSize;
@TableField("file_mime")
@ApiModelProperty("文件类型")
private String fileMime;
@ApiModelProperty("短文件名")
@TableField("file_prefix")
private String filePrefix;
@ApiModelProperty("文件后缀")
@TableField("file_suffix")
private String fileSuffix;
@TableField("file_md5")
@ApiModelProperty("文件md5")
private String fileMd5;
@TableField("file_url")
@ApiModelProperty("文件链接")
private String fileUrl;
@TableField("file_code")
@ApiModelProperty("文件编码")
private String fileCode;
@TableField("object_name")
@ApiModelProperty("对象路径")
private String objectName;
}
@@ -32,19 +32,39 @@ import lombok.Setter;
@TableName("system_quartz_job")
public class SystemQuartzJobTb extends KitBaseUserTimeTb {
public static final String JOB_KEY = "JOB_KEY";
@TableId(value = "id", type = IdType.AUTO) @NotNull(groups = {Update.class}) private Long id;
@TableField(exist = false) @ApiModelProperty(value = "用于子任务唯一标识", hidden = true) private String uuid;
@ApiModelProperty(value = "定时器名称") private String jobName;
@NotBlank @ApiModelProperty(value = "Bean名称") private String beanName;
@NotBlank @ApiModelProperty(value = "方法名称") private String methodName;
@ApiModelProperty(value = "参数") private String params;
@NotBlank @ApiModelProperty(value = "cron表达式") private String cronExpression;
@ApiModelProperty(value = "状态:暂停或启动") private Boolean isPause = false;
@ApiModelProperty(value = "负责人") private String personInCharge;
@ApiModelProperty(value = "报警邮箱") private String email;
@ApiModelProperty(value = "子任务") private String subTask;
@ApiModelProperty(value = "失败后暂停") private Boolean pauseAfterFailure;
@NotBlank @ApiModelProperty(value = "备注") @TableField(updateStrategy = FieldStrategy.ALWAYS) private String
description;
public static final String JOB_KEY = "JOB_KEY";
@TableId(value = "id", type = IdType.AUTO)
@NotNull(groups = {Update.class})
private Long id;
@TableField(exist = false)
@ApiModelProperty(value = "用于子任务唯一标识", hidden = true)
private String uuid;
@ApiModelProperty(value = "定时器名称")
private String jobName;
@NotBlank
@ApiModelProperty(value = "Bean名称")
private String beanName;
@NotBlank
@ApiModelProperty(value = "方法名称")
private String methodName;
@ApiModelProperty(value = "参数")
private String params;
@NotBlank
@ApiModelProperty(value = "cron表达式")
private String cronExpression;
@ApiModelProperty(value = "状态:暂停或启动")
private Boolean isPause = false;
@ApiModelProperty(value = "负责人")
private String personInCharge;
@ApiModelProperty(value = "报警邮箱")
private String email;
@ApiModelProperty(value = "子任务")
private String subTask;
@ApiModelProperty(value = "失败后暂停")
private Boolean pauseAfterFailure;
@NotBlank
@ApiModelProperty(value = "备注")
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String
description;
}
@@ -31,14 +31,26 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemQuartzLogTb extends KitObject {
@TableId(value = "id", type = IdType.AUTO) @ApiModelProperty(value = "ID", hidden = true) private Long id;
@ApiModelProperty(value = "任务名称", hidden = true) private String jobName;
@ApiModelProperty(value = "bean名称", hidden = true) private String beanName;
@ApiModelProperty(value = "方法名称", hidden = true) private String methodName;
@ApiModelProperty(value = "参数", hidden = true) private String params;
@ApiModelProperty(value = "cron表达式", hidden = true) private String cronExpression;
@ApiModelProperty(value = "状态", hidden = true) private Boolean isSuccess;
@ApiModelProperty(value = "异常详情", hidden = true) private String exceptionDetail;
@ApiModelProperty(value = "执行耗时", hidden = true) private Long time;
@TableField(fill = FieldFill.INSERT) @ApiModelProperty(value = "创建时间", hidden = true) private Date createTime;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@ApiModelProperty(value = "任务名称", hidden = true)
private String jobName;
@ApiModelProperty(value = "bean名称", hidden = true)
private String beanName;
@ApiModelProperty(value = "方法名称", hidden = true)
private String methodName;
@ApiModelProperty(value = "参数", hidden = true)
private String params;
@ApiModelProperty(value = "cron表达式", hidden = true)
private String cronExpression;
@ApiModelProperty(value = "状态", hidden = true)
private Boolean isSuccess;
@ApiModelProperty(value = "异常详情", hidden = true)
private String exceptionDetail;
@ApiModelProperty(value = "执行耗时", hidden = true)
private Long time;
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间", hidden = true)
private Date createTime;
}
@@ -26,6 +26,8 @@ import lombok.Setter;
@TableName("system_roles_depts")
public class SystemRoleDeptTb extends KitObject {
@TableField(value = "role_id") private Long roleId;
@TableField(value = "dept_id") private Long deptId;
@TableField(value = "role_id")
private Long roleId;
@TableField(value = "dept_id")
private Long deptId;
}
@@ -13,6 +13,8 @@ import lombok.Setter;
@TableName("system_roles_menus")
public class SystemRoleMenuTb {
@TableField(value = "role_id") private Long roleId;
@TableField(value = "menu_id") private Long menuId;
@TableField(value = "role_id")
private Long roleId;
@TableField(value = "menu_id")
private Long menuId;
}
@@ -37,28 +37,36 @@ import lombok.Setter;
@TableName("system_role")
public class SystemRoleTb extends KitBaseUserTimeTb {
@NotNull(groups = {Update.class}) @TableId(value = "role_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true) private Long id;
@NotBlank @ApiModelProperty(value = "名称", hidden = true) private String name;
@ApiModelProperty(value = "数据权限,全部 、 本级 、 自定义") private String dataScope =
SystemDataScopeEnum.THIS_LEVEL.getValue();
@ApiModelProperty(value = "级别,数值越小,级别越大") private Integer level = 3;
@ApiModelProperty(value = "描述") @TableField(updateStrategy = FieldStrategy.ALWAYS) private String description;
@NotNull(groups = {Update.class})
@TableId(value = "role_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@NotBlank
@ApiModelProperty(value = "名称", hidden = true)
private String name;
@ApiModelProperty(value = "数据权限,全部 、 本级 、 自定义")
private String dataScope =
SystemDataScopeEnum.THIS_LEVEL.getValue();
@ApiModelProperty(value = "级别,数值越小,级别越大")
private Integer level = 3;
@ApiModelProperty(value = "描述")
@TableField(updateStrategy = FieldStrategy.ALWAYS)
private String description;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemRoleTb role = (SystemRoleTb)o;
return Objects.equals(id, role.id);
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemRoleTb role = (SystemRoleTb) o;
return Objects.equals(id, role.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
@@ -13,6 +13,8 @@ import lombok.Setter;
@TableName("system_users_jobs")
public class SystemUserJobTb {
@TableField(value = "user_id") private Long userId;
@TableField(value = "job_id") private Long jobId;
@TableField(value = "user_id")
private Long userId;
@TableField(value = "job_id")
private Long jobId;
}
@@ -26,6 +26,8 @@ import lombok.Setter;
@TableName("system_users_roles")
public class SystemUserRoleTb extends KitObject {
@TableField(value = "user_id") private Long userId;
@TableField(value = "role_id") private Long roleId;
@TableField(value = "user_id")
private Long userId;
@TableField(value = "role_id")
private Long roleId;
}
@@ -34,36 +34,59 @@ import lombok.Setter;
@TableName("system_user")
public class SystemUserTb extends KitBaseUserTimeTb {
@NotNull(groups = Update.class) @TableId(value = "user_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true) private Long id;
@TableField(value = "dept_id") @ApiModelProperty(hidden = true) private Long deptId;
@ApiModelProperty(value = "用户部门") @TableField(exist = false) private SystemDeptTb dept;
@NotBlank @ApiModelProperty(value = "用户名称") private String username;
@NotBlank @ApiModelProperty(value = "用户昵称") private String nickName;
@Email @NotBlank @ApiModelProperty(value = "邮箱") private String email;
@NotBlank @ApiModelProperty(value = "电话号码") private String phone;
@ApiModelProperty(value = "用户性别") private String gender;
@ApiModelProperty(value = "头像真实名称", hidden = true) private String avatarName;
@ApiModelProperty(value = "头像存储的路径", hidden = true) private String avatarPath;
@ApiModelProperty(value = "密码") private String password;
@NotNull @ApiModelProperty(value = "是否启用") private Boolean enabled;
@ApiModelProperty(value = "是否为admin账号", hidden = true) private Boolean isAdmin = false;
@ApiModelProperty(value = "最后修改密码的时间", hidden = true) private Date pwdResetTime;
@NotNull(groups = Update.class)
@TableId(value = "user_id", type = IdType.AUTO)
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@TableField(value = "dept_id")
@ApiModelProperty(hidden = true)
private Long deptId;
@ApiModelProperty(value = "用户部门")
@TableField(exist = false)
private SystemDeptTb dept;
@NotBlank
@ApiModelProperty(value = "用户名称")
private String username;
@NotBlank
@ApiModelProperty(value = "用户昵称")
private String nickName;
@Email
@NotBlank
@ApiModelProperty(value = "邮箱")
private String email;
@NotBlank
@ApiModelProperty(value = "电话号码")
private String phone;
@ApiModelProperty(value = "用户性别")
private String gender;
@ApiModelProperty(value = "头像真实名称", hidden = true)
private String avatarName;
@ApiModelProperty(value = "头像存储的路径", hidden = true)
private String avatarPath;
@ApiModelProperty(value = "密码")
private String password;
@NotNull
@ApiModelProperty(value = "是否启用")
private Boolean enabled;
@ApiModelProperty(value = "是否为admin账号", hidden = true)
private Boolean isAdmin = false;
@ApiModelProperty(value = "最后修改密码的时间", hidden = true)
private Date pwdResetTime;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemUserTb user = (SystemUserTb)o;
return Objects.equals(id, user.id) && Objects.equals(username, user.username);
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SystemUserTb user = (SystemUserTb) o;
return Objects.equals(id, user.id) && Objects.equals(username, user.username);
}
@Override
public int hashCode() {
return Objects.hash(id, username);
}
@Override
public int hashCode() {
return Objects.hash(id, username);
}
}
@@ -24,7 +24,10 @@ import lombok.Setter;
@Setter
public class SystemCheckEmailCaptchaArgs extends KitObject {
@NotBlank(message = "邮箱不能为空") private String email;
@NotBlank(message = "验证码不能为空") private String code;
@NotBlank(message = "验证码业务标识不能为空") private String bizCode;
@NotBlank(message = "邮箱不能为空")
private String email;
@NotBlank(message = "验证码不能为空")
private String code;
@NotBlank(message = "验证码业务标识不能为空")
private String bizCode;
}
@@ -25,10 +25,12 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemCreateDeptArgs extends KitObject {
@NotBlank(message = "部门名称必填") private String name;
@NotNull(message = "部门排序必填") private Integer deptSort;
private Boolean enabled;
private String isTop;
private Long pid;
private Integer subCount = 0;
@NotBlank(message = "部门名称必填")
private String name;
@NotNull(message = "部门排序必填")
private Integer deptSort;
private Boolean enabled;
private String isTop;
private Long pid;
private Integer subCount = 0;
}
@@ -24,6 +24,7 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemCreateDictArgs extends KitObject {
@NotBlank(message = "字典名称必填") private String name;
private String description;
@NotBlank(message = "字典名称必填")
private String name;
private String description;
}
@@ -26,8 +26,12 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemCreateDictDetailArgs extends KitObject {
@NotNull(message = "参数dict必填") private SystemDictTb dict;
@NotBlank(message = "字典标签必填") private String label;
@NotBlank(message = "字典值必填") private String value;
@NotNull(message = "字典排序必填") private Integer dictSort;
@NotNull(message = "参数dict必填")
private SystemDictTb dict;
@NotBlank(message = "字典标签必填")
private String label;
@NotBlank(message = "字典值必填")
private String value;
@NotNull(message = "字典排序必填")
private Integer dictSort;
}
@@ -25,7 +25,9 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemCreateJobArgs extends KitObject {
@NotBlank(message = "职位名称必填") private String name;
@NotNull(message = "职位排序必填") private Integer jobSort;
private Boolean enabled;
@NotBlank(message = "职位名称必填")
private String name;
@NotNull(message = "职位排序必填")
private Integer jobSort;
private Boolean enabled;
}
@@ -27,13 +27,16 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemCreateRoleArgs extends KitObject {
@NotBlank(message = "角色名称必填") private String name;
@NotNull(message = "角色级别必填") private Integer level;
@NotBlank(message = "数据范围必填") private String dataScope;
private String description;
/**
* 关联的部门
*/
private Long id;
private Set<SystemDeptTb> depts;
@NotBlank(message = "角色名称必填")
private String name;
@NotNull(message = "角色级别必填")
private Integer level;
@NotBlank(message = "数据范围必填")
private String dataScope;
private String description;
/**
* 关联的部门
*/
private Long id;
private Set<SystemDeptTb> depts;
}
@@ -25,5 +25,6 @@ import lombok.Setter;
@Setter
public class SystemDictDetailVo extends SystemDictDetailTb {
@ApiModelProperty(value = "字典") private SystemDictTb dict;
@ApiModelProperty(value = "字典")
private SystemDictTb dict;
}
@@ -26,7 +26,10 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemMenuMetaVo extends KitObject {
@ApiModelProperty(value = "菜单标题") private String title;
@ApiModelProperty(value = "菜单图标") private String icon;
@ApiModelProperty(value = "缓存") private Boolean noCache;
@ApiModelProperty(value = "菜单标题")
private String title;
@ApiModelProperty(value = "菜单图标")
private String icon;
@ApiModelProperty(value = "缓存")
private Boolean noCache;
}
@@ -28,12 +28,20 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemMenuVo extends KitObject {
@ApiModelProperty(value = "菜单名称") private String name;
@ApiModelProperty(value = "路径") private String path;
@ApiModelProperty(value = "隐藏状态") private Boolean hidden;
@ApiModelProperty(value = "重定向") private String redirect;
@ApiModelProperty(value = "组件") private String component;
@ApiModelProperty(value = "总是显示") private Boolean alwaysShow;
@ApiModelProperty(value = "元数据") private SystemMenuMetaVo meta;
@ApiModelProperty(value = "子路由") private List<SystemMenuVo> children;
@ApiModelProperty(value = "菜单名称")
private String name;
@ApiModelProperty(value = "路径")
private String path;
@ApiModelProperty(value = "隐藏状态")
private Boolean hidden;
@ApiModelProperty(value = "重定向")
private String redirect;
@ApiModelProperty(value = "组件")
private String component;
@ApiModelProperty(value = "总是显示")
private Boolean alwaysShow;
@ApiModelProperty(value = "元数据")
private SystemMenuMetaVo meta;
@ApiModelProperty(value = "子路由")
private List<SystemMenuVo> children;
}
@@ -23,5 +23,5 @@ import lombok.Setter;
@Setter
public class SystemOssStorageVo extends SystemOssStorageTb {
private String fileSizeDesc;
private String fileSizeDesc;
}
@@ -24,7 +24,7 @@ import lombok.Setter;
@Setter
public class SystemProductLineTreeVo extends KitObject {
private String value;
private String label;
private List<SystemProductLineTreeVo> children;
private String value;
private String label;
private List<SystemProductLineTreeVo> children;
}
@@ -23,16 +23,16 @@ import lombok.Setter;
@Setter
public class SystemProductLineVo extends KitObject {
/**
* 部门ID(select的value)
*/
private Long value;
/**
* 产品线,如"研发部/华南分部/devops组"
*/
private String label;
/**
* ID路径,如"1-2-3"
*/
private String idPath;
/**
* 部门ID(select的value)
*/
private Long value;
/**
* 产品线,如"研发部/华南分部/devops组"
*/
private String label;
/**
* ID路径,如"1-2-3"
*/
private String idPath;
}
@@ -23,10 +23,16 @@ import lombok.Data;
@Data
public class SystemQueryDeptArgs {
@ApiModelProperty(value = "部门id集合") private List<Long> ids;
@ApiModelProperty(value = "部门名称") private String name;
@ApiModelProperty(value = "是否启用") private Boolean enabled;
@ApiModelProperty(value = "上级部门") private Long pid;
@ApiModelProperty(value = "PID为空查询") private Boolean pidIsNull;
@ApiModelProperty(value = "创建时间") private List<Date> createTime;
@ApiModelProperty(value = "部门id集合")
private List<Long> ids;
@ApiModelProperty(value = "部门名称")
private String name;
@ApiModelProperty(value = "是否启用")
private Boolean enabled;
@ApiModelProperty(value = "上级部门")
private Long pid;
@ApiModelProperty(value = "PID为空查询")
private Boolean pidIsNull;
@ApiModelProperty(value = "创建时间")
private List<Date> createTime;
}
@@ -21,7 +21,10 @@ import lombok.Data;
@Data
public class SystemQueryDictArgs {
@ApiModelProperty(value = "模糊查询") private String blurry;
@ApiModelProperty(value = "页码", example = "1") private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10") private Integer size = 10;
@ApiModelProperty(value = "模糊查询")
private String blurry;
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
}
@@ -24,6 +24,8 @@ import lombok.Setter;
@Setter
public class SystemQueryDictDetailArgs extends KitObject {
@ApiModelProperty(value = "标签") private String label;
@ApiModelProperty(value = "字典名称") private String dictName;
@ApiModelProperty(value = "标签")
private String label;
@ApiModelProperty(value = "字典名称")
private String dictName;
}
@@ -25,9 +25,14 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class SystemQueryJobArgs {
@ApiModelProperty(value = "岗位名称") private String name;
@ApiModelProperty(value = "是否启用") private Boolean enabled;
@ApiModelProperty(value = "创建时间") private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1") private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10") private Integer size = 10;
@ApiModelProperty(value = "岗位名称")
private String name;
@ApiModelProperty(value = "是否启用")
private Boolean enabled;
@ApiModelProperty(value = "创建时间")
private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
}
@@ -23,8 +23,12 @@ import lombok.Data;
@Data
public class SystemQueryMenuArgs {
@ApiModelProperty(value = "模糊查询") private String blurry;
@ApiModelProperty(value = "创建时间") private List<Date> createTime;
@ApiModelProperty(value = "PID为空查询") private Boolean pidIsNull;
@ApiModelProperty(value = "PID") private Long pid;
@ApiModelProperty(value = "模糊查询")
private String blurry;
@ApiModelProperty(value = "创建时间")
private List<Date> createTime;
@ApiModelProperty(value = "PID为空查询")
private Boolean pidIsNull;
@ApiModelProperty(value = "PID")
private Long pid;
}
@@ -23,9 +23,14 @@ import lombok.Data;
@Data
public class SystemQueryQuartzJobArgs {
@ApiModelProperty(value = "定时任务名称") private String jobName;
@ApiModelProperty(value = "是否成功") private Boolean isSuccess;
@ApiModelProperty(value = "创建时间") private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1") private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10") private Integer size = 10;
@ApiModelProperty(value = "定时任务名称")
private String jobName;
@ApiModelProperty(value = "是否成功")
private Boolean isSuccess;
@ApiModelProperty(value = "创建时间")
private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
}
@@ -23,9 +23,14 @@ import lombok.Data;
@Data
public class SystemQueryRoleArgs {
@ApiModelProperty(value = "模糊查询") private String blurry;
@ApiModelProperty(value = "创建时间") private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1") private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10") private Integer size = 10;
@ApiModelProperty(value = "偏移量", hidden = true) private long offset;
@ApiModelProperty(value = "模糊查询")
private String blurry;
@ApiModelProperty(value = "创建时间")
private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
@ApiModelProperty(value = "偏移量", hidden = true)
private long offset;
}
@@ -23,8 +23,12 @@ import lombok.Data;
@Data
public class SystemQueryStorageArgs {
@ApiModelProperty(value = "模糊查询") private String blurry;
@ApiModelProperty(value = "创建时间") private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1") private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10") private Integer size = 10;
@ApiModelProperty(value = "模糊查询")
private String blurry;
@ApiModelProperty(value = "创建时间")
private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
}
@@ -28,12 +28,20 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemQueryUserArgs extends KitObject {
@ApiModelProperty(value = "ID") private Long id;
@ApiModelProperty(value = "多个ID") private Set<Long> deptIds = new HashSet<>();
@ApiModelProperty(value = "模糊查询") private String blurry;
@ApiModelProperty(value = "是否启用") private Boolean enabled;
@ApiModelProperty(value = "部门ID") private Long deptId;
@ApiModelProperty(value = "创建时间") private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1") private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10") private Integer size = 10;
@ApiModelProperty(value = "ID")
private Long id;
@ApiModelProperty(value = "多个ID")
private Set<Long> deptIds = new HashSet<>();
@ApiModelProperty(value = "模糊查询")
private String blurry;
@ApiModelProperty(value = "是否启用")
private Boolean enabled;
@ApiModelProperty(value = "部门ID")
private Long deptId;
@ApiModelProperty(value = "创建时间")
private List<Date> createTime;
@ApiModelProperty(value = "页码", example = "1")
private Integer page = 1;
@ApiModelProperty(value = "每页数据量", example = "10")
private Integer size = 10;
}
@@ -29,5 +29,6 @@ import org.springframework.security.core.GrantedAuthority;
@AllArgsConstructor
public class SystemRoleCodeVo implements GrantedAuthority {
@ApiModelProperty(value = "角色名") private String authority;
@ApiModelProperty(value = "角色名")
private String authority;
}
@@ -13,7 +13,10 @@ import lombok.Setter;
@Setter
public class SystemRoleVo extends SystemRoleTb {
@ApiModelProperty(value = "用户", hidden = true) private Set<SystemUserTb> users;
@ApiModelProperty(value = "菜单", hidden = true) private Set<SystemMenuTb> menus;
@ApiModelProperty(value = "部门", hidden = true) private Set<SystemDeptTb> depts;
@ApiModelProperty(value = "用户", hidden = true)
private Set<SystemUserTb> users;
@ApiModelProperty(value = "菜单", hidden = true)
private Set<SystemMenuTb> menus;
@ApiModelProperty(value = "部门", hidden = true)
private Set<SystemDeptTb> depts;
}
@@ -31,7 +31,13 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class SystemSendEmailArgs {
@NotEmpty @ApiModelProperty(value = "收件人") private List<String> tos;
@NotBlank @ApiModelProperty(value = "主题") private String subject;
@NotBlank @ApiModelProperty(value = "内容") private String content;
@NotEmpty
@ApiModelProperty(value = "收件人")
private List<String> tos;
@NotBlank
@ApiModelProperty(value = "主题")
private String subject;
@NotBlank
@ApiModelProperty(value = "内容")
private String content;
}
@@ -29,17 +29,29 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemSimpleUserVo extends KitObject {
@ApiModelProperty(value = "ID", hidden = true) private Long id;
@ApiModelProperty(value = "用户角色") private Set<SystemRoleTb> roles;
@ApiModelProperty(value = "用户岗位") private Set<SystemJobTb> jobs;
private Long deptId;
@TableField(exist = false) private SystemDeptTb dept;
@ApiModelProperty(value = "用户名称") private String username;
@ApiModelProperty(value = "用户昵称") private String nickName;
@ApiModelProperty(value = "邮箱") private String email;
@ApiModelProperty(value = "电话号码") private String phone;
@ApiModelProperty(value = "用户性别") private String gender;
@ApiModelProperty(value = "头像真实名称", hidden = true) private String avatarName;
@ApiModelProperty(value = "是否启用") private Boolean enabled;
@ApiModelProperty(value = "是否为admin账号", hidden = true) private Boolean isAdmin = false;
@ApiModelProperty(value = "ID", hidden = true)
private Long id;
@ApiModelProperty(value = "用户角色")
private Set<SystemRoleTb> roles;
@ApiModelProperty(value = "用户岗位")
private Set<SystemJobTb> jobs;
private Long deptId;
@TableField(exist = false)
private SystemDeptTb dept;
@ApiModelProperty(value = "用户名称")
private String username;
@ApiModelProperty(value = "用户昵称")
private String nickName;
@ApiModelProperty(value = "邮箱")
private String email;
@ApiModelProperty(value = "电话号码")
private String phone;
@ApiModelProperty(value = "用户性别")
private String gender;
@ApiModelProperty(value = "头像真实名称", hidden = true)
private String avatarName;
@ApiModelProperty(value = "是否启用")
private Boolean enabled;
@ApiModelProperty(value = "是否为admin账号", hidden = true)
private Boolean isAdmin = false;
}
@@ -25,17 +25,22 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemUpdateQuartzJobArgs extends KitObject {
@NotNull private Long id;
private String uuid;
private String jobName;
@NotBlank private String beanName;
@NotBlank private String methodName;
private String params;
@NotBlank private String cronExpression;
private Boolean isPause = false;
private String personInCharge;
private String email;
private String subTask;
private Boolean pauseAfterFailure;
@NotBlank private String description;
@NotNull
private Long id;
private String uuid;
private String jobName;
@NotBlank
private String beanName;
@NotBlank
private String methodName;
private String params;
@NotBlank
private String cronExpression;
private Boolean isPause = false;
private String personInCharge;
private String email;
private String subTask;
private Boolean pauseAfterFailure;
@NotBlank
private String description;
}
@@ -24,6 +24,8 @@ import lombok.Data;
@Data
public class SystemUpdateUserPasswordArgs {
@ApiModelProperty(value = "旧密码") private String oldPass;
@ApiModelProperty(value = "新密码") private String newPass;
@ApiModelProperty(value = "旧密码")
private String oldPass;
@ApiModelProperty(value = "新密码")
private String newPass;
}
@@ -26,11 +26,11 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
public class SystemUserInfoVo extends KitObject {
private SystemSimpleUserVo user;
private List<Long> dataScopes;
private List<SystemRoleCodeVo> authorities;
private SystemSimpleUserVo user;
private List<Long> dataScopes;
private List<SystemRoleCodeVo> authorities;
public Set<String> getRoles() {
return authorities.stream().map(SystemRoleCodeVo::getAuthority).collect(Collectors.toSet());
}
public Set<String> getRoles() {
return authorities.stream().map(SystemRoleCodeVo::getAuthority).collect(Collectors.toSet());
}
}
@@ -24,8 +24,14 @@ import lombok.Setter;
@Setter
public class SystemUserLoginArgs {
@NotBlank @ApiModelProperty(value = "用户名") private String username;
@NotBlank @ApiModelProperty(value = "密码") private String password;
@ApiModelProperty(value = "验证码") private String code;
@ApiModelProperty(value = "验证码的key") private String uuid = "";
@NotBlank
@ApiModelProperty(value = "用户名")
private String username;
@NotBlank
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "验证码")
private String code;
@ApiModelProperty(value = "验证码的key")
private String uuid = "";
}
@@ -25,13 +25,22 @@ import lombok.Data;
@Data
public class SystemUserOnlineVo {
@ApiModelProperty(value = "Token编号") private String uid;
@ApiModelProperty(value = "用户名") private String userName;
@ApiModelProperty(value = "昵称") private String nickName;
@ApiModelProperty(value = "岗位") private String dept;
@ApiModelProperty(value = "浏览器") private String browser;
@ApiModelProperty(value = "IP") private String ip;
@ApiModelProperty(value = "地址") private String address;
@ApiModelProperty(value = "token") private String key;
@ApiModelProperty(value = "登录时间") private Date loginTime;
@ApiModelProperty(value = "Token编号")
private String uid;
@ApiModelProperty(value = "用户名")
private String userName;
@ApiModelProperty(value = "昵称")
private String nickName;
@ApiModelProperty(value = "岗位")
private String dept;
@ApiModelProperty(value = "浏览器")
private String browser;
@ApiModelProperty(value = "IP")
private String ip;
@ApiModelProperty(value = "地址")
private String address;
@ApiModelProperty(value = "token")
private String key;
@ApiModelProperty(value = "登录时间")
private Date loginTime;
}
@@ -12,6 +12,8 @@ import lombok.Setter;
@Setter
public class SystemUserVo extends SystemUserTb {
@ApiModelProperty(value = "用户角色") private Set<SystemRoleTb> roles;
@ApiModelProperty(value = "用户岗位") private Set<SystemJobTb> jobs;
@ApiModelProperty(value = "用户角色")
private Set<SystemRoleTb> roles;
@ApiModelProperty(value = "用户岗位")
private Set<SystemJobTb> jobs;
}
@@ -13,6 +13,8 @@ import cn.odboy.util.KitFileUtil;
import cn.odboy.util.KitIPUtil;
import cn.odboy.util.KitPageUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -22,8 +24,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -35,28 +35,29 @@ import java.util.concurrent.TimeUnit;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface BizLock {
/**
* 业务名称
*/
String bizName() default "";
/**
* 锁key,支持SpEL表达式 示例: #user.id - 获取参数user的id属性 #p0 - 获取第一个参数 #root.methodName - 获取方法名
*/
String key() default "";
/**
* 业务名称
*/
String bizName() default "";
/**
* 锁持有时间(秒),默认60秒。锁的持有时间,超过这个时间锁会自动释放
*/
long leaseTime() default 60;
/**
* 锁key,支持SpEL表达式 示例: #user.id - 获取参数user的id属性 #p0 - 获取第一个参数 #root.methodName - 获取方法名
*/
String key() default "";
/**
* 时间单位,默认为秒
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* 锁持有时间(秒),默认60秒。锁的持有时间,超过这个时间锁会自动释放
*/
long leaseTime() default 60;
/**
* 获取锁失败后的异常信息
*/
String errorMessage() default "操作太繁忙,请稍后再试";
/**
* 时间单位,默认为秒
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* 获取锁失败后的异常信息
*/
String errorMessage() default "操作太繁忙,请稍后再试";
}
@@ -32,62 +32,63 @@ import org.springframework.stereotype.Component;
@Aspect
@Component
public class BizLockAspect {
private final KitRedisHelper redisHelper;
private final ExpressionParser parser = new SpelExpressionParser();
public BizLockAspect(KitRedisHelper redisHelper) {
this.redisHelper = redisHelper;
private final KitRedisHelper redisHelper;
private final ExpressionParser parser = new SpelExpressionParser();
public BizLockAspect(KitRedisHelper redisHelper) {
this.redisHelper = redisHelper;
}
@Around("@annotation(bizLock)")
public Object around(ProceedingJoinPoint joinPoint, BizLock bizLock) throws Throwable {
// 解析SpEL表达式生成最终的lockKey
String lockKey = parseLockKey(joinPoint, bizLock);
Boolean success = redisHelper.setIfAbsent(lockKey, lockKey, bizLock.leaseTime(), bizLock.timeUnit());
if (success == null || !success) {
throw new BadRequestException(bizLock.errorMessage());
}
try {
return joinPoint.proceed();
} catch (Exception e) {
throw e;
} finally {
redisHelper.del(lockKey);
}
}
private String parseLockKey(ProceedingJoinPoint joinPoint, BizLock bizLock) {
// 如果key为空,使用默认格式: bizName:方法名
if (bizLock.key().isEmpty()) {
return String.format("%s:%s", bizLock.bizName(),
((MethodSignature) joinPoint.getSignature()).getMethod().getName());
}
@Around("@annotation(bizLock)")
public Object around(ProceedingJoinPoint joinPoint, BizLock bizLock) throws Throwable {
// 解析SpEL表达式生成最终的lockKey
String lockKey = parseLockKey(joinPoint, bizLock);
Boolean success = redisHelper.setIfAbsent(lockKey, lockKey, bizLock.leaseTime(), bizLock.timeUnit());
if (success == null || !success) {
throw new BadRequestException(bizLock.errorMessage());
}
try {
return joinPoint.proceed();
} catch (Exception e) {
throw e;
} finally {
redisHelper.del(lockKey);
}
// 解析SpEL表达式
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 创建SpEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
// 设置方法参数
String[] parameterNames = signature.getParameterNames();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
// 支持#p0,#p1格式
context.setVariable("p" + i, args[i]);
}
private String parseLockKey(ProceedingJoinPoint joinPoint, BizLock bizLock) {
// 如果key为空,使用默认格式: bizName:方法名
if (bizLock.key().isEmpty()) {
return String.format("%s:%s", bizLock.bizName(),
((MethodSignature)joinPoint.getSignature()).getMethod().getName());
}
// 设置方法相关信息
context.setVariable("methodName", method.getName());
context.setVariable("targetClass", joinPoint.getTarget().getClass().getName());
// 解析SpEL表达式
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
// 解析表达式
Expression expression = parser.parseExpression(bizLock.key());
String value = expression.getValue(context, String.class);
// 创建SpEL上下文
StandardEvaluationContext context = new StandardEvaluationContext();
// 设置方法参数
String[] parameterNames = signature.getParameterNames();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
// 支持#p0,#p1格式
context.setVariable("p" + i, args[i]);
}
// 设置方法相关信息
context.setVariable("methodName", method.getName());
context.setVariable("targetClass", joinPoint.getTarget().getClass().getName());
// 解析表达式
Expression expression = parser.parseExpression(bizLock.key());
String value = expression.getValue(context, String.class);
// 组合最终的key: bizName:解析后的key
return String.format("%s:%s", bizLock.bizName(), value);
}
// 组合最终的key: bizName:解析后的key
return String.format("%s:%s", bizLock.bizName(), value);
}
}
@@ -26,33 +26,33 @@ import org.springframework.stereotype.Component;
@Component
public class MpMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
DateTime now = DateTime.now();
/* 创建时间 */
this.strictInsertFill(metaObject, "createTime", Date.class, now);
this.strictInsertFill(metaObject, "updateTime", Date.class, now);
/* 操作人 */
String username = "System";
try {
username = KitSecurityHelper.getCurrentUsername();
} catch (Exception ignored) {
}
this.strictInsertFill(metaObject, "createBy", String.class, username);
this.strictInsertFill(metaObject, "updateBy", String.class, username);
@Override
public void insertFill(MetaObject metaObject) {
DateTime now = DateTime.now();
/* 创建时间 */
this.strictInsertFill(metaObject, "createTime", Date.class, now);
this.strictInsertFill(metaObject, "updateTime", Date.class, now);
/* 操作人 */
String username = "System";
try {
username = KitSecurityHelper.getCurrentUsername();
} catch (Exception ignored) {
}
this.strictInsertFill(metaObject, "createBy", String.class, username);
this.strictInsertFill(metaObject, "updateBy", String.class, username);
}
@Override
public void updateFill(MetaObject metaObject) {
/* 更新时间 */
this.strictUpdateFill(metaObject, "updateTime", Date.class, DateTime.now());
/* 操作人 */
String username = "System";
try {
username = KitSecurityHelper.getCurrentUsername();
} catch (Exception ignored) {
}
this.strictUpdateFill(metaObject, "updateBy", String.class, username);
@Override
public void updateFill(MetaObject metaObject) {
/* 更新时间 */
this.strictUpdateFill(metaObject, "updateTime", Date.class, DateTime.now());
/* 操作人 */
String username = "System";
try {
username = KitSecurityHelper.getCurrentUsername();
} catch (Exception ignored) {
}
this.strictUpdateFill(metaObject, "updateBy", String.class, username);
}
}
@@ -34,5 +34,6 @@ import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
String bizName() default "";
String bizName() default "";
}
@@ -47,72 +47,74 @@ import org.springframework.stereotype.Component;
@Aspect
@Component
public class OperationLogAspect {
@Autowired private SystemOperationLogMapper systemOperationLogMapper;
@Around("@annotation(operationLog)")
public Object operationLogCatch(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
return handleLog(joinPoint, operationLog);
}
@Autowired
private SystemOperationLogMapper systemOperationLogMapper;
private Object handleLog(ProceedingJoinPoint joinPoint, OperationLog annotation) throws Throwable {
TimeInterval timeInterval = new TimeInterval();
@Around("@annotation(operationLog)")
public Object operationLogCatch(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
return handleLog(joinPoint, operationLog);
}
private Object handleLog(ProceedingJoinPoint joinPoint, OperationLog annotation) throws Throwable {
TimeInterval timeInterval = new TimeInterval();
try {
Object result = joinPoint.proceed();
SystemOperationLogTb record = getOperationLogTb(joinPoint, annotation, timeInterval);
Thread.startVirtualThread(() -> {
try {
Object result = joinPoint.proceed();
SystemOperationLogTb record = getOperationLogTb(joinPoint, annotation, timeInterval);
Thread.startVirtualThread(() -> {
try {
systemOperationLogMapper.insert(record);
} catch (Exception e) {
// 忽略
}
});
return result;
} catch (Throwable exception) {
SystemOperationLogTb record = getOperationLogTb(joinPoint, annotation, timeInterval);
record.setExceptionDetail(ExceptionUtil.stacktraceToString(exception));
Thread.startVirtualThread(() -> {
try {
systemOperationLogMapper.insert(record);
} catch (Exception e) {
// 忽略
}
});
throw exception;
systemOperationLogMapper.insert(record);
} catch (Exception e) {
// 忽略
}
});
return result;
} catch (Throwable exception) {
SystemOperationLogTb record = getOperationLogTb(joinPoint, annotation, timeInterval);
record.setExceptionDetail(ExceptionUtil.stacktraceToString(exception));
Thread.startVirtualThread(() -> {
try {
systemOperationLogMapper.insert(record);
} catch (Exception e) {
// 忽略
}
});
throw exception;
}
}
private SystemOperationLogTb getOperationLogTb(ProceedingJoinPoint joinPoint, OperationLog annotation,
TimeInterval timeInterval) {
long executeTime = timeInterval.intervalMs();
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
String bizName = annotation.bizName();
if (StrUtil.isBlank(bizName)) {
Method method = signature.getMethod();
Operation apiOperation = method.getAnnotation(Operation.class);
if (apiOperation != null) {
bizName = apiOperation.summary();
}
}
if (StrUtil.isBlank(bizName)) {
bizName = "默认业务";
}
String method = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args);
HttpServletRequest request = KitRequestHolder.getHttpServletRequest();
String requestIp = KitBrowserUtil.getIp(request);
String browserInfo = KitBrowserUtil.getVersion(request);
String address = KitIPUtil.getCityInfo(requestIp);
String username = KitSecurityHelper.getCurrentUsername();
SystemOperationLogTb record = new SystemOperationLogTb();
record.setBizName(bizName);
record.setMethod(method);
record.setParams(params);
record.setRequestIp(requestIp);
record.setExecuteTime(executeTime);
record.setUsername(username);
record.setAddress(address);
record.setBrowserInfo(browserInfo);
return record;
private SystemOperationLogTb getOperationLogTb(ProceedingJoinPoint joinPoint, OperationLog annotation,
TimeInterval timeInterval) {
long executeTime = timeInterval.intervalMs();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String bizName = annotation.bizName();
if (StrUtil.isBlank(bizName)) {
Method method = signature.getMethod();
Operation apiOperation = method.getAnnotation(Operation.class);
if (apiOperation != null) {
bizName = apiOperation.summary();
}
}
if (StrUtil.isBlank(bizName)) {
bizName = "默认业务";
}
String method = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args);
HttpServletRequest request = KitRequestHolder.getHttpServletRequest();
String requestIp = KitBrowserUtil.getIp(request);
String browserInfo = KitBrowserUtil.getVersion(request);
String address = KitIPUtil.getCityInfo(requestIp);
String username = KitSecurityHelper.getCurrentUsername();
SystemOperationLogTb record = new SystemOperationLogTb();
record.setBizName(bizName);
record.setMethod(method);
record.setParams(params);
record.setRequestIp(requestIp);
record.setExecuteTime(executeTime);
record.setUsername(username);
record.setAddress(address);
record.setBrowserInfo(browserInfo);
return record;
}
}
@@ -33,5 +33,6 @@ import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
String bizName() default "";
String bizName() default "";
}
@@ -47,74 +47,76 @@ import org.springframework.stereotype.Component;
@Aspect
@Component
public class SystemOperationLogAspect {
@Autowired private SystemOperationLogMapper systemOperationLogMapper;
@Around("@annotation(operationLog)")
public Object operationLogCatch(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
return handleLog(joinPoint, operationLog);
}
@Autowired
private SystemOperationLogMapper systemOperationLogMapper;
private Object handleLog(ProceedingJoinPoint joinPoint, OperationLog annotation) throws Throwable {
TimeInterval timeInterval = new TimeInterval();
@Around("@annotation(operationLog)")
public Object operationLogCatch(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
return handleLog(joinPoint, operationLog);
}
private Object handleLog(ProceedingJoinPoint joinPoint, OperationLog annotation) throws Throwable {
TimeInterval timeInterval = new TimeInterval();
try {
Object result = joinPoint.proceed();
SystemOperationLogTb record = getOperationLogTb(joinPoint, annotation, timeInterval);
Thread.startVirtualThread(() -> {
try {
Object result = joinPoint.proceed();
SystemOperationLogTb record = getOperationLogTb(joinPoint, annotation, timeInterval);
Thread.startVirtualThread(() -> {
try {
systemOperationLogMapper.insert(record);
} catch (Exception e) {
// log.error("保存审计日志失败", e);
// 忽略
}
});
return result;
} catch (Throwable exception) {
SystemOperationLogTb record = getOperationLogTb(joinPoint, annotation, timeInterval);
record.setExceptionDetail(ExceptionUtil.stacktraceToString(exception));
Thread.startVirtualThread(() -> {
try {
systemOperationLogMapper.insert(record);
} catch (Exception e) {
// log.error("保存审计日志失败", e);
// 忽略
}
});
throw exception;
systemOperationLogMapper.insert(record);
} catch (Exception e) {
// log.error("保存审计日志失败", e);
// 忽略
}
});
return result;
} catch (Throwable exception) {
SystemOperationLogTb record = getOperationLogTb(joinPoint, annotation, timeInterval);
record.setExceptionDetail(ExceptionUtil.stacktraceToString(exception));
Thread.startVirtualThread(() -> {
try {
systemOperationLogMapper.insert(record);
} catch (Exception e) {
// log.error("保存审计日志失败", e);
// 忽略
}
});
throw exception;
}
}
private SystemOperationLogTb getOperationLogTb(ProceedingJoinPoint joinPoint, OperationLog annotation,
TimeInterval timeInterval) {
long executeTime = timeInterval.intervalMs();
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
String bizName = annotation.bizName();
if (StrUtil.isBlank(bizName)) {
Method method = signature.getMethod();
Operation apiOperation = method.getAnnotation(Operation.class);
if (apiOperation != null) {
bizName = apiOperation.summary();
}
}
if (StrUtil.isBlank(bizName)) {
bizName = "默认业务";
}
String method = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args);
HttpServletRequest request = KitRequestHolder.getHttpServletRequest();
String requestIp = KitBrowserUtil.getIp(request);
String browserInfo = KitBrowserUtil.getVersion(request);
String address = KitIPUtil.getCityInfo(requestIp);
String username = KitSecurityHelper.getCurrentUsername();
SystemOperationLogTb record = new SystemOperationLogTb();
record.setBizName(bizName);
record.setMethod(method);
record.setParams(params);
record.setRequestIp(requestIp);
record.setExecuteTime(executeTime);
record.setUsername(username);
record.setAddress(address);
record.setBrowserInfo(browserInfo);
return record;
private SystemOperationLogTb getOperationLogTb(ProceedingJoinPoint joinPoint, OperationLog annotation,
TimeInterval timeInterval) {
long executeTime = timeInterval.intervalMs();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
String bizName = annotation.bizName();
if (StrUtil.isBlank(bizName)) {
Method method = signature.getMethod();
Operation apiOperation = method.getAnnotation(Operation.class);
if (apiOperation != null) {
bizName = apiOperation.summary();
}
}
if (StrUtil.isBlank(bizName)) {
bizName = "默认业务";
}
String method = joinPoint.getTarget().getClass().getName() + "." + signature.getName() + "()";
Object[] args = joinPoint.getArgs();
String params = JSON.toJSONString(args);
HttpServletRequest request = KitRequestHolder.getHttpServletRequest();
String requestIp = KitBrowserUtil.getIp(request);
String browserInfo = KitBrowserUtil.getVersion(request);
String address = KitIPUtil.getCityInfo(requestIp);
String username = KitSecurityHelper.getCurrentUsername();
SystemOperationLogTb record = new SystemOperationLogTb();
record.setBizName(bizName);
record.setMethod(method);
record.setParams(params);
record.setRequestIp(requestIp);
record.setExecuteTime(executeTime);
record.setUsername(username);
record.setAddress(address);
record.setBrowserInfo(browserInfo);
return record;
}
}
@@ -25,17 +25,17 @@ import org.springframework.stereotype.Service;
@Service(value = "el")
public class AuthorityConfig {
/**
* 判断接口是否有权限
*
* @param permissions 权限
* @return /
*/
public Boolean check(String... permissions) {
// 获取当前用户的所有权限
List<String> roleList =
KitSecurityHelper.getCurrentUser().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
// 判断当前用户的所有权限是否包含接口上定义的权限
return roleList.contains("admin") || Arrays.stream(permissions).anyMatch(roleList::contains);
}
/**
* 判断接口是否有权限
*
* @param permissions 权限
* @return /
*/
public Boolean check(String... permissions) {
// 获取当前用户的所有权限
List<String> roleList =
KitSecurityHelper.getCurrentUser().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
// 判断当前用户的所有权限是否包含接口上定义的权限
return roleList.contains("admin") || Arrays.stream(permissions).anyMatch(roleList::contains);
}
}
@@ -45,77 +45,84 @@ import org.springframework.web.filter.CorsFilter;
@Configuration
@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SpringSecurityConfig {
@Autowired private TokenProvider tokenProvider;
@Autowired private CorsFilter corsFilter;
@Autowired private JwtAuthenticationEntryPoint authenticationErrorHandler;
@Autowired private JwtAccessDeniedHandler jwtAccessDeniedHandler;
@Autowired private ApplicationContext applicationContext;
@Autowired private SystemUserOnlineInfoDAO systemUserOnlineInfoDAO;
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
// 去除 ROLE_ 前缀
return new GrantedAuthorityDefaults("");
}
@Autowired
private TokenProvider tokenProvider;
@Autowired
private CorsFilter corsFilter;
@Autowired
private JwtAuthenticationEntryPoint authenticationErrorHandler;
@Autowired
private JwtAccessDeniedHandler jwtAccessDeniedHandler;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private SystemUserOnlineInfoDAO systemUserOnlineInfoDAO;
@Bean
public PasswordEncoder passwordEncoder() {
// 密码加密方式
return new BCryptPasswordEncoder();
}
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
// 去除 ROLE_ 前缀
return new GrantedAuthorityDefaults("");
}
@Bean
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// 获取匿名标记
Map<String, Set<String>> anonymousUrls = KitAnonTagUtil.getAnonymousUrl(applicationContext);
@Bean
public PasswordEncoder passwordEncoder() {
// 密码加密方式
return new BCryptPasswordEncoder();
}
// 创建TokenFilter实例
TokenFilter customFilter = new TokenFilter(tokenProvider, systemUserOnlineInfoDAO);
@Bean
protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
// 获取匿名标记
Map<String, Set<String>> anonymousUrls = KitAnonTagUtil.getAnonymousUrl(applicationContext);
return httpSecurity
// 禁用 CSRF
.csrf(AbstractHttpConfigurer::disable).addFilter(corsFilter)
.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class)
// 授权异常
.exceptionHandling(
exceptionHandling -> exceptionHandling.authenticationEntryPoint(authenticationErrorHandler)
.accessDeniedHandler(jwtAccessDeniedHandler))
// 防止iframe 造成跨域
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
// 不创建会话
.sessionManagement(
sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorizeRequests -> authorizeRequests
// 静态资源等等
.requestMatchers(HttpMethod.GET, "*.html", "*.css", "*.js", "/websocket/**").permitAll()
// Knife4j 和 OpenAPI 相关路径
.requestMatchers("/doc.html", "/webjars/**", "/v3/api-docs", "/v3/api-docs/**", "/swagger-resources",
"/swagger-resources/**", "/configuration/ui", "/configuration/security").permitAll()
// 文件
.requestMatchers("/avatar/**").permitAll().requestMatchers("/file/**").permitAll()
// 阿里巴巴 druid
.requestMatchers("/druid/**").permitAll()
// 放行OPTIONS请求
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型
// GET
.requestMatchers(HttpMethod.GET,
anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll()
// POST
.requestMatchers(HttpMethod.POST,
anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll()
// PUT
.requestMatchers(HttpMethod.PUT,
anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll()
// PATCH
.requestMatchers(HttpMethod.PATCH,
anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll()
// DELETE
.requestMatchers(HttpMethod.DELETE,
anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll()
// 所有类型的接口都放行
.requestMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll()
// 所有请求都需要认证
.anyRequest().authenticated()).build();
}
// 创建TokenFilter实例
TokenFilter customFilter = new TokenFilter(tokenProvider, systemUserOnlineInfoDAO);
return httpSecurity
// 禁用 CSRF
.csrf(AbstractHttpConfigurer::disable).addFilter(corsFilter)
.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class)
// 授权异常
.exceptionHandling(
exceptionHandling -> exceptionHandling.authenticationEntryPoint(authenticationErrorHandler)
.accessDeniedHandler(jwtAccessDeniedHandler))
// 防止iframe 造成跨域
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
// 不创建会话
.sessionManagement(
sessionManagement -> sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorizeRequests -> authorizeRequests
// 静态资源等等
.requestMatchers(HttpMethod.GET, "*.html", "*.css", "*.js", "/websocket/**").permitAll()
// Knife4j 和 OpenAPI 相关路径
.requestMatchers("/doc.html", "/webjars/**", "/v3/api-docs", "/v3/api-docs/**", "/swagger-resources",
"/swagger-resources/**", "/configuration/ui", "/configuration/security").permitAll()
// 文件
.requestMatchers("/avatar/**").permitAll().requestMatchers("/file/**").permitAll()
// 阿里巴巴 druid
.requestMatchers("/druid/**").permitAll()
// 放行OPTIONS请求
.requestMatchers(HttpMethod.OPTIONS, "/**").permitAll()
// 自定义匿名访问所有url放行:允许匿名和带Token访问,细腻化到每个 Request 类型
// GET
.requestMatchers(HttpMethod.GET,
anonymousUrls.get(RequestMethodEnum.GET.getType()).toArray(new String[0])).permitAll()
// POST
.requestMatchers(HttpMethod.POST,
anonymousUrls.get(RequestMethodEnum.POST.getType()).toArray(new String[0])).permitAll()
// PUT
.requestMatchers(HttpMethod.PUT,
anonymousUrls.get(RequestMethodEnum.PUT.getType()).toArray(new String[0])).permitAll()
// PATCH
.requestMatchers(HttpMethod.PATCH,
anonymousUrls.get(RequestMethodEnum.PATCH.getType()).toArray(new String[0])).permitAll()
// DELETE
.requestMatchers(HttpMethod.DELETE,
anonymousUrls.get(RequestMethodEnum.DELETE.getType()).toArray(new String[0])).permitAll()
// 所有类型的接口都放行
.requestMatchers(anonymousUrls.get(RequestMethodEnum.ALL.getType()).toArray(new String[0])).permitAll()
// 所有请求都需要认证
.anyRequest().authenticated()).build();
}
}
@@ -25,17 +25,18 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class TokenConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private final TokenProvider tokenProvider;
private final SystemUserOnlineInfoDAO systemUserOnlineInfoDAO;
public TokenConfigurer(TokenProvider tokenProvider, SystemUserOnlineInfoDAO systemUserOnlineInfoDAO) {
this.tokenProvider = tokenProvider;
this.systemUserOnlineInfoDAO = systemUserOnlineInfoDAO;
}
private final TokenProvider tokenProvider;
private final SystemUserOnlineInfoDAO systemUserOnlineInfoDAO;
@Override
public void configure(HttpSecurity http) {
TokenFilter customFilter = new TokenFilter(tokenProvider, systemUserOnlineInfoDAO);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
public TokenConfigurer(TokenProvider tokenProvider, SystemUserOnlineInfoDAO systemUserOnlineInfoDAO) {
this.tokenProvider = tokenProvider;
this.systemUserOnlineInfoDAO = systemUserOnlineInfoDAO;
}
@Override
public void configure(HttpSecurity http) {
TokenFilter customFilter = new TokenFilter(tokenProvider, systemUserOnlineInfoDAO);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
@@ -41,95 +41,96 @@ import org.springframework.web.context.request.ServletRequestAttributes;
@Slf4j
@Component
public class KitSecurityHelper {
/**
* 获取当前登录的用户
*
* @return UserDetails
*/
public static UserDetails getCurrentUser() {
UserDetailsService userDetailsService = KitSpringBeanHolder.getBean(UserDetailsService.class);
return userDetailsService.loadUserByUsername(getCurrentUsername());
}
/**
* 获取当前用户的数据权限
*
* @return /
*/
public static List<Long> getCurrentUserDataScope() {
UserDetails userDetails = getCurrentUser();
// 将 Java 对象转换为 JSONObject 对象
JSONObject jsonObject = (JSONObject)JSON.toJSON(userDetails);
JSONArray jsonArray = jsonObject.getJSONArray("dataScopes");
return JSON.parseArray(jsonArray.toJSONString(), Long.class);
}
/**
* 获取当前登录的用户
*
* @return UserDetails
*/
public static UserDetails getCurrentUser() {
UserDetailsService userDetailsService = KitSpringBeanHolder.getBean(UserDetailsService.class);
return userDetailsService.loadUserByUsername(getCurrentUsername());
}
/**
* 获取数据权限级别
*
* @return 级别
*/
public static String getDataScopeType() {
List<Long> dataScopes = getCurrentUserDataScope();
if (CollUtil.isEmpty(dataScopes)) {
return "";
}
return SystemDataScopeEnum.ALL.getValue();
}
/**
* 获取当前用户的数据权限
*
* @return /
*/
public static List<Long> getCurrentUserDataScope() {
UserDetails userDetails = getCurrentUser();
// 将 Java 对象转换为 JSONObject 对象
JSONObject jsonObject = (JSONObject) JSON.toJSON(userDetails);
JSONArray jsonArray = jsonObject.getJSONArray("dataScopes");
return JSON.parseArray(jsonArray.toJSONString(), Long.class);
}
/**
* 获取用户ID
*
* @return 系统用户ID
*/
public static Long getCurrentUserId() {
return getCurrentUserId(getToken());
/**
* 获取数据权限级别
*
* @return 级别
*/
public static String getDataScopeType() {
List<Long> dataScopes = getCurrentUserDataScope();
if (CollUtil.isEmpty(dataScopes)) {
return "";
}
return SystemDataScopeEnum.ALL.getValue();
}
/**
* 获取用户ID
*
* @return 系统用户ID
*/
public static Long getCurrentUserId(String token) {
JWT jwt = JWTUtil.parseToken(token);
return Long.valueOf(jwt.getPayload("userId").toString());
}
/**
* 获取用户ID
*
* @return 系统用户ID
*/
public static Long getCurrentUserId() {
return getCurrentUserId(getToken());
}
/**
* 获取系统用户名称
*
* @return 系统用户名称
*/
public static String getCurrentUsername() {
return getCurrentUsername(getToken());
}
/**
* 获取用户ID
*
* @return 系统用户ID
*/
public static Long getCurrentUserId(String token) {
JWT jwt = JWTUtil.parseToken(token);
return Long.valueOf(jwt.getPayload("userId").toString());
}
/**
* 获取系统用户名称
*
* @return 系统用户名称
*/
public static String getCurrentUsername(String token) {
JWT jwt = JWTUtil.parseToken(token);
return jwt.getPayload("sub").toString();
}
/**
* 获取系统用户名称
*
* @return 系统用户名称
*/
public static String getCurrentUsername() {
return getCurrentUsername(getToken());
}
/**
* 获取Token
*
* @return /
*/
public static String getToken() {
HttpServletRequest request = ((ServletRequestAttributes)Objects.requireNonNull(
RequestContextHolder.getRequestAttributes())).getRequest();
String bearerToken = request.getHeader(SystemConst.HEADER_NAME);
if (bearerToken != null && bearerToken.startsWith(SystemConst.TOKEN_PREFIX)) {
// 去掉令牌前缀
return bearerToken.replace(SystemConst.TOKEN_PREFIX, "");
} else {
log.debug("非法Token:{}", bearerToken);
}
return null;
/**
* 获取系统用户名称
*
* @return 系统用户名称
*/
public static String getCurrentUsername(String token) {
JWT jwt = JWTUtil.parseToken(token);
return jwt.getPayload("sub").toString();
}
/**
* 获取Token
*
* @return /
*/
public static String getToken() {
HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(
RequestContextHolder.getRequestAttributes())).getRequest();
String bearerToken = request.getHeader(SystemConst.HEADER_NAME);
if (bearerToken != null && bearerToken.startsWith(SystemConst.TOKEN_PREFIX)) {
// 去掉令牌前缀
return bearerToken.replace(SystemConst.TOKEN_PREFIX, "");
} else {
log.debug("非法Token:{}", bearerToken);
}
return null;
}
}
@@ -29,15 +29,15 @@ import org.springframework.stereotype.Component;
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
//当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json;charset=UTF-8");
ObjectMapper objectMapper = new ObjectMapper();
String jsonResponse = objectMapper.writeValueAsString(
ApiError.error(HttpStatus.FORBIDDEN.value(), "禁止访问,您没有权限访问此资源"));
response.getWriter().write(jsonResponse);
}
@Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
//当用户在没有授权的情况下访问受保护的REST资源时,将调用此方法发送403 Forbidden响应
response.setStatus(HttpStatus.FORBIDDEN.value());
response.setContentType("application/json;charset=UTF-8");
ObjectMapper objectMapper = new ObjectMapper();
String jsonResponse = objectMapper.writeValueAsString(
ApiError.error(HttpStatus.FORBIDDEN.value(), "禁止访问,您没有权限访问此资源"));
response.getWriter().write(jsonResponse);
}
}
@@ -31,16 +31,16 @@ import org.springframework.stereotype.Component;
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
int code = HttpStatus.UNAUTHORIZED.value();
response.setStatus(code);
response.setContentType("application/json;charset=UTF-8");
ObjectMapper objectMapper = new ObjectMapper();
String jsonResponse = objectMapper.writeValueAsString(
ApiError.error(HttpStatus.UNAUTHORIZED.value(), "登录状态已过期,请重新登录"));
response.getWriter().write(jsonResponse);
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException {
// 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
int code = HttpStatus.UNAUTHORIZED.value();
response.setStatus(code);
response.setContentType("application/json;charset=UTF-8");
ObjectMapper objectMapper = new ObjectMapper();
String jsonResponse = objectMapper.writeValueAsString(
ApiError.error(HttpStatus.UNAUTHORIZED.value(), "登录状态已过期,请重新登录"));
response.getWriter().write(jsonResponse);
}
}
@@ -34,54 +34,55 @@ import org.springframework.web.filter.GenericFilterBean;
@Slf4j
public class TokenFilter extends GenericFilterBean {
private final TokenProvider tokenProvider;
private final SystemUserOnlineInfoDAO systemUserOnlineInfoDAO;
/**
* @param tokenProvider Token
* @param systemUserOnlineInfoDAO 用户在线
*/
public TokenFilter(TokenProvider tokenProvider, SystemUserOnlineInfoDAO systemUserOnlineInfoDAO) {
this.systemUserOnlineInfoDAO = systemUserOnlineInfoDAO;
this.tokenProvider = tokenProvider;
}
private final TokenProvider tokenProvider;
private final SystemUserOnlineInfoDAO systemUserOnlineInfoDAO;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest;
String token = resolveToken(httpServletRequest);
// 对于 Token 为空的不需要去查 Redis
if (StrUtil.isNotBlank(token)) {
// 获取用户Token的Key
String loginKey = tokenProvider.loginKey(token);
SystemUserOnlineVo userOnlineVo = systemUserOnlineInfoDAO.queryUserOnlineModelByKey(loginKey);
// 判断用户在线信息是否为空
if (userOnlineVo != null) {
// Token 续期判断
tokenProvider.checkRenewal(token);
// 获取认证信息,设置上下文
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
/**
* @param tokenProvider Token
* @param systemUserOnlineInfoDAO 用户在线
*/
public TokenFilter(TokenProvider tokenProvider, SystemUserOnlineInfoDAO systemUserOnlineInfoDAO) {
this.systemUserOnlineInfoDAO = systemUserOnlineInfoDAO;
this.tokenProvider = tokenProvider;
}
/**
* 初步检测Token
*
* @param request /
* @return /
*/
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(SystemConst.HEADER_NAME);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(SystemConst.TOKEN_PREFIX)) {
// 去掉令牌前缀
return bearerToken.replace(SystemConst.TOKEN_PREFIX + " ", "");
} else {
log.debug("非法Token:{}", bearerToken);
}
return null;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
String token = resolveToken(httpServletRequest);
// 对于 Token 为空的不需要去查 Redis
if (StrUtil.isNotBlank(token)) {
// 获取用户Token的Key
String loginKey = tokenProvider.loginKey(token);
SystemUserOnlineVo userOnlineVo = systemUserOnlineInfoDAO.queryUserOnlineModelByKey(loginKey);
// 判断用户在线信息是否为空
if (userOnlineVo != null) {
// Token 续期判断
tokenProvider.checkRenewal(token);
// 获取认证信息,设置上下文
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(servletRequest, servletResponse);
}
/**
* 初步检测Token
*
* @param request /
* @return /
*/
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader(SystemConst.HEADER_NAME);
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(SystemConst.TOKEN_PREFIX)) {
// 去掉令牌前缀
return bearerToken.replace(SystemConst.TOKEN_PREFIX + " ", "");
} else {
log.debug("非法Token:{}", bearerToken);
}
return null;
}
}
@@ -47,118 +47,121 @@ import org.springframework.stereotype.Component;
@Slf4j
@Component
public class TokenProvider implements InitializingBean {
public static final String AUTHORITIES_UUID_KEY = "uid";
public static final String AUTHORITIES_UID_KEY = "userId";
@Autowired private KitRedisHelper redisHelper;
@Autowired private AppProperties properties;
public static final String AUTHORITIES_UUID_KEY = "uid";
public static final String AUTHORITIES_UID_KEY = "userId";
@Autowired
private KitRedisHelper redisHelper;
@Autowired
private AppProperties properties;
// 0.11.x版本
// private Key signingKey;
// 0.12.x版本
private SecretKey signingKey;
private JwtParser jwtParser;
@Override
public void afterPropertiesSet() {
// 解码Base64密钥并创建签名密钥
byte[] keyBytes = Decoders.BASE64.decode(properties.getJwt().getBase64Secret());
signingKey = Keys.hmacShaKeyFor(keyBytes);
// 0.11.x版本
// private Key signingKey;
// jwtParser = Jwts.parserBuilder()
// // 使用预生成的签名密钥
// .setSigningKey(signingKey).build();
// 0.12.x版本
private SecretKey signingKey;
private JwtParser jwtParser;
jwtParser = Jwts.parser().verifyWith(signingKey).build();
}
@Override
public void afterPropertiesSet() {
// 解码Base64密钥并创建签名密钥
byte[] keyBytes = Decoders.BASE64.decode(properties.getJwt().getBase64Secret());
signingKey = Keys.hmacShaKeyFor(keyBytes);
// 0.11.x版本
// jwtParser = Jwts.parserBuilder()
// // 使用预生成的签名密钥
// .setSigningKey(signingKey).build();
// 0.12.x版本
jwtParser = Jwts.parser().verifyWith(signingKey).build();
}
/**
* 创建Token 设置永不过期, Token 的时间有效性转到Redis 维护
*
* @param user /
* @return /
*/
public String createToken(SystemUserJwtVo user) {
// 设置参数
Map<String, Object> claims = new HashMap<>(6);
// 设置用户ID
claims.put(AUTHORITIES_UID_KEY, user.getUser().getId());
// 设置UUID,确保每次Token不一样
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
// 0.11.x版本
// 直接调用 Jwts.builder() 创建新实例Add commentMore actions
// return Jwts.builder()
// // 设置自定义 Claims
// .setClaims(claims)
// // 设置主题
// .setSubject(user.getUsername())
// // 使用预生成的签名密钥和算法签名
// .signWith(signingKey, SignatureAlgorithm.HS512).compact();
// 0.12.x版本
return Jwts.builder().claims(claims).subject(user.getUsername()).signWith(signingKey).compact();
}
/**
* 创建Token 设置永不过期, Token 的时间有效性转到Redis 维护
*
* @param user /
* @return /
*/
public String createToken(SystemUserJwtVo user) {
// 设置参数
Map<String, Object> claims = new HashMap<>(6);
// 设置用户ID
claims.put(AUTHORITIES_UID_KEY, user.getUser().getId());
// 设置UUID,确保每次Token不一样
claims.put(AUTHORITIES_UUID_KEY, IdUtil.simpleUUID());
// 0.11.x版本
// 直接调用 Jwts.builder() 创建新实例Add commentMore actions
// return Jwts.builder()
// // 设置自定义 Claims
// .setClaims(claims)
// // 设置主题
// .setSubject(user.getUsername())
// // 使用预生成的签名密钥和算法签名
// .signWith(signingKey, SignatureAlgorithm.HS512).compact();
// 0.12.x版本
return Jwts.builder().claims(claims).subject(user.getUsername()).signWith(signingKey).compact();
}
/**
* 依据Token 获取鉴权信息
*
* @param token /
* @return /
*/
Authentication getAuthentication(String token) {
Claims claims = getClaims(token);
User principal = new User(claims.getSubject(), "******", new ArrayList<>());
return new UsernamePasswordAuthenticationToken(principal, token, new ArrayList<>());
}
/**
* 依据Token 获取鉴权信息
*
* @param token /
* @return /
*/
Authentication getAuthentication(String token) {
Claims claims = getClaims(token);
User principal = new User(claims.getSubject(), "******", new ArrayList<>());
return new UsernamePasswordAuthenticationToken(principal, token, new ArrayList<>());
}
public Claims getClaims(String token) {
// 0.11.x版本
// return jwtParser.parseClaimsJws(token).getBody();
// 0.12.x版本
return jwtParser.parseSignedClaims(token).getPayload();
}
public Claims getClaims(String token) {
// 0.11.x版本
// return jwtParser.parseClaimsJws(token).getBody();
// 0.12.x版本
return jwtParser.parseSignedClaims(token).getPayload();
/**
* @param token 需要检查的token
*/
public void checkRenewal(String token) {
// 判断是否续期token,计算token的过期时间
String loginKey = loginKey(token);
long time = redisHelper.getExpire(loginKey) * 1000;
Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int) time);
// 判断当前时间与过期时间的时间差
long differ = expireDate.getTime() - System.currentTimeMillis();
// 如果在续期检查的范围内,则续期
if (differ <= properties.getJwt().getDetect()) {
long renew = time + properties.getJwt().getRenew();
redisHelper.expire(loginKey, renew, TimeUnit.MILLISECONDS);
}
}
/**
* @param token 需要检查的token
*/
public void checkRenewal(String token) {
// 判断是否续期token,计算token的过期时间
String loginKey = loginKey(token);
long time = redisHelper.getExpire(loginKey) * 1000;
Date expireDate = DateUtil.offset(new Date(), DateField.MILLISECOND, (int)time);
// 判断当前时间与过期时间的时间差
long differ = expireDate.getTime() - System.currentTimeMillis();
// 如果在续期检查的范围内,则续期
if (differ <= properties.getJwt().getDetect()) {
long renew = time + properties.getJwt().getRenew();
redisHelper.expire(loginKey, renew, TimeUnit.MILLISECONDS);
}
public String getToken(HttpServletRequest request) {
final String requestHeader = request.getHeader(SystemConst.HEADER_NAME);
if (requestHeader != null && requestHeader.startsWith(SystemConst.TOKEN_PREFIX)) {
return requestHeader.substring(7);
}
return null;
}
public String getToken(HttpServletRequest request) {
final String requestHeader = request.getHeader(SystemConst.HEADER_NAME);
if (requestHeader != null && requestHeader.startsWith(SystemConst.TOKEN_PREFIX)) {
return requestHeader.substring(7);
}
return null;
}
/**
* 获取登录用户RedisKey
*
* @param token /
* @return key
*/
public String loginKey(String token) {
Claims claims = getClaims(token);
return SystemCacheKey.ONLINE_USER + claims.getSubject() + ":" + getId(token);
}
/**
* 获取登录用户RedisKey
*
* @param token /
* @return key
*/
public String loginKey(String token) {
Claims claims = getClaims(token);
return SystemCacheKey.ONLINE_USER + claims.getSubject() + ":" + getId(token);
}
/**
* 获取会话编号
*
* @param token /
* @return /
*/
public String getId(String token) {
Claims claims = getClaims(token);
return claims.get(AUTHORITIES_UUID_KEY, String.class);
}
/**
* 获取会话编号
*
* @param token /
* @return /
*/
public String getId(String token) {
Claims claims = getClaims(token);
return claims.get(AUTHORITIES_UUID_KEY, String.class);
}
}
@@ -28,18 +28,21 @@ import org.springframework.stereotype.Component;
@Slf4j
@Component
public class ActiveJobRegister implements ApplicationRunner {
@Autowired private SystemQuartzJobService systemQuartzJobService;
@Autowired private QuartzManage quartzManage;
/**
* 项目启动时重新激活启用的定时任务
*
* @param applicationArguments /
*/
@Override
public void run(ApplicationArguments applicationArguments) {
List<SystemQuartzJobTb> quartzJobs = systemQuartzJobService.listEnableQuartzJob();
quartzJobs.forEach(quartzManage::addJob);
log.info("Timing task injection complete");
}
@Autowired
private SystemQuartzJobService systemQuartzJobService;
@Autowired
private QuartzManage quartzManage;
/**
* 项目启动时重新激活启用的定时任务
*
* @param applicationArguments /
*/
@Override
public void run(ApplicationArguments applicationArguments) {
List<SystemQuartzJobTb> quartzJobs = systemQuartzJobService.listEnableQuartzJob();
quartzJobs.forEach(quartzManage::addJob);
log.info("Timing task injection complete");
}
}
@@ -43,91 +43,92 @@ import org.springframework.scheduling.quartz.QuartzJobBean;
@Slf4j
public class ExecutionJobBean extends QuartzJobBean {
/**
* 此处仅供参考,可根据任务执行情况自定义线程池参数
*/
private final ThreadPoolTaskExecutor executor = KitSpringBeanHolder.getBean("taskAsync");
@Override
public void executeInternal(JobExecutionContext context) {
// 获取任务
SystemQuartzJobTb quartzJob = (SystemQuartzJobTb)context.getMergedJobDataMap().get(SystemQuartzJobTb.JOB_KEY);
// 获取spring bean
SystemQuartzLogMapper quartzLogMapper = KitSpringBeanHolder.getBean(SystemQuartzLogMapper.class);
SystemQuartzJobService systemQuartzJobService = KitSpringBeanHolder.getBean(SystemQuartzJobService.class);
KitRedisHelper redisHelper = KitSpringBeanHolder.getBean(KitRedisHelper.class);
/**
* 此处仅供参考,可根据任务执行情况自定义线程池参数
*/
private final ThreadPoolTaskExecutor executor = KitSpringBeanHolder.getBean("taskAsync");
String uuid = quartzJob.getUuid();
@Override
public void executeInternal(JobExecutionContext context) {
// 获取任务
SystemQuartzJobTb quartzJob = (SystemQuartzJobTb) context.getMergedJobDataMap().get(SystemQuartzJobTb.JOB_KEY);
// 获取spring bean
SystemQuartzLogMapper quartzLogMapper = KitSpringBeanHolder.getBean(SystemQuartzLogMapper.class);
SystemQuartzJobService systemQuartzJobService = KitSpringBeanHolder.getBean(SystemQuartzJobService.class);
KitRedisHelper redisHelper = KitSpringBeanHolder.getBean(KitRedisHelper.class);
SystemQuartzLogTb quartzLog = new SystemQuartzLogTb();
quartzLog.setJobName(quartzJob.getJobName());
quartzLog.setBeanName(quartzJob.getBeanName());
quartzLog.setMethodName(quartzJob.getMethodName());
quartzLog.setParams(quartzJob.getParams());
long startTime = System.currentTimeMillis();
quartzLog.setCronExpression(quartzJob.getCronExpression());
try {
// 执行任务
QuartzRunnable task =
new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(), quartzJob.getParams());
Future<?> future = executor.submit(task);
// 忽略任务执行结果
future.get();
long times = System.currentTimeMillis() - startTime;
quartzLog.setTime(times);
if (StrUtil.isNotBlank(uuid)) {
redisHelper.set(uuid, true);
}
// 任务状态
quartzLog.setIsSuccess(true);
log.info("任务执行成功,任务名称:{}, 执行时间:{}毫秒", quartzJob.getJobName(), times);
// 判断是否存在子任务
if (StrUtil.isNotBlank(quartzJob.getSubTask())) {
String[] tasks = quartzJob.getSubTask().split("[,,]");
// 执行子任务
systemQuartzJobService.startSubQuartJob(tasks);
}
} catch (Exception e) {
if (StrUtil.isNotBlank(uuid)) {
redisHelper.set(uuid, false);
}
log.error("任务执行失败,任务名称:{}", quartzJob.getJobName(), e);
long times = System.currentTimeMillis() - startTime;
quartzLog.setTime(times);
// 任务状态 0:成功 1:失败
quartzLog.setIsSuccess(false);
quartzLog.setExceptionDetail(ExceptionUtil.stacktraceToString(e));
// 任务如果失败了则暂停
if (quartzJob.getPauseAfterFailure() != null && quartzJob.getPauseAfterFailure()) {
quartzJob.setIsPause(false);
// 更新状态
systemQuartzJobService.switchQuartzJobStatus(quartzJob);
}
if (quartzJob.getEmail() != null) {
SystemEmailService emailService = KitSpringBeanHolder.getBean(SystemEmailService.class);
// 邮箱报警
if (StrUtil.isNotBlank(quartzJob.getEmail())) {
SystemSendEmailArgs sendEmailRequest = taskAlarm(quartzJob, ExceptionUtil.stacktraceToString(e));
emailService.sendEmail(sendEmailRequest);
}
}
} finally {
quartzLogMapper.insert(quartzLog);
String uuid = quartzJob.getUuid();
SystemQuartzLogTb quartzLog = new SystemQuartzLogTb();
quartzLog.setJobName(quartzJob.getJobName());
quartzLog.setBeanName(quartzJob.getBeanName());
quartzLog.setMethodName(quartzJob.getMethodName());
quartzLog.setParams(quartzJob.getParams());
long startTime = System.currentTimeMillis();
quartzLog.setCronExpression(quartzJob.getCronExpression());
try {
// 执行任务
QuartzRunnable task =
new QuartzRunnable(quartzJob.getBeanName(), quartzJob.getMethodName(), quartzJob.getParams());
Future<?> future = executor.submit(task);
// 忽略任务执行结果
future.get();
long times = System.currentTimeMillis() - startTime;
quartzLog.setTime(times);
if (StrUtil.isNotBlank(uuid)) {
redisHelper.set(uuid, true);
}
// 任务状态
quartzLog.setIsSuccess(true);
log.info("任务执行成功,任务名称:{}, 执行时间:{}毫秒", quartzJob.getJobName(), times);
// 判断是否存在子任务
if (StrUtil.isNotBlank(quartzJob.getSubTask())) {
String[] tasks = quartzJob.getSubTask().split("[,,]");
// 执行子任务
systemQuartzJobService.startSubQuartJob(tasks);
}
} catch (Exception e) {
if (StrUtil.isNotBlank(uuid)) {
redisHelper.set(uuid, false);
}
log.error("任务执行失败,任务名称:{}", quartzJob.getJobName(), e);
long times = System.currentTimeMillis() - startTime;
quartzLog.setTime(times);
// 任务状态 0:成功 1:失败
quartzLog.setIsSuccess(false);
quartzLog.setExceptionDetail(ExceptionUtil.stacktraceToString(e));
// 任务如果失败了则暂停
if (quartzJob.getPauseAfterFailure() != null && quartzJob.getPauseAfterFailure()) {
quartzJob.setIsPause(false);
// 更新状态
systemQuartzJobService.switchQuartzJobStatus(quartzJob);
}
if (quartzJob.getEmail() != null) {
SystemEmailService emailService = KitSpringBeanHolder.getBean(SystemEmailService.class);
// 邮箱报警
if (StrUtil.isNotBlank(quartzJob.getEmail())) {
SystemSendEmailArgs sendEmailRequest = taskAlarm(quartzJob, ExceptionUtil.stacktraceToString(e));
emailService.sendEmail(sendEmailRequest);
}
}
} finally {
quartzLogMapper.insert(quartzLog);
}
}
private SystemSendEmailArgs taskAlarm(SystemQuartzJobTb quartzJob, String msg) {
SystemSendEmailArgs sendEmailRequest = new SystemSendEmailArgs();
sendEmailRequest.setSubject("定时任务【" + quartzJob.getJobName() + "】执行失败,请尽快处理!");
Map<String, Object> data = new HashMap<>(16);
data.put("task", quartzJob);
data.put("msg", msg);
TemplateEngine engine =
TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
Template template = engine.getTemplate("SystemQuartzJobTaskAlarmTemplate.ftl");
sendEmailRequest.setContent(template.render(data));
List<String> emails = Arrays.asList(quartzJob.getEmail().split("[,,]"));
sendEmailRequest.setTos(emails);
return sendEmailRequest;
}
private SystemSendEmailArgs taskAlarm(SystemQuartzJobTb quartzJob, String msg) {
SystemSendEmailArgs sendEmailRequest = new SystemSendEmailArgs();
sendEmailRequest.setSubject("定时任务【" + quartzJob.getJobName() + "】执行失败,请尽快处理!");
Map<String, Object> data = new HashMap<>(16);
data.put("task", quartzJob);
data.put("msg", msg);
TemplateEngine engine =
TemplateUtil.createEngine(new TemplateConfig("template", TemplateConfig.ResourceMode.CLASSPATH));
Template template = engine.getTemplate("SystemQuartzJobTaskAlarmTemplate.ftl");
sendEmailRequest.setContent(template.render(data));
List<String> emails = Arrays.asList(quartzJob.getEmail().split("[,,]"));
sendEmailRequest.setTos(emails);
return sendEmailRequest;
}
}
@@ -39,145 +39,146 @@ import org.springframework.stereotype.Component;
@Component
public class QuartzManage {
private static final String JOB_NAME = "TASK_";
private static final String JOB_NAME = "TASK_";
@Resource private Scheduler scheduler;
@Resource
private Scheduler scheduler;
public void addJob(SystemQuartzJobTb quartzJob) {
try {
// 构建job信息
JobDetail jobDetail =
JobBuilder.newJob(ExecutionJobBean.class).withIdentity(JOB_NAME + quartzJob.getId()).build();
public void addJob(SystemQuartzJobTb quartzJob) {
try {
// 构建job信息
JobDetail jobDetail =
JobBuilder.newJob(ExecutionJobBean.class).withIdentity(JOB_NAME + quartzJob.getId()).build();
// 通过触发器名和cron 表达式创建 Trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(JOB_NAME + quartzJob.getId()).startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression())).build();
// 通过触发器名和cron 表达式创建 Trigger
Trigger trigger = TriggerBuilder.newTrigger().withIdentity(JOB_NAME + quartzJob.getId()).startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression())).build();
trigger.getJobDataMap().put(SystemQuartzJobTb.JOB_KEY, quartzJob);
trigger.getJobDataMap().put(SystemQuartzJobTb.JOB_KEY, quartzJob);
// 重置启动时间
((CronTriggerImpl)trigger).setStartTime(new Date());
// 重置启动时间
((CronTriggerImpl) trigger).setStartTime(new Date());
// 执行定时任务,如果是持久化的,这里会报错,捕获输出
try {
scheduler.scheduleJob(jobDetail, trigger);
} catch (ObjectAlreadyExistsException e) {
log.warn("定时任务已存在,跳过加载");
}
// 执行定时任务,如果是持久化的,这里会报错,捕获输出
try {
scheduler.scheduleJob(jobDetail, trigger);
} catch (ObjectAlreadyExistsException e) {
log.warn("定时任务已存在,跳过加载");
}
// 暂停任务
if (quartzJob.getIsPause()) {
pauseJob(quartzJob);
}
} catch (Exception e) {
log.error("创建定时任务失败", e);
throw new BadRequestException("创建定时任务失败");
}
// 暂停任务
if (quartzJob.getIsPause()) {
pauseJob(quartzJob);
}
} catch (Exception e) {
log.error("创建定时任务失败", e);
throw new BadRequestException("创建定时任务失败");
}
}
/**
* 更新job cron表达式
*
* @param quartzJob /
*/
public void updateJobCron(SystemQuartzJobTb quartzJob) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if (trigger == null) {
addJob(quartzJob);
trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
}
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression());
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 重置启动时间
((CronTriggerImpl)trigger).setStartTime(new Date());
trigger.getJobDataMap().put(SystemQuartzJobTb.JOB_KEY, quartzJob);
/**
* 更新job cron表达式
*
* @param quartzJob /
*/
public void updateJobCron(SystemQuartzJobTb quartzJob) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if (trigger == null) {
addJob(quartzJob);
trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
}
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression());
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 重置启动时间
((CronTriggerImpl) trigger).setStartTime(new Date());
trigger.getJobDataMap().put(SystemQuartzJobTb.JOB_KEY, quartzJob);
scheduler.rescheduleJob(triggerKey, trigger);
// 暂停任务
if (quartzJob.getIsPause()) {
pauseJob(quartzJob);
}
} catch (Exception e) {
log.error("更新定时任务失败", e);
throw new BadRequestException("更新定时任务失败");
}
scheduler.rescheduleJob(triggerKey, trigger);
// 暂停任务
if (quartzJob.getIsPause()) {
pauseJob(quartzJob);
}
} catch (Exception e) {
log.error("更新定时任务失败", e);
throw new BadRequestException("更新定时任务失败");
}
}
/**
* 删除一个job
*
* @param quartzJob /
*/
public void deleteJob(SystemQuartzJobTb quartzJob) {
try {
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.pauseJob(jobKey);
scheduler.deleteJob(jobKey);
} catch (Exception e) {
log.error("删除定时任务失败", e);
throw new BadRequestException("删除定时任务失败");
}
/**
* 删除一个job
*
* @param quartzJob /
*/
public void deleteJob(SystemQuartzJobTb quartzJob) {
try {
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.pauseJob(jobKey);
scheduler.deleteJob(jobKey);
} catch (Exception e) {
log.error("删除定时任务失败", e);
throw new BadRequestException("删除定时任务失败");
}
}
/**
* 恢复一个job
*
* @param quartzJob /
*/
public void resumeJob(SystemQuartzJobTb quartzJob) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if (trigger == null) {
addJob(quartzJob);
}
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.resumeJob(jobKey);
} catch (Exception e) {
log.error("恢复定时任务失败", e);
throw new BadRequestException("恢复定时任务失败");
}
/**
* 恢复一个job
*
* @param quartzJob /
*/
public void resumeJob(SystemQuartzJobTb quartzJob) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if (trigger == null) {
addJob(quartzJob);
}
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.resumeJob(jobKey);
} catch (Exception e) {
log.error("恢复定时任务失败", e);
throw new BadRequestException("恢复定时任务失败");
}
}
/**
* 立即执行job
*
* @param quartzJob /
*/
public void runJobNow(SystemQuartzJobTb quartzJob) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if (trigger == null) {
addJob(quartzJob);
}
JobDataMap dataMap = new JobDataMap();
dataMap.put(SystemQuartzJobTb.JOB_KEY, quartzJob);
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.triggerJob(jobKey, dataMap);
} catch (Exception e) {
log.error("定时任务执行失败", e);
throw new BadRequestException("定时任务执行失败");
}
/**
* 立即执行job
*
* @param quartzJob /
*/
public void runJobNow(SystemQuartzJobTb quartzJob) {
try {
TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 如果不存在则创建一个定时任务
if (trigger == null) {
addJob(quartzJob);
}
JobDataMap dataMap = new JobDataMap();
dataMap.put(SystemQuartzJobTb.JOB_KEY, quartzJob);
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.triggerJob(jobKey, dataMap);
} catch (Exception e) {
log.error("定时任务执行失败", e);
throw new BadRequestException("定时任务执行失败");
}
}
/**
* 暂停一个job
*
* @param quartzJob /
*/
public void pauseJob(SystemQuartzJobTb quartzJob) {
try {
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.pauseJob(jobKey);
} catch (Exception e) {
log.error("定时任务暂停失败", e);
throw new BadRequestException("定时任务暂停失败");
}
/**
* 暂停一个job
*
* @param quartzJob /
*/
public void pauseJob(SystemQuartzJobTb quartzJob) {
try {
JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
scheduler.pauseJob(jobKey);
} catch (Exception e) {
log.error("定时任务暂停失败", e);
throw new BadRequestException("定时任务暂停失败");
}
}
}
@@ -28,17 +28,19 @@ import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
public class OssStorageConfiguration {
@Autowired private AppProperties properties;
@Bean
public MinioClient minioClient() {
try {
OSSConfigModel minio = properties.getOss().getMinio();
return MinioClient.builder().endpoint(minio.getEndpoint())
.credentials(minio.getAccessKey(), minio.getSecretKey()).build();
} catch (Exception e) {
log.error("创建Minio客户端失败", e);
throw new BadRequestException("创建Minio客户端失败");
}
@Autowired
private AppProperties properties;
@Bean
public MinioClient minioClient() {
try {
OSSConfigModel minio = properties.getOss().getMinio();
return MinioClient.builder().endpoint(minio.getEndpoint())
.credentials(minio.getAccessKey(), minio.getSecretKey()).build();
} catch (Exception e) {
log.error("创建Minio客户端失败", e);
throw new BadRequestException("创建Minio客户端失败");
}
}
}
@@ -31,26 +31,29 @@ import org.springframework.util.unit.DataSize;
@Slf4j
@Configuration
public class MultipartConfig {
@Value("${spring.application.name}") private String name;
@Autowired private AppProperties properties;
/**
* 文件上传临时路径
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(DataSize.ofMegabytes(properties.getOss().getMaxSize()));
factory.setMaxRequestSize(DataSize.ofMegabytes(properties.getOss().getMaxSize()));
String location = System.getProperty("user.home") + "/" + name + "/tmp";
File tmpFile = new File(location);
if (!tmpFile.exists()) {
if (!tmpFile.mkdirs()) {
log.error("创建临时文件失败");
throw new BadRequestException("创建临时文件失败");
}
}
factory.setLocation(location);
return factory.createMultipartConfig();
@Value("${spring.application.name}")
private String name;
@Autowired
private AppProperties properties;
/**
* 文件上传临时路径
*/
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(DataSize.ofMegabytes(properties.getOss().getMaxSize()));
factory.setMaxRequestSize(DataSize.ofMegabytes(properties.getOss().getMaxSize()));
String location = System.getProperty("user.home") + "/" + name + "/tmp";
File tmpFile = new File(location);
if (!tmpFile.exists()) {
if (!tmpFile.mkdirs()) {
log.error("创建临时文件失败");
throw new BadRequestException("创建临时文件失败");
}
}
factory.setLocation(location);
return factory.createMultipartConfig();
}
}
@@ -59,324 +59,327 @@ import org.springframework.web.multipart.MultipartFile;
@Slf4j
@Component
public class MinioRepository {
@Autowired private AppProperties properties;
@Autowired private MinioClient minioClient;
/**
* 检查存储bucket是否存在
*
* @param bucketName Bucket 名称
* @return boolean 存在返回 true,否则返回 false
*/
public boolean existsBucket(String bucketName) {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("检查 bucket 是否存在失败,bucketName: {}, 错误信息", bucketName, e);
return false;
}
@Autowired
private AppProperties properties;
@Autowired
private MinioClient minioClient;
/**
* 检查存储bucket是否存在
*
* @param bucketName Bucket 名称
* @return boolean 存在返回 true,否则返回 false
*/
public boolean existsBucket(String bucketName) {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("检查 bucket 是否存在失败,bucketName: {}, 错误信息", bucketName, e);
return false;
}
}
/**
* 创建存储bucket
*
* @param bucketName Bucket 名称
* @return boolean 创建成功返回 true,否则返回 false
*/
public boolean createBucket(String bucketName) {
try {
if (!existsBucket(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
log.info("Bucket 创建成功,bucketName: {}", bucketName);
return true;
} else {
log.warn("Bucket 已经存在,bucketName: {}", bucketName);
}
} catch (Exception e) {
log.error("创建 bucket 失败,bucketName: {}, 错误信息", bucketName, e);
}
return false;
}
/**
* 删除存储bucket
*
* @param bucketName Bucket 名称
* @return boolean 删除成功返回 true,否则返回 false
*/
public boolean removeBucket(String bucketName) {
try {
if (existsBucket(bucketName)) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
log.info("Bucket 删除成功,bucketName: {}", bucketName);
return true;
} else {
log.warn("Bucket 不存在,bucketName: {}", bucketName);
}
} catch (Exception e) {
log.error("删除 bucket 失败,bucketName: {}, 错误信息", bucketName, e);
}
return false;
}
/**
* 获取所有 bucket 列表
*
* @return List<Bucket> 所有的 Bucket 列表
*/
public List<Bucket> queryAllBuckets() {
try {
return minioClient.listBuckets();
} catch (Exception e) {
log.error("获取所有 buckets 失败,错误信息", e);
}
return Collections.emptyList();
}
/**
* 文件上传
*
* @param tempFile 文件
* @param originalFilename 源文件名
* @param fileSize 文件大小
* @param contentType 文件类型
* @param md5 文件md5
* @return String 上传后的文件名,上传失败返回 null
*/
public SystemOssStorageTb upload(File tempFile, String originalFilename, long fileSize, String contentType,
String md5) {
StorageOSSModel ossConfig = properties.getOss();
OSSConfigModel ossMinioConfig = ossConfig.getMinio();
String type = FileTypeUtil.getType(tempFile);
String prefix = KitFileUtil.getPrefix(tempFile);
String suffix = KitFileUtil.getSuffix(tempFile);
String fileCode = IdUtil.fastSimpleUUID();
String fileName = fileCode + FileUtil.getSuffix(originalFilename);
String objectName = KitDateUtil.getNowDateStr() + "/" + fileName;
log.info("上传文件,原文件名:{},上传后文件名:{}", originalFilename, fileName);
String fileUrl = ossMinioConfig.getEndpoint() + "/" + ossMinioConfig.getBucketName() + "/" + objectName;
SystemOssStorageTb systemOssStorageTb = new SystemOssStorageTb();
systemOssStorageTb.setServiceType("minio");
systemOssStorageTb.setEndpoint(ossMinioConfig.getEndpoint());
systemOssStorageTb.setBucketName(StrUtil.trim(ossMinioConfig.getBucketName()));
systemOssStorageTb.setFileName(originalFilename);
systemOssStorageTb.setFileSize(fileSize);
systemOssStorageTb.setFileMime(type);
systemOssStorageTb.setFilePrefix(prefix);
systemOssStorageTb.setFileSuffix(suffix);
systemOssStorageTb.setFileMd5(md5);
systemOssStorageTb.setFileUrl(fileUrl);
systemOssStorageTb.setFileCode(fileCode);
systemOssStorageTb.setObjectName(objectName);
try (InputStream fileInputStream = KitFileUtil.getInputStream(tempFile)) {
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(ossMinioConfig.getBucketName()).object(objectName)
.stream(fileInputStream, fileSize, -1).contentType(contentType).build();
minioClient.putObject(objectArgs);
log.info("文件上传成功,并删除临时文件,文件名:{}", originalFilename);
return systemOssStorageTb;
} catch (Exception e) {
log.error("文件上传失败,文件名:{},错误信息", originalFilename, e);
} finally {
try {
KitFileUtil.del(tempFile);
} catch (IORuntimeException e) {
// 删除失败忽略
}
}
return null;
}
/**
* 优化文件上传 文件上传:优化版
*
* @param file
* @return {@link String }
*/
public String optimizedUpload(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
if (StrUtil.isBlank(originalFilename)) {
throw new BadRequestException("文件名不能为空");
}
/**
* 创建存储bucket
*
* @param bucketName Bucket 名称
* @return boolean 创建成功返回 true,否则返回 false
*/
public boolean createBucket(String bucketName) {
try {
if (!existsBucket(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
log.info("Bucket 创建成功,bucketName: {}", bucketName);
return true;
} else {
log.warn("Bucket 已经存在,bucketName: {}", bucketName);
}
} catch (Exception e) {
log.error("创建 bucket 失败,bucketName: {}, 错误信息", bucketName, e);
}
return false;
long size = file.getSize();
KitFileUtil.checkSize(properties.getOss().getMaxSize(), size);
String fileName = UUID.randomUUID() + FileUtil.getSuffix(originalFilename);
// yyyy/MM/dd/fileName.xxx
String objectName = KitDateUtil.getNowDateTimeStr() + "/" + fileName;
log.info("准备上传文件,原文件名:{},上传后文件名:{}", originalFilename, fileName);
try (InputStream fileInputStream = file.getInputStream()) {
PutObjectArgs objectArgs =
PutObjectArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).object(objectName)
.stream(fileInputStream, file.getSize(), -1).contentType(file.getContentType()).build();
// 使用重试机制进行上传
uploadWithRetry(objectArgs, 3);
log.info("文件上传成功,objectName: {}", objectName);
return objectName;
} catch (Exception e) {
log.error("文件上传失败,文件名:{},错误信息", originalFilename, e);
}
return null;
}
/**
* 删除存储bucket
*
* @param bucketName Bucket 名称
* @return boolean 删除成功返回 true,否则返回 false
*/
public boolean removeBucket(String bucketName) {
try {
if (existsBucket(bucketName)) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
log.info("Bucket 删除成功,bucketName: {}", bucketName);
return true;
} else {
log.warn("Bucket 不存在,bucketName: {}", bucketName);
}
} catch (Exception e) {
log.error("删除 bucket 失败,bucketName: {}, 错误信息", bucketName, e);
/**
* 上传重试机制
*
* @param objectArgs 对象参数
* @param maxRetries 最大重试次数
* @throws Exception 例外
*/
private void uploadWithRetry(PutObjectArgs objectArgs, int maxRetries) throws Exception {
int attempt = 0;
while (attempt < maxRetries) {
try {
minioClient.putObject(objectArgs);
return; // 上传成功,返回
} catch (Exception e) {
attempt++;
log.warn("上传失败,第 {} 次尝试,错误信息", attempt, e);
if (attempt == maxRetries) {
throw new BadRequestException("上传失败,已达到最大重试次数");
}
return false;
// 等待一段时间后重试
Thread.sleep(2000);
}
}
}
/**
* 获取所有 bucket 列表
*
* @return List<Bucket> 所有的 Bucket 列表
*/
public List<Bucket> queryAllBuckets() {
try {
return minioClient.listBuckets();
} catch (Exception e) {
log.error("获取所有 buckets 失败,错误信息", e);
}
return Collections.emptyList();
/**
* 生成文件的预签名 URL,用于文件预览
*
* @param fullFileName 文件完整路径
* @return String 预签名 URL
*/
public String generatePreviewUrl(String fullFileName) {
if (StrUtil.isBlank(fullFileName)) {
log.warn("文件名为空,无法生成预签名 URL");
return null;
}
/**
* 文件上传
*
* @param tempFile 文件
* @param originalFilename 源文件名
* @param fileSize 文件大小
* @param contentType 文件类型
* @param md5 文件md5
* @return String 上传后的文件名,上传失败返回 null
*/
public SystemOssStorageTb upload(File tempFile, String originalFilename, long fileSize, String contentType,
String md5) {
StorageOSSModel ossConfig = properties.getOss();
OSSConfigModel ossMinioConfig = ossConfig.getMinio();
String type = FileTypeUtil.getType(tempFile);
String prefix = KitFileUtil.getPrefix(tempFile);
String suffix = KitFileUtil.getSuffix(tempFile);
String fileCode = IdUtil.fastSimpleUUID();
String fileName = fileCode + FileUtil.getSuffix(originalFilename);
String objectName = KitDateUtil.getNowDateStr() + "/" + fileName;
log.info("上传文件,原文件名:{},上传后文件名:{}", originalFilename, fileName);
String fileUrl = ossMinioConfig.getEndpoint() + "/" + ossMinioConfig.getBucketName() + "/" + objectName;
SystemOssStorageTb systemOssStorageTb = new SystemOssStorageTb();
systemOssStorageTb.setServiceType("minio");
systemOssStorageTb.setEndpoint(ossMinioConfig.getEndpoint());
systemOssStorageTb.setBucketName(StrUtil.trim(ossMinioConfig.getBucketName()));
systemOssStorageTb.setFileName(originalFilename);
systemOssStorageTb.setFileSize(fileSize);
systemOssStorageTb.setFileMime(type);
systemOssStorageTb.setFilePrefix(prefix);
systemOssStorageTb.setFileSuffix(suffix);
systemOssStorageTb.setFileMd5(md5);
systemOssStorageTb.setFileUrl(fileUrl);
systemOssStorageTb.setFileCode(fileCode);
systemOssStorageTb.setObjectName(objectName);
try (InputStream fileInputStream = KitFileUtil.getInputStream(tempFile)) {
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(ossMinioConfig.getBucketName()).object(objectName)
.stream(fileInputStream, fileSize, -1).contentType(contentType).build();
minioClient.putObject(objectArgs);
log.info("文件上传成功,并删除临时文件,文件名:{}", originalFilename);
return systemOssStorageTb;
} catch (Exception e) {
log.error("文件上传失败,文件名:{},错误信息", originalFilename, e);
} finally {
try {
KitFileUtil.del(tempFile);
} catch (IORuntimeException e) {
// 删除失败忽略
}
}
return null;
try {
GetPresignedObjectUrlArgs presignedUrlArgs =
GetPresignedObjectUrlArgs.builder().bucket(properties.getOss().getMinio().getBucketName())
.object(fullFileName).method(Method.GET)
// 设置 URL 过期时间为 7 天, 最大 7 天, 别挣扎了
.expiry(3600 * 24 * 7).build();
String url = minioClient.getPresignedObjectUrl(presignedUrlArgs);
log.info("生成预签名 URL 成功,fileName: {}, url: {}", fullFileName, url);
return url;
} catch (Exception e) {
log.error("生成预签名 URL 失败,fileName: {}", fullFileName, e);
}
return null;
}
/**
* 优化文件上传 文件上传:优化版
*
* @param file
* @return {@link String }
*/
public String optimizedUpload(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
if (StrUtil.isBlank(originalFilename)) {
throw new BadRequestException("文件名不能为空");
}
/**
* 下载文件
*
* @param fileName 文件名称
* @param res HTTP 响应
*/
public void download(String fileName, HttpServletResponse res) {
GetObjectArgs objectArgs =
GetObjectArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).object(fileName).build();
long size = file.getSize();
KitFileUtil.checkSize(properties.getOss().getMaxSize(), size);
try (GetObjectResponse response = minioClient.getObject(objectArgs);
FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
String fileName = UUID.randomUUID() + FileUtil.getSuffix(originalFilename);
// yyyy/MM/dd/fileName.xxx
String objectName = KitDateUtil.getNowDateTimeStr() + "/" + fileName;
byte[] buf = new byte[1024];
int len;
while ((len = response.read(buf)) != -1) {
os.write(buf, 0, len);
}
os.flush();
log.info("准备上传文件,原文件名:{},上传后文件名:{}", originalFilename, fileName);
try (InputStream fileInputStream = file.getInputStream()) {
PutObjectArgs objectArgs =
PutObjectArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).object(objectName)
.stream(fileInputStream, file.getSize(), -1).contentType(file.getContentType()).build();
// 使用重试机制进行上传
uploadWithRetry(objectArgs, 3);
log.info("文件上传成功,objectName: {}", objectName);
return objectName;
} catch (Exception e) {
log.error("文件上传失败,文件名:{},错误信息", originalFilename, e);
}
return null;
byte[] bytes = os.toByteArray();
res.setCharacterEncoding("utf-8");
res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
try (ServletOutputStream stream = res.getOutputStream()) {
stream.write(bytes);
stream.flush();
}
} catch (Exception e) {
log.error("下载文件失败,fileName: {}, 错误信息", fileName, e);
}
}
/**
* 上传重试机制
*
* @param objectArgs 对象参数
* @param maxRetries 最大重试次数
* @throws Exception 例外
*/
private void uploadWithRetry(PutObjectArgs objectArgs, int maxRetries) throws Exception {
int attempt = 0;
while (attempt < maxRetries) {
try {
minioClient.putObject(objectArgs);
return; // 上传成功,返回
} catch (Exception e) {
attempt++;
log.warn("上传失败,第 {} 次尝试,错误信息", attempt, e);
if (attempt == maxRetries) {
throw new BadRequestException("上传失败,已达到最大重试次数");
}
// 等待一段时间后重试
Thread.sleep(2000);
}
/**
* 文件下载:优化版
*
* @param fileName 文件名称
* @param res HTTP 响应
*/
public void downloadOptimizedVersion(String fileName, HttpServletResponse res) {
GetObjectArgs objectArgs =
GetObjectArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).object(fileName).build();
try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
// 增大缓冲区
byte[] buf = new byte[1024 * 4];
int len;
// 分块下载并输出到响应流
try (ServletOutputStream stream = res.getOutputStream()) {
res.setCharacterEncoding("utf-8");
res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
res.setHeader("Cache-Control", "no-store");
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", "0");
while ((len = response.read(buf)) != -1) {
stream.write(buf, 0, len);
// 确保实时写入
stream.flush();
}
log.info("文件下载成功,fileName: {}", fileName);
}
} catch (Exception e) {
log.error("下载文件失败,fileName: {}, 错误信息", fileName, e);
}
}
/**
* 生成文件的预签名 URL,用于文件预览
*
* @param fullFileName 文件完整路径
* @return String 预签名 URL
*/
public String generatePreviewUrl(String fullFileName) {
if (StrUtil.isBlank(fullFileName)) {
log.warn("文件名为空,无法生成预签名 URL");
return null;
}
try {
GetPresignedObjectUrlArgs presignedUrlArgs =
GetPresignedObjectUrlArgs.builder().bucket(properties.getOss().getMinio().getBucketName())
.object(fullFileName).method(Method.GET)
// 设置 URL 过期时间为 7 天, 最大 7 天, 别挣扎了
.expiry(3600 * 24 * 7).build();
String url = minioClient.getPresignedObjectUrl(presignedUrlArgs);
log.info("生成预签名 URL 成功,fileName: {}, url: {}", fullFileName, url);
return url;
} catch (Exception e) {
log.error("生成预签名 URL 失败,fileName: {}", fullFileName, e);
}
return null;
/**
* 查看文件对象列表
*
* @return List<Item> 文件对象列表
*/
public List<Item> queryAllObjects() {
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).build());
List<Item> items = new ArrayList<>();
for (Result<Item> result : results) {
items.add(result.get());
}
return items;
} catch (Exception e) {
log.error("列出文件对象失败,错误信息", e);
}
return Collections.emptyList();
}
/**
* 下载文件
*
* @param fileName 文件名称
* @param res HTTP 响应
*/
public void download(String fileName, HttpServletResponse res) {
GetObjectArgs objectArgs =
GetObjectArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).object(fileName).build();
try (GetObjectResponse response = minioClient.getObject(objectArgs);
FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
byte[] buf = new byte[1024];
int len;
while ((len = response.read(buf)) != -1) {
os.write(buf, 0, len);
}
os.flush();
byte[] bytes = os.toByteArray();
res.setCharacterEncoding("utf-8");
res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
try (ServletOutputStream stream = res.getOutputStream()) {
stream.write(bytes);
stream.flush();
}
} catch (Exception e) {
log.error("下载文件失败,fileName: {}, 错误信息", fileName, e);
}
}
/**
* 文件下载:优化版
*
* @param fileName 文件名称
* @param res HTTP 响应
*/
public void downloadOptimizedVersion(String fileName, HttpServletResponse res) {
GetObjectArgs objectArgs =
GetObjectArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).object(fileName).build();
try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
// 增大缓冲区
byte[] buf = new byte[1024 * 4];
int len;
// 分块下载并输出到响应流
try (ServletOutputStream stream = res.getOutputStream()) {
res.setCharacterEncoding("utf-8");
res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
res.setHeader("Cache-Control", "no-store");
res.setHeader("Pragma", "no-cache");
res.setHeader("Expires", "0");
while ((len = response.read(buf)) != -1) {
stream.write(buf, 0, len);
// 确保实时写入
stream.flush();
}
log.info("文件下载成功,fileName: {}", fileName);
}
} catch (Exception e) {
log.error("下载文件失败,fileName: {}, 错误信息", fileName, e);
}
}
/**
* 查看文件对象列表
*
* @return List<Item> 文件对象列表
*/
public List<Item> queryAllObjects() {
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).build());
List<Item> items = new ArrayList<>();
for (Result<Item> result : results) {
items.add(result.get());
}
return items;
} catch (Exception e) {
log.error("列出文件对象失败,错误信息", e);
}
return Collections.emptyList();
}
/**
* 删除文件
*
* @param fileName 文件名
* @return boolean 删除成功返回 true,否则返回 false
*/
public boolean removeBucketFile(String fileName) {
try {
minioClient.removeObject(
RemoveObjectArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).object(fileName)
.build());
log.info("删除文件成功,fileName: {}", fileName);
return true;
} catch (Exception e) {
log.error("删除文件失败,fileName: {}, 错误信息", fileName, e);
}
return false;
/**
* 删除文件
*
* @param fileName 文件名
* @return boolean 删除成功返回 true,否则返回 false
*/
public boolean removeBucketFile(String fileName) {
try {
minioClient.removeObject(
RemoveObjectArgs.builder().bucket(properties.getOss().getMinio().getBucketName()).object(fileName)
.build());
log.info("删除文件成功,fileName: {}", fileName);
return true;
} catch (Exception e) {
log.error("删除文件失败,fileName: {}, 错误信息", fileName, e);
}
return false;
}
}
@@ -22,15 +22,15 @@ import org.springframework.stereotype.Service;
@Service
public class TestTask {
public void run() {
log.info("run 执行成功");
}
public void run() {
log.info("run 执行成功");
}
public void run1(String str) {
log.info("run1 执行成功, 参数为: {}", str);
}
public void run1(String str) {
log.info("run1 执行成功, 参数为: {}", str);
}
public void run2() {
log.info("run2 执行成功");
}
public void run2() {
log.info("run2 执行成功");
}
}
@@ -18,10 +18,10 @@ import cn.odboy.system.framework.permission.core.handler.TokenProvider;
import cn.odboy.system.framework.permission.core.handler.UserDetailsHandler;
import cn.odboy.util.KitRsaEncryptUtil;
import com.wf.captcha.base.Captcha;
import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -33,6 +33,7 @@ import cn.odboy.system.framework.permission.core.KitSecurityHelper;
import cn.odboy.util.KitClassUtil;
import cn.odboy.util.KitFileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -46,7 +47,6 @@ import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -29,13 +29,13 @@ import cn.odboy.util.KitFileUtil;
import cn.odboy.util.KitPageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -28,13 +28,13 @@ import cn.odboy.util.KitFileUtil;
import cn.odboy.util.KitPageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -33,6 +33,7 @@ import cn.odboy.util.KitFileUtil;
import cn.odboy.util.KitPageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -40,7 +41,6 @@ import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -36,6 +36,7 @@ import cn.odboy.util.KitClassUtil;
import cn.odboy.util.KitFileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
@@ -48,7 +49,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -21,9 +21,9 @@ import cn.odboy.system.dal.model.SystemOssStorageVo;
import cn.odboy.system.dal.model.SystemQueryStorageArgs;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
/**
@@ -34,6 +34,7 @@ import cn.odboy.util.KitFileUtil;
import cn.odboy.util.KitPageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -41,7 +42,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import jakarta.servlet.http.HttpServletResponse;
import org.quartz.CronExpression;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -35,6 +35,7 @@ import cn.odboy.util.KitFileUtil;
import cn.odboy.util.KitPageUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
@@ -44,7 +45,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -50,6 +50,7 @@ import cn.odboy.util.KitRsaEncryptUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -62,7 +63,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@@ -36,13 +36,13 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -37,150 +37,150 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public final class KitSqlUtil {
/**
* 获取数据源
*
* @param jdbcUrl /
* @param userName /
* @param password /
* @return DataSource
*/
private static DataSource getDataSource(String jdbcUrl, String userName, String password) {
HikariDataSource druidDataSource = new HikariDataSource();
String className;
try {
className = DriverManager.getDriver(jdbcUrl.trim()).getClass().getName();
} catch (SQLException e) {
throw new BadRequestException("Get class name error: =" + jdbcUrl);
/**
* 获取数据源
*
* @param jdbcUrl /
* @param userName /
* @param password /
* @return DataSource
*/
private static DataSource getDataSource(String jdbcUrl, String userName, String password) {
HikariDataSource druidDataSource = new HikariDataSource();
String className;
try {
className = DriverManager.getDriver(jdbcUrl.trim()).getClass().getName();
} catch (SQLException e) {
throw new BadRequestException("Get class name error: =" + jdbcUrl);
}
if (StringUtils.isEmpty(className)) {
DataTypeEnum dataTypeEnum = DataTypeEnum.urlOf(jdbcUrl);
if (null == dataTypeEnum) {
throw new BadRequestException("Not supported data type: jdbcUrl=" + jdbcUrl);
}
druidDataSource.setDriverClassName(dataTypeEnum.getDriver());
} else {
druidDataSource.setDriverClassName(className);
}
druidDataSource.setJdbcUrl(jdbcUrl);
druidDataSource.setUsername(userName);
druidDataSource.setPassword(password);
druidDataSource.setConnectionTimeout(3000);
return druidDataSource;
}
private static Connection getConnection(String jdbcUrl, String userName, String password) {
DataSource dataSource = getDataSource(jdbcUrl, userName, password);
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (Exception ignored) {
}
try {
int timeOut = 5;
if (null == connection || connection.isClosed() || !connection.isValid(timeOut)) {
log.info("connection is closed or invalid, retry get connection!");
connection = dataSource.getConnection();
}
} catch (Exception e) {
log.error("create connection error, jdbcUrl: {}", jdbcUrl);
throw new BadRequestException("create connection error, jdbcUrl: " + jdbcUrl);
} finally {
KitCloseUtil.close(connection);
}
return connection;
}
private static void releaseConnection(Connection connection) {
if (null != connection) {
try {
connection.close();
} catch (Exception e) {
log.error(e.getMessage(), e);
log.error("connection close error:" + e.getMessage());
}
}
}
public static boolean testConnection(String jdbcUrl, String userName, String password) {
Connection connection = null;
try {
connection = getConnection(jdbcUrl, userName, password);
if (null != connection) {
return true;
}
} catch (Exception e) {
log.info("Get connection failed:" + e.getMessage());
} finally {
releaseConnection(connection);
}
return false;
}
public static String executeFile(String jdbcUrl, String username, String password, File sqlFile) {
Connection connection = getConnection(jdbcUrl, username, password);
try {
batchExecute(connection, readSqlList(sqlFile));
} catch (Exception e) {
log.error("sql脚本执行发生异常:{}", e.getMessage());
return e.getMessage();
} finally {
releaseConnection(connection);
}
return "success";
}
/**
* 批量执行sql
*
* @param connection /
* @param sqlList /
*/
public static void batchExecute(Connection connection, List<String> sqlList) {
Statement st = null;
try {
st = connection.createStatement();
for (String sql : sqlList) {
if (sql.endsWith(";")) {
sql = sql.substring(0, sql.length() - 1);
}
if (StringUtils.isEmpty(className)) {
DataTypeEnum dataTypeEnum = DataTypeEnum.urlOf(jdbcUrl);
if (null == dataTypeEnum) {
throw new BadRequestException("Not supported data type: jdbcUrl=" + jdbcUrl);
}
druidDataSource.setDriverClassName(dataTypeEnum.getDriver());
st.addBatch(sql);
}
st.executeBatch();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
KitCloseUtil.close(st);
}
}
/**
* 将文件中的sql语句以;为单位读取到列表中
*
* @param sqlFile /
* @return /
* @throws Exception e
*/
private static List<String> readSqlList(File sqlFile) throws Exception {
List<String> sqlList = Lists.newArrayList();
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(Files.newInputStream(sqlFile.toPath()), StandardCharsets.UTF_8))) {
String tmp;
while ((tmp = reader.readLine()) != null) {
log.info("line:{}", tmp);
if (tmp.endsWith(";")) {
sb.append(tmp);
sqlList.add(sb.toString());
sb.delete(0, sb.length());
} else {
druidDataSource.setDriverClassName(className);
sb.append(tmp);
}
druidDataSource.setJdbcUrl(jdbcUrl);
druidDataSource.setUsername(userName);
druidDataSource.setPassword(password);
druidDataSource.setConnectionTimeout(3000);
return druidDataSource;
}
private static Connection getConnection(String jdbcUrl, String userName, String password) {
DataSource dataSource = getDataSource(jdbcUrl, userName, password);
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (Exception ignored) {
}
try {
int timeOut = 5;
if (null == connection || connection.isClosed() || !connection.isValid(timeOut)) {
log.info("connection is closed or invalid, retry get connection!");
connection = dataSource.getConnection();
}
} catch (Exception e) {
log.error("create connection error, jdbcUrl: {}", jdbcUrl);
throw new BadRequestException("create connection error, jdbcUrl: " + jdbcUrl);
} finally {
KitCloseUtil.close(connection);
}
return connection;
}
private static void releaseConnection(Connection connection) {
if (null != connection) {
try {
connection.close();
} catch (Exception e) {
log.error(e.getMessage(), e);
log.error("connection close error:" + e.getMessage());
}
}
}
public static boolean testConnection(String jdbcUrl, String userName, String password) {
Connection connection = null;
try {
connection = getConnection(jdbcUrl, userName, password);
if (null != connection) {
return true;
}
} catch (Exception e) {
log.info("Get connection failed:" + e.getMessage());
} finally {
releaseConnection(connection);
}
return false;
}
public static String executeFile(String jdbcUrl, String username, String password, File sqlFile) {
Connection connection = getConnection(jdbcUrl, username, password);
try {
batchExecute(connection, readSqlList(sqlFile));
} catch (Exception e) {
log.error("sql脚本执行发生异常:{}", e.getMessage());
return e.getMessage();
} finally {
releaseConnection(connection);
}
return "success";
}
/**
* 批量执行sql
*
* @param connection /
* @param sqlList /
*/
public static void batchExecute(Connection connection, List<String> sqlList) {
Statement st = null;
try {
st = connection.createStatement();
for (String sql : sqlList) {
if (sql.endsWith(";")) {
sql = sql.substring(0, sql.length() - 1);
}
st.addBatch(sql);
}
st.executeBatch();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
KitCloseUtil.close(st);
}
}
/**
* 将文件中的sql语句以;为单位读取到列表中
*
* @param sqlFile /
* @return /
* @throws Exception e
*/
private static List<String> readSqlList(File sqlFile) throws Exception {
List<String> sqlList = Lists.newArrayList();
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(Files.newInputStream(sqlFile.toPath()), StandardCharsets.UTF_8))) {
String tmp;
while ((tmp = reader.readLine()) != null) {
log.info("line:{}", tmp);
if (tmp.endsWith(";")) {
sb.append(tmp);
sqlList.add(sb.toString());
sb.delete(0, sb.length());
} else {
sb.append(tmp);
}
}
if (!"".endsWith(sb.toString().trim())) {
sqlList.add(sb.toString());
}
}
return sqlList;
}
if (!"".endsWith(sb.toString().trim())) {
sqlList.add(sb.toString());
}
}
return sqlList;
}
}
@@ -3,9 +3,9 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style>
@page {
margin: 0;
}
@page {
margin: 0;
}
</style>
</head>
<body style="margin: 0px;
@@ -3,9 +3,9 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style>
@page {
margin: 0;
}
@page {
margin: 0;
}
</style>
</head>
<body style="margin: 0px;