From e41c7c27f55f05746dc369e3ce1e5ab9ff3b75b9 Mon Sep 17 00:00:00 2001 From: dzq Date: Tue, 11 Nov 2025 17:10:57 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E7=BC=93=E5=AD=98):=20=E6=B7=BB=E5=8A=A0C?= =?UTF-8?q?affeine=E7=BC=93=E5=AD=98=E7=AE=A1=E7=90=86=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=8F=8A=E5=8A=9F=E8=83=BD=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增CaffeineController提供缓存查询、统计和清空功能 - 在AbstractCaffeineCacheTemplate中添加getAll和getAllWithOptional方法 - 更新.claude配置添加mvnw相关命令 --- .claude/settings.local.json | 3 +- .../api/controller/CaffeineController.java | 185 ++++++++++++++++++ .../AbstractCaffeineCacheTemplate.java | 32 +++ 3 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 agileboot-api/src/main/java/com/agileboot/api/controller/CaffeineController.java diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 3e4f402..6a1c655 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -36,7 +36,8 @@ "Bash(cat \"/e/code/智柜宝/shop-back-end/agileboot-domain/src/main/java/com/agileboot/domain/system/user/query/SearchUserQuery.java\")", "Bash(./mvnw clean compile -pl agileboot-api -am -DskipTests)", "Bash(./mvnw clean compile -pl agileboot-api,agileboot-domain -am -DskipTests)", - "Bash(./mvnw clean compile -pl agileboot-domain -am -DskipTests)" + "Bash(./mvnw clean compile -pl agileboot-domain -am -DskipTests)", + "Bash(./mvnw:*)" ], "deny": [], "ask": [] diff --git a/agileboot-api/src/main/java/com/agileboot/api/controller/CaffeineController.java b/agileboot-api/src/main/java/com/agileboot/api/controller/CaffeineController.java new file mode 100644 index 0000000..83d0a53 --- /dev/null +++ b/agileboot-api/src/main/java/com/agileboot/api/controller/CaffeineController.java @@ -0,0 +1,185 @@ +package com.agileboot.api.controller; + +import com.agileboot.domain.common.cache.CaffeineCacheService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * Caffeine缓存管理控制器 + * 提供缓存查询、统计、清空等管理功能 + * + * @author valarchie + */ +@Slf4j +@RestController +@RequestMapping("/api/caffeine") +@Tag(name = "Caffeine缓存管理", description = "Caffeine本地缓存查询和管理接口") +@RequiredArgsConstructor +public class CaffeineController { + + @Autowired + private CaffeineCacheService caffeineCacheService; + + /** + * 获取所有Caffeine缓存的统计信息 + */ + @GetMapping("/stats") + @Operation(summary = "获取缓存统计信息", description = "返回所有Caffeine缓存的详细统计信息") + public ResponseEntity getCacheStats() { + try { + String stats = caffeineCacheService.getCacheStats(); + return ResponseEntity.ok(stats); + } catch (Exception e) { + log.error("获取缓存统计信息失败", e); + return ResponseEntity.internalServerError() + .body("获取缓存统计信息失败: " + e.getMessage()); + } + } + + /** + * 清空指定类型的缓存 + * + * @param cacheName 缓存名称 (captchaCache, loginUserCache, userCache, roleCache, postCache, qyUseridCache, dynamicCodeCache) + */ + @DeleteMapping("/cache/{cacheName}") + @Operation(summary = "清空指定缓存", description = "清空指定类型的Caffeine缓存") + public ResponseEntity clearCache(@PathVariable String cacheName) { + try { + switch (cacheName) { + case "captchaCache": + caffeineCacheService.captchaCache.invalidateAll(); + break; + case "loginUserCache": + caffeineCacheService.loginUserCache.invalidateAll(); + break; + case "userCache": + caffeineCacheService.userCache.invalidateAll(); + break; + case "roleCache": + caffeineCacheService.roleCache.invalidateAll(); + break; + case "postCache": + caffeineCacheService.postCache.invalidateAll(); + break; + case "qyUseridCache": + caffeineCacheService.qyUseridCache.invalidateAll(); + break; + case "dynamicCodeCache": + caffeineCacheService.dynamicCodeCache.invalidateAll(); + break; + default: + return ResponseEntity.badRequest() + .body("未知的缓存类型: " + cacheName + ",支持的类型: captchaCache, loginUserCache, userCache, roleCache, postCache, qyUseridCache, dynamicCodeCache"); + } + return ResponseEntity.ok("缓存 [" + cacheName + "] 已清空"); + } catch (Exception e) { + log.error("清空缓存失败: {}", cacheName, e); + return ResponseEntity.internalServerError() + .body("清空缓存失败: " + e.getMessage()); + } + } + + /** + * 清空所有Caffeine缓存 + */ + @DeleteMapping("/cache") + @Operation(summary = "清空所有缓存", description = "清空所有类型的Caffeine缓存") + public ResponseEntity clearAllCaches() { + try { + caffeineCacheService.captchaCache.invalidateAll(); + caffeineCacheService.loginUserCache.invalidateAll(); + caffeineCacheService.userCache.invalidateAll(); + caffeineCacheService.roleCache.invalidateAll(); + caffeineCacheService.postCache.invalidateAll(); + caffeineCacheService.qyUseridCache.invalidateAll(); + caffeineCacheService.dynamicCodeCache.invalidateAll(); + + return ResponseEntity.ok("所有Caffeine缓存已清空"); + } catch (Exception e) { + log.error("清空所有缓存失败", e); + return ResponseEntity.internalServerError() + .body("清空所有缓存失败: " + e.getMessage()); + } + } + + /** + * 获取所有缓存数据 + */ + @GetMapping("/cache/all") + @Operation(summary = "获取所有缓存数据", description = "获取所有Caffeine缓存中的数据") + public ResponseEntity getAllCacheData() { + try { + Map allCaches = new HashMap<>(); + allCaches.put("captchaCache", caffeineCacheService.captchaCache.getAll()); + allCaches.put("loginUserCache", caffeineCacheService.loginUserCache.getAll()); + allCaches.put("userCache", caffeineCacheService.userCache.getAll()); + allCaches.put("roleCache", caffeineCacheService.roleCache.getAll()); + allCaches.put("postCache", caffeineCacheService.postCache.getAll()); + allCaches.put("qyUseridCache", caffeineCacheService.qyUseridCache.getAll()); + allCaches.put("dynamicCodeCache", caffeineCacheService.dynamicCodeCache.getAll()); + + return ResponseEntity.ok(allCaches); + } catch (Exception e) { + log.error("获取所有缓存数据失败", e); + return ResponseEntity.internalServerError() + .body("获取所有缓存数据失败: " + e.getMessage()); + } + } + + /** + * 获取指定缓存的数据 + */ + @GetMapping("/cache/{cacheName}/data") + @Operation(summary = "获取指定缓存数据", description = "获取指定类型的Caffeine缓存数据") + public ResponseEntity getCacheData(@PathVariable String cacheName) { + try { + Map result = new HashMap<>(); + result.put("cacheName", cacheName); + + switch (cacheName) { + case "captchaCache": + result.put("data", caffeineCacheService.captchaCache.getAll()); + break; + case "loginUserCache": + result.put("data", caffeineCacheService.loginUserCache.getAll()); + break; + case "userCache": + result.put("data", caffeineCacheService.userCache.getAll()); + break; + case "roleCache": + result.put("data", caffeineCacheService.roleCache.getAll()); + break; + case "postCache": + result.put("data", caffeineCacheService.postCache.getAll()); + break; + case "qyUseridCache": + result.put("data", caffeineCacheService.qyUseridCache.getAll()); + break; + case "dynamicCodeCache": + result.put("data", caffeineCacheService.dynamicCodeCache.getAll()); + break; + default: + return ResponseEntity.badRequest() + .body(Map.of("error", "未知的缓存类型: " + cacheName, + "supportedTypes", new String[]{ + "captchaCache", "loginUserCache", "userCache", + "roleCache", "postCache", "qyUseridCache", "dynamicCodeCache" + })); + } + + return ResponseEntity.ok(result); + } catch (Exception e) { + log.error("获取缓存数据失败: {}", cacheName, e); + return ResponseEntity.internalServerError() + .body(Map.of("error", "获取缓存数据失败: " + e.getMessage())); + } + } +} diff --git a/agileboot-infrastructure/src/main/java/com/agileboot/infrastructure/cache/caffeine/AbstractCaffeineCacheTemplate.java b/agileboot-infrastructure/src/main/java/com/agileboot/infrastructure/cache/caffeine/AbstractCaffeineCacheTemplate.java index 8a4a1e4..30da212 100644 --- a/agileboot-infrastructure/src/main/java/com/agileboot/infrastructure/cache/caffeine/AbstractCaffeineCacheTemplate.java +++ b/agileboot-infrastructure/src/main/java/com/agileboot/infrastructure/cache/caffeine/AbstractCaffeineCacheTemplate.java @@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil; import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; +import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -132,6 +133,37 @@ public abstract class AbstractCaffeineCacheTemplate { return caffeineCache.stats().toString(); } + /** + * 获取缓存中所有数据 + * @return 缓存中的所有数据(不包含值为null的条目) + */ + public java.util.Map getAll() { + try { + return caffeineCache.asMap().entrySet().stream() + .filter(entry -> entry.getValue().isPresent()) + .collect(java.util.stream.Collectors.toMap( + Map.Entry::getKey, + entry -> entry.getValue().get() + )); + } catch (Exception e) { + log.error("获取Caffeine缓存所有数据失败", e); + return new java.util.HashMap<>(); + } + } + + /** + * 获取缓存中所有数据(包括key和value) + * @return 包含key和Optional的Map + */ + public java.util.Map> getAllWithOptional() { + try { + return new java.util.HashMap<>(caffeineCache.asMap()); + } catch (Exception e) { + log.error("获取Caffeine缓存所有数据失败", e); + return new java.util.HashMap<>(); + } + } + /** * 从数据库加载数据 * @param id 数据ID