From 4ba4af2f533dcb7691ae19ef4cae2d927e8ac928 Mon Sep 17 00:00:00 2001
From: zhangxiang
Date: Sun, 2 Aug 2026 18:26:15 +0800
Subject: [PATCH] =?UTF-8?q?refactor(cache):=20=E7=BC=93=E5=AD=98=E6=93=8D?=
=?UTF-8?q?=E4=BD=9C=E6=8E=A5=E5=8F=A3=E5=8C=96=EF=BC=8C=E6=94=AF=E6=8C=81?=
=?UTF-8?q?=20Redis/Caffeine=20=E5=8F=8C=E5=AE=9E=E7=8E=B0=E5=88=87?=
=?UTF-8?q?=E6=8D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
将系统所有缓存操作从 `RedisUtil` 迁移至 `CacheOperations` 接口,
通过 `gym.cache.type` 配置选择 Redis 或 Caffeine 实现。
变更内容:
- 新增 CacheOperations 接口,定义 get/set/delete/hasKey/expire/deleteByPattern 等标准方法
- 新增 RedisCacheOperations 实现(基于 ReactiveRedisTemplate,默认实现)
- 新增 CaffeineCacheOperations 实现(基于 Caffeine 本地缓存)
- 新增 CacheProperties 配置类,支持 Caffeine 容量/过期等参数配置
- 新增 CacheAutoConfiguration 自动装配,通过 AutoConfiguration.imports 注册
- 全系统 17 个业务服务 + 18 个测试文件从 RedisUtil 迁移至 CacheOperations
- manage-gateway 配置 caffeine 实现(无需 Redis),manage-app 配置 redis 实现
- 分布式锁等 Redis 独有特性保持直接使用 ReactiveRedisTemplate
测试:202 通过,0 失败,0 错误
---
.../auth/service/impl/SmsServiceImpl.java | 12 +-
.../service/impl/CheckServiceImpl.java | 14 +-
.../gym/manage/checkin/CheckInModuleTest.java | 18 +--
.../service/impl/CheckServiceImplTest.java | 38 ++---
.../service/CoachTimeRuleService.java | 14 +-
.../coach/scheduler/CoachCourseScheduler.java | 14 +-
.../coach/service/CoachCourseService.java | 12 +-
.../scheduler/CoachCourseSchedulerTest.java | 30 ++--
.../coach/service/CoachCourseServiceTest.java | 34 ++---
.../scheduler/DataStatisticsScheduler.java | 3 +-
.../impl/DataStatisticsServiceImpl.java | 16 +--
.../handler/GroupCourseHandler.java | 10 +-
.../service/impl/CourseLabelService.java | 10 +-
.../service/impl/GroupCourseRedisService.java | 63 ++++-----
.../service/impl/GroupCourseService.java | 34 ++---
.../handler/GroupCourseHandlerTest.java | 6 +-
.../service/impl/CourseLabelServiceTest.java | 32 ++---
.../impl/GroupCourseRedisServiceTest.java | 45 +++---
.../service/impl/GroupCourseServiceTest.java | 62 ++++----
.../impl/MemberCardRecordServiceImpl.java | 14 +-
.../service/impl/MemberCardServiceImpl.java | 14 +-
.../service/impl/MemberServiceImpl.java | 16 +--
.../impl/RefundApplicationServiceImpl.java | 14 +-
.../service/impl/WechatApiServiceImpl.java | 8 +-
.../service/impl/WechatAuthServiceImpl.java | 6 +-
.../impl/WechatOfficialServiceImpl.java | 10 +-
.../impl/MemberCardRecordServiceImplTest.java | 20 +--
.../impl/MemberCardServiceImplTest.java | 14 +-
.../service/impl/MemberServiceImplTest.java | 50 +++----
.../service/impl/PaymentServiceImpl.java | 12 +-
.../service/impl/PaymentServiceImplTest.java | 26 ++--
.../src/main/resources/application.yml | 4 +
.../manage/app/contract/BaseContractTest.java | 6 +-
.../GroupCourseHandlerIntegrationTest.java | 8 +-
.../MemberHandlerIntegrationTest.java | 8 +-
gym-manage-api/manage-common/pom.xml | 4 +
.../common/cache/CacheAutoConfiguration.java | 21 +++
.../manage/common/cache/CacheOperations.java | 88 ++++++++++++
.../manage/common/cache/CacheProperties.java | 88 ++++++++++++
.../common/cache/CaffeineCacheOperations.java | 111 +++++++++++++++
.../common/cache/RedisCacheOperations.java | 83 +++++++++++
...ot.autoconfigure.AutoConfiguration.imports | 2 +-
.../gateway/cache/RequestCacheService.java | 133 +++++-------------
.../src/main/resources/application.yml | 5 +
.../cache/RequestCacheServiceTest.java | 42 +++---
45 files changed, 798 insertions(+), 476 deletions(-)
create mode 100644 gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheAutoConfiguration.java
create mode 100644 gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheOperations.java
create mode 100644 gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheProperties.java
create mode 100644 gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CaffeineCacheOperations.java
create mode 100644 gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/RedisCacheOperations.java
diff --git a/gym-manage-api/gym-auth/src/main/java/cn/novalon/gym/manage/auth/service/impl/SmsServiceImpl.java b/gym-manage-api/gym-auth/src/main/java/cn/novalon/gym/manage/auth/service/impl/SmsServiceImpl.java
index c04c196..d8b78f3 100644
--- a/gym-manage-api/gym-auth/src/main/java/cn/novalon/gym/manage/auth/service/impl/SmsServiceImpl.java
+++ b/gym-manage-api/gym-auth/src/main/java/cn/novalon/gym/manage/auth/service/impl/SmsServiceImpl.java
@@ -3,7 +3,7 @@ package cn.novalon.gym.manage.auth.service.impl;
import cn.novalon.gym.manage.auth.config.SmsProperties;
import cn.novalon.gym.manage.auth.service.SmsService;
import cn.novalon.gym.manage.common.constant.RedisKeyConstants;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
@@ -31,7 +31,7 @@ public class SmsServiceImpl implements SmsService {
private static final long CODE_EXPIRE_SECONDS = 300;
private final SmsProperties smsProperties;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private final ObjectMapper objectMapper;
@Override
@@ -40,7 +40,7 @@ public class SmsServiceImpl implements SmsService {
String rateLimitKey = RedisKeyConstants.SMS_CODE + phone + ":rate_limit";
- return redisUtil.get(rateLimitKey, Long.class)
+ return cacheOperations.get(rateLimitKey, Long.class)
.defaultIfEmpty(0L)
.flatMap(lastSendTime -> {
long currentTime = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
@@ -97,7 +97,7 @@ public class SmsServiceImpl implements SmsService {
if (verifyCodeNode != null) {
String verifyCode = verifyCodeNode.asText();
String smsCodeKey = RedisKeyConstants.SMS_CODE + phone;
- redisUtil.setWithExpire(smsCodeKey, verifyCode, CODE_EXPIRE_SECONDS).subscribe();
+ cacheOperations.setWithExpire(smsCodeKey, verifyCode, CODE_EXPIRE_SECONDS).subscribe();
log.info("验证码已存入Redis, phone: {}, key: {}, expire: {}秒", phone, smsCodeKey, CODE_EXPIRE_SECONDS);
} else {
log.warn("响应中未找到Model.VerifyCode字段, 原始响应: {}", responseData);
@@ -106,7 +106,7 @@ public class SmsServiceImpl implements SmsService {
log.warn("响应中未找到Model字段, 原始响应: {}", responseData);
}
- redisUtil.setWithExpire(rateLimitKey, currentTime, SEND_INTERVAL_SECONDS).subscribe();
+ cacheOperations.setWithExpire(rateLimitKey, currentTime, SEND_INTERVAL_SECONDS).subscribe();
return true;
}
@@ -180,7 +180,7 @@ public class SmsServiceImpl implements SmsService {
public Mono getVerificationCode(String phone) {
try {
String cacheKey = RedisKeyConstants.SMS_CODE + phone;
- return redisUtil.get(cacheKey, String.class);
+ return cacheOperations.get(cacheKey, String.class);
} catch (Exception e) {
log.error("获取验证码异常, phone: {}", phone, e);
return Mono.empty();
diff --git a/gym-manage-api/gym-checkIn/src/main/java/cn/novalon/gym/manage/checkIn/service/impl/CheckServiceImpl.java b/gym-manage-api/gym-checkIn/src/main/java/cn/novalon/gym/manage/checkIn/service/impl/CheckServiceImpl.java
index adf9177..6a004c2 100644
--- a/gym-manage-api/gym-checkIn/src/main/java/cn/novalon/gym/manage/checkIn/service/impl/CheckServiceImpl.java
+++ b/gym-manage-api/gym-checkIn/src/main/java/cn/novalon/gym/manage/checkIn/service/impl/CheckServiceImpl.java
@@ -14,7 +14,7 @@ import cn.novalon.gym.manage.checkIn.vo.SignInRecordVO;
import cn.novalon.gym.manage.checkIn.vo.SignInStatsVO;
import cn.novalon.gym.manage.checkIn.websocket.MyWebSocketHandler;
import cn.novalon.gym.manage.common.constant.RedisKeyConstants;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourseBooking;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseBookingRepository;
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseBookingService;
@@ -44,7 +44,7 @@ import java.util.Map;
public class CheckServiceImpl implements ICheckInService {
private final QRCodeConfig qrCodeConfig;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private final MemberCardRecordRepository memberCardRecordRepository;
private final MemberCardRepository memberCardRepository;
private final SignInRecordRepository signInRecordRepository;
@@ -63,7 +63,7 @@ public class CheckServiceImpl implements ICheckInService {
redisMap.put("isUsed", false);
redisMap.put("memberId", memberId);
- return redisUtil.setWithExpire(
+ return cacheOperations.setWithExpire(
RedisKeyConstants.QRCODE_USER_DAILY + memberId + LocalDate.now(),
redisMap,
getSecondsUntilEndOfDay()
@@ -87,7 +87,7 @@ public class CheckServiceImpl implements ICheckInService {
MyWebSocketHandler.sendFailure(qrContent, "您已经在" + checkInTime + "完成签到,请勿重复签到");
return Mono.error(new RuntimeException("您已经在" + checkInTime + "完成签到,请勿重复签到"));
})
- .then(Mono.defer(() -> redisUtil.get(key)))
+ .then(Mono.defer(() -> cacheOperations.get(key)))
.flatMap(cachedObj -> {
if (cachedObj != null) {
Map map;
@@ -157,11 +157,11 @@ public class CheckServiceImpl implements ICheckInService {
redisMap.put("checkInTime", now.format(DATE_FORMATTER));
return saveSignInRecord(memberId, null, null)
- .then(redisUtil.set(RedisKeyConstants.QRCODE_USER_DAILY + memberId + LocalDate.now(), redisMap))
+ .then(cacheOperations.set(RedisKeyConstants.QRCODE_USER_DAILY + memberId + LocalDate.now(), redisMap))
// 清除统计缓存和课程缓存,确保管理端/教练端立即反映最新数据
.then(Mono.defer(() ->
- redisUtil.deleteByPattern("datacount:statistics:*")
- .then(Mono.defer(() -> redisUtil.deleteByPattern("group_course:*")))
+ cacheOperations.deleteByPattern("datacount:statistics:*")
+ .then(Mono.defer(() -> cacheOperations.deleteByPattern("group_course:*")))
.doOnSuccess(v -> log.info("已清除统计缓存和课程缓存, memberId: {}", memberId))
.then()
))
diff --git a/gym-manage-api/gym-checkIn/src/test/java/cn/novalon/gym/manage/checkin/CheckInModuleTest.java b/gym-manage-api/gym-checkIn/src/test/java/cn/novalon/gym/manage/checkin/CheckInModuleTest.java
index 3b8616e..bdf9007 100644
--- a/gym-manage-api/gym-checkIn/src/test/java/cn/novalon/gym/manage/checkin/CheckInModuleTest.java
+++ b/gym-manage-api/gym-checkIn/src/test/java/cn/novalon/gym/manage/checkin/CheckInModuleTest.java
@@ -10,7 +10,7 @@ import cn.novalon.gym.manage.checkIn.vo.QRCodeVo;
import cn.novalon.gym.manage.checkIn.vo.SignInRecordVO;
import cn.novalon.gym.manage.checkIn.vo.SignInStatsVO;
import cn.novalon.gym.manage.common.constant.RedisKeyConstants;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.entity.MemberCard;
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.repository.MemberCardRecordRepository;
@@ -44,7 +44,7 @@ class CheckInModuleTest {
private QRCodeConfig qrCodeConfig;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@Mock
private MemberCardRecordRepository memberCardRecordRepository;
@@ -75,7 +75,7 @@ class CheckInModuleTest {
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
- checkService = new CheckServiceImpl(qrCodeConfig, redisUtil, memberCardRecordRepository,
+ checkService = new CheckServiceImpl(qrCodeConfig, cacheOperations, memberCardRecordRepository,
memberCardRepository, signInRecordRepository, groupCourseBookingService,
groupCourseBookingRepository);
@@ -103,7 +103,7 @@ class CheckInModuleTest {
void testGetQRCode() {
when(memberCardRecordRepository.findActiveCardsByMemberId(1L))
.thenReturn(Flux.just(mockMemberCardRecord));
- when(redisUtil.setWithExpire(any(String.class), any(Map.class), any(Long.class)))
+ when(cacheOperations.setWithExpire(any(String.class), any(Map.class), any(Long.class)))
.thenReturn(Mono.just(true));
Mono result = checkService.getQRCode(1L);
@@ -130,12 +130,12 @@ class CheckInModuleTest {
String key = RedisKeyConstants.QRCODE_USER_DAILY + memberId + LocalDate.now();
- when(redisUtil.get(eq(key))).thenReturn(Mono.just(qrData));
- when(redisUtil.deleteByPattern(any(String.class))).thenReturn(Mono.empty());
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.just(qrData));
+ when(cacheOperations.deleteByPattern(any(String.class))).thenReturn(Mono.empty());
when(memberCardRecordRepository.findById(1L)).thenReturn(Mono.just(mockMemberCardRecord));
when(memberCardRepository.findByMemberCardIdAndDeletedAtIsNull(1L)).thenReturn(Mono.just(mockMemberCard));
when(signInRecordRepository.save(any(SignInRecord.class))).thenReturn(Mono.just(mockSignInRecord));
- when(redisUtil.set(any(String.class), any(Map.class))).thenReturn(Mono.just(true));
+ when(cacheOperations.set(any(String.class), any(Map.class))).thenReturn(Mono.just(true));
when(groupCourseBookingService.getBookingsByMemberId(memberId)).thenReturn(Flux.empty());
when(signInRecordRepository.findByMemberIdAndDate(eq(memberId), any(LocalDateTime.class), any(LocalDateTime.class)))
.thenReturn(Mono.empty());
@@ -259,7 +259,7 @@ class CheckInModuleTest {
void testCheckIn_QRCodeInvalid() {
Long memberId = 1L;
String key = RedisKeyConstants.QRCODE_USER_DAILY + memberId + LocalDate.now();
- when(redisUtil.get(eq(key))).thenReturn(Mono.just(new HashMap<>()));
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.just(new HashMap<>()));
when(signInRecordRepository.findByMemberIdAndDate(eq(memberId), any(LocalDateTime.class), any(LocalDateTime.class)))
.thenReturn(Mono.empty());
@@ -275,7 +275,7 @@ class CheckInModuleTest {
void testCheckIn_QRCodeNotFound() {
Long memberId = 1L;
String key = RedisKeyConstants.QRCODE_USER_DAILY + memberId + LocalDate.now();
- when(redisUtil.get(eq(key))).thenReturn(Mono.empty());
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.empty());
when(signInRecordRepository.findByMemberIdAndDate(eq(memberId), any(LocalDateTime.class), any(LocalDateTime.class)))
.thenReturn(Mono.empty());
diff --git a/gym-manage-api/gym-checkIn/src/test/java/cn/novalon/gym/manage/checkin/service/impl/CheckServiceImplTest.java b/gym-manage-api/gym-checkIn/src/test/java/cn/novalon/gym/manage/checkin/service/impl/CheckServiceImplTest.java
index 35b4405..055fb19 100644
--- a/gym-manage-api/gym-checkIn/src/test/java/cn/novalon/gym/manage/checkin/service/impl/CheckServiceImplTest.java
+++ b/gym-manage-api/gym-checkIn/src/test/java/cn/novalon/gym/manage/checkin/service/impl/CheckServiceImplTest.java
@@ -8,7 +8,7 @@ import cn.novalon.gym.manage.checkIn.vo.QRCodeVo;
import cn.novalon.gym.manage.checkIn.vo.SignInRecordVO;
import cn.novalon.gym.manage.checkIn.vo.SignInStatsVO;
import cn.novalon.gym.manage.common.constant.RedisKeyConstants;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourseBooking;
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseBookingRepository;
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseBookingService;
@@ -40,7 +40,7 @@ class CheckServiceImplTest {
private QRCodeConfig qrCodeConfig;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@Mock
private MemberCardRecordRepository memberCardRecordRepository;
@@ -70,7 +70,7 @@ class CheckServiceImplTest {
void getQRCode_shouldReturnQRCodeVo() {
when(qrCodeConfig.getWidth()).thenReturn(300);
when(qrCodeConfig.getHeight()).thenReturn(300);
- when(redisUtil.setWithExpire(anyString(), any(Map.class), anyLong()))
+ when(cacheOperations.setWithExpire(anyString(), any(Map.class), anyLong()))
.thenReturn(Mono.just(true));
Mono result = checkService.getQRCode(MEMBER_ID);
@@ -100,7 +100,7 @@ class CheckServiceImplTest {
// Redis 中有有效二维码数据
String key = RedisKeyConstants.QRCODE_USER_DAILY + MEMBER_ID + LocalDate.now();
Map qrData = buildQrData(false);
- when(redisUtil.get(eq(key))).thenReturn(Mono.just(qrData));
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.just(qrData));
// 无预约
when(groupCourseBookingService.getBookingsByMemberId(MEMBER_ID)).thenReturn(Flux.empty());
@@ -110,10 +110,10 @@ class CheckServiceImplTest {
.thenReturn(Mono.just(createMockSignInRecord()));
// 更新缓存
- when(redisUtil.set(anyString(), any(Map.class))).thenReturn(Mono.just(true));
+ when(cacheOperations.set(anyString(), any(Map.class))).thenReturn(Mono.just(true));
// 清除缓存
- when(redisUtil.deleteByPattern(anyString())).thenReturn(Mono.empty());
+ when(cacheOperations.deleteByPattern(anyString())).thenReturn(Mono.empty());
Mono result = checkService.checkIn(MEMBER_ID, QR_CONTENT);
@@ -122,8 +122,8 @@ class CheckServiceImplTest {
.verifyComplete();
verify(signInRecordRepository).save(any(SignInRecord.class));
- verify(redisUtil).set(anyString(), any(Map.class));
- verify(redisUtil, times(2)).deleteByPattern(anyString());
+ verify(cacheOperations).set(anyString(), any(Map.class));
+ verify(cacheOperations, times(2)).deleteByPattern(anyString());
}
@Test
@@ -136,7 +136,7 @@ class CheckServiceImplTest {
// Redis 中有有效二维码数据
String key = RedisKeyConstants.QRCODE_USER_DAILY + MEMBER_ID + LocalDate.now();
Map qrData = buildQrData(false);
- when(redisUtil.get(eq(key))).thenReturn(Mono.just(qrData));
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.just(qrData));
// 有有效预约
GroupCourseBooking booking = createValidBooking();
@@ -148,10 +148,10 @@ class CheckServiceImplTest {
.thenReturn(Mono.just(createMockSignInRecord()));
// 更新缓存
- when(redisUtil.set(anyString(), any(Map.class))).thenReturn(Mono.just(true));
+ when(cacheOperations.set(anyString(), any(Map.class))).thenReturn(Mono.just(true));
// 清除缓存
- when(redisUtil.deleteByPattern(anyString())).thenReturn(Mono.empty());
+ when(cacheOperations.deleteByPattern(anyString())).thenReturn(Mono.empty());
Mono result = checkService.checkIn(MEMBER_ID, QR_CONTENT);
@@ -172,7 +172,7 @@ class CheckServiceImplTest {
// Redis 中缓存数据为 JSON 字符串格式
String key = RedisKeyConstants.QRCODE_USER_DAILY + MEMBER_ID + LocalDate.now();
String jsonData = "{\"qrContent\":\"" + QR_CONTENT + "\",\"isUsed\":false,\"memberId\":" + MEMBER_ID + "}";
- when(redisUtil.get(eq(key))).thenReturn(Mono.just(jsonData));
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.just(jsonData));
// 无预约
when(groupCourseBookingService.getBookingsByMemberId(MEMBER_ID)).thenReturn(Flux.empty());
@@ -182,10 +182,10 @@ class CheckServiceImplTest {
.thenReturn(Mono.just(createMockSignInRecord()));
// 更新缓存
- when(redisUtil.set(anyString(), any(Map.class))).thenReturn(Mono.just(true));
+ when(cacheOperations.set(anyString(), any(Map.class))).thenReturn(Mono.just(true));
// 清除缓存
- when(redisUtil.deleteByPattern(anyString())).thenReturn(Mono.empty());
+ when(cacheOperations.deleteByPattern(anyString())).thenReturn(Mono.empty());
Mono result = checkService.checkIn(MEMBER_ID, QR_CONTENT);
@@ -217,7 +217,7 @@ class CheckServiceImplTest {
&& e.getMessage().contains("请勿重复签到"))
.verify();
- verify(redisUtil, never()).get(anyString());
+ verify(cacheOperations, never()).get(anyString());
}
@Test
@@ -231,7 +231,7 @@ class CheckServiceImplTest {
String key = RedisKeyConstants.QRCODE_USER_DAILY + MEMBER_ID + LocalDate.now();
Map qrData = buildQrData(true);
qrData.put("checkInTime", "2026-07-31 10:00:00");
- when(redisUtil.get(eq(key))).thenReturn(Mono.just(qrData));
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.just(qrData));
Mono result = checkService.checkIn(MEMBER_ID, QR_CONTENT);
@@ -252,7 +252,7 @@ class CheckServiceImplTest {
String key = RedisKeyConstants.QRCODE_USER_DAILY + MEMBER_ID + LocalDate.now();
Map qrData = buildQrData(false);
qrData.put("qrContent", "different-qr-content");
- when(redisUtil.get(eq(key))).thenReturn(Mono.just(qrData));
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.just(qrData));
Mono result = checkService.checkIn(MEMBER_ID, QR_CONTENT);
@@ -271,7 +271,7 @@ class CheckServiceImplTest {
// Redis 中无数据
String key = RedisKeyConstants.QRCODE_USER_DAILY + MEMBER_ID + LocalDate.now();
- when(redisUtil.get(eq(key))).thenReturn(Mono.empty());
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.empty());
Mono result = checkService.checkIn(MEMBER_ID, "not-exist");
@@ -289,7 +289,7 @@ class CheckServiceImplTest {
// Redis 返回非 Map 非 String 的数据
String key = RedisKeyConstants.QRCODE_USER_DAILY + MEMBER_ID + LocalDate.now();
- when(redisUtil.get(eq(key))).thenReturn(Mono.just(12345));
+ when(cacheOperations.get(eq(key))).thenReturn(Mono.just(12345));
Mono result = checkService.checkIn(MEMBER_ID, QR_CONTENT);
diff --git a/gym-manage-api/gym-coach-config/src/main/java/cn/novalon/gym/manage/coachconfig/service/CoachTimeRuleService.java b/gym-manage-api/gym-coach-config/src/main/java/cn/novalon/gym/manage/coachconfig/service/CoachTimeRuleService.java
index d097916..8c39d35 100644
--- a/gym-manage-api/gym-coach-config/src/main/java/cn/novalon/gym/manage/coachconfig/service/CoachTimeRuleService.java
+++ b/gym-manage-api/gym-coach-config/src/main/java/cn/novalon/gym/manage/coachconfig/service/CoachTimeRuleService.java
@@ -3,7 +3,7 @@ package cn.novalon.gym.manage.coachconfig.service;
import cn.novalon.gym.manage.coachconfig.domain.CoachTimeRule;
import cn.novalon.gym.manage.coachconfig.repository.ICoachTimeRuleRepository;
import cn.novalon.gym.manage.common.constant.RedisKeyConstants;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@@ -43,11 +43,11 @@ public class CoachTimeRuleService {
private static final long CACHE_TTL_SECONDS = 300;
private final ICoachTimeRuleRepository repository;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
- public CoachTimeRuleService(ICoachTimeRuleRepository repository, RedisUtil redisUtil) {
+ public CoachTimeRuleService(ICoachTimeRuleRepository repository, CacheOperations cacheOperations) {
this.repository = repository;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
}
// ==================== 规则匹配 ====================
@@ -118,7 +118,7 @@ public class CoachTimeRuleService {
* 获取所有启用规则(带 Redis 缓存)
*/
private Flux getActiveRules() {
- return redisUtil.get(RedisKeyConstants.COACH_TIME_RULES)
+ return cacheOperations.get(RedisKeyConstants.COACH_TIME_RULES)
.flatMapMany(cached -> {
@SuppressWarnings("unchecked")
List list = (List) cached;
@@ -130,7 +130,7 @@ public class CoachTimeRuleService {
return repository.findByStatusAndDeletedAtIsNull("1")
.collectList()
.flatMapMany(list -> {
- redisUtil.setWithExpire(RedisKeyConstants.COACH_TIME_RULES, list, CACHE_TTL_SECONDS)
+ cacheOperations.setWithExpire(RedisKeyConstants.COACH_TIME_RULES, list, CACHE_TTL_SECONDS)
.subscribe(
ok -> logger.debug("教练时间规则已写入 Redis 缓存,共 {} 条", list.size()),
err -> logger.warn("教练时间规则写入 Redis 缓存失败: {}", err.getMessage())
@@ -144,7 +144,7 @@ public class CoachTimeRuleService {
* 写操作后清除缓存(热更新入口)
*/
private void invalidateCache() {
- redisUtil.delete(RedisKeyConstants.COACH_TIME_RULES)
+ cacheOperations.delete(RedisKeyConstants.COACH_TIME_RULES)
.subscribe(
count -> logger.info("教练时间规则缓存已清除"),
err -> logger.warn("教练时间规则缓存清除失败: {}", err.getMessage())
diff --git a/gym-manage-api/gym-coach/src/main/java/cn/novalon/gym/manage/coach/scheduler/CoachCourseScheduler.java b/gym-manage-api/gym-coach/src/main/java/cn/novalon/gym/manage/coach/scheduler/CoachCourseScheduler.java
index 09250c9..18b29fb 100644
--- a/gym-manage-api/gym-coach/src/main/java/cn/novalon/gym/manage/coach/scheduler/CoachCourseScheduler.java
+++ b/gym-manage-api/gym-coach/src/main/java/cn/novalon/gym/manage/coach/scheduler/CoachCourseScheduler.java
@@ -2,7 +2,7 @@ package cn.novalon.gym.manage.coach.scheduler;
import cn.novalon.gym.manage.coach.enums.ViolationReason;
import cn.novalon.gym.manage.coachconfig.service.CoachTimeRuleService;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseBookingDao;
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseDao;
import cn.novalon.gym.manage.groupcourse.entity.GroupCourseEntity;
@@ -35,18 +35,18 @@ public class CoachCourseScheduler {
private final GroupCourseDao groupCourseDao;
private final GroupCourseBookingDao groupCourseBookingDao;
private final DatabaseClient databaseClient;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private final CoachTimeRuleService timeRuleService;
public CoachCourseScheduler(GroupCourseDao groupCourseDao,
GroupCourseBookingDao groupCourseBookingDao,
DatabaseClient databaseClient,
- RedisUtil redisUtil,
+ CacheOperations cacheOperations,
CoachTimeRuleService timeRuleService) {
this.groupCourseDao = groupCourseDao;
this.groupCourseBookingDao = groupCourseBookingDao;
this.databaseClient = databaseClient;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
this.timeRuleService = timeRuleService;
}
@@ -166,17 +166,17 @@ public class CoachCourseScheduler {
/**
* 清除统计缓存和团课缓存 —— 调度器触发时,如有课程状态变更则必须及时失效。
*
- * 注意:测试环境中 RedisUtil 可能被 Mock 返回 null,需做 null 安全处理。
+ * 注意:测试环境中 CacheOperations 可能被 Mock 返回 null,需做 null 安全处理。
*/
private void invalidateCache() {
- Mono statsMono = redisUtil.deleteByPattern("datacount:statistics:*");
+ Mono statsMono = cacheOperations.deleteByPattern("datacount:statistics:*");
if (statsMono != null) {
statsMono.subscribe(
deleted -> logger.debug("调度器清除统计缓存,已删除 {} 条", deleted),
error -> logger.warn("调度器清除统计缓存失败: {}", error.getMessage())
);
}
- Mono courseMono = redisUtil.deleteByPattern("group_course:*");
+ Mono courseMono = cacheOperations.deleteByPattern("group_course:*");
if (courseMono != null) {
courseMono.subscribe(
deleted -> logger.debug("调度器清除团课缓存,已删除 {} 条", deleted),
diff --git a/gym-manage-api/gym-coach/src/main/java/cn/novalon/gym/manage/coach/service/CoachCourseService.java b/gym-manage-api/gym-coach/src/main/java/cn/novalon/gym/manage/coach/service/CoachCourseService.java
index 84cb4a9..7a2c9d4 100644
--- a/gym-manage-api/gym-coach/src/main/java/cn/novalon/gym/manage/coach/service/CoachCourseService.java
+++ b/gym-manage-api/gym-coach/src/main/java/cn/novalon/gym/manage/coach/service/CoachCourseService.java
@@ -5,7 +5,7 @@ import cn.novalon.gym.manage.coach.entity.CoachViolationEntity;
import cn.novalon.gym.manage.coach.enums.ViolationReason;
import cn.novalon.gym.manage.coachconfig.domain.CoachTimeRule;
import cn.novalon.gym.manage.coachconfig.service.CoachTimeRuleService;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.common.util.StatusConstants;
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseBookingDao;
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseDao;
@@ -56,7 +56,7 @@ public class CoachCourseService {
private final CoachViolationDao violationDao;
private final DatabaseClient databaseClient;
private final PasswordEncoder passwordEncoder;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private final CoachTimeRuleService timeRuleService;
public CoachCourseService(ISysUserRepository userRepository,
@@ -69,7 +69,7 @@ public class CoachCourseService {
CoachViolationDao violationDao,
DatabaseClient databaseClient,
PasswordEncoder passwordEncoder,
- RedisUtil redisUtil,
+ CacheOperations cacheOperations,
CoachTimeRuleService timeRuleService) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
@@ -81,7 +81,7 @@ public class CoachCourseService {
this.violationDao = violationDao;
this.databaseClient = databaseClient;
this.passwordEncoder = passwordEncoder;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
this.timeRuleService = timeRuleService;
}
@@ -334,8 +334,8 @@ public class CoachCourseService {
* 清除统计缓存和团课缓存 —— 课程状态变更后调用,返回 Mono 确保链式执行
*/
private Mono invalidateStatisticsCache() {
- return redisUtil.deleteByPattern("datacount:statistics:*")
- .then(redisUtil.deleteByPattern("group_course:*"))
+ return cacheOperations.deleteByPattern("datacount:statistics:*")
+ .then(cacheOperations.deleteByPattern("group_course:*"))
.doOnNext(count -> {})
.then();
}
diff --git a/gym-manage-api/gym-coach/src/test/java/cn/novalon/gym/manage/coach/scheduler/CoachCourseSchedulerTest.java b/gym-manage-api/gym-coach/src/test/java/cn/novalon/gym/manage/coach/scheduler/CoachCourseSchedulerTest.java
index 00d5e74..29a1d02 100644
--- a/gym-manage-api/gym-coach/src/test/java/cn/novalon/gym/manage/coach/scheduler/CoachCourseSchedulerTest.java
+++ b/gym-manage-api/gym-coach/src/test/java/cn/novalon/gym/manage/coach/scheduler/CoachCourseSchedulerTest.java
@@ -3,7 +3,7 @@ package cn.novalon.gym.manage.coach.scheduler;
import cn.novalon.gym.manage.coach.enums.ViolationReason;
import cn.novalon.gym.manage.coachconfig.domain.CoachTimeRule;
import cn.novalon.gym.manage.coachconfig.service.CoachTimeRuleService;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseBookingDao;
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseDao;
import cn.novalon.gym.manage.groupcourse.entity.GroupCourseEntity;
@@ -38,7 +38,7 @@ class CoachCourseSchedulerTest {
private DatabaseClient databaseClient;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@Mock
private CoachTimeRuleService timeRuleService;
@@ -54,7 +54,7 @@ class CoachCourseSchedulerTest {
void setUp() {
scheduler = new CoachCourseScheduler(
groupCourseDao, groupCourseBookingDao,
- databaseClient, redisUtil, timeRuleService
+ databaseClient, cacheOperations, timeRuleService
);
}
@@ -372,7 +372,7 @@ class CoachCourseSchedulerTest {
verify(groupCourseDao).findByStatusInAndEndTimeBefore(eq(databaseClient), any(String[].class), any(LocalDateTime.class));
// 验证未触发缓存清除(count=0,不进入 if 分支)
- verifyNoInteractions(redisUtil);
+ verifyNoInteractions(cacheOperations);
}
@Test
@@ -403,20 +403,20 @@ class CoachCourseSchedulerTest {
.thenReturn(Flux.empty());
// mock RedisUtil 返回非 null 的 Mono
- when(redisUtil.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
- when(redisUtil.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
// 执行
scheduler.checkAndProcessCourses();
// 验证缓存清除被调用
- verify(redisUtil).deleteByPattern("datacount:statistics:*");
- verify(redisUtil).deleteByPattern("group_course:*");
+ verify(cacheOperations).deleteByPattern("datacount:statistics:*");
+ verify(cacheOperations).deleteByPattern("group_course:*");
}
@Test
- void checkAndProcessCourses_shouldHandleRedisUtilReturningNull() {
- // 准备:测试 RedisUtil 返回 null 的边界情况
+ void checkAndProcessCourses_shouldHandleCacheOperationsReturningNull() {
+ // 准备:测试 CacheOperations 返回 null 的边界情况
LocalDateTime startTime = LocalDateTime.of(2026, 7, 31, 10, 0);
LocalDateTime endTime = LocalDateTime.of(2026, 7, 31, 11, 0);
@@ -440,15 +440,15 @@ class CoachCourseSchedulerTest {
.thenReturn(Flux.empty());
// RedisUtil.deleteByPattern 返回 null(模拟 null 安全检查)
- when(redisUtil.deleteByPattern("datacount:statistics:*")).thenReturn(null);
- when(redisUtil.deleteByPattern("group_course:*")).thenReturn(null);
+ when(cacheOperations.deleteByPattern("datacount:statistics:*")).thenReturn(null);
+ when(cacheOperations.deleteByPattern("group_course:*")).thenReturn(null);
// 执行:不应抛出 NPE
scheduler.checkAndProcessCourses();
// 验证:RedisUtil 被调用(即使返回 null)
- verify(redisUtil).deleteByPattern("datacount:statistics:*");
- verify(redisUtil).deleteByPattern("group_course:*");
+ verify(cacheOperations).deleteByPattern("datacount:statistics:*");
+ verify(cacheOperations).deleteByPattern("group_course:*");
}
@Test
@@ -465,6 +465,6 @@ class CoachCourseSchedulerTest {
// 验证
verify(groupCourseDao).findByStatusAndStartTimeBefore(eq(databaseClient), eq("0"), any(LocalDateTime.class));
verify(groupCourseDao).findByStatusInAndEndTimeBefore(eq(databaseClient), any(String[].class), any(LocalDateTime.class));
- verifyNoInteractions(redisUtil);
+ verifyNoInteractions(cacheOperations);
}
}
\ No newline at end of file
diff --git a/gym-manage-api/gym-coach/src/test/java/cn/novalon/gym/manage/coach/service/CoachCourseServiceTest.java b/gym-manage-api/gym-coach/src/test/java/cn/novalon/gym/manage/coach/service/CoachCourseServiceTest.java
index ffcc8c1..8302656 100644
--- a/gym-manage-api/gym-coach/src/test/java/cn/novalon/gym/manage/coach/service/CoachCourseServiceTest.java
+++ b/gym-manage-api/gym-coach/src/test/java/cn/novalon/gym/manage/coach/service/CoachCourseServiceTest.java
@@ -4,7 +4,7 @@ import cn.novalon.gym.manage.coach.dao.CoachViolationDao;
import cn.novalon.gym.manage.coach.enums.ViolationReason;
import cn.novalon.gym.manage.coachconfig.domain.CoachTimeRule;
import cn.novalon.gym.manage.coachconfig.service.CoachTimeRuleService;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.common.util.StatusConstants;
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseBookingDao;
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseDao;
@@ -76,7 +76,7 @@ class CoachCourseServiceTest {
private PasswordEncoder passwordEncoder;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@Mock
private CoachTimeRuleService timeRuleService;
@@ -95,7 +95,7 @@ class CoachCourseServiceTest {
groupCourseRepository, bookingRepository,
groupCourseDao, groupCourseBookingDao,
violationDao, databaseClient,
- passwordEncoder, redisUtil, timeRuleService
+ passwordEncoder, cacheOperations, timeRuleService
);
}
@@ -328,8 +328,8 @@ class CoachCourseServiceTest {
updatedUser.setStatus(StatusConstants.DISABLED);
when(userRepository.update(any(SysUser.class))).thenReturn(Mono.just(updatedUser));
- when(redisUtil.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
- when(redisUtil.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
StepVerifier.create(coachCourseService.disableCoach(COACH_USER_ID))
.verifyComplete();
@@ -338,15 +338,15 @@ class CoachCourseServiceTest {
verify(groupCourseRepository).countByCoachIdAndStatus(COACH_USER_ID, 3L);
verify(groupCourseRepository).cancelCoursesByCoachIdExceptStatus(COACH_USER_ID, 3L);
verify(userRepository).update(any(SysUser.class));
- verify(redisUtil).deleteByPattern("datacount:statistics:*");
- verify(redisUtil).deleteByPattern("group_course:*");
+ verify(cacheOperations).deleteByPattern("datacount:statistics:*");
+ verify(cacheOperations).deleteByPattern("group_course:*");
}
@Test
void disableCoach_shouldThrowWhenCoachNotFound() {
when(userRepository.findById(COACH_USER_ID)).thenReturn(Mono.empty());
// invalidateStatisticsCache() 会在 .then() 参数求值时被调用,需要 stub
- lenient().when(redisUtil.deleteByPattern(anyString())).thenReturn(Mono.just(1L));
+ lenient().when(cacheOperations.deleteByPattern(anyString())).thenReturn(Mono.just(1L));
StepVerifier.create(coachCourseService.disableCoach(COACH_USER_ID))
.expectErrorMatches(e -> e instanceof RuntimeException
@@ -363,7 +363,7 @@ class CoachCourseServiceTest {
when(groupCourseRepository.countByCoachIdAndStatus(COACH_USER_ID, 3L)).thenReturn(Mono.just(2L));
// invalidateStatisticsCache() 会在 .then() 参数求值时被调用,需要 stub
- lenient().when(redisUtil.deleteByPattern(anyString())).thenReturn(Mono.just(1L));
+ lenient().when(cacheOperations.deleteByPattern(anyString())).thenReturn(Mono.just(1L));
StepVerifier.create(coachCourseService.disableCoach(COACH_USER_ID))
.expectErrorMatches(e -> e instanceof RuntimeException
@@ -461,8 +461,8 @@ class CoachCourseServiceTest {
when(groupCourseDao.updateStartInfo(eq(COURSE_ID), eq("3"), any(LocalDateTime.class), any(LocalDateTime.class))).thenReturn(Mono.just(1));
- when(redisUtil.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
- when(redisUtil.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
StepVerifier.create(coachCourseService.startCourse(COURSE_ID, COACH_USER_ID))
.assertNext(entity -> {
@@ -500,8 +500,8 @@ class CoachCourseServiceTest {
when(groupCourseDao.updateStartInfo(eq(COURSE_ID), eq("7"), any(LocalDateTime.class), any(LocalDateTime.class))).thenReturn(Mono.just(1));
- when(redisUtil.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
- when(redisUtil.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
StepVerifier.create(coachCourseService.startCourse(COURSE_ID, COACH_USER_ID))
.assertNext(entity -> {
@@ -608,8 +608,8 @@ class CoachCourseServiceTest {
when(groupCourseDao.updateEndInfo(eq(COURSE_ID), eq("2"), any(LocalDateTime.class), any(LocalDateTime.class))).thenReturn(Mono.just(1));
- when(redisUtil.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
- when(redisUtil.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
StepVerifier.create(coachCourseService.endCourse(COURSE_ID, COACH_USER_ID))
.assertNext(entity -> {
@@ -697,8 +697,8 @@ class CoachCourseServiceTest {
when(groupCourseDao.updateEndInfo(eq(COURSE_ID), eq("2"), any(LocalDateTime.class), any(LocalDateTime.class))).thenReturn(Mono.just(1));
- when(redisUtil.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
- when(redisUtil.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(1L));
+ when(cacheOperations.deleteByPattern("group_course:*")).thenReturn(Mono.just(1L));
StepVerifier.create(coachCourseService.endCourse(COURSE_ID, COACH_USER_ID))
.assertNext(entity -> {
diff --git a/gym-manage-api/gym-dataCount/src/main/java/cn/novalon/gym/manage/datacount/scheduler/DataStatisticsScheduler.java b/gym-manage-api/gym-dataCount/src/main/java/cn/novalon/gym/manage/datacount/scheduler/DataStatisticsScheduler.java
index 65ae641..c988b94 100644
--- a/gym-manage-api/gym-dataCount/src/main/java/cn/novalon/gym/manage/datacount/scheduler/DataStatisticsScheduler.java
+++ b/gym-manage-api/gym-dataCount/src/main/java/cn/novalon/gym/manage/datacount/scheduler/DataStatisticsScheduler.java
@@ -1,5 +1,6 @@
package cn.novalon.gym.manage.datacount.scheduler;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.datacount.domain.DataStatistics;
import cn.novalon.gym.manage.datacount.service.IDataStatisticsService;
import org.slf4j.Logger;
@@ -98,7 +99,7 @@ public class DataStatisticsScheduler {
// 清理Redis中的旧统计数据
String pattern = "datacount:statistics:*:" + cutoffDate.toString();
- cn.novalon.gym.manage.common.util.RedisUtil redisUtil = null;
+ CacheOperations cacheOperations = null;
try {
// 这里可以通过注入的service来清理,但当前实现使用Redis缓存30天自动过期
log.info("Old statistics cleanup completed, cutoff date: {}", cutoffDate);
diff --git a/gym-manage-api/gym-dataCount/src/main/java/cn/novalon/gym/manage/datacount/service/impl/DataStatisticsServiceImpl.java b/gym-manage-api/gym-dataCount/src/main/java/cn/novalon/gym/manage/datacount/service/impl/DataStatisticsServiceImpl.java
index 1c7fc79..87ee272 100644
--- a/gym-manage-api/gym-dataCount/src/main/java/cn/novalon/gym/manage/datacount/service/impl/DataStatisticsServiceImpl.java
+++ b/gym-manage-api/gym-dataCount/src/main/java/cn/novalon/gym/manage/datacount/service/impl/DataStatisticsServiceImpl.java
@@ -1,7 +1,7 @@
package cn.novalon.gym.manage.datacount.service.impl;
import cn.novalon.gym.manage.checkIn.entity.SignInRecord;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.datacount.dao.DataStatisticsDao;
import cn.novalon.gym.manage.datacount.domain.*;
import cn.novalon.gym.manage.datacount.service.IDataStatisticsService;
@@ -46,7 +46,7 @@ public class DataStatisticsServiceImpl implements IDataStatisticsService {
private DataStatisticsDao dataStatisticsDao;
@Autowired
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@Autowired
private ObjectMapper objectMapper;
@@ -223,7 +223,7 @@ public class DataStatisticsServiceImpl implements IDataStatisticsService {
public reactor.core.publisher.Flux queryHistoricalStatistics(StatisticsQuery query) {
// 历史统计数据查询(从Redis缓存中获取)
String cacheKey = buildCacheKey(query);
- return redisUtil.get(cacheKey, String.class)
+ return cacheOperations.get(cacheKey, String.class)
.flatMapMany(json -> {
try {
java.util.List stats = objectMapper.readValue(json,
@@ -270,9 +270,9 @@ public class DataStatisticsServiceImpl implements IDataStatisticsService {
String signInJson = objectMapper.writeValueAsString(signInStats);
return reactor.core.publisher.Flux.merge(
- redisUtil.setWithExpire(memberKey, memberJson, Duration.ofDays(30).getSeconds()),
- redisUtil.setWithExpire(bookingKey, bookingJson, Duration.ofDays(30).getSeconds()),
- redisUtil.setWithExpire(signInKey, signInJson, Duration.ofDays(30).getSeconds())
+ cacheOperations.setWithExpire(memberKey, memberJson, Duration.ofDays(30).getSeconds()),
+ cacheOperations.setWithExpire(bookingKey, bookingJson, Duration.ofDays(30).getSeconds()),
+ cacheOperations.setWithExpire(signInKey, signInJson, Duration.ofDays(30).getSeconds())
).then();
} catch (Exception e) {
log.error("Failed to serialize statistics data", e);
@@ -445,13 +445,13 @@ public class DataStatisticsServiceImpl implements IDataStatisticsService {
public Mono getStatisticsSummaryWithCache(StatisticsQuery query) {
String cacheKey = buildCacheKey(query);
- return redisUtil.get(cacheKey, StatisticsSummary.class)
+ return cacheOperations.get(cacheKey, StatisticsSummary.class)
.switchIfEmpty(
getStatisticsSummary(query)
.flatMap(summary -> {
try {
String json = objectMapper.writeValueAsString(summary);
- return redisUtil.setWithExpire(cacheKey, json, cacheExpireSeconds)
+ return cacheOperations.setWithExpire(cacheKey, json, cacheExpireSeconds)
.thenReturn(summary);
} catch (Exception e) {
log.error("Failed to serialize statistics summary", e);
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandler.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandler.java
index 7895a70..7713dfd 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandler.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandler.java
@@ -2,7 +2,7 @@
package cn.novalon.gym.manage.groupcourse.handler;
import cn.novalon.gym.manage.common.dto.PageRequest;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourseDetail;
import cn.novalon.gym.manage.groupcourse.dto.GroupCourseQueryDto;
@@ -30,16 +30,16 @@ public class GroupCourseHandler {
private static final Logger logger = LoggerFactory.getLogger(GroupCourseHandler.class);
private final IGroupCourseService groupCourseService;
private final Validator validator;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private final ObjectMapper objectMapper;
public GroupCourseHandler(IGroupCourseService groupCourseService,
Validator validator,
- RedisUtil redisUtil,
+ CacheOperations cacheOperations,
ObjectMapper objectMapper){
this.groupCourseService = groupCourseService;
this.validator = validator;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
this.objectMapper = objectMapper;
}
@@ -258,7 +258,7 @@ public class GroupCourseHandler {
return ServerResponse.badRequest().bodyValue(error);
}
- return redisUtil.get(key)
+ return cacheOperations.get(key)
.map(cachedValue -> {
Map result = new HashMap<>();
if (cachedValue != null) {
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/CourseLabelService.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/CourseLabelService.java
index 0155470..802e78e 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/CourseLabelService.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/CourseLabelService.java
@@ -2,7 +2,7 @@ package cn.novalon.gym.manage.groupcourse.service.impl;
import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.domain.CourseLabel;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
import cn.novalon.gym.manage.groupcourse.repository.ICourseLabelRepository;
@@ -24,21 +24,21 @@ public class CourseLabelService implements ICourseLabelService {
private final ICourseLabelRepository courseLabelRepository;
private final IGroupCourseRepository groupCourseRepository;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
public CourseLabelService(ICourseLabelRepository courseLabelRepository,
IGroupCourseRepository groupCourseRepository,
- RedisUtil redisUtil) {
+ CacheOperations cacheOperations) {
this.courseLabelRepository = courseLabelRepository;
this.groupCourseRepository = groupCourseRepository;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
}
private Mono invalidateGroupCourseDetailCache(Long typeId) {
return groupCourseRepository.findByCourseType(typeId)
.flatMap(course -> {
String cacheKey = CACHE_KEY_DETAIL_PREFIX + course.getId();
- return redisUtil.delete(cacheKey)
+ return cacheOperations.delete(cacheKey)
.doOnSuccess(deleted -> logger.debug("清除团课详情缓存 - courseId={}", course.getId()));
})
.then();
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRedisService.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRedisService.java
index 1e7a6df..18aad12 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRedisService.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRedisService.java
@@ -1,8 +1,7 @@
package cn.novalon.gym.manage.groupcourse.service.impl;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
@@ -13,9 +12,11 @@ import java.time.Duration;
/**
* 团课Redis缓存服务
- *
- * 负责团课信息的缓存管理和分布式锁实现
- *
+ *
+ * 缓存操作委托给 {@link CacheOperations} 接口,支持通过配置切换 Redis / Caffeine 实现。
+ * 分布式锁操作仍直接使用 {@link ReactiveRedisTemplate}(锁为 Redis 独有特性)。
+ *
+ *
* @author 张翔
* @date 2026-06-01
*/
@@ -33,13 +34,13 @@ public class GroupCourseRedisService {
// 锁过期时间(30秒)
private static final Duration LOCK_EXPIRE_TIME = Duration.ofSeconds(30);
+ private final CacheOperations cacheOperations;
private final ReactiveRedisTemplate reactiveRedisTemplate;
- private final ObjectMapper objectMapper;
- public GroupCourseRedisService(ReactiveRedisTemplate reactiveRedisTemplate,
- ObjectMapper objectMapper) {
+ public GroupCourseRedisService(CacheOperations cacheOperations,
+ ReactiveRedisTemplate reactiveRedisTemplate) {
+ this.cacheOperations = cacheOperations;
this.reactiveRedisTemplate = reactiveRedisTemplate;
- this.objectMapper = objectMapper;
}
/**
@@ -61,16 +62,9 @@ public class GroupCourseRedisService {
*/
public Mono cacheCourse(GroupCourse course) {
String key = getCourseCacheKey(course.getId());
- try {
- String value = objectMapper.writeValueAsString(course);
- return reactiveRedisTemplate.opsForValue()
- .set(key, value, CACHE_EXPIRE_TIME)
- .doOnSuccess(result -> logger.debug("团课信息已缓存:courseId={}", course.getId()))
- .then();
- } catch (JsonProcessingException e) {
- logger.error("序列化团课信息失败:courseId={}", course.getId(), e);
- return Mono.error(e);
- }
+ return cacheOperations.setWithExpire(key, course, CACHE_EXPIRE_TIME.toSeconds())
+ .doOnSuccess(result -> logger.debug("团课信息已缓存:courseId={}", course.getId()))
+ .then();
}
/**
@@ -78,19 +72,8 @@ public class GroupCourseRedisService {
*/
public Mono getCachedCourse(Long courseId) {
String key = getCourseCacheKey(courseId);
- return reactiveRedisTemplate.opsForValue()
- .get(key)
- .cast(String.class)
- .flatMap(value -> {
- try {
- GroupCourse course = objectMapper.readValue(value, GroupCourse.class);
- logger.debug("从缓存获取团课信息:courseId={}", courseId);
- return Mono.just(course);
- } catch (JsonProcessingException e) {
- logger.error("反序列化团课信息失败:courseId={}", courseId, e);
- return Mono.empty();
- }
- })
+ return cacheOperations.get(key, GroupCourse.class)
+ .doOnNext(course -> logger.debug("从缓存获取团课信息:courseId={}", courseId))
.switchIfEmpty(Mono.fromRunnable(() -> logger.debug("缓存中未找到团课信息:courseId={}", courseId)));
}
@@ -99,15 +82,16 @@ public class GroupCourseRedisService {
*/
public Mono invalidateCourseCache(Long courseId) {
String key = getCourseCacheKey(courseId);
- return reactiveRedisTemplate.delete(key)
+ return cacheOperations.delete(key)
.doOnSuccess(result -> logger.debug("团课缓存已删除:courseId={}", courseId))
.then();
}
/**
* 获取分布式锁
- *
- * @param courseId 课程ID
+ * 注意:分布式锁为 Redis 独有特性,始终保持直接使用 RedisTemplate。
+ *
+ * @param courseId 课程ID
* @param requestId 请求ID(用于锁的唯一性校验)
* @return 是否获取成功
*/
@@ -126,8 +110,9 @@ public class GroupCourseRedisService {
/**
* 释放分布式锁
- *
- * @param courseId 课程ID
+ * 注意:分布式锁为 Redis 独有特性,始终保持直接使用 RedisTemplate。
+ *
+ * @param courseId 课程ID
* @param requestId 请求ID(用于锁的唯一性校验)
* @return 是否释放成功
*/
@@ -142,11 +127,11 @@ public class GroupCourseRedisService {
.map(deleted -> deleted > 0)
.doOnSuccess(result -> logger.debug("释放预约锁成功:courseId={}, requestId={}", courseId, requestId));
} else {
- logger.warn("锁归属校验失败:courseId={}, expectedRequestId={}, actualRequestId={}",
+ logger.warn("锁归属校验失败:courseId={}, expectedRequestId={}, actualRequestId={}",
courseId, requestId, storedRequestId);
return Mono.just(false);
}
})
.defaultIfEmpty(false);
}
-}
+}
\ No newline at end of file
diff --git a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseService.java b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseService.java
index 2a30763..4bb60f2 100644
--- a/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseService.java
+++ b/gym-manage-api/gym-groupCourse/src/main/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseService.java
@@ -1,9 +1,9 @@
package cn.novalon.gym.manage.groupcourse.service.impl;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
-import cn.novalon.gym.manage.common.util.RedisUtil;
import cn.novalon.gym.manage.groupcourse.domain.CourseLabel;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourseBooking;
@@ -53,7 +53,7 @@ public class GroupCourseService implements IGroupCourseService {
private final ICourseLabelRepository courseLabelRepository;
private final IMemberCardRecordService memberCardRecordService;
private final MemberCardRepository memberCardRepository;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private final ObjectMapper objectMapper;
private final GroupCourseStateMachine stateMachine;
private final DatabaseClient databaseClient;
@@ -73,7 +73,7 @@ public class GroupCourseService implements IGroupCourseService {
ICourseLabelRepository courseLabelRepository,
IMemberCardRecordService memberCardRecordService,
MemberCardRepository memberCardRepository,
- RedisUtil redisUtil,
+ CacheOperations cacheOperations,
ObjectMapper objectMapper,
GroupCourseStateMachine stateMachine,
DatabaseClient databaseClient,
@@ -85,7 +85,7 @@ public class GroupCourseService implements IGroupCourseService {
this.courseLabelRepository = courseLabelRepository;
this.memberCardRecordService = memberCardRecordService;
this.memberCardRepository = memberCardRepository;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
this.objectMapper = objectMapper;
this.stateMachine = stateMachine;
this.databaseClient = databaseClient;
@@ -97,7 +97,7 @@ public class GroupCourseService implements IGroupCourseService {
public Mono findDetailById(Long id) {
String cacheKey = CACHE_KEY_DETAIL_PREFIX + id;
- Mono cachedMono = redisUtil.get(cacheKey, String.class);
+ Mono cachedMono = cacheOperations.get(cacheKey, String.class);
return cachedMono
.flatMap(cachedJson -> {
if (cachedJson != null && !cachedJson.isEmpty()) {
@@ -107,7 +107,7 @@ public class GroupCourseService implements IGroupCourseService {
return Mono.just(detail);
} catch (JsonProcessingException e) {
logger.warn("缓存解析失败,删除缓存 - id: {}, error: {}", id, e.getMessage());
- return redisUtil.delete(cacheKey).then(Mono.empty());
+ return cacheOperations.delete(cacheKey).then(Mono.empty());
}
}
return Mono.empty();
@@ -141,7 +141,7 @@ public class GroupCourseService implements IGroupCourseService {
.flatMap(detail -> {
try {
String jsonData = objectMapper.writeValueAsString(detail);
- return redisUtil.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
+ return cacheOperations.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
.thenReturn(detail)
.doOnSuccess(d -> logger.debug("缓存已设置 - findDetailById: id={}", id));
} catch (JsonProcessingException e) {
@@ -233,7 +233,7 @@ public class GroupCourseService implements IGroupCourseService {
public Mono findById(Long id) {
String cacheKey = CACHE_KEY_ID_PREFIX + id;
- Mono cachedMono = redisUtil.get(cacheKey, String.class);
+ Mono cachedMono = cacheOperations.get(cacheKey, String.class);
return cachedMono
.flatMap(cachedJson -> {
if (cachedJson != null && !cachedJson.isEmpty()) {
@@ -243,7 +243,7 @@ public class GroupCourseService implements IGroupCourseService {
return Mono.just(groupCourse);
} catch (JsonProcessingException e) {
logger.warn("缓存解析失败,删除缓存 - id: {}, error: {}", id, e.getMessage());
- return redisUtil.delete(cacheKey).then(Mono.empty());
+ return cacheOperations.delete(cacheKey).then(Mono.empty());
}
}
return Mono.empty();
@@ -253,7 +253,7 @@ public class GroupCourseService implements IGroupCourseService {
.flatMap(groupCourse -> {
try {
String jsonData = objectMapper.writeValueAsString(groupCourse);
- return redisUtil.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
+ return cacheOperations.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
.thenReturn(groupCourse)
.doOnSuccess(gc -> logger.debug("缓存已设置 - findById: id={}", id));
} catch (JsonProcessingException e) {
@@ -332,7 +332,7 @@ public class GroupCourseService implements IGroupCourseService {
String cacheKey = CACHE_KEY_PREFIX + page + ":" + size + ":" + includeDeleted + ":" + sort + ":" + order + ":" + keyword + ":" + status;
- Mono cachedMono = redisUtil.get(cacheKey, String.class);
+ Mono cachedMono = cacheOperations.get(cacheKey, String.class);
return cachedMono
.flatMap(cachedJson -> {
if (cachedJson != null && !cachedJson.isEmpty()) {
@@ -343,7 +343,7 @@ public class GroupCourseService implements IGroupCourseService {
return Mono.>just(pageResponse);
} catch (JsonProcessingException e) {
logger.warn("缓存解析失败,删除缓存 - key: {}, error: {}", cacheKey, e.getMessage());
- return redisUtil.delete(cacheKey).then(Mono.>empty());
+ return cacheOperations.delete(cacheKey).then(Mono.>empty());
}
}
return Mono.>empty();
@@ -361,7 +361,7 @@ public class GroupCourseService implements IGroupCourseService {
return resultMono.flatMap(pageResponse -> {
try {
String jsonData = objectMapper.writeValueAsString(pageResponse);
- return redisUtil.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
+ return cacheOperations.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS)
.thenReturn(pageResponse)
.doOnSuccess(pr -> logger.debug("缓存已设置 - findByPage: key={}", cacheKey));
} catch (JsonProcessingException e) {
@@ -739,10 +739,10 @@ public class GroupCourseService implements IGroupCourseService {
}
private Mono clearCache() {
- return redisUtil.deleteByPattern(CACHE_KEY_PREFIX + "*")
- .then(redisUtil.deleteByPattern(CACHE_KEY_ID_PREFIX + "*"))
- .then(redisUtil.deleteByPattern(CACHE_KEY_DETAIL_PREFIX + "*"))
- .then(redisUtil.deleteByPattern("datacount:statistics:*"))
+ return cacheOperations.deleteByPattern(CACHE_KEY_PREFIX + "*")
+ .then(cacheOperations.deleteByPattern(CACHE_KEY_ID_PREFIX + "*"))
+ .then(cacheOperations.deleteByPattern(CACHE_KEY_DETAIL_PREFIX + "*"))
+ .then(cacheOperations.deleteByPattern("datacount:statistics:*"))
.then();
}
diff --git a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandlerTest.java b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandlerTest.java
index cc55cc0..f9eb8aa 100644
--- a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandlerTest.java
+++ b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/handler/GroupCourseHandlerTest.java
@@ -1,6 +1,6 @@
package cn.novalon.gym.manage.groupcourse.handler;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourseDetail;
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseService;
@@ -35,7 +35,7 @@ class GroupCourseHandlerTest {
private Validator validator;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@Mock
private ObjectMapper objectMapper;
@@ -44,7 +44,7 @@ class GroupCourseHandlerTest {
@BeforeEach
void setUp() {
- handler = new GroupCourseHandler(groupCourseService, validator, redisUtil, objectMapper);
+ handler = new GroupCourseHandler(groupCourseService, validator, cacheOperations, objectMapper);
}
// ==================== getAllGroupCourse ====================
diff --git a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/CourseLabelServiceTest.java b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/CourseLabelServiceTest.java
index 61260d9..83c7d93 100644
--- a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/CourseLabelServiceTest.java
+++ b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/CourseLabelServiceTest.java
@@ -2,7 +2,7 @@ package cn.novalon.gym.manage.groupcourse.service.impl;
import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.domain.CourseLabel;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
import cn.novalon.gym.manage.groupcourse.repository.ICourseLabelRepository;
@@ -38,13 +38,13 @@ class CourseLabelServiceTest {
private IGroupCourseRepository groupCourseRepository;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
private CourseLabelService courseLabelService;
@BeforeEach
void setUp() {
- courseLabelService = new CourseLabelService(courseLabelRepository, groupCourseRepository, redisUtil);
+ courseLabelService = new CourseLabelService(courseLabelRepository, groupCourseRepository, cacheOperations);
}
// ==================== findById ====================
@@ -287,16 +287,16 @@ class CourseLabelServiceTest {
GroupCourse course2 = new GroupCourse();
course2.setId(102L);
when(groupCourseRepository.findByCourseType(1L)).thenReturn(Flux.just(course1, course2));
- when(redisUtil.delete("group_course:detail:101")).thenReturn(Mono.just(1L));
- when(redisUtil.delete("group_course:detail:102")).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("group_course:detail:101")).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("group_course:detail:102")).thenReturn(Mono.just(1L));
StepVerifier.create(courseLabelService.addLabelsToType(1L, List.of(1L, 2L)))
.verifyComplete();
verify(courseLabelRepository).addLabelsToType(1L, List.of(1L, 2L));
verify(groupCourseRepository).findByCourseType(1L);
- verify(redisUtil).delete("group_course:detail:101");
- verify(redisUtil).delete("group_course:detail:102");
+ verify(cacheOperations).delete("group_course:detail:101");
+ verify(cacheOperations).delete("group_course:detail:102");
}
@Test
@@ -309,7 +309,7 @@ class CourseLabelServiceTest {
verify(courseLabelRepository).addLabelsToType(1L, List.of(1L));
verify(groupCourseRepository).findByCourseType(1L);
- verify(redisUtil, never()).delete(anyString());
+ verify(cacheOperations, never()).delete(anyString());
}
// ==================== removeLabelFromType ====================
@@ -321,14 +321,14 @@ class CourseLabelServiceTest {
GroupCourse course = new GroupCourse();
course.setId(101L);
when(groupCourseRepository.findByCourseType(1L)).thenReturn(Flux.just(course));
- when(redisUtil.delete("group_course:detail:101")).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("group_course:detail:101")).thenReturn(Mono.just(1L));
StepVerifier.create(courseLabelService.removeLabelFromType(1L, 1L))
.verifyComplete();
verify(courseLabelRepository).removeLabelFromType(1L, 1L);
verify(groupCourseRepository).findByCourseType(1L);
- verify(redisUtil).delete("group_course:detail:101");
+ verify(cacheOperations).delete("group_course:detail:101");
}
@Test
@@ -340,7 +340,7 @@ class CourseLabelServiceTest {
.verifyComplete();
verify(courseLabelRepository).removeLabelFromType(1L, 1L);
- verify(redisUtil, never()).delete(anyString());
+ verify(cacheOperations, never()).delete(anyString());
}
// ==================== clearLabelsFromType ====================
@@ -354,16 +354,16 @@ class CourseLabelServiceTest {
GroupCourse course2 = new GroupCourse();
course2.setId(102L);
when(groupCourseRepository.findByCourseType(1L)).thenReturn(Flux.just(course1, course2));
- when(redisUtil.delete("group_course:detail:101")).thenReturn(Mono.just(1L));
- when(redisUtil.delete("group_course:detail:102")).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("group_course:detail:101")).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("group_course:detail:102")).thenReturn(Mono.just(1L));
StepVerifier.create(courseLabelService.clearLabelsFromType(1L))
.verifyComplete();
verify(courseLabelRepository).clearLabelsFromType(1L);
verify(groupCourseRepository).findByCourseType(1L);
- verify(redisUtil).delete("group_course:detail:101");
- verify(redisUtil).delete("group_course:detail:102");
+ verify(cacheOperations).delete("group_course:detail:101");
+ verify(cacheOperations).delete("group_course:detail:102");
}
@Test
@@ -375,7 +375,7 @@ class CourseLabelServiceTest {
.verifyComplete();
verify(courseLabelRepository).clearLabelsFromType(1L);
- verify(redisUtil, never()).delete(anyString());
+ verify(cacheOperations, never()).delete(anyString());
}
// ==================== findByPage ====================
diff --git a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRedisServiceTest.java b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRedisServiceTest.java
index 3c7d276..c419fc2 100644
--- a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRedisServiceTest.java
+++ b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseRedisServiceTest.java
@@ -1,7 +1,7 @@
package cn.novalon.gym.manage.groupcourse.service.impl;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -21,14 +21,15 @@ import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class GroupCourseRedisServiceTest {
+ @Mock
+ private CacheOperations cacheOperations;
+
@Mock
private ReactiveRedisTemplate reactiveRedisTemplate;
@Mock
private ReactiveValueOperations reactiveValueOps;
- private ObjectMapper objectMapper = new ObjectMapper();
-
private GroupCourseRedisService service;
private GroupCourse testCourse;
@@ -36,7 +37,7 @@ class GroupCourseRedisServiceTest {
@BeforeEach
void setUp() {
lenient().when(reactiveRedisTemplate.opsForValue()).thenReturn(reactiveValueOps);
- service = new GroupCourseRedisService(reactiveRedisTemplate, objectMapper);
+ service = new GroupCourseRedisService(cacheOperations, reactiveRedisTemplate);
testCourse = new GroupCourse();
testCourse.setId(1L);
@@ -47,20 +48,20 @@ class GroupCourseRedisServiceTest {
// ==================== cacheCourse ====================
@Test
- void cacheCourse_shouldSerializeAndSetInRedis() {
- when(reactiveValueOps.set(eq("group_course:1"), anyString(), eq(Duration.ofMinutes(5))))
+ void cacheCourse_shouldSetInCache() {
+ when(cacheOperations.setWithExpire(eq("group_course:1"), eq(testCourse), eq(300L)))
.thenReturn(Mono.just(true));
StepVerifier.create(service.cacheCourse(testCourse))
.verifyComplete();
- verify(reactiveValueOps).set(eq("group_course:1"), anyString(), eq(Duration.ofMinutes(5)));
+ verify(cacheOperations).setWithExpire(eq("group_course:1"), eq(testCourse), eq(300L));
}
@Test
- void cacheCourse_shouldErrorWhenRedisFails() {
- when(reactiveValueOps.set(eq("group_course:1"), anyString(), eq(Duration.ofMinutes(5))))
- .thenReturn(Mono.error(new RuntimeException("Redis error")));
+ void cacheCourse_shouldErrorWhenCacheFails() {
+ when(cacheOperations.setWithExpire(eq("group_course:1"), eq(testCourse), eq(300L)))
+ .thenReturn(Mono.error(new RuntimeException("Cache error")));
StepVerifier.create(service.cacheCourse(testCourse))
.expectError(RuntimeException.class)
@@ -70,9 +71,9 @@ class GroupCourseRedisServiceTest {
// ==================== getCachedCourse ====================
@Test
- void getCachedCourse_shouldReturnCachedCourseWhenFound() throws Exception {
- String json = objectMapper.writeValueAsString(testCourse);
- when(reactiveValueOps.get("group_course:1")).thenReturn(Mono.just(json));
+ void getCachedCourse_shouldReturnCachedCourseWhenFound() {
+ when(cacheOperations.get(eq("group_course:1"), eq(GroupCourse.class)))
+ .thenReturn(Mono.just(testCourse));
StepVerifier.create(service.getCachedCourse(1L))
.assertNext(course -> {
@@ -84,16 +85,8 @@ class GroupCourseRedisServiceTest {
@Test
void getCachedCourse_shouldReturnEmptyWhenCacheMiss() {
- when(reactiveValueOps.get("group_course:1")).thenReturn(Mono.empty());
-
- StepVerifier.create(service.getCachedCourse(1L))
- .verifyComplete();
- }
-
- @Test
- void getCachedCourse_shouldReturnEmptyWhenDeserializationFails() {
- String invalidJson = "invalid json";
- when(reactiveValueOps.get("group_course:1")).thenReturn(Mono.just(invalidJson));
+ when(cacheOperations.get(eq("group_course:1"), eq(GroupCourse.class)))
+ .thenReturn(Mono.empty());
StepVerifier.create(service.getCachedCourse(1L))
.verifyComplete();
@@ -102,13 +95,13 @@ class GroupCourseRedisServiceTest {
// ==================== invalidateCourseCache ====================
@Test
- void invalidateCourseCache_shouldDeleteFromRedis() {
- when(reactiveRedisTemplate.delete("group_course:1")).thenReturn(Mono.just(1L));
+ void invalidateCourseCache_shouldDeleteFromCache() {
+ when(cacheOperations.delete("group_course:1")).thenReturn(Mono.just(1L));
StepVerifier.create(service.invalidateCourseCache(1L))
.verifyComplete();
- verify(reactiveRedisTemplate).delete("group_course:1");
+ verify(cacheOperations).delete("group_course:1");
}
// ==================== acquireLock ====================
diff --git a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseServiceTest.java b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseServiceTest.java
index b5842e8..925b10b 100644
--- a/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseServiceTest.java
+++ b/gym-manage-api/gym-groupCourse/src/test/java/cn/novalon/gym/manage/groupcourse/service/impl/GroupCourseServiceTest.java
@@ -2,7 +2,7 @@ package cn.novalon.gym.manage.groupcourse.service.impl;
import cn.novalon.gym.manage.common.dto.PageRequest;
import cn.novalon.gym.manage.common.dto.PageResponse;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.file.core.domain.SysFile;
import cn.novalon.gym.manage.file.core.service.ISysFileService;
import cn.novalon.gym.manage.groupcourse.domain.CourseLabel;
@@ -63,7 +63,7 @@ class GroupCourseServiceTest {
@Mock
private MemberCardRepository memberCardRepository;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@Mock
private GroupCourseStateMachine stateMachine;
@Mock
@@ -90,7 +90,7 @@ class GroupCourseServiceTest {
groupCourseService = new GroupCourseService(
groupCourseRepository, bookingRepository, groupCourseTypeRepository,
courseLabelRepository, memberCardRecordService, memberCardRepository,
- redisUtil, objectMapper, stateMachine, databaseClient, fileService, sysUserRepository
+ cacheOperations, objectMapper, stateMachine, databaseClient, fileService, sysUserRepository
);
testCourse = new GroupCourse();
@@ -136,10 +136,10 @@ class GroupCourseServiceTest {
}
private void mockClearCache() {
- when(redisUtil.deleteByPattern("group_course:page:*")).thenReturn(Mono.just(0L));
- when(redisUtil.deleteByPattern("group_course:id:*")).thenReturn(Mono.just(0L));
- when(redisUtil.deleteByPattern("group_course:detail:*")).thenReturn(Mono.just(0L));
- when(redisUtil.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(0L));
+ when(cacheOperations.deleteByPattern("group_course:page:*")).thenReturn(Mono.just(0L));
+ when(cacheOperations.deleteByPattern("group_course:id:*")).thenReturn(Mono.just(0L));
+ when(cacheOperations.deleteByPattern("group_course:detail:*")).thenReturn(Mono.just(0L));
+ when(cacheOperations.deleteByPattern("datacount:statistics:*")).thenReturn(Mono.just(0L));
}
private void mockEnrichCurrentMembers() {
@@ -157,7 +157,7 @@ class GroupCourseServiceTest {
detail.setCoachName("张教练");
String json = objectMapper.writeValueAsString(detail);
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.just(json));
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.just(json));
// switchIfEmpty 的 Mono 参数被急切求值,需要 mock 以避免 NPE
when(groupCourseRepository.findByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.empty());
@@ -169,7 +169,7 @@ class GroupCourseServiceTest {
})
.verifyComplete();
- verify(redisUtil).get(cacheKey, String.class);
+ verify(cacheOperations).get(cacheKey, String.class);
// switchIfEmpty 的 Mono 参数被急切求值,findByIdAndDeletedAtIsNull 会被调用但不会订阅
}
@@ -177,13 +177,13 @@ class GroupCourseServiceTest {
void findDetailById_cacheMissWithType_shouldBuildAndCacheDetail() throws Exception {
String cacheKey = "group_course:detail:1";
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.empty());
when(groupCourseRepository.findByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.just(testCourse));
when(groupCourseTypeRepository.findById(100L)).thenReturn(Mono.just(testType));
when(courseLabelRepository.findByTypeId(100L)).thenReturn(Flux.just(testLabel));
when(sysUserRepository.findByIdIncludingDeleted(10L)).thenReturn(Mono.just(testCoach));
when(bookingRepository.countValidBookings(1L)).thenReturn(Mono.just(8L));
- when(redisUtil.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
+ when(cacheOperations.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
StepVerifier.create(groupCourseService.findDetailById(1L))
.assertNext(result -> {
@@ -198,13 +198,13 @@ class GroupCourseServiceTest {
})
.verifyComplete();
- verify(redisUtil).get(cacheKey, String.class);
+ verify(cacheOperations).get(cacheKey, String.class);
verify(groupCourseRepository).findByIdAndDeletedAtIsNull(1L);
verify(groupCourseTypeRepository).findById(100L);
verify(courseLabelRepository).findByTypeId(100L);
verify(sysUserRepository).findByIdIncludingDeleted(10L);
verify(bookingRepository).countValidBookings(1L);
- verify(redisUtil).setWithExpire(eq(cacheKey), anyString(), eq(300L));
+ verify(cacheOperations).setWithExpire(eq(cacheKey), anyString(), eq(300L));
}
@Test
@@ -212,11 +212,11 @@ class GroupCourseServiceTest {
String cacheKey = "group_course:detail:1";
testCourse.setCourseType(null);
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.empty());
when(groupCourseRepository.findByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.just(testCourse));
when(sysUserRepository.findByIdIncludingDeleted(10L)).thenReturn(Mono.just(testCoach));
when(bookingRepository.countValidBookings(1L)).thenReturn(Mono.just(3L));
- when(redisUtil.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
+ when(cacheOperations.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
StepVerifier.create(groupCourseService.findDetailById(1L))
.assertNext(result -> {
@@ -236,7 +236,7 @@ class GroupCourseServiceTest {
void findDetailById_cacheMissNotFound_shouldReturnEmpty() {
String cacheKey = "group_course:detail:1";
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.empty());
when(groupCourseRepository.findByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.empty());
StepVerifier.create(groupCourseService.findDetailById(1L))
@@ -250,15 +250,15 @@ class GroupCourseServiceTest {
void findDetailById_cacheParseError_shouldDeleteCacheAndFallback() throws Exception {
String cacheKey = "group_course:detail:1";
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.just("invalid-json"));
- when(redisUtil.delete(cacheKey)).thenReturn(Mono.just(1L));
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.just("invalid-json"));
+ when(cacheOperations.delete(cacheKey)).thenReturn(Mono.just(1L));
// After cache delete, switchIfEmpty kicks in
when(groupCourseRepository.findByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.just(testCourse));
when(groupCourseTypeRepository.findById(100L)).thenReturn(Mono.just(testType));
when(courseLabelRepository.findByTypeId(100L)).thenReturn(Flux.just(testLabel));
when(sysUserRepository.findByIdIncludingDeleted(10L)).thenReturn(Mono.just(testCoach));
when(bookingRepository.countValidBookings(1L)).thenReturn(Mono.just(5L));
- when(redisUtil.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
+ when(cacheOperations.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
StepVerifier.create(groupCourseService.findDetailById(1L))
.assertNext(result -> {
@@ -267,7 +267,7 @@ class GroupCourseServiceTest {
})
.verifyComplete();
- verify(redisUtil).delete(cacheKey);
+ verify(cacheOperations).delete(cacheKey);
verify(groupCourseRepository).findByIdAndDeletedAtIsNull(1L);
}
@@ -278,7 +278,7 @@ class GroupCourseServiceTest {
String cacheKey = "group_course:id:1";
String json = objectMapper.writeValueAsString(testCourse);
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.just(json));
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.just(json));
// switchIfEmpty 的 Mono 参数被急切求值,需要 mock 以避免 NPE
when(groupCourseRepository.findByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.empty());
when(bookingRepository.countValidBookings(1L)).thenReturn(Mono.just(5L));
@@ -290,7 +290,7 @@ class GroupCourseServiceTest {
})
.verifyComplete();
- verify(redisUtil).get(cacheKey, String.class);
+ verify(cacheOperations).get(cacheKey, String.class);
verify(bookingRepository).countValidBookings(1L);
// switchIfEmpty 的 Mono 参数被急切求值,findByIdAndDeletedAtIsNull 会被调用但不会订阅
}
@@ -299,9 +299,9 @@ class GroupCourseServiceTest {
void findById_cacheMiss_shouldQueryDbAndCache() throws Exception {
String cacheKey = "group_course:id:1";
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.empty());
when(groupCourseRepository.findByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.just(testCourse));
- when(redisUtil.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
+ when(cacheOperations.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
when(bookingRepository.countValidBookings(1L)).thenReturn(Mono.just(5L));
StepVerifier.create(groupCourseService.findById(1L))
@@ -312,7 +312,7 @@ class GroupCourseServiceTest {
.verifyComplete();
verify(groupCourseRepository).findByIdAndDeletedAtIsNull(1L);
- verify(redisUtil).setWithExpire(eq(cacheKey), anyString(), eq(300L));
+ verify(cacheOperations).setWithExpire(eq(cacheKey), anyString(), eq(300L));
verify(bookingRepository).countValidBookings(1L);
}
@@ -320,7 +320,7 @@ class GroupCourseServiceTest {
void findById_cacheMissNotFound_shouldReturnEmpty() {
String cacheKey = "group_course:id:1";
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.empty());
when(groupCourseRepository.findByIdAndDeletedAtIsNull(1L)).thenReturn(Mono.empty());
StepVerifier.create(groupCourseService.findById(1L))
@@ -383,7 +383,7 @@ class GroupCourseServiceTest {
String cacheKey = "group_course:page:0:10:false:id:asc::";
String json = objectMapper.writeValueAsString(pageResponse);
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.just(json));
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.just(json));
StepVerifier.create(groupCourseService.findByPage(pageRequest, false))
.assertNext(result -> {
@@ -393,7 +393,7 @@ class GroupCourseServiceTest {
})
.verifyComplete();
- verify(redisUtil).get(cacheKey, String.class);
+ verify(cacheOperations).get(cacheKey, String.class);
verifyNoInteractions(groupCourseRepository);
}
@@ -409,9 +409,9 @@ class GroupCourseServiceTest {
String cacheKey = "group_course:page:0:10:false:id:asc::";
- when(redisUtil.get(cacheKey, String.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(cacheKey, String.class)).thenReturn(Mono.empty());
when(groupCourseRepository.findByPageAndNotDeleted(pageRequest)).thenReturn(Mono.just(pageResponse));
- when(redisUtil.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
+ when(cacheOperations.setWithExpire(eq(cacheKey), anyString(), eq(300L))).thenReturn(Mono.just(true));
StepVerifier.create(groupCourseService.findByPage(pageRequest, false))
.assertNext(result -> {
@@ -421,7 +421,7 @@ class GroupCourseServiceTest {
.verifyComplete();
verify(groupCourseRepository).findByPageAndNotDeleted(pageRequest);
- verify(redisUtil).setWithExpire(eq(cacheKey), anyString(), eq(300L));
+ verify(cacheOperations).setWithExpire(eq(cacheKey), anyString(), eq(300L));
}
// ==================== create ====================
diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberCardRecordServiceImpl.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberCardRecordServiceImpl.java
index 3dd8f65..f00d981 100644
--- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberCardRecordServiceImpl.java
+++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberCardRecordServiceImpl.java
@@ -6,7 +6,7 @@ import cn.novalon.gym.manage.member.enums.MemberCardRecordStatus;
import cn.novalon.gym.manage.member.repository.MemberCardRecordRepository;
import cn.novalon.gym.manage.member.repository.MemberCardRepository;
import cn.novalon.gym.manage.member.service.IMemberCardRecordService;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@@ -29,30 +29,30 @@ import java.util.stream.Collectors;
public class MemberCardRecordServiceImpl implements IMemberCardRecordService {
private final MemberCardRecordRepository memberCardRecordRepository;
private final MemberCardRepository memberCardRepository;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private static final String MEMBER_CARD_RECORD_CACHE_PREFIX = "member:card:record:";
private static final long CACHE_EXPIRE_SECONDS = 300;
public MemberCardRecordServiceImpl(MemberCardRecordRepository memberCardRecordRepository,
MemberCardRepository memberCardRepository,
- RedisUtil redisUtil) {
+ CacheOperations cacheOperations) {
this.memberCardRecordRepository = memberCardRecordRepository;
this.memberCardRepository = memberCardRepository;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
}
@Override
public Mono findById(Long recordId) {
String cacheKey = MEMBER_CARD_RECORD_CACHE_PREFIX + recordId;
- return redisUtil.get(cacheKey)
+ return cacheOperations.get(cacheKey)
.filter(cached -> cached instanceof MemberCardRecord)
.map(cached -> (MemberCardRecord) cached)
.switchIfEmpty(Mono.defer(() ->
memberCardRecordRepository.findById(recordId)
.doOnSuccess(record -> {
if (record != null) {
- redisUtil.setWithExpire(cacheKey, record, CACHE_EXPIRE_SECONDS);
+ cacheOperations.setWithExpire(cacheKey, record, CACHE_EXPIRE_SECONDS);
}
})
));
@@ -255,7 +255,7 @@ public class MemberCardRecordServiceImpl implements IMemberCardRecordService {
private void clearRecordCache(Long recordId) {
String cacheKey = MEMBER_CARD_RECORD_CACHE_PREFIX + recordId;
- redisUtil.delete(cacheKey);
+ cacheOperations.delete(cacheKey);
log.debug("清除会员卡记录缓存, recordId: {}", recordId);
}
}
diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberCardServiceImpl.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberCardServiceImpl.java
index 23b1cf8..253e0bc 100644
--- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberCardServiceImpl.java
+++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberCardServiceImpl.java
@@ -1,6 +1,6 @@
package cn.novalon.gym.manage.member.service.impl;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.entity.MemberCard;
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.entity.MemberCardTransaction;
@@ -40,7 +40,7 @@ public class MemberCardServiceImpl implements IMemberCardService {
private final DistributedLockService distributedLockService;
private final ExpirationReminderService expirationReminderService;
private final RefundSagaHandler refundSagaHandler;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private static final String MEMBER_CARD_CACHE_PREFIX = "member:card:";
private static final long CACHE_EXPIRE_SECONDS = 300;
@@ -53,7 +53,7 @@ public class MemberCardServiceImpl implements IMemberCardService {
DistributedLockService distributedLockService,
ExpirationReminderService expirationReminderService,
RefundSagaHandler refundSagaHandler,
- RedisUtil redisUtil) {
+ CacheOperations cacheOperations) {
this.memberCardRepository = memberCardRepository;
this.recordRepository = recordRepository;
this.transactionService = transactionService;
@@ -61,13 +61,13 @@ public class MemberCardServiceImpl implements IMemberCardService {
this.distributedLockService = distributedLockService;
this.expirationReminderService = expirationReminderService;
this.refundSagaHandler = refundSagaHandler;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
}
@Override
public Mono findByMemberCardIdAndDeletedAtIsNull(Long memberCardId) {
String cacheKey = MEMBER_CARD_CACHE_PREFIX + memberCardId;
- Object cached = redisUtil.get(cacheKey);
+ Object cached = cacheOperations.get(cacheKey);
if (cached != null && cached instanceof MemberCard) {
log.debug("从缓存获取会员卡信息, memberCardId: {}", memberCardId);
return Mono.just((MemberCard) cached);
@@ -76,7 +76,7 @@ public class MemberCardServiceImpl implements IMemberCardService {
return memberCardRepository.findByMemberCardIdAndDeletedAtIsNull(memberCardId)
.doOnSuccess(card -> {
if (card != null) {
- redisUtil.setWithExpire(cacheKey, card, CACHE_EXPIRE_SECONDS);
+ cacheOperations.setWithExpire(cacheKey, card, CACHE_EXPIRE_SECONDS);
}
});
}
@@ -439,7 +439,7 @@ public class MemberCardServiceImpl implements IMemberCardService {
private void clearCardCache(Long memberCardId) {
String cacheKey = MEMBER_CARD_CACHE_PREFIX + memberCardId;
- redisUtil.delete(cacheKey);
+ cacheOperations.delete(cacheKey);
log.debug("清除会员卡缓存, memberCardId: {}", memberCardId);
}
}
diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberServiceImpl.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberServiceImpl.java
index f2003a2..98cf817 100644
--- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberServiceImpl.java
+++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/MemberServiceImpl.java
@@ -18,7 +18,7 @@ import cn.novalon.gym.manage.member.util.AesUtil;
import cn.novalon.gym.manage.member.util.BeanConvertUtil;
import cn.novalon.gym.manage.member.util.EsSyncUtils;
import cn.novalon.gym.manage.member.util.WechatPhoneUtil;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.vo.MemberCardInfoVO;
import cn.novalon.gym.manage.member.vo.MemberDetailVO;
import cn.novalon.gym.manage.member.vo.MemberInfoVO;
@@ -50,7 +50,7 @@ public class MemberServiceImpl implements MemberService {
private final IMemberRepository memberRepository;
private final MemberESRepository memberESRepository;
private final EsSyncUtils esSyncUtils;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private EsSyncUtils.EntitySyncer memberSyncer;
@@ -68,7 +68,7 @@ public class MemberServiceImpl implements MemberService {
String cacheKey = MEMBER_INFO_CACHE_PREFIX + memberId;
// 先查缓存
- return redisUtil.get(cacheKey, MemberInfoVO.class)
+ return cacheOperations.get(cacheKey, MemberInfoVO.class)
.flatMap(cached -> {
if (cached != null) {
log.debug("从缓存获取会员信息, memberId: {}", memberId);
@@ -91,7 +91,7 @@ public class MemberServiceImpl implements MemberService {
.map(this::buildMemberInfoResponse)
.flatMap(vo -> {
// 查询到数据后更新缓存
- return redisUtil.setWithExpire(cacheKey, vo, CACHE_EXPIRE_SECONDS)
+ return cacheOperations.setWithExpire(cacheKey, vo, CACHE_EXPIRE_SECONDS)
.then(Mono.just(vo));
})
.switchIfEmpty(Mono.error(() -> {
@@ -269,7 +269,7 @@ public class MemberServiceImpl implements MemberService {
String cacheKey = MEMBER_DETAIL_CACHE_PREFIX + memberId;
- return redisUtil.get(cacheKey, MemberDetailVO.class)
+ return cacheOperations.get(cacheKey, MemberDetailVO.class)
.filter(cached -> cached != null)
.switchIfEmpty(Mono.defer(() ->
memberRepository.findById(memberId)
@@ -312,7 +312,7 @@ public class MemberServiceImpl implements MemberService {
return memberDetailVO;
}
)
- .flatMap(vo -> redisUtil.setWithExpire(cacheKey, vo, CACHE_EXPIRE_SECONDS)
+ .flatMap(vo -> cacheOperations.setWithExpire(cacheKey, vo, CACHE_EXPIRE_SECONDS)
.then(Mono.just(vo)))
));
}
@@ -383,8 +383,8 @@ public class MemberServiceImpl implements MemberService {
private Mono clearMemberCache(Long memberId) {
String infoCacheKey = MEMBER_INFO_CACHE_PREFIX + memberId;
String detailCacheKey = MEMBER_DETAIL_CACHE_PREFIX + memberId;
- return redisUtil.delete(infoCacheKey)
- .then(redisUtil.delete(detailCacheKey))
+ return cacheOperations.delete(infoCacheKey)
+ .then(cacheOperations.delete(detailCacheKey))
.doOnSuccess(result -> log.debug("清除会员缓存, memberId: {}", memberId));
}
}
\ No newline at end of file
diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/RefundApplicationServiceImpl.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/RefundApplicationServiceImpl.java
index e505ef7..6182ad9 100644
--- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/RefundApplicationServiceImpl.java
+++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/RefundApplicationServiceImpl.java
@@ -5,7 +5,7 @@ import cn.novalon.gym.manage.member.entity.RefundApplication;
import cn.novalon.gym.manage.member.enums.RefundStatus;
import cn.novalon.gym.manage.member.repository.RefundApplicationRepository;
import cn.novalon.gym.manage.member.service.IRefundApplicationService;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@@ -23,14 +23,14 @@ import java.time.LocalDateTime;
public class RefundApplicationServiceImpl implements IRefundApplicationService {
private final RefundApplicationRepository refundApplicationRepository;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private static final String REFUND_APPLICATION_CACHE_PREFIX = "member:refund:";
private static final long CACHE_EXPIRE_SECONDS = 300;
- public RefundApplicationServiceImpl(RefundApplicationRepository refundApplicationRepository, RedisUtil redisUtil) {
+ public RefundApplicationServiceImpl(RefundApplicationRepository refundApplicationRepository, CacheOperations cacheOperations) {
this.refundApplicationRepository = refundApplicationRepository;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
}
@Override
@@ -103,7 +103,7 @@ public class RefundApplicationServiceImpl implements IRefundApplicationService {
@Override
public Mono findByRecordId(Long recordId) {
String cacheKey = REFUND_APPLICATION_CACHE_PREFIX + recordId;
- Object cached = redisUtil.get(cacheKey);
+ Object cached = cacheOperations.get(cacheKey);
if (cached != null && cached instanceof RefundApplication) {
log.debug("从缓存获取退款申请, recordId: {}", recordId);
return Mono.just((RefundApplication) cached);
@@ -112,14 +112,14 @@ public class RefundApplicationServiceImpl implements IRefundApplicationService {
return refundApplicationRepository.findByRecordId(recordId)
.doOnSuccess(application -> {
if (application != null) {
- redisUtil.setWithExpire(cacheKey, application, CACHE_EXPIRE_SECONDS);
+ cacheOperations.setWithExpire(cacheKey, application, CACHE_EXPIRE_SECONDS);
}
});
}
private void clearRefundCache(Long recordId) {
String cacheKey = REFUND_APPLICATION_CACHE_PREFIX + recordId;
- redisUtil.delete(cacheKey);
+ cacheOperations.delete(cacheKey);
log.debug("清除退款申请缓存, recordId: {}", recordId);
}
}
diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatApiServiceImpl.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatApiServiceImpl.java
index 2eddee9..cda3792 100644
--- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatApiServiceImpl.java
+++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatApiServiceImpl.java
@@ -4,7 +4,7 @@ import cn.novalon.gym.manage.common.exception.ErrorCode;
import cn.novalon.gym.manage.common.exception.SystemException;
import cn.novalon.gym.manage.member.config.WechatProperties;
import cn.novalon.gym.manage.member.service.WechatApiService;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
@@ -31,7 +31,7 @@ import java.util.Map;
public class WechatApiServiceImpl implements WechatApiService {
private final WechatProperties wechatProperties;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private static final String ACCESS_TOKEN_CACHE_PREFIX = "wechat:access_token:";
private static final long ACCESS_TOKEN_EXPIRE_SECONDS = 7000; // 比官方过期时间短100秒
@@ -173,7 +173,7 @@ public class WechatApiServiceImpl implements WechatApiService {
String cacheKey = ACCESS_TOKEN_CACHE_PREFIX + appType;
- return redisUtil.get(cacheKey, String.class)
+ return cacheOperations.get(cacheKey, String.class)
.flatMap(cachedToken -> {
if (cachedToken != null) {
log.debug("从缓存获取access_token, appType: {}", appType);
@@ -203,7 +203,7 @@ public class WechatApiServiceImpl implements WechatApiService {
String accessToken = (String) response.get("access_token");
Integer expiresIn = (Integer) response.get("expires_in");
log.info("获取access_token成功, expires_in: {}s", expiresIn);
- return redisUtil.setWithExpire(cacheKey, accessToken, ACCESS_TOKEN_EXPIRE_SECONDS)
+ return cacheOperations.setWithExpire(cacheKey, accessToken, ACCESS_TOKEN_EXPIRE_SECONDS)
.then(Mono.just(accessToken));
} else {
String errmsg = (String) response.get("errmsg");
diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatAuthServiceImpl.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatAuthServiceImpl.java
index af116b4..86c50b1 100644
--- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatAuthServiceImpl.java
+++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatAuthServiceImpl.java
@@ -14,7 +14,7 @@ import cn.novalon.gym.manage.member.service.WechatAuthService;
import cn.novalon.gym.manage.member.util.AesUtil;
import cn.novalon.gym.manage.member.util.EsSyncUtils;
import cn.novalon.gym.manage.member.util.MemberNoGenerator;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.util.WechatPhoneUtil;
import cn.novalon.gym.manage.member.vo.WechatLoginVO;
import cn.novalon.gym.manage.sys.security.JwtTokenProvider;
@@ -46,7 +46,7 @@ public class WechatAuthServiceImpl implements WechatAuthService {
private final MemberESRepository memberESRepository;
private final EsSyncUtils esSyncUtils;
private final JwtTokenProvider jwtTokenProvider;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private EsSyncUtils.EntitySyncer memberSyncer;
@@ -217,7 +217,7 @@ public class WechatAuthServiceImpl implements WechatAuthService {
private void clearMemberCache(Long memberId) {
String cacheKey = MEMBER_INFO_CACHE_PREFIX + memberId;
- redisUtil.delete(cacheKey);
+ cacheOperations.delete(cacheKey);
log.debug("清除会员缓存, memberId: {}", memberId);
}
diff --git a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatOfficialServiceImpl.java b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatOfficialServiceImpl.java
index 41aca11..34167ae 100644
--- a/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatOfficialServiceImpl.java
+++ b/gym-manage-api/gym-member/src/main/java/cn/novalon/gym/manage/member/service/impl/WechatOfficialServiceImpl.java
@@ -8,7 +8,7 @@ import cn.novalon.gym.manage.member.es.repository.MemberESRepository;
import cn.novalon.gym.manage.member.repository.IMemberRepository;
import cn.novalon.gym.manage.member.service.WechatOfficialService;
import cn.novalon.gym.manage.member.util.EsSyncUtils;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.vo.WechatUserInfoVO;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -43,7 +43,7 @@ public class WechatOfficialServiceImpl implements WechatOfficialService {
private final ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
private final EsSyncUtils esSyncUtils;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private EsSyncUtils.EntitySyncer memberSyncer;
@@ -297,7 +297,7 @@ public class WechatOfficialServiceImpl implements WechatOfficialService {
private Mono getAccessToken() {
String cacheKey = ACCESS_TOKEN_CACHE_PREFIX + "mp";
- return redisUtil.get(cacheKey, String.class)
+ return cacheOperations.get(cacheKey, String.class)
.flatMap(cachedToken -> {
if (cachedToken != null) {
log.debug("从缓存获取服务号access_token");
@@ -322,7 +322,7 @@ public class WechatOfficialServiceImpl implements WechatOfficialService {
throw new RuntimeException("获取AccessToken失败: " + response.get("errmsg"));
}
String accessToken = (String) response.get("access_token");
- return redisUtil.setWithExpire(cacheKey, accessToken, ACCESS_TOKEN_EXPIRE_SECONDS)
+ return cacheOperations.setWithExpire(cacheKey, accessToken, ACCESS_TOKEN_EXPIRE_SECONDS)
.then(Mono.just(accessToken));
});
});
@@ -373,7 +373,7 @@ public class WechatOfficialServiceImpl implements WechatOfficialService {
private Mono clearMemberCache(Long memberId) {
String cacheKey = MEMBER_INFO_CACHE_PREFIX + memberId;
- return redisUtil.delete(cacheKey)
+ return cacheOperations.delete(cacheKey)
.doOnSuccess(result -> log.debug("清除会员缓存, memberId: {}", memberId));
}
}
diff --git a/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberCardRecordServiceImplTest.java b/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberCardRecordServiceImplTest.java
index 1ed2992..fdafd59 100644
--- a/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberCardRecordServiceImplTest.java
+++ b/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberCardRecordServiceImplTest.java
@@ -1,6 +1,6 @@
package cn.novalon.gym.manage.member.service.impl;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.entity.MemberCard;
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.enums.MemberCardRecordStatus;
@@ -37,7 +37,7 @@ class MemberCardRecordServiceImplTest {
private MemberCardRepository memberCardRepository;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@InjectMocks
private MemberCardRecordServiceImpl memberCardRecordService;
@@ -82,7 +82,7 @@ class MemberCardRecordServiceImplTest {
@DisplayName("缓存命中时应从缓存返回")
void shouldReturnFromCacheWhenHit() {
MemberCardRecord record = createRecord();
- when(redisUtil.get("member:card:record:" + RECORD_ID)).thenReturn(Mono.just(record));
+ when(cacheOperations.get("member:card:record:" + RECORD_ID)).thenReturn(Mono.just(record));
Mono result = memberCardRecordService.findById(RECORD_ID);
@@ -97,7 +97,7 @@ class MemberCardRecordServiceImplTest {
@DisplayName("缓存未命中时应从数据库查询并更新缓存")
void shouldQueryDatabaseWhenCacheMiss() {
MemberCardRecord record = createRecord();
- when(redisUtil.get("member:card:record:" + RECORD_ID)).thenReturn(Mono.empty());
+ when(cacheOperations.get("member:card:record:" + RECORD_ID)).thenReturn(Mono.empty());
when(memberCardRecordRepository.findById(anyLong())).thenReturn(Mono.just(record));
Mono result = memberCardRecordService.findById(RECORD_ID);
@@ -112,7 +112,7 @@ class MemberCardRecordServiceImplTest {
@Test
@DisplayName("记录不存在时应返回空")
void shouldReturnEmptyWhenNotFound() {
- when(redisUtil.get("member:card:record:" + RECORD_ID)).thenReturn(Mono.empty());
+ when(cacheOperations.get("member:card:record:" + RECORD_ID)).thenReturn(Mono.empty());
when(memberCardRecordRepository.findById(anyLong())).thenReturn(Mono.empty());
Mono result = memberCardRecordService.findById(RECORD_ID);
@@ -210,7 +210,7 @@ class MemberCardRecordServiceImplTest {
@DisplayName("扣减成功时应清除缓存")
void shouldClearCacheWhenDeducted() {
when(memberCardRecordRepository.deductUsage(RECORD_ID, 1, 0.0)).thenReturn(Mono.just(1));
- when(redisUtil.delete("member:card:record:" + RECORD_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:card:record:" + RECORD_ID)).thenReturn(Mono.just(1L));
Mono result = memberCardRecordService.deductUsage(RECORD_ID, 1, 0.0);
@@ -218,7 +218,7 @@ class MemberCardRecordServiceImplTest {
.assertNext(updated -> assertThat(updated).isEqualTo(1))
.verifyComplete();
- verify(redisUtil).delete("member:card:record:" + RECORD_ID);
+ verify(cacheOperations).delete("member:card:record:" + RECORD_ID);
}
@Test
@@ -232,7 +232,7 @@ class MemberCardRecordServiceImplTest {
.assertNext(updated -> assertThat(updated).isZero())
.verifyComplete();
- verify(redisUtil, never()).delete(anyString());
+ verify(cacheOperations, never()).delete(anyString());
}
}
@@ -247,7 +247,7 @@ class MemberCardRecordServiceImplTest {
void shouldClearCacheWhenRenewed() {
LocalDateTime newExpire = LocalDateTime.now().plusDays(30);
when(memberCardRecordRepository.renewCard(RECORD_ID, 10, null, newExpire)).thenReturn(Mono.just(1));
- when(redisUtil.delete("member:card:record:" + RECORD_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:card:record:" + RECORD_ID)).thenReturn(Mono.just(1L));
Mono result = memberCardRecordService.renewCard(RECORD_ID, 10, null, newExpire);
@@ -267,7 +267,7 @@ class MemberCardRecordServiceImplTest {
@DisplayName("状态更新成功时应清除缓存")
void shouldClearCacheWhenStatusUpdated() {
when(memberCardRecordRepository.updateStatus(RECORD_ID, "USED_UP")).thenReturn(Mono.just(1));
- when(redisUtil.delete("member:card:record:" + RECORD_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:card:record:" + RECORD_ID)).thenReturn(Mono.just(1L));
Mono result = memberCardRecordService.updateStatus(RECORD_ID, "USED_UP");
diff --git a/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberCardServiceImplTest.java b/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberCardServiceImplTest.java
index 18cd79a..781d1da 100644
--- a/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberCardServiceImplTest.java
+++ b/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberCardServiceImplTest.java
@@ -1,6 +1,6 @@
package cn.novalon.gym.manage.member.service.impl;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.entity.MemberCard;
import cn.novalon.gym.manage.member.entity.MemberCardRecord;
import cn.novalon.gym.manage.member.enums.CardEvent;
@@ -57,7 +57,7 @@ class MemberCardServiceImplTest {
private RefundSagaHandler refundSagaHandler;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@InjectMocks
private MemberCardServiceImpl memberCardService;
@@ -155,7 +155,7 @@ class MemberCardServiceImplTest {
@DisplayName("缓存未命中时应从数据库查询并更新缓存")
void shouldQueryDatabaseWhenCacheMiss() {
MemberCard card = createTimeCard();
- when(redisUtil.get("member:card:" + MEMBER_CARD_ID)).thenReturn(Mono.empty());
+ when(cacheOperations.get("member:card:" + MEMBER_CARD_ID)).thenReturn(Mono.empty());
when(memberCardRepository.findByMemberCardIdAndDeletedAtIsNull(MEMBER_CARD_ID))
.thenReturn(Mono.just(card));
@@ -171,7 +171,7 @@ class MemberCardServiceImplTest {
@Test
@DisplayName("卡片不存在时应返回空")
void shouldReturnEmptyWhenCardNotFound() {
- when(redisUtil.get("member:card:" + MEMBER_CARD_ID)).thenReturn(Mono.empty());
+ when(cacheOperations.get("member:card:" + MEMBER_CARD_ID)).thenReturn(Mono.empty());
when(memberCardRepository.findByMemberCardIdAndDeletedAtIsNull(MEMBER_CARD_ID))
.thenReturn(Mono.empty());
@@ -214,7 +214,7 @@ class MemberCardServiceImplTest {
void shouldClearCacheWhenSaved() {
MemberCard card = createTimeCard();
when(memberCardRepository.save(card)).thenReturn(Mono.just(card));
- when(redisUtil.delete("member:card:" + MEMBER_CARD_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:card:" + MEMBER_CARD_ID)).thenReturn(Mono.just(1L));
Mono result = memberCardService.save(card);
@@ -222,7 +222,7 @@ class MemberCardServiceImplTest {
.assertNext(c -> assertThat(c.getMemberCardName()).isEqualTo("月卡"))
.verifyComplete();
- verify(redisUtil).delete("member:card:" + MEMBER_CARD_ID);
+ verify(cacheOperations).delete("member:card:" + MEMBER_CARD_ID);
}
}
@@ -274,7 +274,7 @@ class MemberCardServiceImplTest {
@DisplayName("购买时间卡应成功创建记录")
void shouldPurchaseTimeCardSuccessfully() {
MemberCard timeCard = createTimeCard();
- lenient().when(redisUtil.get("member:card:" + MEMBER_CARD_ID)).thenReturn(Mono.empty());
+ lenient().when(cacheOperations.get("member:card:" + MEMBER_CARD_ID)).thenReturn(Mono.empty());
when(memberCardRepository.findByIdAndDeletedAtIsNull(MEMBER_CARD_ID)).thenReturn(Mono.just(timeCard));
when(recordRepository.insertActiveRecord(anyLong(), anyLong(), any(), anyInt(), anyDouble(), any()))
.thenReturn(Mono.just(createActiveRecord(timeCard, MemberCardRecordStatus.ACTIVE)));
diff --git a/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberServiceImplTest.java b/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberServiceImplTest.java
index 871738b..df21f5e 100644
--- a/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberServiceImplTest.java
+++ b/gym-manage-api/gym-member/src/test/java/cn/novalon/gym/manage/member/service/impl/MemberServiceImplTest.java
@@ -4,7 +4,7 @@ import cn.novalon.gym.manage.common.exception.ConflictException;
import cn.novalon.gym.manage.common.exception.ErrorCode;
import cn.novalon.gym.manage.common.exception.NotFoundException;
import cn.novalon.gym.manage.common.util.HtmlEscapeUtil;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.dto.SearchMemberDto;
import cn.novalon.gym.manage.member.dto.UpdateMemberInfoDto;
import cn.novalon.gym.manage.member.entity.Member;
@@ -52,7 +52,7 @@ class MemberServiceImplTest {
private EsSyncUtils esSyncUtils;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@InjectMocks
private MemberServiceImpl memberServiceImpl;
@@ -140,7 +140,7 @@ class MemberServiceImplTest {
@DisplayName("缓存命中时应直接返回缓存数据")
void shouldReturnCachedDataWhenCacheHit() {
MemberInfoVO cachedVO = createTestMemberInfoVO();
- when(redisUtil.get(CACHE_INFO_KEY, MemberInfoVO.class)).thenReturn(Mono.just(cachedVO));
+ when(cacheOperations.get(CACHE_INFO_KEY, MemberInfoVO.class)).thenReturn(Mono.just(cachedVO));
Mono result = memberServiceImpl.getMemberInfo(MEMBER_ID);
@@ -151,17 +151,17 @@ class MemberServiceImplTest {
})
.verifyComplete();
- verify(redisUtil).get(CACHE_INFO_KEY, MemberInfoVO.class);
+ verify(cacheOperations).get(CACHE_INFO_KEY, MemberInfoVO.class);
verifyNoInteractions(memberRepository);
}
@Test
@DisplayName("缓存未命中时应从数据库查询并更新缓存")
void shouldQueryDatabaseAndCacheWhenCacheMiss() {
- when(redisUtil.get(CACHE_INFO_KEY, MemberInfoVO.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(CACHE_INFO_KEY, MemberInfoVO.class)).thenReturn(Mono.empty());
Member member = createTestMember();
when(memberRepository.findById(MEMBER_ID)).thenReturn(Mono.just(member));
- when(redisUtil.setWithExpire(eq(CACHE_INFO_KEY), any(MemberInfoVO.class), anyLong()))
+ when(cacheOperations.setWithExpire(eq(CACHE_INFO_KEY), any(MemberInfoVO.class), anyLong()))
.thenReturn(Mono.just(true));
try (MockedStatic aesUtilMock = mockStatic(AesUtil.class)) {
@@ -179,14 +179,14 @@ class MemberServiceImplTest {
.verifyComplete();
verify(memberRepository).findById(MEMBER_ID);
- verify(redisUtil).setWithExpire(eq(CACHE_INFO_KEY), any(MemberInfoVO.class), eq(300L));
+ verify(cacheOperations).setWithExpire(eq(CACHE_INFO_KEY), any(MemberInfoVO.class), eq(300L));
}
}
@Test
@DisplayName("会员不存在时应抛出 NotFoundException")
void shouldThrowNotFoundExceptionWhenMemberNotFound() {
- when(redisUtil.get(CACHE_INFO_KEY, MemberInfoVO.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(CACHE_INFO_KEY, MemberInfoVO.class)).thenReturn(Mono.empty());
when(memberRepository.findById(MEMBER_ID)).thenReturn(Mono.empty());
Mono result = memberServiceImpl.getMemberInfo(MEMBER_ID);
@@ -198,7 +198,7 @@ class MemberServiceImplTest {
&& throwable.getMessage().equals("会员不存在"))
.verify();
- verify(redisUtil).get(CACHE_INFO_KEY, MemberInfoVO.class);
+ verify(cacheOperations).get(CACHE_INFO_KEY, MemberInfoVO.class);
verify(memberRepository).findById(MEMBER_ID);
}
}
@@ -217,8 +217,8 @@ class MemberServiceImplTest {
when(memberRepository.findById(MEMBER_ID)).thenReturn(Mono.just(member));
when(memberRepository.save(any(Member.class))).thenAnswer(invocation -> Mono.just(invocation.getArgument(0)));
- when(redisUtil.delete("member:info:" + MEMBER_ID)).thenReturn(Mono.just(1L));
- when(redisUtil.delete("member:detail:" + MEMBER_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:info:" + MEMBER_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:detail:" + MEMBER_ID)).thenReturn(Mono.just(1L));
try (MockedStatic aesUtilMock = mockStatic(AesUtil.class)) {
aesUtilMock.when(() -> AesUtil.decrypt(ENCRYPTED_PHONE)).thenReturn(DECRYPTED_PHONE);
@@ -236,8 +236,8 @@ class MemberServiceImplTest {
verify(memberRepository).findById(MEMBER_ID);
verify(memberRepository).save(any(Member.class));
verify(mockSyncer).sync(any(Member.class));
- verify(redisUtil).delete("member:info:" + MEMBER_ID);
- verify(redisUtil).delete("member:detail:" + MEMBER_ID);
+ verify(cacheOperations).delete("member:info:" + MEMBER_ID);
+ verify(cacheOperations).delete("member:detail:" + MEMBER_ID);
}
}
@@ -274,8 +274,8 @@ class MemberServiceImplTest {
when(memberRepository.findById(MEMBER_ID)).thenReturn(Mono.just(member));
when(memberRepository.save(any(Member.class))).thenAnswer(invocation -> Mono.just(invocation.getArgument(0)));
- when(redisUtil.delete("member:info:" + MEMBER_ID)).thenReturn(Mono.just(1L));
- when(redisUtil.delete("member:detail:" + MEMBER_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:info:" + MEMBER_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:detail:" + MEMBER_ID)).thenReturn(Mono.just(1L));
try (MockedStatic aesUtilMock = mockStatic(AesUtil.class)) {
aesUtilMock.when(() -> AesUtil.encrypt(TEST_PHONE)).thenReturn(ENCRYPTED_PHONE);
@@ -493,7 +493,7 @@ class MemberServiceImplTest {
.nickname("测试会员")
.build();
- when(redisUtil.get(CACHE_DETAIL_KEY, MemberDetailVO.class)).thenReturn(Mono.just(cachedDetail));
+ when(cacheOperations.get(CACHE_DETAIL_KEY, MemberDetailVO.class)).thenReturn(Mono.just(cachedDetail));
Mono result = memberServiceImpl.getMemberDetail(MEMBER_ID);
@@ -504,14 +504,14 @@ class MemberServiceImplTest {
})
.verifyComplete();
- verify(redisUtil).get(CACHE_DETAIL_KEY, MemberDetailVO.class);
+ verify(cacheOperations).get(CACHE_DETAIL_KEY, MemberDetailVO.class);
verifyNoInteractions(memberRepository);
}
@Test
@DisplayName("缓存未命中时应从数据库查询并构建详情")
void shouldQueryDatabaseAndBuildDetailWhenCacheMiss() {
- when(redisUtil.get(CACHE_DETAIL_KEY, MemberDetailVO.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(CACHE_DETAIL_KEY, MemberDetailVO.class)).thenReturn(Mono.empty());
Member member = createTestMember();
when(memberRepository.findById(MEMBER_ID)).thenReturn(Mono.just(member));
@@ -520,7 +520,7 @@ class MemberServiceImplTest {
when(memberRepository.findCardRecordsWithCardInfoByMemberId(MEMBER_ID))
.thenReturn(Flux.just(cardInfo));
- when(redisUtil.setWithExpire(eq(CACHE_DETAIL_KEY), any(MemberDetailVO.class), anyLong()))
+ when(cacheOperations.setWithExpire(eq(CACHE_DETAIL_KEY), any(MemberDetailVO.class), anyLong()))
.thenReturn(Mono.just(true));
Mono result = memberServiceImpl.getMemberDetail(MEMBER_ID);
@@ -539,13 +539,13 @@ class MemberServiceImplTest {
verify(memberRepository).findById(MEMBER_ID);
verify(memberRepository).findCardRecordsWithCardInfoByMemberId(MEMBER_ID);
- verify(redisUtil).setWithExpire(eq(CACHE_DETAIL_KEY), any(MemberDetailVO.class), eq(300L));
+ verify(cacheOperations).setWithExpire(eq(CACHE_DETAIL_KEY), any(MemberDetailVO.class), eq(300L));
}
@Test
@DisplayName("会员不存在时应抛出 NotFoundException")
void shouldThrowNotFoundExceptionWhenMemberNotFound() {
- when(redisUtil.get(CACHE_DETAIL_KEY, MemberDetailVO.class)).thenReturn(Mono.empty());
+ when(cacheOperations.get(CACHE_DETAIL_KEY, MemberDetailVO.class)).thenReturn(Mono.empty());
when(memberRepository.findById(MEMBER_ID)).thenReturn(Mono.empty());
// zipWith 会订阅所有源,即使另一个是 error,也需要桩避免 NPE
lenient().when(memberRepository.findCardRecordsWithCardInfoByMemberId(MEMBER_ID))
@@ -577,8 +577,8 @@ class MemberServiceImplTest {
when(memberRepository.findById(MEMBER_ID)).thenReturn(Mono.just(member));
when(memberRepository.save(any(Member.class))).thenAnswer(invocation -> Mono.just(invocation.getArgument(0)));
- when(redisUtil.delete("member:info:" + MEMBER_ID)).thenReturn(Mono.just(1L));
- when(redisUtil.delete("member:detail:" + MEMBER_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:info:" + MEMBER_ID)).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete("member:detail:" + MEMBER_ID)).thenReturn(Mono.just(1L));
Mono result = memberServiceImpl.adminUpdateMemberInfo(MEMBER_ID, dto);
@@ -589,8 +589,8 @@ class MemberServiceImplTest {
verify(memberRepository).findById(MEMBER_ID);
verify(memberRepository).save(any(Member.class));
verify(mockSyncer).sync(any(Member.class));
- verify(redisUtil).delete("member:info:" + MEMBER_ID);
- verify(redisUtil).delete("member:detail:" + MEMBER_ID);
+ verify(cacheOperations).delete("member:info:" + MEMBER_ID);
+ verify(cacheOperations).delete("member:detail:" + MEMBER_ID);
}
@Test
diff --git a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImpl.java b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImpl.java
index b9445b9..0b5ba39 100644
--- a/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImpl.java
+++ b/gym-manage-api/gym-payment/src/main/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImpl.java
@@ -1,6 +1,6 @@
package cn.novalon.gym.manage.payment.service.impl;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.payment.config.HuifuProperties;
import cn.novalon.gym.manage.payment.dto.CreatePaymentRequest;
import cn.novalon.gym.manage.payment.dto.PaymentResponse;
@@ -46,16 +46,16 @@ public class PaymentServiceImpl implements PaymentService {
private final HuifuProperties huifuProperties;
private final PaymentOrderRepository paymentOrderRepository;
- private final RedisUtil redisUtil;
+ private final CacheOperations cacheOperations;
private final PaymentNotifyService paymentNotifyService;
public PaymentServiceImpl(HuifuProperties huifuProperties,
PaymentOrderRepository paymentOrderRepository,
- RedisUtil redisUtil,
+ CacheOperations cacheOperations,
PaymentNotifyService paymentNotifyService) {
this.huifuProperties = huifuProperties;
this.paymentOrderRepository = paymentOrderRepository;
- this.redisUtil = redisUtil;
+ this.cacheOperations = cacheOperations;
this.paymentNotifyService = paymentNotifyService;
}
@@ -860,7 +860,7 @@ public class PaymentServiceImpl implements PaymentService {
}
private Mono isTradeProcessed(String reqSeqId) {
- return redisUtil.hasKey(PROCESSED_TRADE_PREFIX + reqSeqId)
+ return cacheOperations.hasKey(PROCESSED_TRADE_PREFIX + reqSeqId)
.onErrorResume(e -> {
log.error("[Huifu] 检查交易处理状态异常: reqSeqId={}", reqSeqId, e);
return Mono.just(false);
@@ -868,7 +868,7 @@ public class PaymentServiceImpl implements PaymentService {
}
private Mono markTradeProcessed(String reqSeqId) {
- return redisUtil.setWithExpire(PROCESSED_TRADE_PREFIX + reqSeqId, "1", IDEMPOTENT_EXPIRE_SECONDS)
+ return cacheOperations.setWithExpire(PROCESSED_TRADE_PREFIX + reqSeqId, "1", IDEMPOTENT_EXPIRE_SECONDS)
.onErrorResume(e -> {
log.error("[Huifu] 标记交易处理状态异常: reqSeqId={}", reqSeqId, e);
return Mono.empty();
diff --git a/gym-manage-api/gym-payment/src/test/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImplTest.java b/gym-manage-api/gym-payment/src/test/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImplTest.java
index 1e5d001..5856623 100644
--- a/gym-manage-api/gym-payment/src/test/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImplTest.java
+++ b/gym-manage-api/gym-payment/src/test/java/cn/novalon/gym/manage/payment/service/impl/PaymentServiceImplTest.java
@@ -1,6 +1,6 @@
package cn.novalon.gym.manage.payment.service.impl;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.payment.config.HuifuProperties;
import cn.novalon.gym.manage.payment.dto.CreatePaymentRequest;
import cn.novalon.gym.manage.payment.dto.PaymentResponse;
@@ -35,7 +35,7 @@ class PaymentServiceImplTest {
private PaymentOrderRepository paymentOrderRepository;
@Mock
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@Mock
private PaymentNotifyService paymentNotifyService;
@@ -58,7 +58,7 @@ class PaymentServiceImplTest {
lenient().when(huifuProperties.isSdkInitialized()).thenReturn(false);
- paymentService = new PaymentServiceImpl(huifuProperties, paymentOrderRepository, redisUtil, paymentNotifyService);
+ paymentService = new PaymentServiceImpl(huifuProperties, paymentOrderRepository, cacheOperations, paymentNotifyService);
}
private PaymentOrder createTestOrder(String status) {
@@ -369,7 +369,7 @@ class PaymentServiceImplTest {
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"S\"}");
params.put("sign", "testSign");
- when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(true));
+ when(cacheOperations.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(true));
Mono result = paymentService.handleAlipayNotify(params);
@@ -388,10 +388,10 @@ class PaymentServiceImplTest {
PaymentOrder order = createTestOrder("PENDING");
- when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
+ when(cacheOperations.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.just(order));
when(paymentOrderRepository.save(any(PaymentOrder.class))).thenReturn(Mono.just(order));
- when(redisUtil.setWithExpire("huifu:processed:trade:REQ123", "1", 86400)).thenReturn(Mono.just(true));
+ when(cacheOperations.setWithExpire("huifu:processed:trade:REQ123", "1", 86400)).thenReturn(Mono.just(true));
when(paymentNotifyService.notifyPaymentStatus(order.getOrderNo(), "SUCCESS", MEMBER_ID)).thenReturn(Mono.empty());
Mono result = paymentService.handleAlipayNotify(params);
@@ -401,7 +401,7 @@ class PaymentServiceImplTest {
.verifyComplete();
verify(paymentOrderRepository).save(any(PaymentOrder.class));
- verify(redisUtil).setWithExpire("huifu:processed:trade:REQ123", "1", 86400);
+ verify(cacheOperations).setWithExpire("huifu:processed:trade:REQ123", "1", 86400);
verify(paymentNotifyService).notifyPaymentStatus(order.getOrderNo(), "SUCCESS", MEMBER_ID);
}
@@ -413,10 +413,10 @@ class PaymentServiceImplTest {
PaymentOrder order = createTestOrder("PENDING");
- when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
+ when(cacheOperations.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.just(order));
when(paymentOrderRepository.save(any(PaymentOrder.class))).thenReturn(Mono.just(order));
- when(redisUtil.setWithExpire("huifu:processed:trade:REQ123", "1", 86400)).thenReturn(Mono.just(true));
+ when(cacheOperations.setWithExpire("huifu:processed:trade:REQ123", "1", 86400)).thenReturn(Mono.just(true));
when(paymentNotifyService.notifyPaymentStatus(order.getOrderNo(), "FAIL", MEMBER_ID)).thenReturn(Mono.empty());
Mono result = paymentService.handleAlipayNotify(params);
@@ -426,7 +426,7 @@ class PaymentServiceImplTest {
.verifyComplete();
verify(paymentOrderRepository).save(any(PaymentOrder.class));
- verify(redisUtil).setWithExpire("huifu:processed:trade:REQ123", "1", 86400);
+ verify(cacheOperations).setWithExpire("huifu:processed:trade:REQ123", "1", 86400);
verify(paymentNotifyService).notifyPaymentStatus(order.getOrderNo(), "FAIL", MEMBER_ID);
}
@@ -438,7 +438,7 @@ class PaymentServiceImplTest {
PaymentOrder order = createTestOrder("PENDING");
- when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
+ when(cacheOperations.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.just(order));
when(paymentNotifyService.notifyPaymentStatus(order.getOrderNo(), "PENDING", MEMBER_ID)).thenReturn(Mono.empty());
@@ -458,7 +458,7 @@ class PaymentServiceImplTest {
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"S\"}");
params.put("sign", "testSign");
- when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
+ when(cacheOperations.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.empty());
Mono result = paymentService.handleAlipayNotify(params);
@@ -474,7 +474,7 @@ class PaymentServiceImplTest {
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"S\"}");
params.put("sign", "testSign");
- when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
+ when(cacheOperations.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.error(new RuntimeException("DB error")));
Mono result = paymentService.handleAlipayNotify(params);
diff --git a/gym-manage-api/manage-app/src/main/resources/application.yml b/gym-manage-api/manage-app/src/main/resources/application.yml
index 9cabfe0..5cb4448 100644
--- a/gym-manage-api/manage-app/src/main/resources/application.yml
+++ b/gym-manage-api/manage-app/src/main/resources/application.yml
@@ -1,6 +1,10 @@
server:
port: 8084
+gym:
+ cache:
+ type: redis
+
spring:
aop:
proxy-target-class: true
diff --git a/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/contract/BaseContractTest.java b/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/contract/BaseContractTest.java
index 384ead2..a0ab3e0 100644
--- a/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/contract/BaseContractTest.java
+++ b/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/contract/BaseContractTest.java
@@ -4,7 +4,7 @@ import cn.novalon.gym.manage.app.ManageApplication;
import cn.novalon.gym.manage.auth.config.DCloudUniverifyConfig;
import cn.novalon.gym.manage.auth.service.PhoneAuthService;
import cn.novalon.gym.manage.auth.service.SmsService;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.es.repository.MemberESRepository;
import cn.novalon.gym.manage.payment.config.HuifuProperties;
import cn.novalon.gym.manage.payment.service.PaymentNotifyService;
@@ -74,9 +74,9 @@ public abstract class BaseContractTest {
@MockBean
protected ReactiveStringRedisTemplate reactiveStringRedisTemplate;
- /** Mock RedisUtil — 阻止 Handler 中的 Redis 操作导致异常 */
+ /** Mock CacheOperations — 阻止 Handler 中的 Redis 操作导致异常 */
@MockBean
- protected RedisUtil redisUtil;
+ protected CacheOperations cacheOperations;
/** Mock ES Repository — 防止连接 Elasticsearch */
@MockBean
diff --git a/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/integration/GroupCourseHandlerIntegrationTest.java b/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/integration/GroupCourseHandlerIntegrationTest.java
index 54dbeaf..7692ce5 100644
--- a/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/integration/GroupCourseHandlerIntegrationTest.java
+++ b/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/integration/GroupCourseHandlerIntegrationTest.java
@@ -1,7 +1,7 @@
package cn.novalon.gym.manage.app.integration;
import cn.novalon.gym.manage.app.contract.BaseContractTest;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -26,13 +26,13 @@ import static org.mockito.Mockito.when;
class GroupCourseHandlerIntegrationTest extends BaseContractTest {
@Autowired
- private RedisUtil redisUtil;
+ private CacheOperations cacheOperations;
@BeforeEach
void setUp() {
// Mock Redis 操作,避免 GroupCourseService 中的 Redis 缓存逻辑导致 NPE
- when(redisUtil.get(anyString(), any())).thenReturn(Mono.empty());
- when(redisUtil.setWithExpire(anyString(), any(), anyLong())).thenReturn(Mono.just(true));
+ when(cacheOperations.get(anyString(), any())).thenReturn(Mono.empty());
+ when(cacheOperations.setWithExpire(anyString(), any(), anyLong())).thenReturn(Mono.just(true));
}
@Test
diff --git a/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/integration/MemberHandlerIntegrationTest.java b/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/integration/MemberHandlerIntegrationTest.java
index 74d44f5..a7f38db 100644
--- a/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/integration/MemberHandlerIntegrationTest.java
+++ b/gym-manage-api/manage-app/src/test/java/cn/novalon/gym/manage/app/integration/MemberHandlerIntegrationTest.java
@@ -1,7 +1,7 @@
package cn.novalon.gym.manage.app.integration;
import cn.novalon.gym.manage.app.contract.BaseContractTest;
-import cn.novalon.gym.manage.common.util.RedisUtil;
+import cn.novalon.gym.manage.common.cache.CacheOperations;
import cn.novalon.gym.manage.member.entity.Member;
import cn.novalon.gym.manage.member.es.entity.MemberES;
import cn.novalon.gym.manage.member.es.repository.MemberESRepository;
@@ -70,10 +70,10 @@ class MemberHandlerIntegrationTest extends BaseContractTest {
memberRepository.deleteAll().block();
when(authUtil.getMemberIdOrThrow(any())).thenReturn(1L);
// Mock Redis 缓存未命中,确保每次查询都走数据库
- when(redisUtil.get(anyString(), any())).thenReturn(Mono.empty());
- when(redisUtil.setWithExpire(anyString(), any(), anyLong())).thenReturn(Mono.just(true));
+ when(cacheOperations.get(anyString(), any())).thenReturn(Mono.empty());
+ when(cacheOperations.setWithExpire(anyString(), any(), anyLong())).thenReturn(Mono.just(true));
// Mock Redis 删除操作(clearMemberCache 调用),防止 NPE 导致 adminUpdateMemberInfo 返回 false
- when(redisUtil.delete(anyString())).thenReturn(Mono.just(1L));
+ when(cacheOperations.delete(anyString())).thenReturn(Mono.just(1L));
// Mock ES 搜索返回空结果,防止因 ES 不可用导致 500
when(memberESRepository.findByMemberNoOrPhoneOrNicknameContaining(anyString(), anyString(), anyString(), any()))
.thenReturn(Flux.empty());
diff --git a/gym-manage-api/manage-common/pom.xml b/gym-manage-api/manage-common/pom.xml
index e5a81c2..f7f3d7f 100644
--- a/gym-manage-api/manage-common/pom.xml
+++ b/gym-manage-api/manage-common/pom.xml
@@ -60,6 +60,10 @@
org.springframework.data
spring-data-redis
+
+ com.github.ben-manes.caffeine
+ caffeine
+
diff --git a/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheAutoConfiguration.java b/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheAutoConfiguration.java
new file mode 100644
index 0000000..d6613f2
--- /dev/null
+++ b/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheAutoConfiguration.java
@@ -0,0 +1,21 @@
+package cn.novalon.gym.manage.common.cache;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 缓存自动配置
+ *
+ * 启用 {@link CacheProperties} 配置绑定,并扫描 {@code cn.novalon.gym.manage.common.cache}
+ * 包下的缓存实现组件({@link RedisCacheOperations} / {@link CaffeineCacheOperations})。
+ *
+ *
+ * @author 张翔
+ * @date 2026-08-02
+ */
+@Configuration
+@EnableConfigurationProperties(CacheProperties.class)
+@ComponentScan(basePackageClasses = CacheAutoConfiguration.class)
+public class CacheAutoConfiguration {
+}
\ No newline at end of file
diff --git a/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheOperations.java b/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheOperations.java
new file mode 100644
index 0000000..1e51742
--- /dev/null
+++ b/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CacheOperations.java
@@ -0,0 +1,88 @@
+package cn.novalon.gym.manage.common.cache;
+
+import reactor.core.publisher.Mono;
+
+/**
+ * 缓存操作接口
+ *
+ * 定义统一的缓存操作抽象,支持通过配置切换不同的缓存实现(Caffeine / Redis)。
+ * 所有业务模块应依赖此接口而非具体实现,实现类由 {@link CacheAutoConfiguration} 按配置自动装配。
+ *
+ *
+ * @author 张翔
+ * @date 2026-08-02
+ * @see RedisCacheOperations
+ * @see CaffeineCacheOperations
+ */
+public interface CacheOperations {
+
+ /**
+ * 获取缓存值(带类型转换)
+ *
+ * @param key 缓存键
+ * @param clazz 目标类型
+ * @param 返回值类型
+ * @return 缓存值,不存在时返回 {@link Mono#empty()}
+ */
+ Mono get(String key, Class clazz);
+
+ /**
+ * 获取缓存值(返回 Object)
+ *
+ * @param key 缓存键
+ * @return 缓存值,不存在时返回 {@link Mono#empty()}
+ */
+ Mono
+ *
+ * {@code
+ * gym:
+ * cache:
+ * type: redis # redis | caffeine
+ * caffeine:
+ * initial-capacity: 100
+ * max-size: 10000
+ * expire-after-write-seconds: 300
+ * }
+ *
+ * @author 张翔
+ * @date 2026-08-02
+ */
+@ConfigurationProperties(prefix = "gym.cache")
+public class CacheProperties {
+
+ /** 缓存类型:redis / caffeine,默认 redis */
+ private String type = "redis";
+
+ /** Caffeine 本地缓存专属配置 */
+ private Caffeine caffeine = new Caffeine();
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public Caffeine getCaffeine() {
+ return caffeine;
+ }
+
+ public void setCaffeine(Caffeine caffeine) {
+ this.caffeine = caffeine;
+ }
+
+ public static class Caffeine {
+
+ /** 初始容量 */
+ private int initialCapacity = 100;
+
+ /** 最大条数 */
+ private long maxSize = 10000;
+
+ /** 写入后过期时间(秒),默认 5 分钟 */
+ private long expireAfterWriteSeconds = 300;
+
+ public int getInitialCapacity() {
+ return initialCapacity;
+ }
+
+ public void setInitialCapacity(int initialCapacity) {
+ this.initialCapacity = initialCapacity;
+ }
+
+ public long getMaxSize() {
+ return maxSize;
+ }
+
+ public void setMaxSize(long maxSize) {
+ this.maxSize = maxSize;
+ }
+
+ public long getExpireAfterWriteSeconds() {
+ return expireAfterWriteSeconds;
+ }
+
+ public void setExpireAfterWriteSeconds(long expireAfterWriteSeconds) {
+ this.expireAfterWriteSeconds = expireAfterWriteSeconds;
+ }
+ }
+}
\ No newline at end of file
diff --git a/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CaffeineCacheOperations.java b/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CaffeineCacheOperations.java
new file mode 100644
index 0000000..8e0438c
--- /dev/null
+++ b/gym-manage-api/manage-common/src/main/java/cn/novalon/gym/manage/common/cache/CaffeineCacheOperations.java
@@ -0,0 +1,111 @@
+package cn.novalon.gym.manage.common.cache;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+/**
+ * Caffeine 本地缓存实现
+ *
+ * 基于 Caffeine 的进程内缓存,适合单机部署或开发测试环境。
+ * 当 {@code gym.cache.type=caffeine} 时生效。
+ *
+ *
+ * @author 张翔
+ * @date 2026-08-02
+ * @see CacheOperations
+ * @see RedisCacheOperations
+ */
+@Component
+@ConditionalOnProperty(name = "gym.cache.type", havingValue = "caffeine")
+public class CaffeineCacheOperations implements CacheOperations {
+
+ private static final Logger log = LoggerFactory.getLogger(CaffeineCacheOperations.class);
+
+ private final Cache cache;
+
+ public CaffeineCacheOperations(CacheProperties cacheProperties) {
+ CacheProperties.Caffeine config = cacheProperties.getCaffeine();
+ this.cache = Caffeine.newBuilder()
+ .initialCapacity(config.getInitialCapacity())
+ .maximumSize(config.getMaxSize())
+ .expireAfterWrite(config.getExpireAfterWriteSeconds(), TimeUnit.SECONDS)
+ .recordStats()
+ .build();
+ log.info("Caffeine 缓存已初始化: initialCapacity={}, maxSize={}, expireAfterWrite={}s",
+ config.getInitialCapacity(), config.getMaxSize(), config.getExpireAfterWriteSeconds());
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Mono get(String key, Class clazz) {
+ Object value = cache.getIfPresent(key);
+ if (value == null) {
+ return Mono.empty();
+ }
+ if (clazz.isInstance(value)) {
+ return Mono.just((T) value);
+ }
+ log.warn("Caffeine 缓存类型不匹配: key={}, expected={}, actual={}",
+ key, clazz.getName(), value.getClass().getName());
+ return Mono.empty();
+ }
+
+ @Override
+ public Mono