diff --git a/aurora-springboot/src/main/java/com/aurora/interceptor/AccessLimitInterceptor.java b/aurora-springboot/src/main/java/com/aurora/interceptor/AccessLimitInterceptor.java index 7446d86..5a11c94 100644 --- a/aurora-springboot/src/main/java/com/aurora/interceptor/AccessLimitInterceptor.java +++ b/aurora-springboot/src/main/java/com/aurora/interceptor/AccessLimitInterceptor.java @@ -1,7 +1,6 @@ package com.aurora.interceptor; import com.alibaba.fastjson.JSON; - import com.aurora.annotation.AccessLimit; import com.aurora.model.vo.ResultVO; import com.aurora.service.RedisService; @@ -20,47 +19,106 @@ import java.nio.charset.StandardCharsets; import static com.aurora.constant.CommonConstant.APPLICATION_JSON; +/** + * 自定义的访问限制拦截器,实现 HandlerInterceptor 接口, + * 用于限制用户在指定时间窗口内对某个接口的访问次数,防止恶意刷接口或接口滥用。 + */ @Log4j2 @Component @SuppressWarnings("all") public class AccessLimitInterceptor implements HandlerInterceptor { + // 自动注入 RedisService,用于与 Redis 进行交互,实现访问次数的计数和过期时间设置 @Autowired private RedisService redisService; + /** + * 在请求处理之前执行的方法,用于拦截和处理请求。 + * 该方法会在请求到达 Controller 方法之前被调用,用于执行前置处理逻辑。 + * + * @param httpServletRequest 当前的 HTTP 请求对象,包含请求的详细信息 + * @param httpServletResponse 当前的 HTTP 响应对象,用于设置响应内容和状态码 + * @param handler 当前请求的处理对象,通常是 HandlerMethod 类型,表示一个 Controller 方法 + * @return 如果返回 true,表示继续处理请求;如果返回 false,表示中断请求处理链 + * @throws Exception 如果在处理过程中发生异常 + */ @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { + // 检查当前的 handler 是否是 HandlerMethod 类型,即是否对应一个 Controller 方法 if (handler instanceof HandlerMethod) { + // 将 handler 强制转换为 HandlerMethod 类型,以便获取方法级别的注解 HandlerMethod handlerMethod = (HandlerMethod) handler; + + // 获取当前方法上的 @AccessLimit 注解,用于获取访问限制的配置(如时间窗口、最大访问次数) AccessLimit accessLimit = handlerMethod.getMethodAnnotation(AccessLimit.class); + + // 如果当前方法没有被 @AccessLimit 注解修饰,则不进行访问限制,直接放行 if (accessLimit != null) { + // 从 @AccessLimit 注解中获取时间窗口(秒)和最大访问次数 long seconds = accessLimit.seconds(); int maxCount = accessLimit.maxCount(); + + // 生成用于 Redis 计数的唯一键,通常结合客户端 IP 和方法名,确保不同用户和方法有不同的计数 String key = IpUtil.getIpAddress(httpServletRequest) + "-" + handlerMethod.getMethod().getName(); + try { + // 使用 RedisService 的 incrExpire 方法,对指定键进行自增操作,并设置过期时间为指定的时间窗口 + // incrExpire 方法返回当前键的计数值 long q = redisService.incrExpire(key, seconds); + + // 如果当前计数值超过了最大访问次数,表示请求过于频繁,触发访问限制 if (q > maxCount) { + // 调用 render 方法,向客户端返回一个表示访问限制的 JSON 响应 render(httpServletResponse, ResultVO.fail("请求过于频繁," + seconds + "秒后再试")); + + // 记录警告日志,提示请求次数超过限制 log.warn(key + "请求次数超过每" + seconds + "秒" + maxCount + "次"); + + // 返回 false,中断请求处理链,不再继续处理该请求 return false; } + + // 如果当前计数值未超过最大访问次数,允许请求继续处理 return true; } catch (RedisConnectionFailureException e) { + // 如果在访问 Redis 时发生连接失败异常,记录警告日志 log.warn("redis错误: " + e.getMessage()); + + // 返回 false,中断请求处理链,不再继续处理该请求 return false; } } } + + // 如果当前请求没有 @AccessLimit 注解,或者不是 HandlerMethod 类型,直接放行 return true; } + /** + * 渲染并返回 JSON 格式的响应结果。 + * 该方法将 ResultVO 对象序列化为 JSON 字符串,并写入 HTTP 响应体。 + * + * @param response 当前 HTTP 响应对象,用于设置响应内容和状态码 + * @param resultVO 包含响应结果的 ResultVO 对象,通常包含状态码、消息和数据 + * @throws Exception 如果在写入响应时发生异常 + */ private void render(HttpServletResponse response, ResultVO resultVO) throws Exception { + // 设置响应的内容类型为 JSON,确保客户端正确解析响应体 response.setContentType(APPLICATION_JSON); + + // 获取响应的输出流,用于写入响应数据 OutputStream out = response.getOutputStream(); + + // 将 ResultVO 对象序列化为 JSON 字符串 String str = JSON.toJSONString(resultVO); + + // 将 JSON 字符串写入响应输出流 out.write(str.getBytes(StandardCharsets.UTF_8)); + + // 刷新输出流,确保数据被发送到客户端 out.flush(); + + // 关闭输出流,释放资源 out.close(); } - -} +} \ No newline at end of file diff --git a/aurora-springboot/src/main/java/com/aurora/interceptor/PaginationInterceptor.java b/aurora-springboot/src/main/java/com/aurora/interceptor/PaginationInterceptor.java index 149f5e0..45fae12 100644 --- a/aurora-springboot/src/main/java/com/aurora/interceptor/PaginationInterceptor.java +++ b/aurora-springboot/src/main/java/com/aurora/interceptor/PaginationInterceptor.java @@ -13,23 +13,57 @@ import java.util.Optional; import static com.aurora.constant.CommonConstant.*; +/** + * 自定义的分页拦截器,实现 HandlerInterceptor 接口, + * 用于自动从 HTTP 请求中提取分页参数(如当前页码和每页大小), + * 并将这些参数设置到 PageUtil 工具类中,以便在后续的业务逻辑中方便地获取和使用。 + * 该拦截器在请求处理之前执行,用于设置分页信息,并在请求完成后清理分页信息,确保线程安全。 + */ @Component @SuppressWarnings("all") public class PaginationInterceptor implements HandlerInterceptor { + /** + * 在请求处理之前执行的方法,用于拦截请求并提取分页参数。 + * 该方法会在请求到达 Controller 方法之前被调用,用于执行前置处理逻辑。 + * + * @param request 当前的 HTTP 请求对象,包含请求的详细信息,如参数、头部信息等 + * @param response 当前的 HTTP 响应对象,用于设置响应内容和状态码 + * @param handler 当前请求的处理对象,通常是 HandlerMethod 类型,表示一个 Controller 方法 + * @return 如果返回 true,表示继续处理请求;如果返回 false,表示中断请求处理链 + */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + // 从 HTTP 请求中获取名为 "current" 的参数,表示当前页码 String currentPage = request.getParameter(CURRENT); + + // 从 HTTP 请求中获取名为 "size" 的参数,表示每页大小(页容量) + // 如果 "size" 参数不存在或为 null,则使用默认的每页大小 DEFAULT_SIZE String pageSize = Optional.ofNullable(request.getParameter(SIZE)).orElse(DEFAULT_SIZE); + + // 检查当前页码参数是否非空且不为空字符串 if (!Objects.isNull(currentPage) && !StringUtils.isEmpty(currentPage)) { + // 将当前页码和每页大小转换为 Long 类型,并创建一个 MyBatis-Plus 的 Page 对象 + // Page 对象包含当前页码和每页大小,用于分页查询 PageUtil.setCurrentPage(new Page<>(Long.parseLong(currentPage), Long.parseLong(pageSize))); } + + // 返回 true,表示继续处理请求 return true; } + /** + * 在请求完成之后执行的方法,用于清理分页信息,确保线程安全。 + * 该方法会在请求处理完成后(无论成功或异常)被调用,用于执行后置处理逻辑。 + * + * @param request 当前的 HTTP 请求对象 + * @param response 当前的 HTTP 响应对象 + * @param handler 当前请求的处理对象 + * @param ex 在请求处理过程中发生的异常,如果有的话 + */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { + // 调用 PageUtil 的 remove 方法,清除当前线程的分页信息,确保下一个请求不会受到上一个请求分页信息的影响 PageUtil.remove(); } - } \ No newline at end of file diff --git a/aurora-springboot/src/main/java/com/aurora/listener/AuroraListener.java b/aurora-springboot/src/main/java/com/aurora/listener/AuroraListener.java index 2f3d016..36b513b 100644 --- a/aurora-springboot/src/main/java/com/aurora/listener/AuroraListener.java +++ b/aurora-springboot/src/main/java/com/aurora/listener/AuroraListener.java @@ -11,25 +11,49 @@ import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; +/** + * 自定义的事件监听器,用于监听并处理操作日志(OperationLogEvent)和异常日志(ExceptionLogEvent)事件。 + * 该监听器通过 Spring 的事件驱动机制,异步地将日志信息保存到数据库中, + * 以确保日志记录不会阻塞主业务流程,提高系统的响应速度和吞吐量。 + */ @Component public class AuroraListener { + // 自动注入 OperationLogMapper,用于将操作日志保存到数据库 @Autowired private OperationLogMapper operationLogMapper; + // 自动注入 ExceptionLogMapper,用于将异常日志保存到数据库 @Autowired private ExceptionLogMapper exceptionLogMapper; + /** + * 监听并处理操作日志事件(OperationLogEvent)。 + * 当 OperationLogEvent 事件被发布时,该方法会被异步调用, + * 从事件中获取操作日志对象并将其保存到数据库中。 + * + * @param operationLogEvent 操作日志事件,包含要保存的操作日志对象 + */ @Async @EventListener(OperationLogEvent.class) public void saveOperationLog(OperationLogEvent operationLogEvent) { + // 从事件中获取源对象(即操作日志对象),并将其强制转换为 OperationLog 类型 + // 然后调用 OperationLogMapper 的 insert 方法,将操作日志保存到数据库 operationLogMapper.insert((OperationLog) operationLogEvent.getSource()); } + /** + * 监听并处理异常日志事件(ExceptionLogEvent)。 + * 当 ExceptionLogEvent 事件被发布时,该方法会被异步调用, + * 从事件中获取异常日志对象并将其保存到数据库中。 + * + * @param exceptionLogEvent 异常日志事件,包含要保存的异常日志对象 + */ @Async @EventListener(ExceptionLogEvent.class) public void saveExceptionLog(ExceptionLogEvent exceptionLogEvent) { + // 从事件中获取源对象(即异常日志对象),并将其强制转换为 ExceptionLog 类型 + // 然后调用 ExceptionLogMapper 的 insert 方法,将异常日志保存到数据库 exceptionLogMapper.insert((ExceptionLog) exceptionLogEvent.getSource()); } - -} +} \ No newline at end of file diff --git a/aurora-springboot/src/main/java/com/aurora/strategy/context/ArticleImportStrategyContext.java b/aurora-springboot/src/main/java/com/aurora/strategy/context/ArticleImportStrategyContext.java index 306eb74..ead49f2 100644 --- a/aurora-springboot/src/main/java/com/aurora/strategy/context/ArticleImportStrategyContext.java +++ b/aurora-springboot/src/main/java/com/aurora/strategy/context/ArticleImportStrategyContext.java @@ -8,13 +8,34 @@ import org.springframework.web.multipart.MultipartFile; import java.util.Map; +/** + * 文章导入策略上下文类,用于根据不同的导入类型选择并执行相应的文章导入策略。 + * 该类利用 Spring 的依赖注入机制,管理多种文章导入策略,并根据传入的类型参数 + * 动态选择合适的策略来处理文章导入操作。 + */ @Service public class ArticleImportStrategyContext { + /** + * 自动注入一个 Map,其中键为策略标识符(通常为 Markdown 类型), + * 值为对应的 ArticleImportStrategy 实现类实例。 + * Spring 会自动将所有实现 ArticleImportStrategy 接口的 Bean 注入到该 Map 中, + * 键为 Bean 的名称或通过其他方式指定的标识符。 + */ @Autowired private Map articleImportStrategyMap; + /** + * 导入文章的方法,根据传入的文件和类型参数,选择合适的导入策略并执行导入操作。 + * + * @param file 上传的文件,通常为包含文章内容的 Markdown 文件 + * @param type 导入类型,用于标识使用哪种具体的导入策略,通常与 Markdown 类型相关 + */ public void importArticles(MultipartFile file, String type) { + // 根据传入的类型参数,获取对应的 Markdown 类型枚举值 + // 然后通过该枚举值获取对应的策略标识符(通常是枚举的 name 或自定义的 key) + // 最后从 articleImportStrategyMap 中获取对应的 ArticleImportStrategy 实例 + // 并调用其 importArticles 方法,传入上传的文件,执行具体的导入逻辑 articleImportStrategyMap.get(MarkdownTypeEnum.getMarkdownType(type)).importArticles(file); } } diff --git a/aurora-springboot/src/main/java/com/aurora/strategy/context/SearchStrategyContext.java b/aurora-springboot/src/main/java/com/aurora/strategy/context/SearchStrategyContext.java index 2aab656..ced409e 100644 --- a/aurora-springboot/src/main/java/com/aurora/strategy/context/SearchStrategyContext.java +++ b/aurora-springboot/src/main/java/com/aurora/strategy/context/SearchStrategyContext.java @@ -11,17 +11,1005 @@ import java.util.Map; import static com.aurora.enums.SearchModeEnum.getStrategy; +/** + * 搜索策略上下文类,用于根据配置的搜索模式选择并执行相应的搜索策略。 + * 该类利用 Spring 的依赖注入机制,管理多种搜索策略,并根据配置的搜索模式 + * 动态选择合适的策略来处理文章搜索操作。 + */ @Service public class SearchStrategyContext { + /** + * 从配置文件中注入搜索模式,通常定义在 application.properties 或 application.yml 中, + * 用于决定使用哪种搜索策略(如全文搜索、标签搜索等)。 + */ @Value("${search.mode}") private String searchMode; + /** + * 自动注入一个 Map,其中键为策略标识符(通常为搜索模式的名称), + * 值为对应的 SearchStrategy 实现类实例。 + * Spring 会自动将所有实现 SearchStrategy 接口的 Bean 注入到该 Map 中, + * 键为 Bean 的名称或通过其他方式指定的标识符。 + */ @Autowired private Map searchStrategyMap; + /** + * 执行搜索策略的方法,根据配置的搜索模式选择合适的搜索策略, + * 并传入关键词参数,执行具体的搜索逻辑,返回搜索结果。 + * + * @param keywords 用户输入的搜索关键词,用于在文章中查找匹配的内容 + * @return 包含搜索结果的 ArticleSearchDTO 列表,通常包括文章的基本信息及匹配度等 + */ public List executeSearchStrategy(String keywords) { + // 根据配置的搜索模式,获取对应的策略标识符(通常是枚举的 name 或自定义的 key) + // 然后从 searchStrategyMap 中获取对应的 SearchStrategy 实例 + // 并调用其 searchArticle 方法,传入关键词,执行具体的搜索逻辑,返回搜索结果 return searchStrategyMap.get(getStrategy(searchMode)).searchArticle(keywords); } +} +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */ \ No newline at end of file diff --git a/aurora-springboot/src/main/java/com/aurora/strategy/context/SocialLoginStrategyContext.java b/aurora-springboot/src/main/java/com/aurora/strategy/context/SocialLoginStrategyContext.java index edac416..c6671e3 100644 --- a/aurora-springboot/src/main/java/com/aurora/strategy/context/SocialLoginStrategyContext.java +++ b/aurora-springboot/src/main/java/com/aurora/strategy/context/SocialLoginStrategyContext.java @@ -1,6 +1,5 @@ package com.aurora.strategy.context; - import com.aurora.model.dto.UserInfoDTO; import com.aurora.enums.LoginTypeEnum; import com.aurora.strategy.SocialLoginStrategy; @@ -9,14 +8,35 @@ import org.springframework.stereotype.Service; import java.util.Map; +/** + * 社交登录策略上下文类,用于根据不同的登录类型选择并执行相应的社交登录策略。 + * 该类利用 Spring 的依赖注入机制,管理多种社交登录策略,并根据传入的登录类型 + * 动态选择合适的策略来处理社交登录操作,最终返回用户信息。 + */ @Service public class SocialLoginStrategyContext { + /** + * 自动注入一个 Map,其中键为策略标识符(通常为登录类型的名称或枚举值对应的策略键), + * 值为对应的 SocialLoginStrategy 实现类实例。 + * Spring 会自动将所有实现 SocialLoginStrategy 接口的 Bean 注入到该 Map 中, + * 键为 Bean 的名称或通过其他方式指定的标识符。 + */ @Autowired private Map socialLoginStrategyMap; + /** + * 执行社交登录策略的方法,根据传入的登录类型和数据,选择合适的登录策略, + * 并执行登录操作,返回用户信息。 + * + * @param data 第三方登录平台返回的登录数据,通常为授权码、令牌或其他必要信息 + * @param loginTypeEnum 登录类型,用于标识使用哪种具体的社交登录策略,如微信、QQ、GitHub等 + * @return 包含用户信息的 UserInfoDTO 对象,通常包括用户的基本信息如用户名、头像、邮箱等 + */ public UserInfoDTO executeLoginStrategy(String data, LoginTypeEnum loginTypeEnum) { + // 根据传入的登录类型,获取对应的策略标识符(通常是枚举的策略键) + // 然后从 socialLoginStrategyMap 中获取对应的 SocialLoginStrategy 实例 + // 并调用其 login 方法,传入登录数据,执行具体的登录逻辑,返回用户信息 return socialLoginStrategyMap.get(loginTypeEnum.getStrategy()).login(data); } - } diff --git a/aurora-springboot/src/main/java/com/aurora/strategy/context/UploadStrategyContext.java b/aurora-springboot/src/main/java/com/aurora/strategy/context/UploadStrategyContext.java index e0453ef..1bfe3b8 100644 --- a/aurora-springboot/src/main/java/com/aurora/strategy/context/UploadStrategyContext.java +++ b/aurora-springboot/src/main/java/com/aurora/strategy/context/UploadStrategyContext.java @@ -11,21 +11,58 @@ import java.util.Map; import static com.aurora.enums.UploadModeEnum.getStrategy; +/** + * 上传策略上下文类,用于根据配置的上传模式选择并执行相应的上传策略。 + * 该类利用 Spring 的依赖注入机制,管理多种上传策略,并根据配置的上传模式 + * 动态选择合适的策略来处理文件上传操作,支持多种上传方式,如本地上传、云存储上传等。 + */ @Service public class UploadStrategyContext { + /** + * 从配置文件中注入上传模式,通常定义在 application.properties 或 application.yml 中, + * 用于决定使用哪种上传策略(如本地上传、云存储上传等)。 + */ @Value("${upload.mode}") private String uploadMode; + /** + * 自动注入一个 Map,其中键为策略标识符(通常为上传模式的名称), + * 值为对应的 UploadStrategy 实现类实例。 + * Spring 会自动将所有实现 UploadStrategy 接口的 Bean 注入到该 Map 中, + * 键为 Bean 的名称或通过其他方式指定的标识符。 + */ @Autowired private Map uploadStrategyMap; + /** + * 执行上传策略的方法,根据配置的上传模式选择合适的上传策略, + * 并传入 MultipartFile 文件和存储路径,执行具体的上传逻辑,返回上传结果(如文件URL)。 + * + * @param file 上传的文件,通常为 MultipartFile 类型,包含文件数据和元信息 + * @param path 文件存储的目标路径,用于指定文件在服务器或云存储中的存储位置 + * @return 上传结果,通常为文件的访问URL或其他标识符 + */ public String executeUploadStrategy(MultipartFile file, String path) { + // 根据配置的上传模式,获取对应的策略标识符(通常是枚举的 name 或自定义的 key) + // 然后从 uploadStrategyMap 中获取对应的 UploadStrategy 实例 + // 并调用其 uploadFile 方法,传入 MultipartFile 文件和路径,执行具体的上传逻辑,返回上传结果 return uploadStrategyMap.get(getStrategy(uploadMode)).uploadFile(file, path); } + /** + * 执行上传策略的方法,根据配置的上传模式选择合适的上传策略, + * 并传入文件名、InputStream 输入流和存储路径,执行具体的上传逻辑,返回上传结果(如文件URL)。 + * + * @param fileName 上传文件的名称,用于在存储时标识文件 + * @param inputStream 文件的输入流,用于读取文件数据 + * @param path 文件存储的目标路径,用于指定文件在服务器或云存储中的存储位置 + * @return 上传结果,通常为文件的访问URL或其他标识符 + */ public String executeUploadStrategy(String fileName, InputStream inputStream, String path) { + // 根据配置的上传模式,获取对应的策略标识符(通常是枚举的 name 或自定义的 key) + // 然后从 uploadStrategyMap 中获取对应的 UploadStrategy 实例 + // 并调用其 uploadFile 方法,传入文件名、InputStream 输入流和路径,执行具体的上传逻辑,返回上传结果 return uploadStrategyMap.get(getStrategy(uploadMode)).uploadFile(fileName, inputStream, path); } - }