feat(puzzle): 添加景区模板列表缓存功能

- 新增景区模板列表缓存KEY常量PUZZLE_TEMPLATES_BY_SCENIC_KEY
- 在清除模板缓存时同步清除对应景区的模板列表缓存
- 实现listTemplateByScenic方法根据景区ID获取启用模板列表并缓存
- 实现clearTemplateByScenicCache方法清除景区模板列表缓存
- 重构人脸匹配编排器使用新的缓存方法替代原有数据库查询
- 移除过期的redisTemplate依赖
This commit is contained in:
2026-01-07 15:03:34 +08:00
parent 917668da0c
commit 3291371dd7
2 changed files with 86 additions and 12 deletions

View File

@@ -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("拼图缓存清除完成");
}