diff --git a/docs/Guwan-awd.exe b/docs/Guwan-awd.exe deleted file mode 100644 index c0fdaf3..0000000 Binary files a/docs/Guwan-awd.exe and /dev/null differ diff --git a/pom.xml b/pom.xml index 3b6e392..3f9eec3 100644 --- a/pom.xml +++ b/pom.xml @@ -310,6 +310,12 @@ janino + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + + diff --git a/src/main/java/com/guwan/backend/Demo1.java b/src/main/java/com/guwan/backend/Demo1.java deleted file mode 100644 index d178b9a..0000000 --- a/src/main/java/com/guwan/backend/Demo1.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.guwan.backend; - -import com.guwan.backend.entity.BookOfUser; -import com.guwan.backend.enums.ReadStatus; - -public class Demo1 { - public static void main(String[] args) { - BookOfUser bookOfUser = new BookOfUser(); - bookOfUser.setReadStatus(ReadStatus.yes); - System.out.println("bookOfUser = " + bookOfUser); - } -} diff --git a/src/main/java/com/guwan/backend/config/CacheMonitor.java b/src/main/java/com/guwan/backend/config/CacheMonitor.java new file mode 100644 index 0000000..fa32081 --- /dev/null +++ b/src/main/java/com/guwan/backend/config/CacheMonitor.java @@ -0,0 +1,89 @@ +package com.guwan.backend.config; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.stats.CacheStats; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@RestController +@RequestMapping("/api/cache") +public class CacheMonitor { + + private final Cache userCache; + private final Cache productCache; + + public CacheMonitor(Cache userCache, + Cache productCache) { + this.userCache = userCache; + this.productCache = productCache; + } + + @GetMapping("/stats") + public Map getStats() { + Map stats = new HashMap<>(); + + // 用户缓存统计 + Map userStats = new HashMap<>(); + userStats.put("stats", userCache.stats()); + userStats.put("estimatedSize", userCache.estimatedSize()); + userStats.put("asMap", userCache.asMap()); + stats.put("userCache", userStats); + + // 产品缓存统计 + Map productStats = new HashMap<>(); + productStats.put("stats", productCache.stats()); + productStats.put("estimatedSize", productCache.estimatedSize()); + productStats.put("asMap", productCache.asMap()); + stats.put("productCache", productStats); + + return stats; + } + + + + @GetMapping("/details") + public String getDetails() { + StringBuilder details = new StringBuilder(); + details.append("=== Cache Details ===\n"); + + // 用户缓存详情 + CacheStats userStats = userCache.stats(); + details.append("User Cache:\n"); + details.append(" Hit count: ").append(userStats.hitCount()).append("\n"); + details.append(" Miss count: ").append(userStats.missCount()).append("\n"); + details.append(" Load success count: ").append(userStats.loadSuccessCount()).append("\n"); + details.append(" Load failure count: ").append(userStats.loadFailureCount()).append("\n"); + details.append(" Total load time: ").append(userStats.totalLoadTime()).append("\n"); + details.append(" Eviction count: ").append(userStats.evictionCount()).append("\n"); + details.append(" Estimated size: ").append(userCache.estimatedSize()).append("\n"); + + // 产品缓存详情 + CacheStats productStats = productCache.stats(); + details.append("\nProduct Cache:\n"); + details.append(" Hit count: ").append(productStats.hitCount()).append("\n"); + details.append(" Miss count: ").append(productStats.missCount()).append("\n"); + details.append(" Load success count: ").append(productStats.loadSuccessCount()).append("\n"); + details.append(" Load failure count: ").append(productStats.loadFailureCount()).append("\n"); + details.append(" Total load time: ").append(productStats.totalLoadTime()).append("\n"); + details.append(" Eviction count: ").append(productStats.evictionCount()).append("\n"); + details.append(" Estimated size: ").append(productCache.estimatedSize()).append("\n"); + + return details.toString(); + } + + + // 每5分钟打印一次缓存统计信息 + @Scheduled(fixedRate = 300000) + public void logCacheStats() { + log.info("=== Cache Stats ==="); + log.info("User Cache: {}", userCache.stats()); + log.info("Product Cache: {}", productCache.stats()); + } +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/config/CaffeineConfig.java b/src/main/java/com/guwan/backend/config/CaffeineConfig.java new file mode 100644 index 0000000..9acf3c2 --- /dev/null +++ b/src/main/java/com/guwan/backend/config/CaffeineConfig.java @@ -0,0 +1,57 @@ +package com.guwan.backend.config; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import java.util.concurrent.TimeUnit; + +@Slf4j +@EnableCaching +@Configuration +public class CaffeineConfig { + + @Bean + @Primary + public CacheManager caffeineCacheManager() { + CaffeineCacheManager cacheManager = new CaffeineCacheManager(); + cacheManager.setCaffeine(Caffeine.newBuilder() + .recordStats() + .expireAfterAccess(60, TimeUnit.MINUTES) + .initialCapacity(100) + .maximumSize(1000)); + return cacheManager; + } + + @Bean + public Cache userCache() { + return Caffeine.newBuilder() + .recordStats() + .expireAfterWrite(10, TimeUnit.MINUTES) + .initialCapacity(100) + .maximumSize(1000) + .build(); + } + + @Bean + public Cache productCache() { + return Caffeine.newBuilder() + .recordStats() + .expireAfterWrite(30, TimeUnit.MINUTES) + .initialCapacity(100) + .maximumSize(1000) + .build(); + } + + @Bean + public CacheMonitor cacheMonitor(Cache userCache, + Cache productCache) { + return new CacheMonitor(userCache, productCache); + } +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/service/impl/UserServiceImpl.java b/src/main/java/com/guwan/backend/service/impl/UserServiceImpl.java index 0975393..a83b106 100644 --- a/src/main/java/com/guwan/backend/service/impl/UserServiceImpl.java +++ b/src/main/java/com/guwan/backend/service/impl/UserServiceImpl.java @@ -1,14 +1,19 @@ package com.guwan.backend.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.guwan.backend.annotation.OperationLog; +import com.guwan.backend.common.BusinessException; import com.guwan.backend.dto.user.ChangePasswordDTO; import com.guwan.backend.dto.user.LoginDto; import com.guwan.backend.dto.user.RegisterDTO; import com.guwan.backend.dto.user.UserDTO; import com.guwan.backend.entity.User; import com.guwan.backend.enums.UserEnums; +import com.guwan.backend.mapper.BookMapper; import com.guwan.backend.mapper.UserMapper; import com.guwan.backend.mybatis.query.LambdaQueryWrapperX; import com.guwan.backend.service.EmailService; @@ -19,11 +24,15 @@ import com.guwan.backend.util.SecurityUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; +import java.util.List; @Slf4j @Service @@ -36,6 +45,7 @@ public class UserServiceImpl extends ServiceImpl implements Us private final RedisUtils redisUtil; private final EmailService emailService; private final SecurityUtil securityUtil; + private final BookMapper bookMapper; private static final String USER_CACHE_KEY = "user:"; private static final long USER_CACHE_DURATION = 3600L; // 1小时 @@ -150,7 +160,7 @@ public class UserServiceImpl extends ServiceImpl implements Us @Override - @OperationLog(description = "获取用户信息", operationType = "获取") + @OperationLog(description = "获取用户信息") public UserDTO getCurrentUser() { Long userId = securityUtil.getCurrentUserId(); if (userId == null) { @@ -163,13 +173,15 @@ public class UserServiceImpl extends ServiceImpl implements Us @Override @OperationLog(description = "根据Id获取用户信息") + @Cacheable(value = "userCache", key = "#id") public UserDTO getUserById(Long id) { + log.info("Checking Redis cache"); // 先从缓存获取 Object cached = redisUtil.get(USER_CACHE_KEY + id); if (cached != null) { return (UserDTO) cached; } - + log.info("Getting user from database, id: {}", id); User user = userMapper.selectById(id); if (user == null) { return null; @@ -191,13 +203,35 @@ public class UserServiceImpl extends ServiceImpl implements Us public void resetPassword(ChangePasswordDTO changePasswordDTO) { if(changePasswordDTO.getChangeWay().equals(UserEnums.ACCOUNT.getValue())){ - User user = this.getOne(new LambdaQueryWrapper() - .eq(User::getUsername, changePasswordDTO.getInfo())); + + + User user = this.lambdaQuery().eq(User::getUsername, changePasswordDTO.getInfo()).one(); + + //传入密码 库中密码 + boolean matches = passwordEncoder.matches(changePasswordDTO.getCode(), user.getPassword()); + if (!matches){ + log.error("原密码不正确"); + throw new BusinessException("原密码不正确"); + }else{ + String newPassword = passwordEncoder.encode(changePasswordDTO.getNewPassword()); + + boolean update = this.update(new LambdaUpdateWrapper() + .eq(User::getId, user.getId()) + .set(User::getPassword, newPassword)); + + if (!update) { + throw new BusinessException("密码更新失败"); + } + + } + + } if (changePasswordDTO.getChangeWay().equals(UserEnums.ACCOUNT.getValue())){ + } @@ -257,4 +291,20 @@ public class UserServiceImpl extends ServiceImpl implements Us BeanUtils.copyProperties(user, dto); return dto; } + + @CachePut(value = "userCache", key = "#user.id") + public UserDTO updateUser(UserDTO user) { + log.info("Updating user in cache, id: {}", user.getId()); + // 更新数据库 + //userMapper.updateById(user); + // 同时更新缓存 + return user; + } + + @CacheEvict(value = "userCache", key = "#id") + public void deleteUser(Long id) { + log.info("Removing user from cache, id: {}", id); + // 删除数据时同时删除缓存 + userMapper.deleteById(id); + } } \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/Demo.java b/src/test/java/com/guwan/backend/Demo.java similarity index 100% rename from src/main/java/com/guwan/backend/Demo.java rename to src/test/java/com/guwan/backend/Demo.java diff --git a/src/main/java/com/guwan/backend/OkHttpExample.java b/src/test/java/com/guwan/backend/OkHttpExample.java similarity index 100% rename from src/main/java/com/guwan/backend/OkHttpExample.java rename to src/test/java/com/guwan/backend/OkHttpExample.java diff --git a/src/main/java/com/guwan/backend/OkHttpExample2.java b/src/test/java/com/guwan/backend/OkHttpExample2.java similarity index 100% rename from src/main/java/com/guwan/backend/OkHttpExample2.java rename to src/test/java/com/guwan/backend/OkHttpExample2.java