增加Redis缓存功能
注:/api/groupCourse/cache/get接口为测试接口
This commit is contained in:
@@ -67,6 +67,13 @@
|
|||||||
<version>2.2.43</version>
|
<version>2.2.43</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Redis依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
+91
-1
@@ -1,8 +1,11 @@
|
|||||||
|
|
||||||
package cn.novalon.gym.manage.groupcourse.handler;
|
package cn.novalon.gym.manage.groupcourse.handler;
|
||||||
|
|
||||||
|
import cn.novalon.gym.manage.common.dto.PageRequest;
|
||||||
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
||||||
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseService;
|
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseService;
|
||||||
|
import cn.novalon.gym.manage.groupcourse.service.RedisService;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.validation.Validator;
|
import jakarta.validation.Validator;
|
||||||
@@ -11,15 +14,25 @@ import org.springframework.web.reactive.function.server.ServerRequest;
|
|||||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@Tag(name="团课管理",description = "团课相关操作")
|
@Tag(name="团课管理",description = "团课相关操作")
|
||||||
public class GroupCourseHandler {
|
public class GroupCourseHandler {
|
||||||
private final IGroupCourseService groupCourseService;
|
private final IGroupCourseService groupCourseService;
|
||||||
private final Validator validator;
|
private final Validator validator;
|
||||||
|
private final RedisService redisService;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
public GroupCourseHandler(IGroupCourseService groupCourseService, Validator validator){
|
public GroupCourseHandler(IGroupCourseService groupCourseService,
|
||||||
|
Validator validator,
|
||||||
|
RedisService redisService,
|
||||||
|
ObjectMapper objectMapper){
|
||||||
this.groupCourseService = groupCourseService;
|
this.groupCourseService = groupCourseService;
|
||||||
this.validator = validator;
|
this.validator = validator;
|
||||||
|
this.redisService = redisService;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "获取所有团课", description = "获取系统中所有团课列表")
|
@Operation(summary = "获取所有团课", description = "获取系统中所有团课列表")
|
||||||
@@ -29,10 +42,87 @@ public class GroupCourseHandler {
|
|||||||
.body(groupCourseService.findAll(includeDeleted), GroupCourse.class);
|
.body(groupCourseService.findAll(includeDeleted), GroupCourse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "分页获取团课", description = "根据分页参数获取团课列表")
|
||||||
|
public Mono<ServerResponse> getGroupCoursesByPage(ServerRequest request) {
|
||||||
|
return request.bodyToMono(PageRequest.class)
|
||||||
|
.flatMap(pageRequest -> {
|
||||||
|
boolean includeDeleted = request.queryParam("includeDeleted")
|
||||||
|
.map(Boolean::parseBoolean)
|
||||||
|
.orElse(false);
|
||||||
|
|
||||||
|
if (pageRequest.getPage() < 0) {
|
||||||
|
pageRequest.setPage(0);
|
||||||
|
}
|
||||||
|
if (pageRequest.getSize() <= 0 || pageRequest.getSize() > 100) {
|
||||||
|
pageRequest.setSize(10);
|
||||||
|
}
|
||||||
|
if (pageRequest.getSort() == null || pageRequest.getSort().isEmpty()) {
|
||||||
|
pageRequest.setSort("id");
|
||||||
|
}
|
||||||
|
if (pageRequest.getOrder() == null || pageRequest.getOrder().isEmpty()) {
|
||||||
|
pageRequest.setOrder("asc");
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupCourseService.findByPage(pageRequest, includeDeleted)
|
||||||
|
.flatMap(response -> ServerResponse.ok().bodyValue(response));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(summary = "根据ID获取团课", description = "根据ID获取团课详情")
|
@Operation(summary = "根据ID获取团课", description = "根据ID获取团课详情")
|
||||||
public Mono<ServerResponse> getGroupCourseById(ServerRequest request){
|
public Mono<ServerResponse> getGroupCourseById(ServerRequest request){
|
||||||
Long id = Long.valueOf(request.pathVariable("id"));
|
Long id = Long.valueOf(request.pathVariable("id"));
|
||||||
return ServerResponse.ok()
|
return ServerResponse.ok()
|
||||||
.body(groupCourseService.findById(id), GroupCourse.class);
|
.body(groupCourseService.findById(id), GroupCourse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "测试-根据Key获取Redis缓存", description = "测试接口:根据传入的key值获取Redis中缓存的数据")
|
||||||
|
public Mono<ServerResponse> getCacheByKey(ServerRequest request) {
|
||||||
|
return request.bodyToMono(Map.class)
|
||||||
|
.flatMap(body -> {
|
||||||
|
String key = (String) body.get("key");
|
||||||
|
|
||||||
|
if (key == null || key.isEmpty()) {
|
||||||
|
Map<String, Object> error = new HashMap<>();
|
||||||
|
error.put("success", false);
|
||||||
|
error.put("message", "key参数不能为空");
|
||||||
|
return ServerResponse.badRequest().bodyValue(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object cachedValue = redisService.get(key);
|
||||||
|
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
if (cachedValue != null) {
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("key", key);
|
||||||
|
result.put("value", cachedValue);
|
||||||
|
result.put("message", "缓存命中");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (cachedValue instanceof String) {
|
||||||
|
Object jsonObject = objectMapper.readValue((String) cachedValue, Object.class);
|
||||||
|
result.put("parsedValue", jsonObject);
|
||||||
|
result.put("valueType", "JSON字符串");
|
||||||
|
} else {
|
||||||
|
result.put("valueType", cachedValue.getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.put("parsedValue", null);
|
||||||
|
result.put("valueType", "无法解析");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("key", key);
|
||||||
|
result.put("value", null);
|
||||||
|
result.put("message", "缓存未命中");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ServerResponse.ok().bodyValue(result);
|
||||||
|
})
|
||||||
|
.onErrorResume(error -> {
|
||||||
|
Map<String, Object> errorResponse = new HashMap<>();
|
||||||
|
errorResponse.put("success", false);
|
||||||
|
errorResponse.put("message", "请求处理失败: " + error.getMessage());
|
||||||
|
return ServerResponse.status(500).bodyValue(errorResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
package cn.novalon.gym.manage.groupcourse.repository;
|
package cn.novalon.gym.manage.groupcourse.repository;
|
||||||
|
|
||||||
|
import cn.novalon.gym.manage.common.dto.PageRequest;
|
||||||
|
import cn.novalon.gym.manage.common.dto.PageResponse;
|
||||||
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
@@ -12,4 +14,7 @@ public interface IGroupCourseRepository {
|
|||||||
Flux<GroupCourse> findAll(Sort sort);
|
Flux<GroupCourse> findAll(Sort sort);
|
||||||
Flux<GroupCourse> findByDeletedAtIsNull();
|
Flux<GroupCourse> findByDeletedAtIsNull();
|
||||||
Flux<GroupCourse> findByDeletedAtIsNull(Sort sort);
|
Flux<GroupCourse> findByDeletedAtIsNull(Sort sort);
|
||||||
|
|
||||||
|
Mono<PageResponse<GroupCourse>> findByPage(PageRequest pageRequest);
|
||||||
|
Mono<PageResponse<GroupCourse>> findByPageAndNotDeleted(PageRequest pageRequest);
|
||||||
}
|
}
|
||||||
|
|||||||
+75
@@ -1,16 +1,22 @@
|
|||||||
|
|
||||||
package cn.novalon.gym.manage.groupcourse.repository.impl;
|
package cn.novalon.gym.manage.groupcourse.repository.impl;
|
||||||
|
|
||||||
|
import cn.novalon.gym.manage.common.dto.PageRequest;
|
||||||
|
import cn.novalon.gym.manage.common.dto.PageResponse;
|
||||||
import cn.novalon.gym.manage.groupcourse.converter.GroupCourseConverter;
|
import cn.novalon.gym.manage.groupcourse.converter.GroupCourseConverter;
|
||||||
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseDao;
|
import cn.novalon.gym.manage.groupcourse.dao.GroupCourseDao;
|
||||||
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
||||||
|
import cn.novalon.gym.manage.groupcourse.entity.GroupCourseEntity;
|
||||||
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
|
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
import org.springframework.data.r2dbc.core.R2dbcEntityTemplate;
|
||||||
|
import org.springframework.data.relational.core.query.Query;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public class GroupCourseRepository implements IGroupCourseRepository {
|
public class GroupCourseRepository implements IGroupCourseRepository {
|
||||||
private final GroupCourseDao groupCourseDao;
|
private final GroupCourseDao groupCourseDao;
|
||||||
@@ -53,4 +59,73 @@ public class GroupCourseRepository implements IGroupCourseRepository {
|
|||||||
return groupCourseDao.findAllByDeletedAtIsNull(sort)
|
return groupCourseDao.findAllByDeletedAtIsNull(sort)
|
||||||
.map(groupCourseConverter::toDomain);
|
.map(groupCourseConverter::toDomain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<PageResponse<GroupCourse>> findByPage(PageRequest pageRequest) {
|
||||||
|
int page = pageRequest.getPage();
|
||||||
|
int size = pageRequest.getSize();
|
||||||
|
String sort = pageRequest.getSort();
|
||||||
|
String order = pageRequest.getOrder();
|
||||||
|
|
||||||
|
Sort sortObj = Sort.unsorted();
|
||||||
|
if (sort != null && !sort.isEmpty()) {
|
||||||
|
sortObj = Sort.by(Sort.Direction.fromString(order), sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
org.springframework.data.domain.PageRequest pageable = org.springframework.data.domain.PageRequest.of(page, size, sortObj);
|
||||||
|
|
||||||
|
Query query = Query.empty();
|
||||||
|
|
||||||
|
return r2dbcEntityTemplate.select(GroupCourseEntity.class)
|
||||||
|
.matching(query.with(pageable))
|
||||||
|
.all()
|
||||||
|
.collectList()
|
||||||
|
.zipWith(r2dbcEntityTemplate.count(query, GroupCourseEntity.class))
|
||||||
|
.map(tuple -> {
|
||||||
|
long total = tuple.getT2();
|
||||||
|
int totalPages = (int) Math.ceil((double) total / size);
|
||||||
|
List<GroupCourse> courseList = tuple.getT1().stream()
|
||||||
|
.map(groupCourseConverter::toDomain)
|
||||||
|
.toList();
|
||||||
|
return new PageResponse<>(courseList, totalPages, total, page, size);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<PageResponse<GroupCourse>> findByPageAndNotDeleted(PageRequest pageRequest) {
|
||||||
|
int page = pageRequest.getPage();
|
||||||
|
int size = pageRequest.getSize();
|
||||||
|
String sort = pageRequest.getSort();
|
||||||
|
String order = pageRequest.getOrder();
|
||||||
|
|
||||||
|
Sort sortObj = Sort.unsorted();
|
||||||
|
if (sort != null && !sort.isEmpty()) {
|
||||||
|
sortObj = Sort.by(Sort.Direction.fromString(order), sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
org.springframework.data.domain.PageRequest pageable = org.springframework.data.domain.PageRequest.of(page, size, sortObj);
|
||||||
|
|
||||||
|
return groupCourseDao.findAllByDeletedAtIsNull(sortObj)
|
||||||
|
.collectList()
|
||||||
|
.zipWith(groupCourseDao.findAllByDeletedAtIsNull().count())
|
||||||
|
.map(tuple -> {
|
||||||
|
List<GroupCourseEntity> allEntities = tuple.getT1();
|
||||||
|
long total = tuple.getT2();
|
||||||
|
|
||||||
|
int fromIndex = page * size;
|
||||||
|
int toIndex = Math.min(fromIndex + size, allEntities.size());
|
||||||
|
|
||||||
|
List<GroupCourse> courseList;
|
||||||
|
if (fromIndex < allEntities.size()) {
|
||||||
|
courseList = allEntities.subList(fromIndex, toIndex).stream()
|
||||||
|
.map(groupCourseConverter::toDomain)
|
||||||
|
.toList();
|
||||||
|
} else {
|
||||||
|
courseList = List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
int totalPages = (int) Math.ceil((double) total / size);
|
||||||
|
return new PageResponse<>(courseList, totalPages, total, page, size);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
@@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
package cn.novalon.gym.manage.groupcourse.service;
|
package cn.novalon.gym.manage.groupcourse.service;
|
||||||
|
|
||||||
|
import cn.novalon.gym.manage.common.dto.PageRequest;
|
||||||
|
import cn.novalon.gym.manage.common.dto.PageResponse;
|
||||||
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
@@ -9,4 +11,6 @@ public interface IGroupCourseService {
|
|||||||
Mono<GroupCourse> findById(Long id);
|
Mono<GroupCourse> findById(Long id);
|
||||||
Flux<GroupCourse> findAll();
|
Flux<GroupCourse> findAll();
|
||||||
Flux<GroupCourse> findAll(boolean includeDeleted);
|
Flux<GroupCourse> findAll(boolean includeDeleted);
|
||||||
|
|
||||||
|
Mono<PageResponse<GroupCourse>> findByPage(PageRequest pageRequest, boolean includeDeleted);
|
||||||
}
|
}
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
package cn.novalon.gym.manage.groupcourse.service;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author:liwentao
|
||||||
|
* @date:2026/5/15-05-15-16:05
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class RedisService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
// 设置值
|
||||||
|
public void set(String key, Object value) {
|
||||||
|
redisTemplate.opsForValue().set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置值并指定过期时间(秒)
|
||||||
|
public void setWithExpire(String key, Object value, long timeout) {
|
||||||
|
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取值
|
||||||
|
public Object get(String key) {
|
||||||
|
return redisTemplate.opsForValue().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除key
|
||||||
|
public Boolean delete(String key) {
|
||||||
|
return redisTemplate.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断key是否存在
|
||||||
|
public Boolean hasKey(String key) {
|
||||||
|
return redisTemplate.hasKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置过期时间
|
||||||
|
public Boolean expire(String key, long timeout) {
|
||||||
|
return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash操作
|
||||||
|
public void putHash(String key, String hashKey, Object value) {
|
||||||
|
redisTemplate.opsForHash().put(key, hashKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getHash(String key, String hashKey) {
|
||||||
|
return redisTemplate.opsForHash().get(key, hashKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// List操作
|
||||||
|
public void leftPush(String key, Object value) {
|
||||||
|
redisTemplate.opsForList().leftPush(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object rightPop(String key) {
|
||||||
|
return redisTemplate.opsForList().rightPop(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set操作
|
||||||
|
public void addToSet(String key, Object... values) {
|
||||||
|
redisTemplate.opsForSet().add(key, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Object> getSet(String key) {
|
||||||
|
return redisTemplate.opsForSet().members(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
+104
-3
@@ -1,24 +1,79 @@
|
|||||||
|
|
||||||
package cn.novalon.gym.manage.groupcourse.service.impl;
|
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.groupcourse.domain.GroupCourse;
|
import cn.novalon.gym.manage.groupcourse.domain.GroupCourse;
|
||||||
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
|
import cn.novalon.gym.manage.groupcourse.repository.IGroupCourseRepository;
|
||||||
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseService;
|
import cn.novalon.gym.manage.groupcourse.service.IGroupCourseService;
|
||||||
|
import cn.novalon.gym.manage.groupcourse.service.RedisService;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class GroupCourseService implements IGroupCourseService {
|
public class GroupCourseService implements IGroupCourseService {
|
||||||
private final IGroupCourseRepository groupCourseRepository;
|
private static final Logger logger = LoggerFactory.getLogger(GroupCourseService.class);
|
||||||
|
|
||||||
public GroupCourseService(IGroupCourseRepository groupCourseRepository){
|
private final IGroupCourseRepository groupCourseRepository;
|
||||||
|
private final RedisService redisService;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
private static final String CACHE_KEY_PREFIX = "group_course:page:";
|
||||||
|
private static final String CACHE_KEY_ID_PREFIX = "group_course:id:";
|
||||||
|
private static final long CACHE_EXPIRE_SECONDS = 300;
|
||||||
|
|
||||||
|
public GroupCourseService(IGroupCourseRepository groupCourseRepository,
|
||||||
|
RedisService redisService,
|
||||||
|
ObjectMapper objectMapper){
|
||||||
this.groupCourseRepository = groupCourseRepository;
|
this.groupCourseRepository = groupCourseRepository;
|
||||||
|
this.redisService = redisService;
|
||||||
|
this.objectMapper = objectMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<GroupCourse> findById(Long id) {
|
public Mono<GroupCourse> findById(Long id) {
|
||||||
return groupCourseRepository.findByIdAndDeletedAtIsNull(id);
|
String cacheKey = CACHE_KEY_ID_PREFIX + id;
|
||||||
|
|
||||||
|
Object cachedData = redisService.get(cacheKey);
|
||||||
|
if (cachedData != null) {
|
||||||
|
try {
|
||||||
|
String json;
|
||||||
|
if (cachedData instanceof String) {
|
||||||
|
json = (String) cachedData;
|
||||||
|
} else {
|
||||||
|
json = objectMapper.writeValueAsString(cachedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupCourse groupCourse = objectMapper.readValue(json, GroupCourse.class);
|
||||||
|
logger.info("缓存命中 - findById: id={}", id);
|
||||||
|
return Mono.just(groupCourse);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
logger.warn("缓存解析失败,删除缓存 - id: {}, error: {}", id, e.getMessage());
|
||||||
|
redisService.delete(cacheKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("缓存未命中,查询数据库 - findById: id={}", id);
|
||||||
|
return groupCourseRepository.findByIdAndDeletedAtIsNull(id)
|
||||||
|
.doOnSuccess(groupCourse -> {
|
||||||
|
if (groupCourse != null) {
|
||||||
|
try {
|
||||||
|
String jsonData = objectMapper.writeValueAsString(groupCourse);
|
||||||
|
redisService.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS);
|
||||||
|
logger.debug("缓存已设置 - findById: id={}", id);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
logger.error("缓存设置失败 - id: {}, error: {}", id, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -34,4 +89,50 @@ public class GroupCourseService implements IGroupCourseService {
|
|||||||
return groupCourseRepository.findByDeletedAtIsNull();
|
return groupCourseRepository.findByDeletedAtIsNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Mono<PageResponse<GroupCourse>> findByPage(PageRequest pageRequest, boolean includeDeleted) {
|
||||||
|
int page = pageRequest.getPage();
|
||||||
|
int size = pageRequest.getSize();
|
||||||
|
|
||||||
|
String cacheKey = CACHE_KEY_PREFIX + page + ":" + size + ":" + includeDeleted;
|
||||||
|
|
||||||
|
Object cachedData = redisService.get(cacheKey);
|
||||||
|
if (cachedData != null) {
|
||||||
|
try {
|
||||||
|
String json;
|
||||||
|
if (cachedData instanceof String) {
|
||||||
|
json = (String) cachedData;
|
||||||
|
} else {
|
||||||
|
json = objectMapper.writeValueAsString(cachedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
PageResponse<GroupCourse> pageResponse = objectMapper.readValue(json,
|
||||||
|
objectMapper.getTypeFactory().constructParametricType(PageResponse.class, GroupCourse.class));
|
||||||
|
logger.info("缓存命中 - findByPage: key={}", cacheKey);
|
||||||
|
return Mono.just(pageResponse);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
logger.warn("缓存解析失败,删除缓存 - key: {}, error: {}", cacheKey, e.getMessage());
|
||||||
|
redisService.delete(cacheKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("缓存未命中,查询数据库 - findByPage: key={}", cacheKey);
|
||||||
|
Mono<PageResponse<GroupCourse>> resultMono;
|
||||||
|
if (includeDeleted) {
|
||||||
|
resultMono = groupCourseRepository.findByPage(pageRequest);
|
||||||
|
} else {
|
||||||
|
resultMono = groupCourseRepository.findByPageAndNotDeleted(pageRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMono.doOnSuccess(pageResponse -> {
|
||||||
|
try {
|
||||||
|
String jsonData = objectMapper.writeValueAsString(pageResponse);
|
||||||
|
redisService.setWithExpire(cacheKey, jsonData, CACHE_EXPIRE_SECONDS);
|
||||||
|
logger.debug("缓存已设置 - findByPage: key={}", cacheKey);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
logger.error("缓存设置失败 - key: {}, error: {}", cacheKey, e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
package cn.novalon.gym.manage.app.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author:liwentao
|
||||||
|
* @date:2026/5/15-05-15-16:01
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class RedisConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
|
||||||
|
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||||
|
template.setConnectionFactory(factory);
|
||||||
|
|
||||||
|
// 创建ObjectMapper并配置
|
||||||
|
ObjectMapper om = new ObjectMapper();
|
||||||
|
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
|
||||||
|
om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
|
||||||
|
|
||||||
|
// 使用GenericJackson2JsonRedisSerializer替代已弃用的方式
|
||||||
|
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(om);
|
||||||
|
|
||||||
|
// 使用StringRedisSerializer来序列化和反序列化redis的key值
|
||||||
|
StringRedisSerializer stringSerializer = new StringRedisSerializer();
|
||||||
|
template.setKeySerializer(stringSerializer);
|
||||||
|
template.setValueSerializer(genericJackson2JsonRedisSerializer);
|
||||||
|
template.setHashKeySerializer(stringSerializer);
|
||||||
|
template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
|
||||||
|
|
||||||
|
template.afterPropertiesSet();
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-25
@@ -24,16 +24,6 @@ import org.springframework.web.reactive.function.server.ServerResponse;
|
|||||||
|
|
||||||
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
|
||||||
|
|
||||||
/**
|
|
||||||
* 系统路由配置类
|
|
||||||
*
|
|
||||||
* 文件定义:配置WebFlux函数式路由,将HTTP请求映射到对应的Handler方法
|
|
||||||
* 涉及业务:用户、角色、字典、菜单、公告、文件等所有RESTful API路由
|
|
||||||
* 算法:使用RouterFunctions.route()构建函数式路由规则
|
|
||||||
*
|
|
||||||
* @author 张翔
|
|
||||||
* @date 2026-03-13
|
|
||||||
*/
|
|
||||||
@Configuration
|
@Configuration
|
||||||
public class SystemRouter {
|
public class SystemRouter {
|
||||||
@Bean
|
@Bean
|
||||||
@@ -56,10 +46,8 @@ public class SystemRouter {
|
|||||||
PasswordDiagnosticHandler passwordDiagnosticHandler) {
|
PasswordDiagnosticHandler passwordDiagnosticHandler) {
|
||||||
|
|
||||||
return route()
|
return route()
|
||||||
// ========== 诊断路由 ==========
|
|
||||||
.GET("/api/diagnostic/password", passwordDiagnosticHandler::diagnose)
|
.GET("/api/diagnostic/password", passwordDiagnosticHandler::diagnose)
|
||||||
|
|
||||||
// ========== 字典路由 ==========
|
|
||||||
.GET("/api/dictionaries", dictionaryHandler::getAllDictionaries)
|
.GET("/api/dictionaries", dictionaryHandler::getAllDictionaries)
|
||||||
.GET("/api/dictionaries/{id}", dictionaryHandler::getDictionaryById)
|
.GET("/api/dictionaries/{id}", dictionaryHandler::getDictionaryById)
|
||||||
.GET("/api/dictionaries/type/{type}", dictionaryHandler::getDictionariesByType)
|
.GET("/api/dictionaries/type/{type}", dictionaryHandler::getDictionariesByType)
|
||||||
@@ -68,7 +56,6 @@ public class SystemRouter {
|
|||||||
.PUT("/api/dictionaries/{id}", dictionaryHandler::updateDictionary)
|
.PUT("/api/dictionaries/{id}", dictionaryHandler::updateDictionary)
|
||||||
.DELETE("/api/dictionaries/{id}", dictionaryHandler::deleteDictionary)
|
.DELETE("/api/dictionaries/{id}", dictionaryHandler::deleteDictionary)
|
||||||
|
|
||||||
// ========== 用户路由 ==========
|
|
||||||
.GET("/api/users", userHandler::getAllUsers)
|
.GET("/api/users", userHandler::getAllUsers)
|
||||||
.GET("/api/users/page", userHandler::getUsersByPage)
|
.GET("/api/users/page", userHandler::getUsersByPage)
|
||||||
.GET("/api/users/count", userHandler::getUserCount)
|
.GET("/api/users/count", userHandler::getUserCount)
|
||||||
@@ -87,7 +74,6 @@ public class SystemRouter {
|
|||||||
.GET("/api/users/{id}/roles", userHandler::getUserRoles)
|
.GET("/api/users/{id}/roles", userHandler::getUserRoles)
|
||||||
.POST("/api/users/{id}/roles", userHandler::assignRoles)
|
.POST("/api/users/{id}/roles", userHandler::assignRoles)
|
||||||
|
|
||||||
// ========== 菜单路由 ==========
|
|
||||||
.GET("/api/menus", menuHandler::getAllMenus)
|
.GET("/api/menus", menuHandler::getAllMenus)
|
||||||
.GET("/api/menus/tree", menuHandler::getMenuTree)
|
.GET("/api/menus/tree", menuHandler::getMenuTree)
|
||||||
.GET("/api/menus/{id}", menuHandler::getMenuById)
|
.GET("/api/menus/{id}", menuHandler::getMenuById)
|
||||||
@@ -95,7 +81,6 @@ public class SystemRouter {
|
|||||||
.PUT("/api/menus/{id}", menuHandler::updateMenu)
|
.PUT("/api/menus/{id}", menuHandler::updateMenu)
|
||||||
.DELETE("/api/menus/{id}", menuHandler::deleteMenu)
|
.DELETE("/api/menus/{id}", menuHandler::deleteMenu)
|
||||||
|
|
||||||
// ========== 角色路由 ==========
|
|
||||||
.GET("/api/roles", roleHandler::getAllRoles)
|
.GET("/api/roles", roleHandler::getAllRoles)
|
||||||
.GET("/api/roles/page", roleHandler::getRolesByPage)
|
.GET("/api/roles/page", roleHandler::getRolesByPage)
|
||||||
.GET("/api/roles/count", roleHandler::getRoleCount)
|
.GET("/api/roles/count", roleHandler::getRoleCount)
|
||||||
@@ -109,7 +94,6 @@ public class SystemRouter {
|
|||||||
.GET("/api/roles/{id}/permissions", permissionHandler::getPermissionsByRoleId)
|
.GET("/api/roles/{id}/permissions", permissionHandler::getPermissionsByRoleId)
|
||||||
.POST("/api/roles/{id}/permissions", permissionHandler::assignPermissionsToRole)
|
.POST("/api/roles/{id}/permissions", permissionHandler::assignPermissionsToRole)
|
||||||
|
|
||||||
// ========== 配置路由 ==========
|
|
||||||
.GET("/api/config", configHandler::getAllConfigs)
|
.GET("/api/config", configHandler::getAllConfigs)
|
||||||
.GET("/api/config/{id}", configHandler::getConfigById)
|
.GET("/api/config/{id}", configHandler::getConfigById)
|
||||||
.GET("/api/config/key/{configKey}", configHandler::getConfigByKey)
|
.GET("/api/config/key/{configKey}", configHandler::getConfigByKey)
|
||||||
@@ -117,7 +101,6 @@ public class SystemRouter {
|
|||||||
.PUT("/api/config/{id}", configHandler::updateConfig)
|
.PUT("/api/config/{id}", configHandler::updateConfig)
|
||||||
.DELETE("/api/config/{id}", configHandler::deleteConfig)
|
.DELETE("/api/config/{id}", configHandler::deleteConfig)
|
||||||
|
|
||||||
// ========== 日志路由 ==========
|
|
||||||
.GET("/api/logs/login", logHandler::getAllLoginLogs)
|
.GET("/api/logs/login", logHandler::getAllLoginLogs)
|
||||||
.GET("/api/logs/login/page", logHandler::getLoginLogsByPage)
|
.GET("/api/logs/login/page", logHandler::getLoginLogsByPage)
|
||||||
.GET("/api/logs/login/count", logHandler::getLoginLogCount)
|
.GET("/api/logs/login/count", logHandler::getLoginLogCount)
|
||||||
@@ -137,15 +120,12 @@ public class SystemRouter {
|
|||||||
.GET("/api/logs/operation/{id}", operationLogHandler::getOperationLogById)
|
.GET("/api/logs/operation/{id}", operationLogHandler::getOperationLogById)
|
||||||
.POST("/api/logs/operation", operationLogHandler::createOperationLog)
|
.POST("/api/logs/operation", operationLogHandler::createOperationLog)
|
||||||
|
|
||||||
// ========== 认证路由 ==========
|
|
||||||
.POST("/api/auth/login", authHandler::login)
|
.POST("/api/auth/login", authHandler::login)
|
||||||
.POST("/api/auth/register", authHandler::register)
|
.POST("/api/auth/register", authHandler::register)
|
||||||
.POST("/api/auth/logout", authHandler::logout)
|
.POST("/api/auth/logout", authHandler::logout)
|
||||||
|
|
||||||
// ========== 统计路由 ==========
|
|
||||||
.GET("/api/stats/overview", statsHandler::getOverview)
|
.GET("/api/stats/overview", statsHandler::getOverview)
|
||||||
|
|
||||||
// ========== 数据字典路由 ==========
|
|
||||||
.GET("/api/dict/types", dictHandler::getAllDictTypes)
|
.GET("/api/dict/types", dictHandler::getAllDictTypes)
|
||||||
.GET("/api/dict/types/{id}", dictHandler::getDictTypeById)
|
.GET("/api/dict/types/{id}", dictHandler::getDictTypeById)
|
||||||
.GET("/api/dict/types/type/{dictType}", dictHandler::getDictTypeByType)
|
.GET("/api/dict/types/type/{dictType}", dictHandler::getDictTypeByType)
|
||||||
@@ -159,7 +139,6 @@ public class SystemRouter {
|
|||||||
.PUT("/api/dict/data/{id}", dictHandler::updateDictData)
|
.PUT("/api/dict/data/{id}", dictHandler::updateDictData)
|
||||||
.DELETE("/api/dict/data/{id}", dictHandler::deleteDictData)
|
.DELETE("/api/dict/data/{id}", dictHandler::deleteDictData)
|
||||||
|
|
||||||
// ========== 公告路由 ==========
|
|
||||||
.GET("/api/notices", noticeHandler::getAllNotices)
|
.GET("/api/notices", noticeHandler::getAllNotices)
|
||||||
.GET("/api/notices/{id}", noticeHandler::getNoticeById)
|
.GET("/api/notices/{id}", noticeHandler::getNoticeById)
|
||||||
.GET("/api/notices/status/{status}", noticeHandler::getNoticesByStatus)
|
.GET("/api/notices/status/{status}", noticeHandler::getNoticesByStatus)
|
||||||
@@ -167,7 +146,6 @@ public class SystemRouter {
|
|||||||
.PUT("/api/notices/{id}", noticeHandler::updateNotice)
|
.PUT("/api/notices/{id}", noticeHandler::updateNotice)
|
||||||
.DELETE("/api/notices/{id}", noticeHandler::deleteNotice)
|
.DELETE("/api/notices/{id}", noticeHandler::deleteNotice)
|
||||||
|
|
||||||
// ========== 消息路由 ==========
|
|
||||||
.GET("/api/messages/user/{userId}", messageHandler::getMessagesByUser)
|
.GET("/api/messages/user/{userId}", messageHandler::getMessagesByUser)
|
||||||
.GET("/api/messages/user/{userId}/unread", messageHandler::getUnreadCount)
|
.GET("/api/messages/user/{userId}/unread", messageHandler::getUnreadCount)
|
||||||
.GET("/api/messages/user/{userId}/unread/list", messageHandler::getUnreadList)
|
.GET("/api/messages/user/{userId}/unread/list", messageHandler::getUnreadList)
|
||||||
@@ -175,7 +153,6 @@ public class SystemRouter {
|
|||||||
.PUT("/api/messages/{id}/read", messageHandler::markAsRead)
|
.PUT("/api/messages/{id}/read", messageHandler::markAsRead)
|
||||||
.DELETE("/api/messages/{id}", messageHandler::deleteMessage)
|
.DELETE("/api/messages/{id}", messageHandler::deleteMessage)
|
||||||
|
|
||||||
// ========== 文件路由 ==========
|
|
||||||
.GET("/api/files", fileHandler::getAllFiles)
|
.GET("/api/files", fileHandler::getAllFiles)
|
||||||
.GET("/api/files/{id}", fileHandler::getFileById)
|
.GET("/api/files/{id}", fileHandler::getFileById)
|
||||||
.POST("/api/files/upload", fileHandler::uploadFile)
|
.POST("/api/files/upload", fileHandler::uploadFile)
|
||||||
@@ -185,7 +162,6 @@ public class SystemRouter {
|
|||||||
.GET("/api/files/preview/{fileName}", fileHandler::previewFileByName)
|
.GET("/api/files/preview/{fileName}", fileHandler::previewFileByName)
|
||||||
.DELETE("/api/files/{id}", fileHandler::deleteFile)
|
.DELETE("/api/files/{id}", fileHandler::deleteFile)
|
||||||
|
|
||||||
// ========== 权限路由 ==========
|
|
||||||
.GET("/api/permissions", permissionHandler::getAllPermissions)
|
.GET("/api/permissions", permissionHandler::getAllPermissions)
|
||||||
.GET("/api/permissions/{id}", permissionHandler::getPermissionById)
|
.GET("/api/permissions/{id}", permissionHandler::getPermissionById)
|
||||||
.GET("/api/permissions/code/{code}", permissionHandler::getPermissionByCode)
|
.GET("/api/permissions/code/{code}", permissionHandler::getPermissionByCode)
|
||||||
@@ -195,8 +171,9 @@ public class SystemRouter {
|
|||||||
.PUT("/api/permissions/{id}", permissionHandler::updatePermission)
|
.PUT("/api/permissions/{id}", permissionHandler::updatePermission)
|
||||||
.DELETE("/api/permissions/{id}", permissionHandler::deletePermission)
|
.DELETE("/api/permissions/{id}", permissionHandler::deletePermission)
|
||||||
|
|
||||||
// ========== 团课路由 ==========
|
|
||||||
.GET("/api/groupCourse", groupCourseHandler::getAllGroupCourse)
|
.GET("/api/groupCourse", groupCourseHandler::getAllGroupCourse)
|
||||||
|
.POST("/api/groupCourse/page", groupCourseHandler::getGroupCoursesByPage)
|
||||||
|
.POST("/api/groupCourse/cache/get", groupCourseHandler::getCacheByKey)
|
||||||
.GET("/api/groupCourse/{id}", groupCourseHandler::getGroupCourseById)
|
.GET("/api/groupCourse/{id}", groupCourseHandler::getGroupCourseById)
|
||||||
|
|
||||||
.build();
|
.build();
|
||||||
|
|||||||
@@ -38,9 +38,22 @@ spring:
|
|||||||
user:
|
user:
|
||||||
name: disabled
|
name: disabled
|
||||||
password: disabled
|
password: disabled
|
||||||
|
data:
|
||||||
|
redis:
|
||||||
|
host: ${REDIS_HOST:localhost}
|
||||||
|
port: ${REDIS_PORT:6379}
|
||||||
|
password: ${REDIS_PASSWORD:novalon123}
|
||||||
|
timeout: 5000
|
||||||
|
lettuce:
|
||||||
|
pool:
|
||||||
|
max-active: 8 # 最大连接数
|
||||||
|
max-idle: 8 # 最大空闲连接
|
||||||
|
min-idle: 0 # 最小空闲连接
|
||||||
|
max-wait: -1ms # 连接等待时间
|
||||||
profiles:
|
profiles:
|
||||||
active: dev
|
active: dev
|
||||||
|
|
||||||
|
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
web:
|
web:
|
||||||
|
|||||||
+2
-1
@@ -50,7 +50,8 @@ public class SecurityConfig {
|
|||||||
spec.pathMatchers("/api/auth/**").permitAll()
|
spec.pathMatchers("/api/auth/**").permitAll()
|
||||||
.pathMatchers("/api/public/**").permitAll()
|
.pathMatchers("/api/public/**").permitAll()
|
||||||
.pathMatchers("/ws/**").permitAll()
|
.pathMatchers("/ws/**").permitAll()
|
||||||
.pathMatchers("/actuator/**").permitAll();
|
.pathMatchers("/actuator/**").permitAll()
|
||||||
|
.pathMatchers("/api/groupCourse/**").permitAll();
|
||||||
|
|
||||||
|
|
||||||
if (isDevOrTest) {
|
if (isDevOrTest) {
|
||||||
|
|||||||
Reference in New Issue
Block a user