update 优化 统一补全代码注释

This commit is contained in:
疯狂的狮子Li
2026-03-13 19:36:14 +08:00
parent 916282ba68
commit 48992b574d
201 changed files with 2554 additions and 465 deletions

View File

@@ -47,7 +47,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
* 认证 * 认证控制器,提供登录、注册、社交绑定和退出能力。
* *
* @author Lion Li * @author Lion Li
*/ */
@@ -103,10 +103,10 @@ public class AuthController {
} }
/** /**
* 获取跳转URL * 获取第三方绑定跳转地址。
* *
* @param source 登录来源 * @param source 登录来源
* @return 结果 * @return 跳转地址
*/ */
@GetMapping("/binding/{source}") @GetMapping("/binding/{source}")
public R<String> authBinding(@PathVariable("source") String source) { public R<String> authBinding(@PathVariable("source") String source) {
@@ -120,10 +120,10 @@ public class AuthController {
} }
/** /**
* 前端回调绑定授权(需要token) * 处理前端回调后的社交账号绑定。
* *
* @param loginBody 请求体 * @param loginBody 请求体
* @return 结果 * @return 操作结果
*/ */
@PostMapping("/social/callback") @PostMapping("/social/callback")
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) { public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
@@ -144,9 +144,10 @@ public class AuthController {
/** /**
* 取消授权(需要token) * 取消当前用户的社交账号授权。
* *
* @param socialId socialId * @param socialId socialId
* @return 操作结果
*/ */
@DeleteMapping(value = "/unlock/{socialId}") @DeleteMapping(value = "/unlock/{socialId}")
public R<Void> unlockSocial(@PathVariable Long socialId) { public R<Void> unlockSocial(@PathVariable Long socialId) {
@@ -167,7 +168,10 @@ public class AuthController {
} }
/** /**
* 用户注册 * 用户注册
*
* @param user 注册信息
* @return 操作结果
*/ */
@ApiEncrypt @ApiEncrypt
@PostMapping("/register") @PostMapping("/register")
@@ -180,9 +184,11 @@ public class AuthController {
} }
/** /**
* 登录页租户下拉框 * 获取登录页租户下拉框数据,当前仅预留返回结构。
* *
* @param request 当前请求
* @return 租户列表 * @return 租户列表
* @throws Exception 异常
*/ */
@RateLimiter(time = 60, count = 20, limitType = LimitType.IP) @RateLimiter(time = 60, count = 20, limitType = LimitType.IP)
@GetMapping("/tenant/list") @GetMapping("/tenant/list")

View File

@@ -53,9 +53,10 @@ public class CaptchaController {
private final MailProperties mailProperties; private final MailProperties mailProperties;
/** /**
* 短信验证码 * 发送短信验证码
* *
* @param phonenumber 用户手机号 * @param phonenumber 用户手机号
* @return 操作结果
*/ */
@RateLimiter(key = "#phonenumber", time = 60, count = 1) @RateLimiter(key = "#phonenumber", time = 60, count = 1)
@GetMapping("/resource/sms/code") @GetMapping("/resource/sms/code")
@@ -77,9 +78,10 @@ public class CaptchaController {
} }
/** /**
* 邮箱验证码 * 发送邮箱验证码
* *
* @param email 邮箱 * @param email 邮箱
* @return 操作结果
*/ */
@GetMapping("/resource/email/code") @GetMapping("/resource/email/code")
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) { public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
@@ -91,8 +93,9 @@ public class CaptchaController {
} }
/** /**
* 邮箱验证码 * 发送邮箱验证码的实际执行方法,拆分出来避免开关关闭时仍触发限流。
* 独立方法避免验证码关闭之后仍然走限流 *
* @param email 邮箱
*/ */
@RateLimiter(key = "#email", time = 60, count = 1) @RateLimiter(key = "#email", time = 60, count = 1)
public void emailCodeImpl(String email) { public void emailCodeImpl(String email) {
@@ -108,7 +111,9 @@ public class CaptchaController {
} }
/** /**
* 生成验证码 * 获取图片验证码
*
* @return 验证码信息
*/ */
@GetMapping("/auth/code") @GetMapping("/auth/code")
public R<CaptchaVo> getCode() { public R<CaptchaVo> getCode() {
@@ -122,8 +127,9 @@ public class CaptchaController {
} }
/** /**
* 生成验证码 * 实际生成图片验证码并缓存结果。
* 独立方法避免验证码关闭之后仍然走限流 *
* @return 验证码信息
*/ */
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP) @RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
public CaptchaVo getCodeImpl() { public CaptchaVo getCodeImpl() {

View File

@@ -18,7 +18,9 @@ import org.springframework.web.bind.annotation.RestController;
public class IndexController { public class IndexController {
/** /**
* 访问首页提示 * 访问首页时返回引导提示
*
* @return 首页提示语
*/ */
@GetMapping("/") @GetMapping("/")
public String index() { public String index() {

View File

@@ -15,6 +15,9 @@ public class CaptchaVo {
*/ */
private Boolean captchaEnabled = true; private Boolean captchaEnabled = true;
/**
* 验证码唯一标识。
*/
private String uuid; private String uuid;
/** /**

View File

@@ -3,7 +3,7 @@ package org.dromara.web.domain.vo;
import lombok.Data; import lombok.Data;
/** /**
* 登录租户对象 * 登录租户信息返回对象
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */

View File

@@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data; import lombok.Data;
/** /**
* 登录验证信息 * 登录成功后的令牌信息返回对象。
* *
* @author Michelle.Chung * @author Michelle.Chung
*/ */

View File

@@ -22,7 +22,7 @@ import org.springframework.stereotype.Component;
import java.time.Duration; import java.time.Duration;
/** /**
* 用户行为 侦听器的实现 * 用户行为监听器,用于同步在线状态和登录日志。
* *
* @author Lion Li * @author Lion Li
*/ */
@@ -34,7 +34,7 @@ public class UserActionListener implements SaTokenListener {
private final SysLoginService loginService; private final SysLoginService loginService;
/** /**
* 每次登录时触发 * 登录成功后记录在线信息并写入登录日志。
*/ */
@Override @Override
public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) {
@@ -71,7 +71,7 @@ public class UserActionListener implements SaTokenListener {
} }
/** /**
* 每次注销时触发 * 注销时清理在线缓存。
*/ */
@Override @Override
public void doLogout(String loginType, Object loginId, String tokenValue) { public void doLogout(String loginType, Object loginId, String tokenValue) {
@@ -80,7 +80,7 @@ public class UserActionListener implements SaTokenListener {
} }
/** /**
* 每次被踢下线时触发 * 被踢下线时清理在线缓存。
*/ */
@Override @Override
public void doKickout(String loginType, Object loginId, String tokenValue) { public void doKickout(String loginType, Object loginId, String tokenValue) {
@@ -89,7 +89,7 @@ public class UserActionListener implements SaTokenListener {
} }
/** /**
* 每次被顶下线时触发 * 被顶下线时清理在线缓存。
*/ */
@Override @Override
public void doReplaced(String loginType, Object loginId, String tokenValue) { public void doReplaced(String loginType, Object loginId, String tokenValue) {

View File

@@ -39,7 +39,7 @@ public interface IAuthStrategy {
* *
* @param body 登录对象 * @param body 登录对象
* @param client 授权管理视图对象 * @param client 授权管理视图对象
* @return 登录验证信息 * @return 当前策略完成认证后的登录结果
*/ */
LoginVo login(String body, SysClientVo client); LoginVo login(String body, SysClientVo client);

View File

@@ -136,7 +136,10 @@ public class SysLoginService {
} }
/** /**
* 构建登录用户 * 根据用户视图对象组装登录态上下文。
*
* @param user 用户基础信息
* @return 包含部门、角色、岗位与权限数据的登录用户
*/ */
public LoginUser buildLoginUser(SysUserVo user) { public LoginUser buildLoginUser(SysUserVo user) {
LoginUser loginUser = new LoginUser(); LoginUser loginUser = new LoginUser();
@@ -168,9 +171,10 @@ public class SysLoginService {
} }
/** /**
* 记录登录信息 * 更新用户最近一次登录IP与登录时间。
* *
* @param userId 用户ID * @param userId 用户ID
* @param ip 登录IP
*/ */
public void recordLoginInfo(Long userId, String ip) { public void recordLoginInfo(Long userId, String ip) {
SysUser sysUser = new SysUser(); SysUser sysUser = new SysUser();
@@ -182,7 +186,11 @@ public class SysLoginService {
} }
/** /**
* 登录校验 * 执行登录失败次数校验,并在成功后清空失败计数。
*
* @param loginType 登录类型
* @param username 登录标识
* @param supplier 返回 {@code true} 表示本次认证失败
*/ */
public void checkLogin(LoginType loginType, String username, Supplier<Boolean> supplier) { public void checkLogin(LoginType loginType, String username, Supplier<Boolean> supplier) {
String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username; String errorKey = CacheConstants.PWD_ERR_CNT_KEY + username;

View File

@@ -38,6 +38,8 @@ public class SysRegisterService {
/** /**
* 注册 * 注册
*
* @param registerBody 注册请求参数
*/ */
public void register(RegisterBody registerBody) { public void register(RegisterBody registerBody) {
String username = registerBody.getUsername(); String username = registerBody.getUsername();
@@ -95,7 +97,6 @@ public class SysRegisterService {
* @param username 用户名 * @param username 用户名
* @param status 状态 * @param status 状态
* @param message 消息内容 * @param message 消息内容
* @return
*/ */
private void recordLoginInfo(String username, String status, String message) { private void recordLoginInfo(String username, String status, String message) {
LoginInfoEvent loginInfoEvent = new LoginInfoEvent(); LoginInfoEvent loginInfoEvent = new LoginInfoEvent();

View File

@@ -42,6 +42,13 @@ public class EmailAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService; private final SysLoginService loginService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
/**
* 执行邮箱验证码登录,并按客户端配置生成访问令牌。
*
* @param body 登录请求体
* @param client 当前客户端配置
* @return 登录结果
*/
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class); EmailLoginBody loginBody = JsonUtils.parseObject(body, EmailLoginBody.class);
@@ -72,7 +79,11 @@ public class EmailAuthStrategy implements IAuthStrategy {
} }
/** /**
* 校验邮箱验证码 * 校验邮箱验证码是否存在且匹配。
*
* @param email 邮箱地址
* @param emailCode 用户输入的邮箱验证码
* @return 是否校验通过
*/ */
private boolean validateEmailCode(String email, String emailCode) { private boolean validateEmailCode(String email, String emailCode) {
String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email); String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + email);
@@ -83,6 +94,12 @@ public class EmailAuthStrategy implements IAuthStrategy {
return code.equals(emailCode); return code.equals(emailCode);
} }
/**
* 按邮箱加载可登录用户,并校验是否存在或被停用。
*
* @param email 邮箱地址
* @return 用户信息
*/
private SysUserVo loadUserByEmail(String email) { private SysUserVo loadUserByEmail(String email) {
SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email)); SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getEmail, email));
if (ObjectUtil.isNull(user)) { if (ObjectUtil.isNull(user)) {

View File

@@ -46,6 +46,13 @@ public class PasswordAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService; private final SysLoginService loginService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
/**
* 执行账号密码登录,并按客户端配置生成访问令牌。
*
* @param body 登录请求体
* @param client 当前客户端配置
* @return 登录结果
*/
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class); PasswordLoginBody loginBody = JsonUtils.parseObject(body, PasswordLoginBody.class);
@@ -84,11 +91,11 @@ public class PasswordAuthStrategy implements IAuthStrategy {
} }
/** /**
* 校验验证码 * 校验图形验证码是否有效且匹配。
* *
* @param username 用户名 * @param username 用户名
* @param code 验证码 * @param code 用户输入的验证码
* @param uuid 唯一标识 * @param uuid 验证码缓存标识
*/ */
private void validateCaptcha(String username, String code, String uuid) { private void validateCaptcha(String username, String code, String uuid) {
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, ""); String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + StringUtils.blankToDefault(uuid, "");
@@ -104,6 +111,12 @@ public class PasswordAuthStrategy implements IAuthStrategy {
} }
} }
/**
* 按用户名加载可登录用户,并校验是否存在或被停用。
*
* @param username 用户名
* @return 用户信息
*/
private SysUserVo loadUserByUsername(String username) { private SysUserVo loadUserByUsername(String username) {
SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username)); SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getUserName, username));
if (ObjectUtil.isNull(user)) { if (ObjectUtil.isNull(user)) {

View File

@@ -42,6 +42,13 @@ public class SmsAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService; private final SysLoginService loginService;
private final SysUserMapper userMapper; private final SysUserMapper userMapper;
/**
* 执行短信验证码登录,并按客户端配置生成访问令牌。
*
* @param body 登录请求体
* @param client 当前客户端配置
* @return 登录结果
*/
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class); SmsLoginBody loginBody = JsonUtils.parseObject(body, SmsLoginBody.class);
@@ -72,7 +79,11 @@ public class SmsAuthStrategy implements IAuthStrategy {
} }
/** /**
* 校验短信验证码 * 校验短信验证码是否存在且匹配。
*
* @param phonenumber 手机号
* @param smsCode 用户输入的短信验证码
* @return 是否校验通过
*/ */
private boolean validateSmsCode(String phonenumber, String smsCode) { private boolean validateSmsCode(String phonenumber, String smsCode) {
String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber); String code = RedisUtils.getCacheObject(GlobalConstants.CAPTCHA_CODE_KEY + phonenumber);
@@ -83,6 +94,12 @@ public class SmsAuthStrategy implements IAuthStrategy {
return code.equals(smsCode); return code.equals(smsCode);
} }
/**
* 按手机号加载可登录用户,并校验是否存在或被停用。
*
* @param phonenumber 手机号
* @return 用户信息
*/
private SysUserVo loadUserByPhonenumber(String phonenumber) { private SysUserVo loadUserByPhonenumber(String phonenumber) {
SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber)); SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper<SysUser>().eq(SysUser::getPhonenumber, phonenumber));
if (ObjectUtil.isNull(user)) { if (ObjectUtil.isNull(user)) {

View File

@@ -46,10 +46,11 @@ public class SocialAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService; private final SysLoginService loginService;
/** /**
* 登录-第三方授权登录 * 执行第三方授权登录,并校验授权账号与系统账号的绑定关系。
* *
* @param body 登录信息 * @param body 登录信息
* @param client 客户端信息 * @param client 客户端信息
* @return 登录结果
*/ */
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
@@ -89,6 +90,12 @@ public class SocialAuthStrategy implements IAuthStrategy {
return loginVo; return loginVo;
} }
/**
* 根据用户ID加载用户并校验账号状态是否允许登录。
*
* @param userId 用户ID
* @return 用户信息
*/
private SysUserVo loadUser(Long userId) { private SysUserVo loadUser(Long userId) {
SysUserVo user = userMapper.selectVoById(userId); SysUserVo user = userMapper.selectVoById(userId);
if (ObjectUtil.isNull(user)) { if (ObjectUtil.isNull(user)) {

View File

@@ -38,6 +38,13 @@ public class XcxAuthStrategy implements IAuthStrategy {
private final SysLoginService loginService; private final SysLoginService loginService;
/**
* 执行微信小程序登录,并根据 openid 构建小程序用户登录态。
*
* @param body 登录请求体
* @param client 当前客户端配置
* @return 登录结果
*/
@Override @Override
public LoginVo login(String body, SysClientVo client) { public LoginVo login(String body, SysClientVo client) {
XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class); XcxLoginBody loginBody = JsonUtils.parseObject(body, XcxLoginBody.class);
@@ -93,6 +100,12 @@ public class XcxAuthStrategy implements IAuthStrategy {
return loginVo; return loginVo;
} }
/**
* 按 openid 查询小程序绑定用户。
*
* @param openid 小程序用户唯一标识
* @return 绑定的系统用户信息
*/
private SysUserVo loadUserByOpenid(String openid) { private SysUserVo loadUserByOpenid(String openid) {
// 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户 // 使用 openid 查询绑定用户 如未绑定用户 则根据业务自行处理 例如 创建默认用户
// todo 自行实现 userService.selectUserByOpenid(openid); // todo 自行实现 userService.selectUserByOpenid(openid);

View File

@@ -28,6 +28,8 @@ public class ThreadPoolConfig {
/** /**
* 执行周期性或定时任务 * 执行周期性或定时任务
*
* @return 全局定时任务线程池
*/ */
@Bean(name = "scheduledExecutorService") @Bean(name = "scheduledExecutorService")
protected ScheduledExecutorService scheduledExecutorService() { protected ScheduledExecutorService scheduledExecutorService() {
@@ -85,6 +87,9 @@ public class ThreadPoolConfig {
/** /**
* 打印线程异常信息 * 打印线程异常信息
*
* @param r 已执行的任务
* @param t 任务执行过程中抛出的异常
*/ */
public static void printException(Runnable r, Throwable t) { public static void printException(Runnable r, Throwable t) {
if (t == null && r instanceof Future<?>) { if (t == null && r instanceof Future<?>) {

View File

@@ -20,6 +20,9 @@ public class ValidatorConfig {
/** /**
* 配置校验框架 快速失败模式 * 配置校验框架 快速失败模式
*
* @param messageSource 国际化消息源
* @return 启用快速失败模式的校验器
*/ */
@Bean @Bean
public Validator validator(MessageSource messageSource) { public Validator validator(MessageSource messageSource) {

View File

@@ -1,7 +1,7 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 缓存的key 常量 * Redis 缓存键前缀常量
* *
* @author Lion Li * @author Lion Li
*/ */

View File

@@ -1,7 +1,7 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 缓存组名称常量 * 缓存组名称常量,统一约定缓存名和缓存策略配置格式。
* <p> * <p>
* key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local * key 格式为 cacheNames#ttl#maxIdleTime#maxSize#local
* <p> * <p>

View File

@@ -1,7 +1,7 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 通用常量信息 * 通用基础常量定义。
* *
* @author ruoyi * @author ruoyi
*/ */

View File

@@ -1,7 +1,7 @@
package org.dromara.common.core.constant; package org.dromara.common.core.constant;
/** /**
* 全局的key常量 (业务无关的key) * 全局通用键常量,主要用于业务无关的 Redis Key 前缀定义。
* *
* @author Lion Li * @author Lion Li
*/ */

View File

@@ -65,6 +65,11 @@ public class CompleteTaskDTO implements Serializable {
*/ */
private String ext; private String ext;
/**
* 获取流程变量并自动剔除空值。
*
* @return 流程变量
*/
public Map<String, Object> getVariables() { public Map<String, Object> getVariables() {
if (variables == null) { if (variables == null) {
variables = new HashMap<>(16); variables = new HashMap<>(16);

View File

@@ -7,7 +7,7 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* OSS对象 * OSS 文件简要信息对象
* *
* @author Lion Li * @author Lion Li
*/ */

View File

@@ -7,7 +7,7 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 岗位 * 岗位简要信息对象。
* *
* @author AprilWind * @author AprilWind
*/ */

View File

@@ -7,11 +7,10 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 角色 * 角色简要信息对象。
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class RoleDTO implements Serializable { public class RoleDTO implements Serializable {

View File

@@ -11,7 +11,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
* 启动流程对象 * 启动流程请求对象
* *
* @author may * @author may
*/ */
@@ -46,6 +46,11 @@ public class StartProcessDTO implements Serializable {
*/ */
private FlowInstanceBizExtDTO bizExt; private FlowInstanceBizExtDTO bizExt;
/**
* 获取流程变量并自动剔除空值。
*
* @return 流程变量
*/
public Map<String, Object> getVariables() { public Map<String, Object> getVariables() {
if (variables == null) { if (variables == null) {
variables = new HashMap<>(16); variables = new HashMap<>(16);
@@ -55,6 +60,11 @@ public class StartProcessDTO implements Serializable {
return variables; return variables;
} }
/**
* 获取流程业务扩展信息,为空时返回默认对象。
*
* @return 业务扩展信息
*/
public FlowInstanceBizExtDTO getBizExt() { public FlowInstanceBizExtDTO getBizExt() {
if (ObjectUtil.isNull(bizExt)) { if (ObjectUtil.isNull(bizExt)) {
bizExt = new FlowInstanceBizExtDTO(); bizExt = new FlowInstanceBizExtDTO();

View File

@@ -7,7 +7,7 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 启动流程返回对象 * 启动流程后的返回结果对象
* *
* @author Lion Li * @author Lion Li
*/ */

View File

@@ -29,7 +29,7 @@ public class TaskAssigneeDTO implements Serializable {
private Long total = 0L; private Long total = 0L;
/** /**
* * 受让人列表。
*/ */
private List<TaskHandler> list; private List<TaskHandler> list;
@@ -67,6 +67,9 @@ public class TaskAssigneeDTO implements Serializable {
)).collect(Collectors.toList()); )).collect(Collectors.toList());
} }
/**
* 任务受让人明细对象。
*/
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor

View File

@@ -7,11 +7,10 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 当前在线会话 * 当前在线会话信息对象。
* *
* @author ruoyi * @author ruoyi
*/ */
@Data @Data
@NoArgsConstructor @NoArgsConstructor
public class UserOnlineDTO implements Serializable { public class UserOnlineDTO implements Serializable {

View File

@@ -6,11 +6,10 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* 邮件登录对象 * 邮箱验证码登录请求对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class EmailLoginBody extends LoginBody { public class EmailLoginBody extends LoginBody {

View File

@@ -7,11 +7,10 @@ import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 用户登录对象 * 通用登录请求对象,封装客户端、授权类型和验证码信息。
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
public class LoginBody implements Serializable { public class LoginBody implements Serializable {
@@ -36,7 +35,7 @@ public class LoginBody implements Serializable {
private String code; private String code;
/** /**
* 唯一标识 * 验证码唯一标识
*/ */
private String uuid; private String uuid;

View File

@@ -12,7 +12,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* 登录用户身份权限 * 登录用户上下文对象,保存当前会话的身份权限和终端信息。
* *
* @author Lion Li * @author Lion Li
*/ */
@@ -134,7 +134,9 @@ public class LoginUser implements Serializable {
private String deviceType; private String deviceType;
/** /**
* 获取登录id * 获取 Sa-Token 使用的登录标识。
*
* @return 登录标识
*/ */
public String getLoginId() { public String getLoginId() {
if (userType == null) { if (userType == null) {

View File

@@ -5,11 +5,10 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* 短信登录对象 * 短信验证码登录请求对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class SmsLoginBody extends LoginBody { public class SmsLoginBody extends LoginBody {

View File

@@ -5,11 +5,10 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* 三方登录对象 * 三方平台登录绑定请求对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class SocialLoginBody extends LoginBody { public class SocialLoginBody extends LoginBody {

View File

@@ -5,11 +5,10 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
/** /**
* 三方登录对象 * 小程序登录请求对象
* *
* @author Lion Li * @author Lion Li
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class XcxLoginBody extends LoginBody { public class XcxLoginBody extends LoginBody {

View File

@@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
/** /**
* 小程序登录用户身份权限 * 小程序登录用户上下文对象。
* *
* @author Lion Li * @author Lion Li
*/ */
@@ -20,7 +20,7 @@ public class XcxLoginUser extends LoginUser {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* openid * 小程序 openid
*/ */
private String openid; private String openid;

View File

@@ -13,7 +13,7 @@ import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* 业务状态枚举 * 流程业务状态枚举,统一定义单据在审批流转中的状态。
* *
* @author may * @author may
*/ */

View File

@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/** /**
* 设备类型 * 登录设备类型枚举。
* *
* @author Lion Li * @author Lion Li
*/ */

View File

@@ -4,7 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
/** /**
* 登录类型 * 登录类型枚举,同时维护不同登录方式对应的重试提示配置。
* *
* @author Lion Li * @author Lion Li
*/ */

View File

@@ -24,7 +24,14 @@ public enum UserStatus {
*/ */
DELETED("2", "删除"); DELETED("2", "删除");
/**
* 状态编码。
*/
private final String code; private final String code;
/**
* 状态说明。
*/
private final String info; private final String info;
} }

View File

@@ -28,6 +28,12 @@ public enum UserType {
*/ */
private final String userType; private final String userType;
/**
* 根据字符串内容匹配用户类型。
*
* @param str 待匹配字符串
* @return 用户类型
*/
public static UserType getUserType(String str) { public static UserType getUserType(String str) {
for (UserType value : values()) { for (UserType value : values()) {
if (StringUtils.contains(str, value.getUserType())) { if (StringUtils.contains(str, value.getUserType())) {

View File

@@ -9,7 +9,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
/** /**
* 业务异常支持占位符 {} * 通用业务异常支持使用占位符拼接错误信息。
* *
* @author ruoyi * @author ruoyi
*/ */
@@ -37,15 +37,32 @@ public final class ServiceException extends RuntimeException {
*/ */
private String detailMessage; private String detailMessage;
/**
* 使用错误消息构造业务异常。
*
* @param message 错误消息
*/
public ServiceException(String message) { public ServiceException(String message) {
this.message = message; this.message = message;
} }
/**
* 使用错误消息和错误码构造业务异常。
*
* @param message 错误消息
* @param code 错误码
*/
public ServiceException(String message, Integer code) { public ServiceException(String message, Integer code) {
this.message = message; this.message = message;
this.code = code; this.code = code;
} }
/**
* 使用占位符参数格式化错误消息。
*
* @param message 模板消息
* @param args 参数
*/
public ServiceException(String message, Object... args) { public ServiceException(String message, Object... args) {
this.message = StrFormatter.format(message, args); this.message = StrFormatter.format(message, args);
} }
@@ -55,11 +72,23 @@ public final class ServiceException extends RuntimeException {
return message; return message;
} }
/**
* 设置错误消息并返回当前异常对象。
*
* @param message 错误消息
* @return 当前异常对象
*/
public ServiceException setMessage(String message) { public ServiceException setMessage(String message) {
this.message = message; this.message = message;
return this; return this;
} }
/**
* 设置错误明细并返回当前异常对象。
*
* @param detailMessage 错误明细
* @return 当前异常对象
*/
public ServiceException setDetailMessage(String detailMessage) { public ServiceException setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage; this.detailMessage = detailMessage;
return this; return this;

View File

@@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
/** /**
* sse 特制异常 * SSE 场景专用异常
* *
* @author LionLi * @author LionLi
*/ */
@@ -36,10 +36,21 @@ public final class SseException extends RuntimeException {
*/ */
private String detailMessage; private String detailMessage;
/**
* 使用错误消息构造 SSE 异常。
*
* @param message 错误消息
*/
public SseException(String message) { public SseException(String message) {
this.message = message; this.message = message;
} }
/**
* 使用错误消息和错误码构造 SSE 异常。
*
* @param message 错误消息
* @param code 错误码
*/
public SseException(String message, Integer code) { public SseException(String message, Integer code) {
this.message = message; this.message = message;
this.code = code; this.code = code;
@@ -50,11 +61,23 @@ public final class SseException extends RuntimeException {
return message; return message;
} }
/**
* 设置错误消息并返回当前异常对象。
*
* @param message 错误消息
* @return 当前异常对象
*/
public SseException setMessage(String message) { public SseException setMessage(String message) {
this.message = message; this.message = message;
return this; return this;
} }
/**
* 设置错误明细并返回当前异常对象。
*
* @param detailMessage 错误明细
* @return 当前异常对象
*/
public SseException setDetailMessage(String detailMessage) { public SseException setDetailMessage(String detailMessage) {
this.detailMessage = detailMessage; this.detailMessage = detailMessage;
return this; return this;

View File

@@ -10,7 +10,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial; import java.io.Serial;
/** /**
* 基础异常 * 基础国际化异常,支持按错误码解析最终提示信息。
* *
* @author ruoyi * @author ruoyi
*/ */
@@ -43,18 +43,42 @@ public class BaseException extends RuntimeException {
*/ */
private String defaultMessage; private String defaultMessage;
/**
* 使用模块、错误码和参数构造异常。
*
* @param module 所属模块
* @param code 错误码
* @param args 参数
*/
public BaseException(String module, String code, Object[] args) { public BaseException(String module, String code, Object[] args) {
this(module, code, args, null); this(module, code, args, null);
} }
/**
* 使用模块和默认消息构造异常。
*
* @param module 所属模块
* @param defaultMessage 默认消息
*/
public BaseException(String module, String defaultMessage) { public BaseException(String module, String defaultMessage) {
this(module, null, null, defaultMessage); this(module, null, null, defaultMessage);
} }
/**
* 使用错误码和参数构造异常。
*
* @param code 错误码
* @param args 参数
*/
public BaseException(String code, Object[] args) { public BaseException(String code, Object[] args) {
this(null, code, args, null); this(null, code, args, null);
} }
/**
* 使用默认消息构造异常。
*
* @param defaultMessage 默认消息
*/
public BaseException(String defaultMessage) { public BaseException(String defaultMessage) {
this(null, null, null, defaultMessage); this(null, null, null, defaultMessage);
} }

View File

@@ -50,6 +50,7 @@ public interface WorkflowService {
* 获取流程变量 * 获取流程变量
* *
* @param instanceId 流程实例id * @param instanceId 流程实例id
* @return 流程变量详情
*/ */
Map<String, Object> instanceVariable(Long instanceId); Map<String, Object> instanceVariable(Long instanceId);
@@ -65,7 +66,7 @@ public interface WorkflowService {
* 启动流程 * 启动流程
* *
* @param startProcess 参数 * @param startProcess 参数
* @return 结果 * @return 启动后的流程实例与首任务信息
*/ */
StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess); StartProcessReturnDTO startWorkFlow(StartProcessDTO startProcess);
@@ -75,7 +76,7 @@ public interface WorkflowService {
* completeTask.getVariables().put("ignore", true); * completeTask.getVariables().put("ignore", true);
* *
* @param completeTask 参数 * @param completeTask 参数
* @return 结果 * @return 办理成功返回 {@code true}
*/ */
boolean completeTask(CompleteTaskDTO completeTask); boolean completeTask(CompleteTaskDTO completeTask);
@@ -84,7 +85,7 @@ public interface WorkflowService {
* *
* @param taskId 任务ID * @param taskId 任务ID
* @param message 办理意见 * @param message 办理意见
* @return 结果 * @return 办理成功返回 {@code true}
*/ */
boolean completeTask(Long taskId, String message); boolean completeTask(Long taskId, String message);
@@ -92,7 +93,7 @@ public interface WorkflowService {
* 启动流程并办理第一个任务 * 启动流程并办理第一个任务
* *
* @param startProcess 参数 * @param startProcess 参数
* @return 结果 * @return 首节点办理成功返回 {@code true}
*/ */
boolean startCompleteTask(StartProcessDTO startProcess); boolean startCompleteTask(StartProcessDTO startProcess);
} }

View File

@@ -44,6 +44,12 @@ public class SpringDocConfig {
private final ServerProperties serverProperties; private final ServerProperties serverProperties;
/**
* 构建基础 OpenAPI 文档对象。
*
* @param properties SpringDoc 配置
* @return OpenAPI 对象
*/
@Bean @Bean
@ConditionalOnMissingBean(OpenAPI.class) @ConditionalOnMissingBean(OpenAPI.class)
public OpenAPI openApi(SpringDocProperties properties) { public OpenAPI openApi(SpringDocProperties properties) {
@@ -68,6 +74,12 @@ public class SpringDocConfig {
return openApi; return openApi;
} }
/**
* 将自定义文档信息配置转换为 OpenAPI Info。
*
* @param infoProperties 文档信息配置
* @return Info 对象
*/
private Info convertInfo(SpringDocProperties.InfoProperties infoProperties) { private Info convertInfo(SpringDocProperties.InfoProperties infoProperties) {
Info info = new Info(); Info info = new Info();
info.setTitle(infoProperties.getTitle()); info.setTitle(infoProperties.getTitle());

View File

@@ -162,6 +162,7 @@ public class SaTokenSecurityMetadata {
/** /**
* 重写mode的获取方法返回符号而非文字 * 重写mode的获取方法返回符号而非文字
*
* @return AND→&OR→|,默认→& * @return AND→&OR→|,默认→&
*/ */
public String getModeSymbol() { public String getModeSymbol() {

View File

@@ -15,6 +15,7 @@ public interface JavadocResolver extends Comparable<JavadocResolver>, Ordered {
/** /**
* 检查解析器是否支持解析 HandlerMethod * 检查解析器是否支持解析 HandlerMethod
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @return 是否支持解析 * @return 是否支持解析
*/ */
@@ -22,6 +23,7 @@ public interface JavadocResolver extends Comparable<JavadocResolver>, Ordered {
/** /**
* 执行解析并返回解析到的 Javadoc 内容 * 执行解析并返回解析到的 Javadoc 内容
*
* @param handlerMethod 处理器方法 * @param handlerMethod 处理器方法
* @param operation Swagger Operation实例 * @param operation Swagger Operation实例
* @return 解析到的 Javadoc 内容 * @return 解析到的 Javadoc 内容
@@ -29,7 +31,9 @@ public interface JavadocResolver extends Comparable<JavadocResolver>, Ordered {
String resolve(HandlerMethod handlerMethod, Operation operation); String resolve(HandlerMethod handlerMethod, Operation operation);
/** /**
* 获取解析器优先级 * 获取解析器优先级
*
* @return 优先级
*/ */
default int getOrder() { default int getOrder() {
return Ordered.LOWEST_PRECEDENCE; return Ordered.LOWEST_PRECEDENCE;
@@ -44,6 +48,12 @@ public interface JavadocResolver extends Comparable<JavadocResolver>, Ordered {
return this.getClass().getSimpleName(); return this.getClass().getSimpleName();
} }
/**
* 比较解析器执行顺序。
*
* @param o 其他解析器
* @return 比较结果
*/
@Override @Override
default int compareTo(@NotNull JavadocResolver o) { default int compareTo(@NotNull JavadocResolver o) {
return Integer.compare(getOrder(), o.getOrder()); return Integer.compare(getOrder(), o.getOrder());

View File

@@ -54,23 +54,53 @@ public class SaTokenAnnotationMetadataJavadocResolver extends AbstractMetadataJa
this(DEFAULT_METADATA_PROVIDER); this(DEFAULT_METADATA_PROVIDER);
} }
/**
* 使用自定义元数据提供者创建解析器。
*
* @param metadataProvider 元数据提供者
*/
public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider) { public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider) {
super(metadataProvider); super(metadataProvider);
} }
/**
* 使用指定顺序创建解析器。
*
* @param order 顺序值
*/
public SaTokenAnnotationMetadataJavadocResolver(int order) { public SaTokenAnnotationMetadataJavadocResolver(int order) {
this(DEFAULT_METADATA_PROVIDER,order); this(DEFAULT_METADATA_PROVIDER,order);
} }
/**
* 使用自定义元数据提供者和顺序创建解析器。
*
* @param metadataProvider 元数据提供者
* @param order 顺序值
*/
public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider, int order) { public SaTokenAnnotationMetadataJavadocResolver(Supplier<SaTokenSecurityMetadata> metadataProvider, int order) {
super(metadataProvider,order); super(metadataProvider,order);
} }
/**
* 判断当前方法是否需要由该解析器处理。
*
* @param handlerMethod Handler 方法
* @return 是否支持
*/
@Override @Override
public boolean supports(HandlerMethod handlerMethod) { public boolean supports(HandlerMethod handlerMethod) {
return hasAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS) || hasAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS) || hasAnnotation(handlerMethod, SA_IGNORE_CLASS); return hasAnnotation(handlerMethod, SA_CHECK_ROLE_CLASS) || hasAnnotation(handlerMethod, SA_CHECK_PERMISSION_CLASS) || hasAnnotation(handlerMethod, SA_IGNORE_CLASS);
} }
/**
* 解析 Sa-Token 注解并转换为文档说明。
*
* @param handlerMethod Handler 方法
* @param operation OpenAPI 操作对象
* @param metadata 权限元数据
* @return Markdown 描述
*/
@Override @Override
public String resolve(HandlerMethod handlerMethod, Operation operation, SaTokenSecurityMetadata metadata) { public String resolve(HandlerMethod handlerMethod, Operation operation, SaTokenSecurityMetadata metadata) {
// 检查是否忽略校验 // 检查是否忽略校验

View File

@@ -124,6 +124,7 @@ public class OpenApiHandler extends OpenAPIService {
* @param openApiBuilderCustomizers the open api builder customisers * @param openApiBuilderCustomizers the open api builder customisers
* @param serverBaseUrlCustomizers the server base url customizers * @param serverBaseUrlCustomizers the server base url customizers
* @param javadocProvider the javadoc provider * @param javadocProvider the javadoc provider
* @param javadocResolvers Javadoc 解析器列表
*/ */
public OpenApiHandler(Optional<OpenAPI> openAPI, SecurityService securityParser, public OpenApiHandler(Optional<OpenAPI> openAPI, SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
@@ -152,6 +153,15 @@ public class OpenApiHandler extends OpenAPIService {
TypeNameResolver.std.setUseFqn(true); TypeNameResolver.std.setUseFqn(true);
} }
/**
* 构建接口标签、权限描述与方法摘要。
*
* @param handlerMethod Handler 方法
* @param operation OpenAPI 操作对象
* @param openAPI OpenAPI 文档对象
* @param locale 当前语言环境
* @return 处理后的操作对象
*/
@Override @Override
public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) { public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) {
@@ -247,6 +257,14 @@ public class OpenApiHandler extends OpenAPIService {
return operation; return operation;
} }
/**
* 从方法注解中提取标签信息。
*
* @param method 方法对象
* @param tags 标签集合
* @param tagsStr 标签名称集合
* @param locale 当前语言环境
*/
private void buildTagsFromMethod(Method method, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr, Locale locale) { private void buildTagsFromMethod(Method method, Set<io.swagger.v3.oas.models.tags.Tag> tags, Set<String> tagsStr, Locale locale) {
// method tags // method tags
Set<Tags> tagsSet = AnnotatedElementUtils Set<Tags> tagsSet = AnnotatedElementUtils
@@ -261,6 +279,13 @@ public class OpenApiHandler extends OpenAPIService {
} }
} }
/**
* 将注解标签转换并合并到 OpenAPI 标签集合。
*
* @param sourceTags 注解标签列表
* @param tags OpenAPI 标签集合
* @param locale 当前语言环境
*/
private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<io.swagger.v3.oas.models.tags.Tag> tags, Locale locale) { private void addTags(List<io.swagger.v3.oas.annotations.tags.Tag> sourceTags, Set<io.swagger.v3.oas.models.tags.Tag> tags, Locale locale) {
Optional<Set<io.swagger.v3.oas.models.tags.Tag>> optionalTagSet = AnnotationsUtils Optional<Set<io.swagger.v3.oas.models.tags.Tag>> optionalTagSet = AnnotationsUtils
.getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true); .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true);

View File

@@ -28,6 +28,11 @@ import java.util.TimeZone;
@AutoConfiguration(before = JacksonAutoConfiguration.class) @AutoConfiguration(before = JacksonAutoConfiguration.class)
public class JacksonConfig { public class JacksonConfig {
/**
* 注册 Jackson 序列化与反序列化模块。
*
* @return Jackson 模块
*/
@Bean @Bean
public SimpleModule registerJavaTimeModule() { public SimpleModule registerJavaTimeModule() {
// 全局配置序列化返回 JSON 处理 // 全局配置序列化返回 JSON 处理
@@ -43,6 +48,11 @@ public class JacksonConfig {
return module; return module;
} }
/**
* 初始化 Jackson 构建器默认配置。
*
* @return Jackson 构建器自定义器
*/
@Bean @Bean
public JsonMapperBuilderCustomizer jsonInitCustomizer() { public JsonMapperBuilderCustomizer jsonInitCustomizer() {
return builder -> { return builder -> {

View File

@@ -24,6 +24,11 @@ public class JsonUtils {
private static final JsonMapper JSON_MAPPER = SpringUtils.getBean(JsonMapper.class); private static final JsonMapper JSON_MAPPER = SpringUtils.getBean(JsonMapper.class);
/**
* 获取全局 JsonMapper 实例。
*
* @return JsonMapper
*/
public static JsonMapper getJsonMapper() { public static JsonMapper getJsonMapper() {
return JSON_MAPPER; return JSON_MAPPER;
} }

View File

@@ -46,7 +46,10 @@ public class LogAspect {
private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>(); private static final ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
/** /**
* 处理请求前执行 * 在目标方法执行前启动耗时统计。
*
* @param joinPoint 切点
* @param controllerLog 日志注解
*/ */
@Before(value = "@annotation(controllerLog)") @Before(value = "@annotation(controllerLog)")
public void doBefore(JoinPoint joinPoint, Log controllerLog) { public void doBefore(JoinPoint joinPoint, Log controllerLog) {
@@ -56,9 +59,11 @@ public class LogAspect {
} }
/** /**
* 处理完请求后执行 * 在目标方法正常返回后记录操作日志。
* *
* @param joinPoint 切点 * @param joinPoint 切点
* @param controllerLog 日志注解
* @param jsonResult 返回结果
*/ */
@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
@@ -66,9 +71,10 @@ public class LogAspect {
} }
/** /**
* 拦截异常操作 * 在目标方法抛出异常后记录操作日志。
* *
* @param joinPoint 切点 * @param joinPoint 切点
* @param controllerLog 日志注解
* @param e 异常 * @param e 异常
*/ */
@AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")
@@ -76,6 +82,14 @@ public class LogAspect {
handleLog(joinPoint, controllerLog, e, null); handleLog(joinPoint, controllerLog, e, null);
} }
/**
* 组装并发布操作日志事件。
*
* @param joinPoint 切点
* @param controllerLog 日志注解
* @param e 异常信息
* @param jsonResult 返回结果
*/
protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) {
try { try {
@@ -119,9 +133,11 @@ public class LogAspect {
/** /**
* 获取注解中对方法的描述信息 用于Controller层注解 * 获取注解中对方法的描述信息 用于Controller层注解
* *
* @param joinPoint 切点
* @param log 日志 * @param log 日志
* @param operLog 操作日志 * @param operLog 操作日志
* @throws Exception * @param jsonResult 返回结果
* @throws Exception 异常
*/ */
public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception { public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperLogEvent operLog, Object jsonResult) throws Exception {
// 设置action动作 // 设置action动作
@@ -144,7 +160,9 @@ public class LogAspect {
/** /**
* 获取请求的参数放到log中 * 获取请求的参数放到log中
* *
* @param joinPoint 切点
* @param operLog 操作日志 * @param operLog 操作日志
* @param excludeParamNames 排除参数名
* @throws Exception 异常 * @throws Exception 异常
*/ */
private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception { private void setRequestValue(JoinPoint joinPoint, OperLogEvent operLog, String[] excludeParamNames) throws Exception {
@@ -161,7 +179,11 @@ public class LogAspect {
} }
/** /**
* 参数拼装 * 将方法参数序列化为日志字符串。
*
* @param paramsArray 参数数组
* @param excludeParamNames 排除字段名
* @return 参数字符串
*/ */
private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) { private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) {
StringJoiner params = new StringJoiner(" "); StringJoiner params = new StringJoiner(" ");

View File

@@ -31,7 +31,9 @@ public class MailUtils {
private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class);
/** /**
* 获取邮件发送实例 * 获取默认邮件账户配置。
*
* @return 邮件账户配置
*/ */
public static MailAccount getMailAccount() { public static MailAccount getMailAccount() {
return ACCOUNT; return ACCOUNT;
@@ -40,8 +42,10 @@ public class MailUtils {
/** /**
* 获取邮件发送实例 (自定义发送人以及授权码) * 获取邮件发送实例 (自定义发送人以及授权码)
* *
* @param from 发送人
* @param user 发送人 * @param user 发送人
* @param pass 授权码 * @param pass 授权码
* @return 邮件账户配置
*/ */
public static MailAccount getMailAccount(String from, String user, String pass) { public static MailAccount getMailAccount(String from, String user, String pass) {
ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom())); ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom()));

View File

@@ -21,7 +21,11 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
public class MybatisExceptionHandler { public class MybatisExceptionHandler {
/** /**
* 主键或UNIQUE索引数据重复异常 * 处理主键或唯一索引冲突异常
*
* @param e 异常信息
* @param request 当前请求
* @return 统一失败响应
*/ */
@ExceptionHandler(DuplicateKeyException.class) @ExceptionHandler(DuplicateKeyException.class)
public R<Void> handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { public R<Void> handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) {
@@ -31,7 +35,11 @@ public class MybatisExceptionHandler {
} }
/** /**
* Mybatis系统异常 通用处理 * 处理 MyBatis 系统异常
*
* @param e 异常信息
* @param request 当前请求
* @return 统一失败响应
*/ */
@ExceptionHandler(MyBatisSystemException.class) @ExceptionHandler(MyBatisSystemException.class)
public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) { public R<Void> handleCannotFindDataSourceException(MyBatisSystemException e, HttpServletRequest request) {

View File

@@ -74,7 +74,9 @@ public class DataBaseHelper {
} }
/** /**
* 获取当前加载的数据库名 * 获取当前注册的数据源名称列表。
*
* @return 数据源名称列表
*/ */
public static List<String> getDataSourceNameList() { public static List<String> getDataSourceNameList() {
return new ArrayList<>(DS.getDataSources().keySet()); return new ArrayList<>(DS.getDataSources().keySet());

View File

@@ -46,7 +46,7 @@ public class DataPermissionHelper {
/** /**
* 设置当前执行mapper权限注解 * 设置当前执行mapper权限注解
* *
* @param dataPermission 数据权限注解 * @param dataPermission 数据权限注解
*/ */
public static void setPermission(DataPermission dataPermission) { public static void setPermission(DataPermission dataPermission) {
PERMISSION_CACHE.set(dataPermission); PERMISSION_CACHE.set(dataPermission);
@@ -82,10 +82,20 @@ public class DataPermissionHelper {
context.put(key, value); context.put(key, value);
} }
/**
* 获取当前数据权限访问控制对象。
*
* @return 访问控制对象
*/
public static DataPermissionAccess getAccess() { public static DataPermissionAccess getAccess() {
return getVariable(ACCESS_KEY); return getVariable(ACCESS_KEY);
} }
/**
* 设置当前数据权限访问控制对象。
*
* @param access 访问控制对象
*/
public static void setAccess(DataPermissionAccess access) { public static void setAccess(DataPermissionAccess access) {
setVariable(ACCESS_KEY, access); setVariable(ACCESS_KEY, access);
} }
@@ -109,6 +119,11 @@ public class DataPermissionHelper {
throw new NullPointerException("data permission context type exception"); throw new NullPointerException("data permission context type exception");
} }
/**
* 获取当前忽略策略。
*
* @return 忽略策略
*/
private static IgnoreStrategy getIgnoreStrategy() { private static IgnoreStrategy getIgnoreStrategy() {
Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL")); Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL"));
if (ignoreStrategyLocal instanceof ThreadLocal<?> IGNORE_STRATEGY_LOCAL) { if (ignoreStrategyLocal instanceof ThreadLocal<?> IGNORE_STRATEGY_LOCAL) {
@@ -173,6 +188,7 @@ public class DataPermissionHelper {
* 在忽略数据权限中执行 * 在忽略数据权限中执行
* *
* @param handle 处理执行方法 * @param handle 处理执行方法
* @return 执行结果
*/ */
public static <T> T ignore(Supplier<T> handle) { public static <T> T ignore(Supplier<T> handle) {
enableIgnore(); enableIgnore();

View File

@@ -547,6 +547,9 @@ public class OssClient {
/** /**
* 检查配置是否相同 * 检查配置是否相同
*
* @param properties OSS 配置
* @return 是否与当前客户端配置一致
*/ */
public boolean checkPropertiesSame(OssProperties properties) { public boolean checkPropertiesSame(OssProperties properties) {
return this.properties.equals(properties); return this.properties.equals(properties);

View File

@@ -10,6 +10,12 @@ import java.io.IOException;
@FunctionalInterface @FunctionalInterface
public interface WriteOutSubscriber<T> { public interface WriteOutSubscriber<T> {
/**
* 将订阅到的数据写出到目标对象。
*
* @param out 写出目标
* @throws IOException 写出异常
*/
void writeTo(T out) throws IOException; void writeTo(T out) throws IOException;
} }

View File

@@ -12,6 +12,11 @@ public class OssException extends RuntimeException {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/**
* 创建 OSS 业务异常。
*
* @param msg 异常消息
*/
public OssException(String msg) { public OssException(String msg) {
super(msg); super(msg);
} }

View File

@@ -27,7 +27,9 @@ public class OssFactory {
private static final ReentrantLock LOCK = new ReentrantLock(); private static final ReentrantLock LOCK = new ReentrantLock();
/** /**
* 获取默认实例 * 获取默认 OSS 客户端实例
*
* @return 默认 OSS 客户端
*/ */
public static OssClient instance() { public static OssClient instance() {
// 获取redis 默认类型 // 获取redis 默认类型
@@ -39,7 +41,10 @@ public class OssFactory {
} }
/** /**
* 根据类型获取实例 * 根据配置键获取 OSS 客户端实例
*
* @param configKey 配置键
* @return OSS 客户端
*/ */
public static OssClient instance(String configKey) { public static OssClient instance(String configKey) {
String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey); String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey);

View File

@@ -14,13 +14,21 @@ public class KeyPrefixHandler implements NameMapper {
private final String keyPrefix; private final String keyPrefix;
/**
* 创建 Redis Key 前缀处理器。
*
* @param keyPrefix Key 前缀
*/
public KeyPrefixHandler(String keyPrefix) { public KeyPrefixHandler(String keyPrefix) {
//前缀为空 则返回空前缀 //前缀为空 则返回空前缀
this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":"; this.keyPrefix = StringUtils.isBlank(keyPrefix) ? "" : keyPrefix + ":";
} }
/** /**
* 增加前缀 * 为原始 Key 增加前缀
*
* @param name 原始 Key
* @return 带前缀的 Key
*/ */
@Override @Override
public String map(String name) { public String map(String name) {
@@ -34,7 +42,10 @@ public class KeyPrefixHandler implements NameMapper {
} }
/** /**
* 去除前缀 * 去除 Key 前缀
*
* @param name 带前缀的 Key
* @return 原始 Key
*/ */
@Override @Override
public String unmap(String name) { public String unmap(String name) {

View File

@@ -18,7 +18,11 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
public class RedisExceptionHandler { public class RedisExceptionHandler {
/** /**
* 分布式锁Lock4j异常 * 处理 Lock4j 分布式锁获取失败异常
*
* @param e 异常信息
* @param request 当前请求
* @return 统一失败响应
*/ */
@ExceptionHandler(LockFailureException.class) @ExceptionHandler(LockFailureException.class)
public R<Void> handleLockFailureException(LockFailureException e, HttpServletRequest request) { public R<Void> handleLockFailureException(LockFailureException e, HttpServletRequest request) {

View File

@@ -18,31 +18,66 @@ public class CaffeineCacheDecorator implements Cache {
private final String name; private final String name;
private final Cache cache; private final Cache cache;
/**
* 创建带 Caffeine 一级缓存的缓存装饰器。
*
* @param name 缓存名称
* @param cache 被装饰的缓存实例
*/
public CaffeineCacheDecorator(String name, Cache cache) { public CaffeineCacheDecorator(String name, Cache cache) {
this.name = name; this.name = name;
this.cache = cache; this.cache = cache;
} }
/**
* 获取缓存名称。
*
* @return 缓存名称
*/
@Override @Override
public String getName() { public String getName() {
return name; return name;
} }
/**
* 获取底层原生缓存对象。
*
* @return 原生缓存对象
*/
@Override @Override
public Object getNativeCache() { public Object getNativeCache() {
return cache.getNativeCache(); return cache.getNativeCache();
} }
/**
* 构造 Caffeine 一级缓存使用的唯一键。
*
* @param key 缓存键
* @return 唯一键
*/
public String getUniqueKey(Object key) { public String getUniqueKey(Object key) {
return name + ":" + key; return name + ":" + key;
} }
/**
* 获取缓存值。
*
* @param key 缓存键
* @return 缓存值包装对象
*/
@Override @Override
public ValueWrapper get(Object key) { public ValueWrapper get(Object key) {
Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key)); Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key));
return (ValueWrapper) o; return (ValueWrapper) o;
} }
/**
* 按指定类型获取缓存值。
*
* @param key 缓存键
* @param type 值类型
* @return 缓存值
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T> T get(Object key, Class<T> type) { public <T> T get(Object key, Class<T> type) {
@@ -50,23 +85,47 @@ public class CaffeineCacheDecorator implements Cache {
return (T) o; return (T) o;
} }
/**
* 写入缓存值,并清理本地一级缓存。
*
* @param key 缓存键
* @param value 缓存值
*/
@Override @Override
public void put(Object key, Object value) { public void put(Object key, Object value) {
CAFFEINE.invalidate(getUniqueKey(key)); CAFFEINE.invalidate(getUniqueKey(key));
cache.put(key, value); cache.put(key, value);
} }
/**
* 当键不存在时写入缓存值。
*
* @param key 缓存键
* @param value 缓存值
* @return 原缓存值包装对象
*/
@Override @Override
public ValueWrapper putIfAbsent(Object key, Object value) { public ValueWrapper putIfAbsent(Object key, Object value) {
CAFFEINE.invalidate(getUniqueKey(key)); CAFFEINE.invalidate(getUniqueKey(key));
return cache.putIfAbsent(key, value); return cache.putIfAbsent(key, value);
} }
/**
* 删除缓存值。
*
* @param key 缓存键
*/
@Override @Override
public void evict(Object key) { public void evict(Object key) {
evictIfPresent(key); evictIfPresent(key);
} }
/**
* 删除缓存值并返回是否删除成功。
*
* @param key 缓存键
* @return 是否删除成功
*/
@Override @Override
public boolean evictIfPresent(Object key) { public boolean evictIfPresent(Object key) {
boolean b = cache.evictIfPresent(key); boolean b = cache.evictIfPresent(key);
@@ -76,17 +135,32 @@ public class CaffeineCacheDecorator implements Cache {
return b; return b;
} }
/**
* 清空缓存。
*/
@Override @Override
public void clear() { public void clear() {
CAFFEINE.invalidateAll(); CAFFEINE.invalidateAll();
cache.clear(); cache.clear();
} }
/**
* 使缓存整体失效。
*
* @return 是否失效成功
*/
@Override @Override
public boolean invalidate() { public boolean invalidate() {
return cache.invalidate(); return cache.invalidate();
} }
/**
* 获取缓存值,不存在时通过回调加载。
*
* @param key 缓存键
* @param valueLoader 回调加载器
* @return 缓存值
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T> T get(Object key, Callable<T> valueLoader) { public <T> T get(Object key, Callable<T> valueLoader) {

View File

@@ -56,7 +56,7 @@ public class PlusSpringCacheManager implements CacheManager {
ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>(); ConcurrentMap<String, Cache> instanceMap = new ConcurrentHashMap<>();
/** /**
* Creates CacheManager supplied by Redisson instance * 创建基于 Redisson 的缓存管理器。
*/ */
public PlusSpringCacheManager() { public PlusSpringCacheManager() {
} }
@@ -113,10 +113,21 @@ public class PlusSpringCacheManager implements CacheManager {
this.configMap = (Map<String, CacheConfig>) config; this.configMap = (Map<String, CacheConfig>) config;
} }
/**
* 创建默认缓存配置。
*
* @return 默认缓存配置
*/
protected CacheConfig createDefaultConfig() { protected CacheConfig createDefaultConfig() {
return new CacheConfig(); return new CacheConfig();
} }
/**
* 按缓存名称获取缓存实例,并支持通过扩展参数动态设置 TTL、最大空闲时间和容量。
*
* @param name 缓存名称,支持 `cacheName#ttl#maxIdle#maxSize#local` 格式
* @return 缓存实例
*/
@Override @Override
public Cache getCache(String name) { public Cache getCache(String name) {
// 重写 cacheName 支持多参数 // 重写 cacheName 支持多参数
@@ -158,6 +169,14 @@ public class PlusSpringCacheManager implements CacheManager {
return createMapCache(name, config, local); return createMapCache(name, config, local);
} }
/**
* 创建普通 Map 类型缓存。
*
* @param name 缓存名称
* @param config 缓存配置
* @param local 是否启用本地一级缓存
* @return 缓存实例
*/
private Cache createMap(String name, CacheConfig config, int local) { private Cache createMap(String name, CacheConfig config, int local) {
RMap<Object, Object> map = RedisUtils.getClient().getMap(name); RMap<Object, Object> map = RedisUtils.getClient().getMap(name);
@@ -175,6 +194,14 @@ public class PlusSpringCacheManager implements CacheManager {
return cache; return cache;
} }
/**
* 创建带过期策略的 MapCache 类型缓存。
*
* @param name 缓存名称
* @param config 缓存配置
* @param local 是否启用本地一级缓存
* @return 缓存实例
*/
private Cache createMapCache(String name, CacheConfig config, int local) { private Cache createMapCache(String name, CacheConfig config, int local) {
RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name); RMapCache<Object, Object> map = RedisUtils.getClient().getMapCache(name);
@@ -197,6 +224,11 @@ public class PlusSpringCacheManager implements CacheManager {
return cache; return cache;
} }
/**
* 获取当前缓存名称集合。
*
* @return 缓存名称集合
*/
@Override @Override
public Collection<String> getCacheNames() { public Collection<String> getCacheNames() {
return Collections.unmodifiableSet(configMap.keySet()); return Collections.unmodifiableSet(configMap.keySet());

View File

@@ -22,6 +22,7 @@ public class CacheUtils {
* *
* @param cacheNames 缓存组名称 * @param cacheNames 缓存组名称
* @param key 缓存key * @param key 缓存key
* @return 缓存值
*/ */
public static <T> T get(String cacheNames, Object key) { public static <T> T get(String cacheNames, Object key) {
Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key); Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);

View File

@@ -25,7 +25,9 @@ public class QueueUtils {
/** /**
* 获取客户端实例 * 获取 Redisson 客户端实例
*
* @return Redisson 客户端
*/ */
public static RedissonClient getClient() { public static RedissonClient getClient() {
return CLIENT; return CLIENT;
@@ -36,6 +38,7 @@ public class QueueUtils {
* *
* @param queueName 队列名 * @param queueName 队列名
* @param data 数据 * @param data 数据
* @return 是否添加成功
*/ */
public static <T> boolean addQueueObject(String queueName, T data) { public static <T> boolean addQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName); RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
@@ -46,6 +49,7 @@ public class QueueUtils {
* 通用获取一个队列数据 没有数据返回 null(不支持延迟队列) * 通用获取一个队列数据 没有数据返回 null(不支持延迟队列)
* *
* @param queueName 队列名 * @param queueName 队列名
* @return 队列数据
*/ */
public static <T> T getQueueObject(String queueName) { public static <T> T getQueueObject(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName); RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
@@ -53,7 +57,11 @@ public class QueueUtils {
} }
/** /**
* 通用删除队列数据(不支持延迟队列) * 删除普通队列中的指定数据。
*
* @param queueName 队列名
* @param data 数据
* @return 是否删除成功
*/ */
public static <T> boolean removeQueueObject(String queueName, T data) { public static <T> boolean removeQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName); RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
@@ -61,7 +69,10 @@ public class QueueUtils {
} }
/** /**
* 通用销毁队列 所有阻塞监听 报错(不支持延迟队列) * 销毁普通队列
*
* @param queueName 队列名
* @return 是否销毁成功
*/ */
public static <T> boolean destroyQueue(String queueName) { public static <T> boolean destroyQueue(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName); RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
@@ -73,6 +84,7 @@ public class QueueUtils {
* *
* @param queueName 队列名 * @param queueName 队列名
* @param data 数据 * @param data 数据
* @return 是否添加成功
*/ */
public static <T> boolean addPriorityQueueObject(String queueName, T data) { public static <T> boolean addPriorityQueueObject(String queueName, T data) {
RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName); RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);
@@ -83,6 +95,7 @@ public class QueueUtils {
* 优先队列获取一个队列数据 没有数据返回 null(不支持延迟队列) * 优先队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
* *
* @param queueName 队列名 * @param queueName 队列名
* @return 队列数据
*/ */
public static <T> T getPriorityQueueObject(String queueName) { public static <T> T getPriorityQueueObject(String queueName) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName); RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
@@ -90,7 +103,11 @@ public class QueueUtils {
} }
/** /**
* 优先队列删除队列数据(不支持延迟队列) * 删除优先队列中的指定数据。
*
* @param queueName 队列名
* @param data 数据
* @return 是否删除成功
*/ */
public static <T> boolean removePriorityQueueObject(String queueName, T data) { public static <T> boolean removePriorityQueueObject(String queueName, T data) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName); RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
@@ -98,7 +115,10 @@ public class QueueUtils {
} }
/** /**
* 优先队列销毁队列 所有阻塞监听 报错(不支持延迟队列) * 销毁优先队列
*
* @param queueName 队列名
* @return 是否销毁成功
*/ */
public static <T> boolean destroyPriorityQueue(String queueName) { public static <T> boolean destroyPriorityQueue(String queueName) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName); RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
@@ -106,7 +126,10 @@ public class QueueUtils {
} }
/** /**
* 订阅阻塞队列(可订阅所有实现类) * 订阅阻塞队列元素。
*
* @param queueName 队列名
* @param consumer 消费逻辑
*/ */
public static <T> void subscribeBlockingQueue(String queueName, Function<T, CompletionStage<Void>> consumer) { public static <T> void subscribeBlockingQueue(String queueName, Function<T, CompletionStage<Void>> consumer) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName); RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);

View File

@@ -546,6 +546,7 @@ public class RedisUtils {
/** /**
* 通过扫描参数获取缓存的基本对象列表 * 通过扫描参数获取缓存的基本对象列表
*
* @param keysScanOptions 扫描参数 * @param keysScanOptions 扫描参数
* <P> * <P>
* limit-设置扫描的限制数量(默认为0,查询全部) * limit-设置扫描的限制数量(默认为0,查询全部)
@@ -554,6 +555,7 @@ public class RedisUtils {
* type-设置键的类型(默认为null,查询全部类型) * type-设置键的类型(默认为null,查询全部类型)
* </P> * </P>
* @see KeysScanOptions * @see KeysScanOptions
* @return 对象列表
*/ */
public static Collection<String> keys(final KeysScanOptions keysScanOptions) { public static Collection<String> keys(final KeysScanOptions keysScanOptions) {
Stream<String> keysStream = CLIENT.getKeys().getKeysStream(keysScanOptions); Stream<String> keysStream = CLIENT.getKeys().getKeysStream(keysScanOptions);
@@ -573,6 +575,7 @@ public class RedisUtils {
* 检查redis中是否存在key * 检查redis中是否存在key
* *
* @param key 键 * @param key 键
* @return 是否存在
*/ */
public static Boolean hasKey(String key) { public static Boolean hasKey(String key) {
RKeys rKeys = CLIENT.getKeys(); RKeys rKeys = CLIENT.getKeys();

View File

@@ -22,7 +22,11 @@ import java.util.List;
public class SaPermissionImpl implements StpInterface { public class SaPermissionImpl implements StpInterface {
/** /**
* 获取菜单权限列表 * 获取指定登录对象的菜单权限列表
*
* @param loginId 登录ID
* @param loginType 登录类型
* @return 菜单权限列表
*/ */
@Override @Override
public List<String> getPermissionList(Object loginId, String loginType) { public List<String> getPermissionList(Object loginId, String loginType) {
@@ -49,7 +53,11 @@ public class SaPermissionImpl implements StpInterface {
} }
/** /**
* 获取角色权限列表 * 获取指定登录对象的角色权限列表
*
* @param loginId 登录ID
* @param loginType 登录类型
* @return 角色权限列表
*/ */
@Override @Override
public List<String> getRoleList(Object loginId, String loginType) { public List<String> getRoleList(Object loginId, String loginType) {
@@ -75,6 +83,11 @@ public class SaPermissionImpl implements StpInterface {
} }
} }
/**
* 获取权限服务实现。
*
* @return 权限服务
*/
private PermissionService getPermissionService() { private PermissionService getPermissionService() {
try { try {
return SpringUtils.getBean(PermissionService.class); return SpringUtils.getBean(PermissionService.class);

View File

@@ -20,7 +20,11 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
public class SaTokenExceptionHandler { public class SaTokenExceptionHandler {
/** /**
* 权限码异常 * 处理权限码校验失败异常
*
* @param e 异常信息
* @param request 当前请求
* @return 统一失败响应
*/ */
@ExceptionHandler(NotPermissionException.class) @ExceptionHandler(NotPermissionException.class)
public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { public R<Void> handleNotPermissionException(NotPermissionException e, HttpServletRequest request) {
@@ -30,7 +34,11 @@ public class SaTokenExceptionHandler {
} }
/** /**
* 角色权限异常 * 处理角色权限校验失败异常
*
* @param e 异常信息
* @param request 当前请求
* @return 统一失败响应
*/ */
@ExceptionHandler(NotRoleException.class) @ExceptionHandler(NotRoleException.class)
public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) { public R<Void> handleNotRoleException(NotRoleException e, HttpServletRequest request) {
@@ -40,7 +48,11 @@ public class SaTokenExceptionHandler {
} }
/** /**
* 认证失败 * 处理未登录或登录态失效异常。
*
* @param e 异常信息
* @param request 当前请求
* @return 统一失败响应
*/ */
@ExceptionHandler(NotLoginException.class) @ExceptionHandler(NotLoginException.class)
public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) { public R<Void> handleNotLoginException(NotLoginException e, HttpServletRequest request) {

View File

@@ -55,7 +55,9 @@ public class LoginHelper {
} }
/** /**
* 获取用户(多级缓存) * 获取当前 Token 对应的登录用户信息。
*
* @return 登录用户
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends LoginUser> T getLoginUser() { public static <T extends LoginUser> T getLoginUser() {
@@ -67,7 +69,10 @@ public class LoginHelper {
} }
/** /**
* 获取用户基于token * 根据指定 Token 获取登录用户信息。
*
* @param token Token
* @return 登录用户
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends LoginUser> T getLoginUser(String token) { public static <T extends LoginUser> T getLoginUser(String token) {
@@ -79,42 +84,54 @@ public class LoginHelper {
} }
/** /**
* 获取用户id * 获取当前登录用户 ID。
*
* @return 用户 ID
*/ */
public static Long getUserId() { public static Long getUserId() {
return Convert.toLong(getExtra(USER_KEY)); return Convert.toLong(getExtra(USER_KEY));
} }
/** /**
* 获取用户id * 获取当前登录用户 ID 字符串。
*
* @return 用户 ID 字符串
*/ */
public static String getUserIdStr() { public static String getUserIdStr() {
return Convert.toStr(getExtra(USER_KEY)); return Convert.toStr(getExtra(USER_KEY));
} }
/** /**
* 获取用户账户 * 获取当前登录用户名。
*
* @return 用户名
*/ */
public static String getUsername() { public static String getUsername() {
return Convert.toStr(getExtra(USER_NAME_KEY)); return Convert.toStr(getExtra(USER_NAME_KEY));
} }
/** /**
* 获取部门ID * 获取当前登录用户部门 ID
*
* @return 部门 ID
*/ */
public static Long getDeptId() { public static Long getDeptId() {
return Convert.toLong(getExtra(DEPT_KEY)); return Convert.toLong(getExtra(DEPT_KEY));
} }
/** /**
* 获取部门名 * 获取当前登录用户部门名称。
*
* @return 部门名称
*/ */
public static String getDeptName() { public static String getDeptName() {
return Convert.toStr(getExtra(DEPT_NAME_KEY)); return Convert.toStr(getExtra(DEPT_NAME_KEY));
} }
/** /**
* 获取部门类别编码 * 获取当前登录用户部门类别编码
*
* @return 部门类别编码
*/ */
public static String getDeptCategory() { public static String getDeptCategory() {
return Convert.toStr(getExtra(DEPT_CATEGORY_KEY)); return Convert.toStr(getExtra(DEPT_CATEGORY_KEY));
@@ -135,7 +152,9 @@ public class LoginHelper {
} }
/** /**
* 获取用户类型 * 获取当前登录用户类型
*
* @return 用户类型
*/ */
public static UserType getUserType() { public static UserType getUserType() {
String loginType = StpUtil.getLoginIdAsString(); String loginType = StpUtil.getLoginIdAsString();
@@ -146,7 +165,7 @@ public class LoginHelper {
* 是否为超级管理员 * 是否为超级管理员
* *
* @param userId 用户ID * @param userId 用户ID
* @return 结果 * @return 是否为超级管理员
*/ */
public static boolean isSuperAdmin(Long userId) { public static boolean isSuperAdmin(Long userId) {
return SystemConstants.SUPER_ADMIN_ID.equals(userId); return SystemConstants.SUPER_ADMIN_ID.equals(userId);
@@ -155,7 +174,7 @@ public class LoginHelper {
/** /**
* 是否为超级管理员 * 是否为超级管理员
* *
* @return 结果 * @return 是否为超级管理员
*/ */
public static boolean isSuperAdmin() { public static boolean isSuperAdmin() {
return isSuperAdmin(getUserId()); return isSuperAdmin(getUserId());
@@ -164,7 +183,7 @@ public class LoginHelper {
/** /**
* 检查当前用户是否已登录 * 检查当前用户是否已登录
* *
* @return 结果 * @return 是否已登录
*/ */
public static boolean isLogin() { public static boolean isLogin() {
try { try {

View File

@@ -43,7 +43,9 @@ public class SecurityConfig implements WebMvcConfigurer {
private String ssePath; private String ssePath;
/** /**
* 注册sa-token的拦截器 * 注册 Sa-Token 路由拦截器并配置鉴权规则。
*
* @param registry 拦截器注册器
*/ */
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
@@ -87,7 +89,9 @@ public class SecurityConfig implements WebMvcConfigurer {
} }
/** /**
* actuator 健康检查接口 做账号密码鉴权 * actuator 健康检查接口配置 Basic Auth 鉴权过滤器。
*
* @return Sa-Token Servlet 过滤器
*/ */
@Bean @Bean
public SaServletFilter getSaServletFilter() { public SaServletFilter getSaServletFilter() {

View File

@@ -16,6 +16,4 @@ public class SecurityProperties {
* 排除路径 * 排除路径
*/ */
private String[] excludes; private String[] excludes;
} }

View File

@@ -23,6 +23,9 @@ public class AllUrlHandler implements InitializingBean {
private List<String> urls = new ArrayList<>(); private List<String> urls = new ArrayList<>();
/**
* 初始化并收集系统中的全部请求路径。
*/
@Override @Override
public void afterPropertiesSet() { public void afterPropertiesSet() {
Set<String> set = new HashSet<>(); Set<String> set = new HashSet<>();

View File

@@ -9,12 +9,18 @@ import org.springframework.context.annotation.Bean;
/** /**
* Social 配置属性 * Social 配置属性
*
* @author thiszhc * @author thiszhc
*/ */
@AutoConfiguration @AutoConfiguration
@EnableConfigurationProperties(SocialProperties.class) @EnableConfigurationProperties(SocialProperties.class)
public class SocialAutoConfiguration { public class SocialAutoConfiguration {
/**
* 注册第三方授权状态缓存实现。
*
* @return 授权状态缓存
*/
@Bean @Bean
public AuthStateCache authStateCache() { public AuthStateCache authStateCache() {
return new AuthRedisStateCache(); return new AuthRedisStateCache();

View File

@@ -23,6 +23,16 @@ public class SocialUtils {
private static final AuthRedisStateCache STATE_CACHE = SpringUtils.getBean(AuthRedisStateCache.class); private static final AuthRedisStateCache STATE_CACHE = SpringUtils.getBean(AuthRedisStateCache.class);
/**
* 执行第三方登录授权回调。
*
* @param source 社交平台类型
* @param code 授权码
* @param state 状态值
* @param socialProperties 社交平台配置
* @return 授权响应
* @throws AuthException 授权异常
*/
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static AuthResponse<AuthUser> loginAuth(String source, String code, String state, SocialProperties socialProperties) throws AuthException { public static AuthResponse<AuthUser> loginAuth(String source, String code, String state, SocialProperties socialProperties) throws AuthException {
AuthRequest authRequest = getAuthRequest(source, socialProperties); AuthRequest authRequest = getAuthRequest(source, socialProperties);
@@ -32,6 +42,14 @@ public class SocialUtils {
return authRequest.login(callback); return authRequest.login(callback);
} }
/**
* 根据平台类型构建授权请求实例。
*
* @param source 社交平台类型
* @param socialProperties 社交平台配置
* @return 授权请求
* @throws AuthException 授权异常
*/
public static AuthRequest getAuthRequest(String source, SocialProperties socialProperties) throws AuthException { public static AuthRequest getAuthRequest(String source, SocialProperties socialProperties) throws AuthException {
SocialLoginConfigProperties obj = socialProperties.getType().get(source); SocialLoginConfigProperties obj = socialProperties.getType().get(source);
if (ObjectUtil.isNull(obj)) { if (ObjectUtil.isNull(obj)) {

View File

@@ -26,7 +26,9 @@ public class SseController implements DisposableBean {
private final SseEmitterManager sseEmitterManager; private final SseEmitterManager sseEmitterManager;
/** /**
* 建立 SSE 连接 * 建立当前登录用户的 SSE 连接
*
* @return SSE 发射器
*/ */
@GetMapping(value = "${sse.path}", produces = MediaType.TEXT_EVENT_STREAM_VALUE) @GetMapping(value = "${sse.path}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter connect() { public SseEmitter connect() {
@@ -39,7 +41,9 @@ public class SseController implements DisposableBean {
} }
/** /**
* 关闭 SSE 连接 * 关闭当前登录用户的 SSE 连接
*
* @return 操作结果
*/ */
@SaIgnore @SaIgnore
@GetMapping(value = "${sse.path}/close") @GetMapping(value = "${sse.path}/close")
@@ -78,7 +82,9 @@ public class SseController implements DisposableBean {
// } // }
/** /**
* 清理资源。此方法目前不执行任何操作,但避免因未实现而导致错误 * 容器销毁时释放资源占位实现。
*
* @throws Exception 销毁异常
*/ */
@Override @Override
public void destroy() throws Exception { public void destroy() throws Exception {

View File

@@ -116,7 +116,7 @@ public class SseEmitterManager {
} }
/** /**
* SSE 心跳检测,关闭无效连接 * 执行 SSE 心跳检测并清理失效连接
*/ */
public void sseMonitor() { public void sseMonitor() {
final SseEmitter.SseEventBuilder heartbeat = SseEmitter.event().comment("heartbeat"); final SseEmitter.SseEventBuilder heartbeat = SseEmitter.event().comment("heartbeat");
@@ -154,7 +154,7 @@ public class SseEmitterManager {
} }
/** /**
* 订阅SSE消息主题,并提供一个消费者函数来处理接收到的消息 * 订阅 SSE 广播主题消息
* *
* @param consumer 处理SSE消息的消费者函数 * @param consumer 处理SSE消息的消费者函数
*/ */
@@ -163,7 +163,7 @@ public class SseEmitterManager {
} }
/** /**
* 向指定用户会话发送消息 * 向指定用户的全部本地 SSE 会话发送消息
* *
* @param userId 要发送消息的用户id * @param userId 要发送消息的用户id
* @param message 要发送的消息内容 * @param message 要发送的消息内容
@@ -189,7 +189,7 @@ public class SseEmitterManager {
} }
/** /**
* 本机全用户会话发送消息 * 向当前节点所有 SSE 会话发送消息
* *
* @param message 要发送的消息内容 * @param message 要发送的消息内容
*/ */
@@ -200,7 +200,7 @@ public class SseEmitterManager {
} }
/** /**
* 发布SSE订阅消息 * 发布 SSE 订阅消息
* *
* @param sseMessageDTO 要发布的SSE消息对象 * @param sseMessageDTO 要发布的SSE消息对象
*/ */
@@ -215,7 +215,7 @@ public class SseEmitterManager {
} }
/** /**
* 向所有的用户发布订阅的消息(群发) * 发布 SSE 广播消息。
* *
* @param message 要发布的消息内容 * @param message 要发布的消息内容
*/ */

View File

@@ -26,7 +26,7 @@ public class SseMessageUtils {
} }
/** /**
* 向指定SSE会话发送消息 * 向指定用户的 SSE 会话发送消息
* *
* @param userId 要发送消息的用户id * @param userId 要发送消息的用户id
* @param message 要发送的消息内容 * @param message 要发送的消息内容
@@ -39,7 +39,7 @@ public class SseMessageUtils {
} }
/** /**
* 本机全用户会话发送消息 * 向当前节点上的所有 SSE 会话发送消息
* *
* @param message 要发送的消息内容 * @param message 要发送的消息内容
*/ */
@@ -51,7 +51,7 @@ public class SseMessageUtils {
} }
/** /**
* 发布SSE订阅消息 * 发布 SSE 订阅消息
* *
* @param sseMessageDTO 要发布的SSE消息对象 * @param sseMessageDTO 要发布的SSE消息对象
*/ */
@@ -63,7 +63,7 @@ public class SseMessageUtils {
} }
/** /**
* 向所有用户发布订阅的消息(群发) * 向所有用户发布 SSE 广播消息。
* *
* @param message 要发布的消息内容 * @param message 要发布的消息内容
*/ */
@@ -75,7 +75,9 @@ public class SseMessageUtils {
} }
/** /**
* 是否开启 * 判断 SSE 功能是否启用。
*
* @return 是否启用
*/ */
public static Boolean isEnable() { public static Boolean isEnable() {
return SSE_ENABLE; return SSE_ENABLE;

View File

@@ -10,11 +10,11 @@ import org.dromara.common.translation.annotation.TranslationType;
public interface TranslationInterface<T> { public interface TranslationInterface<T> {
/** /**
* 翻译 * 翻译键执行转换。
* *
* @param key 需要被翻译的键(不为空) * @param key 需要被翻译的键(不为空)
* @param other 其他参数 * @param other 其他参数
* @return 返回键对应的值 * @return 返回键对应的翻译
*/ */
T translation(Object key, String other); T translation(Object key, String other);
} }

View File

@@ -14,6 +14,14 @@ import java.util.List;
*/ */
public class TranslationBeanSerializerModifier extends ValueSerializerModifier { public class TranslationBeanSerializerModifier extends ValueSerializerModifier {
/**
* 为翻译字段补充空值序列化器,确保字段值为 {@code null} 时仍能走翻译处理链。
*
* @param config 当前序列化配置
* @param beanDesc Bean 描述提供者
* @param beanProperties 当前 Bean 的属性写入器列表
* @return 调整后的属性写入器列表
*/
@Override @Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription.Supplier beanDesc, public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription.Supplier beanDesc,
List<BeanPropertyWriter> beanProperties) { List<BeanPropertyWriter> beanProperties) {

View File

@@ -38,10 +38,23 @@ public class TranslationHandler extends ValueSerializer<Object> {
this.translation = null; this.translation = null;
} }
/**
* 创建绑定指定翻译注解的序列化处理器。
*
* @param translation 当前字段上声明的翻译注解
*/
public TranslationHandler(Translation translation) { public TranslationHandler(Translation translation) {
this.translation = translation; this.translation = translation;
} }
/**
* 将原始字段值翻译为展示值并写回序列化结果。
*
* @param value 原始字段值
* @param gen Json 输出器
* @param ctxt 序列化上下文
* @throws JacksonException Json 序列化异常
*/
@Override @Override
public void serialize(Object value, JsonGenerator gen, SerializationContext ctxt) throws JacksonException { public void serialize(Object value, JsonGenerator gen, SerializationContext ctxt) throws JacksonException {
TranslationInterface<?> trans = TRANSLATION_MAPPER.get(translation.type()); TranslationInterface<?> trans = TRANSLATION_MAPPER.get(translation.type());
@@ -68,6 +81,13 @@ public class TranslationHandler extends ValueSerializer<Object> {
} }
} }
/**
* 按字段上的 {@link Translation} 注解创建上下文相关的翻译序列化器。
*
* @param ctxt 序列化上下文
* @param property 当前序列化属性
* @return 存在翻译注解时返回新的翻译处理器,否则沿用默认序列化器
*/
@Override @Override
public ValueSerializer<?> createContextual(SerializationContext ctxt, BeanProperty property) { public ValueSerializer<?> createContextual(SerializationContext ctxt, BeanProperty property) {
Translation translation = property.getAnnotation(Translation.class); Translation translation = property.getAnnotation(Translation.class);

View File

@@ -17,6 +17,13 @@ public class DeptNameTranslationImpl implements TranslationInterface<String> {
private final DeptService deptService; private final DeptService deptService;
/**
* 将部门 ID 或 ID 集合翻译为部门名称。
*
* @param key 部门 ID 或逗号分隔的 ID 字符串
* @param other 额外参数
* @return 部门名称
*/
@Override @Override
public String translation(Object key, String other) { public String translation(Object key, String other) {
if (key instanceof String ids) { if (key instanceof String ids) {

View File

@@ -18,6 +18,13 @@ public class DictTypeTranslationImpl implements TranslationInterface<String> {
private final DictService dictService; private final DictService dictService;
/**
* 根据字典类型和字典值翻译显示标签。
*
* @param key 字典值
* @param other 字典类型
* @return 字典标签
*/
@Override @Override
public String translation(Object key, String other) { public String translation(Object key, String other) {
if (key instanceof String dictValue && StringUtils.isNotBlank(other)) { if (key instanceof String dictValue && StringUtils.isNotBlank(other)) {

View File

@@ -17,6 +17,13 @@ public class NicknameTranslationImpl implements TranslationInterface<String> {
private final UserService userService; private final UserService userService;
/**
* 将用户 ID 或 ID 集合翻译为用户昵称。
*
* @param key 用户 ID 或逗号分隔的 ID 字符串
* @param other 额外参数
* @return 用户昵称
*/
@Override @Override
public String translation(Object key, String other) { public String translation(Object key, String other) {
if (key instanceof Long id) { if (key instanceof Long id) {

View File

@@ -17,6 +17,13 @@ public class OssUrlTranslationImpl implements TranslationInterface<String> {
private final OssService ossService; private final OssService ossService;
/**
* 将 OSS ID 或 ID 集合翻译为访问地址。
*
* @param key OSS ID 或逗号分隔的 ID 字符串
* @param other 额外参数
* @return 访问地址
*/
@Override @Override
public String translation(Object key, String other) { public String translation(Object key, String other) {
if (key instanceof String ids) { if (key instanceof String ids) {

View File

@@ -18,6 +18,13 @@ public class UserNameTranslationImpl implements TranslationInterface<String> {
private final UserService userService; private final UserService userService;
/**
* 将用户 ID 翻译为用户名。
*
* @param key 用户 ID
* @param other 额外参数
* @return 用户名
*/
@Override @Override
public String translation(Object key, String other) { public String translation(Object key, String other) {
return userService.selectUserNameById(Convert.toLong(key)); return userService.selectUserNameById(Convert.toLong(key));

View File

@@ -20,6 +20,11 @@ import org.springframework.context.annotation.Bean;
@EnableConfigurationProperties(XssProperties.class) @EnableConfigurationProperties(XssProperties.class)
public class FilterConfig { public class FilterConfig {
/**
* 注册 XSS 过滤器。
*
* @return XSS 请求过滤器实例
*/
@Bean @Bean
@ConditionalOnProperty(value = "xss.enabled", havingValue = "true") @ConditionalOnProperty(value = "xss.enabled", havingValue = "true")
@FilterRegistration( @FilterRegistration(
@@ -32,6 +37,11 @@ public class FilterConfig {
return new XssFilter(); return new XssFilter();
} }
/**
* 注册可重复读取请求体过滤器。
*
* @return 请求包装过滤器实例
*/
@Bean @Bean
@FilterRegistration(name = "repeatableFilter", urlPatterns = "/*") @FilterRegistration(name = "repeatableFilter", urlPatterns = "/*")
public RepeatableFilter repeatableFilter() { public RepeatableFilter repeatableFilter() {

View File

@@ -14,6 +14,11 @@ import org.springframework.web.servlet.LocaleResolver;
@AutoConfiguration(before = WebMvcAutoConfiguration.class) @AutoConfiguration(before = WebMvcAutoConfiguration.class)
public class I18nConfig { public class I18nConfig {
/**
* 注册自定义国际化区域解析器。
*
* @return Locale 解析器实例
*/
@Bean @Bean
public LocaleResolver localeResolver() { public LocaleResolver localeResolver() {
return new I18nLocaleResolver(); return new I18nLocaleResolver();

View File

@@ -25,12 +25,22 @@ import java.util.Date;
@AutoConfiguration @AutoConfiguration
public class ResourcesConfig implements WebMvcConfigurer { public class ResourcesConfig implements WebMvcConfigurer {
/**
* 注册全局拦截器。
*
* @param registry 拦截器注册表
*/
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
// 全局访问性能拦截 // 全局访问性能拦截
registry.addInterceptor(new PlusWebInvokeTimeInterceptor()); registry.addInterceptor(new PlusWebInvokeTimeInterceptor());
} }
/**
* 注册全局格式转换器。
*
* @param registry 格式化器注册表
*/
@Override @Override
public void addFormatters(FormatterRegistry registry) { public void addFormatters(FormatterRegistry registry) {
// 全局日期格式转换配置 // 全局日期格式转换配置
@@ -43,12 +53,19 @@ public class ResourcesConfig implements WebMvcConfigurer {
}); });
} }
/**
* 注册静态资源处理器。
*
* @param registry 资源处理器注册表
*/
@Override @Override
public void addResourceHandlers(ResourceHandlerRegistry registry) { public void addResourceHandlers(ResourceHandlerRegistry registry) {
} }
/** /**
* 跨域配置 * 跨域配置
*
* @return 全局 Cors 过滤器
*/ */
@Bean @Bean
public CorsFilter corsFilter() { public CorsFilter corsFilter() {
@@ -71,6 +88,8 @@ public class ResourcesConfig implements WebMvcConfigurer {
/** /**
* 全局异常处理器 * 全局异常处理器
*
* @return 全局异常处理器实例
*/ */
@Bean @Bean
public GlobalExceptionHandler globalExceptionHandler() { public GlobalExceptionHandler globalExceptionHandler() {

View File

@@ -12,6 +12,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "captcha") @ConfigurationProperties(prefix = "captcha")
public class CaptchaProperties { public class CaptchaProperties {
/**
* 是否启用验证码校验。
*/
private Boolean enable; private Boolean enable;
/** /**

View File

@@ -7,7 +7,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* xss过滤 配置属性 * XSS 过滤配置属性,用于控制过滤器开关及排除路径。
* *
* @author Lion Li * @author Lion Li
*/ */
@@ -16,12 +16,12 @@ import java.util.List;
public class XssProperties { public class XssProperties {
/** /**
* Xss开关 * XSS 过滤总开关
*/ */
private Boolean enabled; private Boolean enabled;
/** /**
* 排除路径 * 跳过 XSS 过滤的请求路径集合。
*/ */
private List<String> excludeUrls = new ArrayList<>(); private List<String> excludeUrls = new ArrayList<>();

View File

@@ -32,6 +32,9 @@ public class BaseController {
/** /**
* 页面跳转 * 页面跳转
*
* @param url 目标跳转地址
* @return Spring MVC 重定向路径表达式
*/ */
public String redirect(String url) { public String redirect(String url) {
return StringUtils.format("redirect:{}", url); return StringUtils.format("redirect:{}", url);

View File

@@ -7,12 +7,18 @@ import jakarta.servlet.http.HttpServletResponse;
import java.util.Locale; import java.util.Locale;
/** /**
* 获取请求头国际化信息 * 基于请求头解析国际化区域信息的语言解析器。
* *
* @author Lion Li * @author Lion Li
*/ */
public class I18nLocaleResolver implements LocaleResolver { public class I18nLocaleResolver implements LocaleResolver {
/**
* 从请求头 {@code content-language} 中解析本次请求的区域信息,缺省时回退到系统默认区域。
*
* @param httpServletRequest 当前请求
* @return 当前请求对应的区域设置
*/
@Override @Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) { public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String language = httpServletRequest.getHeader("content-language"); String language = httpServletRequest.getHeader("content-language");
@@ -23,6 +29,13 @@ public class I18nLocaleResolver implements LocaleResolver {
return locale; return locale;
} }
/**
* 当前项目不在服务端主动切换区域信息,因此保留空实现。
*
* @param httpServletRequest 当前请求
* @param httpServletResponse 当前响应
* @param locale 目标区域
*/
@Override @Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {

View File

@@ -14,7 +14,7 @@ import java.io.Serial;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
/** /**
* 带干扰线、波浪、圆的验证码 * 带干扰线、波浪和圆形干扰元素的验证码实现,用于增强验证码识别难度。
* *
* @author Lion Li * @author Lion Li
*/ */
@@ -23,27 +23,70 @@ public class WaveAndCircleCaptcha extends AbstractCaptcha {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
// 构造方法(略,与之前一致) /**
* 构造默认长度为 4 的验证码。
*
* @param width 图片宽度
* @param height 图片高度
*/
public WaveAndCircleCaptcha(int width, int height) { public WaveAndCircleCaptcha(int width, int height) {
this(width, height, 4); this(width, height, 4);
} }
/**
* 构造指定验证码长度的验证码对象。
*
* @param width 图片宽度
* @param height 图片高度
* @param codeCount 验证码字符数
*/
public WaveAndCircleCaptcha(int width, int height, int codeCount) { public WaveAndCircleCaptcha(int width, int height, int codeCount) {
this(width, height, codeCount, 6); this(width, height, codeCount, 6);
} }
/**
* 构造指定字符数与干扰数的验证码对象。
*
* @param width 图片宽度
* @param height 图片高度
* @param codeCount 验证码字符数
* @param interfereCount 干扰元素数量
*/
public WaveAndCircleCaptcha(int width, int height, int codeCount, int interfereCount) { public WaveAndCircleCaptcha(int width, int height, int codeCount, int interfereCount) {
this(width, height, new RandomGenerator(codeCount), interfereCount); this(width, height, new RandomGenerator(codeCount), interfereCount);
} }
/**
* 使用指定验证码生成器构造验证码对象。
*
* @param width 图片宽度
* @param height 图片高度
* @param generator 验证码生成器
* @param interfereCount 干扰元素数量
*/
public WaveAndCircleCaptcha(int width, int height, CodeGenerator generator, int interfereCount) { public WaveAndCircleCaptcha(int width, int height, CodeGenerator generator, int interfereCount) {
super(width, height, generator, interfereCount); super(width, height, generator, interfereCount);
} }
/**
* 构造带字体缩放比例的验证码对象。
*
* @param width 图片宽度
* @param height 图片高度
* @param codeCount 验证码字符数
* @param interfereCount 干扰元素数量
* @param size 字体相对尺寸
*/
public WaveAndCircleCaptcha(int width, int height, int codeCount, int interfereCount, float size) { public WaveAndCircleCaptcha(int width, int height, int codeCount, int interfereCount, float size) {
super(width, height, new RandomGenerator(codeCount), interfereCount, size); super(width, height, new RandomGenerator(codeCount), interfereCount, size);
} }
/**
* 生成验证码图片并绘制文字、扭曲效果及干扰图形。
*
* @param code 验证码文本
* @return 生成后的验证码图片
*/
@Override @Override
public Image createImage(String code) { public Image createImage(String code) {
final BufferedImage image = new BufferedImage( final BufferedImage image = new BufferedImage(
@@ -65,6 +108,12 @@ public class WaveAndCircleCaptcha extends AbstractCaptcha {
return image; return image;
} }
/**
* 绘制验证码文本并开启文字抗锯齿。
*
* @param g 图形上下文
* @param code 验证码文本
*/
private void drawString(Graphics2D g, String code) { private void drawString(Graphics2D g, String code) {
// 设置抗锯齿(让字体渲染更清晰) // 设置抗锯齿(让字体渲染更清晰)
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
@@ -77,6 +126,11 @@ public class WaveAndCircleCaptcha extends AbstractCaptcha {
GraphicsUtil.drawStringColourful(g, code, this.font, this.width, this.height); GraphicsUtil.drawStringColourful(g, code, this.font, this.width, this.height);
} }
/**
* 绘制圆形与波浪线干扰元素。
*
* @param g 图形上下文
*/
protected void drawInterfere(Graphics2D g) { protected void drawInterfere(Graphics2D g) {
ThreadLocalRandom random = RandomUtil.getRandom(); ThreadLocalRandom random = RandomUtil.getRandom();
int circleCount = Math.max(0, this.interfereCount - 1); int circleCount = Math.max(0, this.interfereCount - 1);
@@ -98,6 +152,12 @@ public class WaveAndCircleCaptcha extends AbstractCaptcha {
} }
} }
/**
* 绘制平滑波浪线干扰轨迹。
*
* @param g 图形上下文
* @param random 随机数生成器
*/
private void drawSmoothWave(Graphics2D g, ThreadLocalRandom random) { private void drawSmoothWave(Graphics2D g, ThreadLocalRandom random) {
int amplitude = random.nextInt(8) + 5; // 波动幅度 int amplitude = random.nextInt(8) + 5; // 波动幅度
int wavelength = random.nextInt(40) + 30; // 波长 int wavelength = random.nextInt(40) + 30; // 波长
@@ -122,6 +182,14 @@ public class WaveAndCircleCaptcha extends AbstractCaptcha {
g.drawPolyline(xPoints, yPoints, width); g.drawPolyline(xPoints, yPoints, width);
} }
/**
* 生成指定 RGB 范围内的随机颜色。
*
* @param min 最小颜色值
* @param max 最大颜色值
* @param random 随机数生成器
* @return 随机颜色
*/
private Color getRandomColor(int min, int max, ThreadLocalRandom random) { private Color getRandomColor(int min, int max, ThreadLocalRandom random) {
int range = max - min; int range = max - min;
return new Color( return new Color(

View File

@@ -8,16 +8,32 @@ import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
/** /**
* Repeatable 过滤器 * 可重复读取请求体的过滤器,仅对 JSON 请求包装可重复消费的请求对象。
* *
* @author ruoyi * @author ruoyi
*/ */
public class RepeatableFilter implements Filter { public class RepeatableFilter implements Filter {
/**
* 过滤器初始化入口,当前无额外初始化逻辑。
*
* @param filterConfig 过滤器配置
* @throws ServletException 过滤器初始化异常
*/
@Override @Override
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
} }
/**
* 为 JSON 请求创建可重复读取的包装器,便于日志、验签等场景多次读取请求体。
*
* @param request 原始请求
* @param response 当前响应
* @param chain 过滤器链
* @throws IOException IO 异常
* @throws ServletException Servlet 异常
*/
@Override @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
@@ -33,6 +49,9 @@ public class RepeatableFilter implements Filter {
} }
} }
/**
* 过滤器销毁入口,当前无额外资源需要释放。
*/
@Override @Override
public void destroy() { public void destroy() {

View File

@@ -15,13 +15,20 @@ import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
/** /**
* 构建可重复读取inputStream的request * 构建可重复读取输入流的请求包装器,缓存请求体以支持多次读取。
* *
* @author ruoyi * @author ruoyi
*/ */
public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
private final byte[] body; private final byte[] body;
/**
* 读取原始请求体并缓存到内存,统一设置请求与响应编码。
*
* @param request 原始请求
* @param response 当前响应
* @throws IOException 读取请求体异常
*/
public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException { public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
super(request); super(request);
request.setCharacterEncoding(Constants.UTF8); request.setCharacterEncoding(Constants.UTF8);
@@ -30,11 +37,23 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper {
body = IoUtil.readBytes(request.getInputStream(), false); body = IoUtil.readBytes(request.getInputStream(), false);
} }
/**
* 基于缓存的请求体构造字符读取器。
*
* @return 可重复读取的字符流
* @throws IOException IO 异常
*/
@Override @Override
public BufferedReader getReader() throws IOException { public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream())); return new BufferedReader(new InputStreamReader(getInputStream()));
} }
/**
* 返回基于缓存请求体重新生成的输入流。
*
* @return 可重复读取的输入流
* @throws IOException IO 异常
*/
@Override @Override
public ServletInputStream getInputStream() throws IOException { public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body); final ByteArrayInputStream bais = new ByteArrayInputStream(body);

View File

@@ -13,22 +13,37 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* 防止XSS攻击的过滤器 * 防止 XSS 攻击的过滤器,对非排除请求执行参数与请求体清洗。
* *
* @author ruoyi * @author ruoyi
*/ */
public class XssFilter implements Filter { public class XssFilter implements Filter {
/** /**
* 排除链接 * 跳过 XSS 过滤的请求路径集合。
*/ */
public List<String> excludes = new ArrayList<>(); public List<String> excludes = new ArrayList<>();
/**
* 初始化过滤器并加载配置中的排除路径。
*
* @param filterConfig 过滤器配置
* @throws ServletException 过滤器初始化异常
*/
@Override @Override
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
XssProperties properties = SpringUtils.getBean(XssProperties.class); XssProperties properties = SpringUtils.getBean(XssProperties.class);
excludes.addAll(properties.getExcludeUrls()); excludes.addAll(properties.getExcludeUrls());
} }
/**
* 对请求执行 XSS 包装处理,命中排除规则时直接放行。
*
* @param request 原始请求
* @param response 当前响应
* @param chain 过滤器链
* @throws IOException IO 异常
* @throws ServletException Servlet 异常
*/
@Override @Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException { throws IOException, ServletException {
@@ -42,6 +57,13 @@ public class XssFilter implements Filter {
chain.doFilter(xssRequest, response); chain.doFilter(xssRequest, response);
} }
/**
* 判断当前请求是否需要跳过 XSS 过滤。
*
* @param request 当前请求
* @param response 当前响应
* @return true 表示跳过过滤
*/
private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) { private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) {
String url = request.getServletPath(); String url = request.getServletPath();
String method = request.getMethod(); String method = request.getMethod();
@@ -52,6 +74,9 @@ public class XssFilter implements Filter {
return StringUtils.matches(url, excludes); return StringUtils.matches(url, excludes);
} }
/**
* 过滤器销毁入口,当前无额外资源需要释放。
*/
@Override @Override
public void destroy() { public void destroy() {

View File

@@ -20,18 +20,27 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* XSS过滤处理 * XSS 请求包装器,统一清洗参数与 JSON 请求体中的 HTML 标签内容。
* *
* @author ruoyi * @author ruoyi
*/ */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
/** /**
* @param request * 使用原始请求构造 XSS 包装器。
*
* @param request 原始请求
*/ */
public XssHttpServletRequestWrapper(HttpServletRequest request) { public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request); super(request);
} }
/**
* 获取并清洗单个请求参数。
*
* @param name 参数名
* @return 清洗后的参数值
*/
@Override @Override
public String getParameter(String name) { public String getParameter(String name) {
String value = super.getParameter(name); String value = super.getParameter(name);
@@ -41,6 +50,11 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
return HtmlUtil.cleanHtmlTag(value).trim(); return HtmlUtil.cleanHtmlTag(value).trim();
} }
/**
* 获取并清洗整组请求参数。
*
* @return 清洗后的参数映射
*/
@Override @Override
public Map<String, String[]> getParameterMap() { public Map<String, String[]> getParameterMap() {
Map<String, String[]> valueMap = super.getParameterMap(); Map<String, String[]> valueMap = super.getParameterMap();
@@ -65,6 +79,12 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
return map; return map;
} }
/**
* 获取并清洗指定参数的多值数组。
*
* @param name 参数名
* @return 清洗后的参数值数组
*/
@Override @Override
public String[] getParameterValues(String name) { public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name); String[] values = super.getParameterValues(name);
@@ -80,6 +100,12 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
return escapseValues; return escapseValues;
} }
/**
* 获取输入流并在 JSON 场景下对请求体执行清洗。
*
* @return 清洗后的输入流
* @throws IOException 读取请求体异常
*/
@Override @Override
public ServletInputStream getInputStream() throws IOException { public ServletInputStream getInputStream() throws IOException {
// 非json类型直接返回 // 非json类型直接返回
@@ -125,7 +151,9 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
} }
/** /**
* 是否是Json请求 * 判断当前请求是否为 JSON 请求
*
* @return true 表示 JSON 请求
*/ */
public boolean isJsonRequest() { public boolean isJsonRequest() {
String header = super.getHeader(HttpHeaders.CONTENT_TYPE); String header = super.getHeader(HttpHeaders.CONTENT_TYPE);

View File

@@ -26,7 +26,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* web调用时间统计拦截器 * Web 调用时间统计拦截器,同时记录请求参数并对敏感字段做脱敏处理。
* *
* @author Lion Li * @author Lion Li
* @since 3.3.0 * @since 3.3.0
@@ -36,6 +36,15 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
private final static ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>(); private final static ThreadLocal<StopWatch> KEY_CACHE = new ThreadLocal<>();
/**
* 请求进入控制器前记录入参并启动耗时统计。
*
* @param request 当前请求
* @param response 当前响应
* @param handler 目标处理器
* @return 始终返回 true继续后续处理流程
* @throws Exception 读取请求体或解析 JSON 失败时抛出
*/
@Override @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String url = request.getMethod() + " " + request.getRequestURI(); String url = request.getMethod() + " " + request.getRequestURI();
@@ -71,6 +80,12 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
return true; return true;
} }
/**
* 递归移除 JSON 节点中的敏感字段,避免在日志中输出密码等敏感信息。
*
* @param node 当前 JSON 节点
* @param excludeProperties 需要排除的字段名集合
*/
private void removeSensitiveFields(JsonNode node, String[] excludeProperties) { private void removeSensitiveFields(JsonNode node, String[] excludeProperties) {
if (node == null) { if (node == null) {
return; return;
@@ -100,6 +115,15 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor {
} }
/**
* 请求完成后输出最终耗时,并清理线程内缓存的计时器。
*
* @param request 当前请求
* @param response 当前响应
* @param handler 目标处理器
* @param ex 请求处理过程中的异常
* @throws Exception 拦截器链路抛出的异常
*/
@Override @Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
StopWatch stopWatch = KEY_CACHE.get(); StopWatch stopWatch = KEY_CACHE.get();

Some files were not shown because too many files have changed in this diff Show More