You've already forked FrameTour-BE
feat(puzzle): 添加景区模板列表缓存功能
- 新增景区模板列表缓存KEY常量PUZZLE_TEMPLATES_BY_SCENIC_KEY - 在清除模板缓存时同步清除对应景区的模板列表缓存 - 实现listTemplateByScenic方法根据景区ID获取启用模板列表并缓存 - 实现clearTemplateByScenicCache方法清除景区模板列表缓存 - 重构人脸匹配编排器使用新的缓存方法替代原有数据库查询 - 移除过期的redisTemplate依赖
This commit is contained in:
@@ -51,6 +51,11 @@ public class PuzzleRepository {
|
||||
*/
|
||||
private static final String PUZZLE_RECORDS_BY_FACE_KEY = "puzzle:records:faceId:%s";
|
||||
|
||||
/**
|
||||
* 景区模板列表缓存KEY(根据scenicId)
|
||||
*/
|
||||
private static final String PUZZLE_TEMPLATES_BY_SCENIC_KEY = "puzzle:templates:scenicId:%s";
|
||||
|
||||
/**
|
||||
* 单条生成记录缓存KEY(根据recordId)
|
||||
*/
|
||||
@@ -167,6 +172,8 @@ public class PuzzleRepository {
|
||||
* @param code 模板编码(可为null,此时需要先查询获取)
|
||||
*/
|
||||
public void clearTemplateCache(Long id, String code) {
|
||||
Long scenicId = null;
|
||||
|
||||
// 如果没有传code,尝试从缓存或数据库获取
|
||||
if (code == null && id != null) {
|
||||
String idKey = String.format(PUZZLE_TEMPLATE_BY_ID_KEY, id);
|
||||
@@ -174,10 +181,25 @@ public class PuzzleRepository {
|
||||
if (cacheValue != null) {
|
||||
PuzzleTemplateEntity template = JacksonUtil.parseObject(cacheValue, PuzzleTemplateEntity.class);
|
||||
code = template.getCode();
|
||||
scenicId = template.getScenicId();
|
||||
} else {
|
||||
PuzzleTemplateEntity template = templateMapper.getById(id);
|
||||
if (template != null) {
|
||||
code = template.getCode();
|
||||
scenicId = template.getScenicId();
|
||||
}
|
||||
}
|
||||
} else if (id != null) {
|
||||
// 有 code 但需要获取 scenicId
|
||||
String idKey = String.format(PUZZLE_TEMPLATE_BY_ID_KEY, id);
|
||||
String cacheValue = redisTemplate.opsForValue().get(idKey);
|
||||
if (cacheValue != null) {
|
||||
PuzzleTemplateEntity template = JacksonUtil.parseObject(cacheValue, PuzzleTemplateEntity.class);
|
||||
scenicId = template.getScenicId();
|
||||
} else {
|
||||
PuzzleTemplateEntity template = templateMapper.getById(id);
|
||||
if (template != null) {
|
||||
scenicId = template.getScenicId();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,6 +222,11 @@ public class PuzzleRepository {
|
||||
if (id != null) {
|
||||
clearElementsCache(id);
|
||||
}
|
||||
|
||||
// 清除景区模板列表缓存(确保列表数据一致性)
|
||||
if (scenicId != null) {
|
||||
clearTemplateByScenicCache(scenicId);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 元素缓存 ====================
|
||||
@@ -245,6 +272,58 @@ public class PuzzleRepository {
|
||||
log.debug("清除元素缓存: templateId={}", templateId);
|
||||
}
|
||||
|
||||
// ==================== 景区模板列表缓存 ====================
|
||||
|
||||
/**
|
||||
* 根据景区ID获取启用的模板列表(优先从缓存读取)
|
||||
* 用于人脸匹配后的批量拼图生成场景
|
||||
*
|
||||
* @param scenicId 景区ID
|
||||
* @return 启用状态的模板列表
|
||||
*/
|
||||
public List<PuzzleTemplateEntity> listTemplateByScenic(Long scenicId) {
|
||||
if (scenicId == null) {
|
||||
log.warn("景区ID为空,跳过缓存查询");
|
||||
return templateMapper.list(null, null, 1);
|
||||
}
|
||||
|
||||
String cacheKey = String.format(PUZZLE_TEMPLATES_BY_SCENIC_KEY, scenicId);
|
||||
|
||||
// 1. 尝试从缓存读取
|
||||
Boolean hasKey = redisTemplate.hasKey(cacheKey);
|
||||
if (Boolean.TRUE.equals(hasKey)) {
|
||||
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
|
||||
if (cacheValue != null) {
|
||||
log.debug("从缓存读取景区模板列表: scenicId={}", scenicId);
|
||||
return JacksonUtil.parseObject(cacheValue, new TypeReference<List<PuzzleTemplateEntity>>() {});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 从数据库查询(只查启用状态 status=1)
|
||||
List<PuzzleTemplateEntity> templates = templateMapper.list(scenicId, null, 1);
|
||||
|
||||
// 3. 写入缓存(即使是空列表也缓存,避免缓存穿透)
|
||||
String json = JacksonUtil.toJSONString(templates);
|
||||
redisTemplate.opsForValue().set(cacheKey, json, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
|
||||
log.debug("景区模板列表缓存写入: scenicId={}, count={}", scenicId, templates.size());
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除景区模板列表缓存
|
||||
*
|
||||
* @param scenicId 景区ID
|
||||
*/
|
||||
public void clearTemplateByScenicCache(Long scenicId) {
|
||||
if (scenicId == null) {
|
||||
return;
|
||||
}
|
||||
String cacheKey = String.format(PUZZLE_TEMPLATES_BY_SCENIC_KEY, scenicId);
|
||||
redisTemplate.delete(cacheKey);
|
||||
log.debug("清除景区模板列表缓存: scenicId={}", scenicId);
|
||||
}
|
||||
|
||||
// ==================== 批量清除 ====================
|
||||
|
||||
/**
|
||||
@@ -258,6 +337,8 @@ public class PuzzleRepository {
|
||||
deleteByPattern("puzzle:template:*");
|
||||
// 使用 SCAN 删除元素缓存
|
||||
deleteByPattern("puzzle:elements:*");
|
||||
// 使用 SCAN 删除景区模板列表缓存
|
||||
deleteByPattern("puzzle:templates:*");
|
||||
|
||||
log.warn("拼图缓存清除完成");
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import com.ycwl.basic.integration.scenic.dto.scenic.ScenicV2DTO;
|
||||
import com.ycwl.basic.puzzle.dto.PuzzleGenerateRequest;
|
||||
import com.ycwl.basic.puzzle.dto.PuzzleGenerateResponse;
|
||||
import com.ycwl.basic.puzzle.dto.PuzzleTemplateDTO;
|
||||
import com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity;
|
||||
import com.ycwl.basic.puzzle.repository.PuzzleRepository;
|
||||
import com.ycwl.basic.puzzle.service.IPuzzleGenerateService;
|
||||
import com.ycwl.basic.puzzle.service.IPuzzleTemplateService;
|
||||
|
||||
@@ -102,9 +104,9 @@ public class FaceMatchingOrchestrator {
|
||||
@Autowired
|
||||
private IPuzzleGenerateService puzzleGenerateService;
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
@Autowired
|
||||
private FaceStatusManager faceStatusManager;
|
||||
@Autowired
|
||||
private PuzzleRepository puzzleRepository;
|
||||
|
||||
/**
|
||||
* 编排人脸匹配的完整流程
|
||||
@@ -364,17 +366,8 @@ public class FaceMatchingOrchestrator {
|
||||
* @return
|
||||
*/
|
||||
private void asyncGeneratePuzzleTemplate(Long scenicId, Long faceId, Long memberId, String scene) {
|
||||
if (redisTemplate.hasKey("puzzle_generated:face:" + faceId)) {
|
||||
return;
|
||||
}
|
||||
redisTemplate.opsForValue().set(
|
||||
"puzzle_generated:face:" + faceId,
|
||||
"1",
|
||||
60 * 10, TimeUnit.SECONDS);
|
||||
|
||||
// 查询该景区所有启用状态的拼图模板
|
||||
List<PuzzleTemplateDTO> templateList = puzzleTemplateService.listTemplates(
|
||||
scenicId, null, 1); // 查询启用状态的模板
|
||||
List<PuzzleTemplateEntity> templateList = puzzleRepository.listTemplateByScenic(scenicId); // 查询启用状态的模板
|
||||
|
||||
if (templateList == null || templateList.isEmpty()) {
|
||||
log.debug("景区不存在启用的拼图模板,跳过生成: scenicId={}", scenicId);
|
||||
|
||||
Reference in New Issue
Block a user