You've already forked FrameTour-BE
feat(puzzle): 添加拼图素材版本缓存优化重复生成
- 新增 puzzleSourceVersionCache 缓存用于记录拼图素材版本 - 实现 isPuzzleSourceChanged 方法判断素材是否变化 - 添加 markPuzzleSourceVersion 方法标记当前素材版本 - 实现 invalidatePuzzleSourceVersion 方法清除指定人脸缓存 - 在人脸关系变更时自动清除相关拼图素材版本缓存 - 重构 AppPuzzleController 使用 PuzzleRepository 替代直接访问 Mapper - 添加生成记录缓存机制,包括按人脸ID和记录ID的缓存 - 实现素材版本缓存命中时复用历史记录功能 - 优化重复内容检测逻辑,添加缓存标记机制 - 在各种生成流程中添加缓存清除逻辑确保数据一致性
This commit is contained in:
@@ -42,6 +42,13 @@ public class FaceStatusManager {
|
|||||||
*/
|
*/
|
||||||
private final Cache<String, Integer> templateRenderCache;
|
private final Cache<String, Integer> templateRenderCache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼图素材版本缓存
|
||||||
|
* 键:faceId:puzzleTemplateId -> 当时的图片源数量
|
||||||
|
* 用于判断拼图模板的素材是否发生变化,避免重复生成
|
||||||
|
*/
|
||||||
|
private final Cache<String, Integer> puzzleSourceVersionCache;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private TaskMapper taskMapper;
|
private TaskMapper taskMapper;
|
||||||
|
|
||||||
@@ -61,6 +68,11 @@ public class FaceStatusManager {
|
|||||||
.expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
|
.expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
|
||||||
.maximumSize(10000)
|
.maximumSize(10000)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
this.puzzleSourceVersionCache = Caffeine.newBuilder()
|
||||||
|
.expireAfterWrite(DEFAULT_EXPIRE_SECONDS, TimeUnit.SECONDS)
|
||||||
|
.maximumSize(10000)
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 切片状态相关方法 ====================
|
// ==================== 切片状态相关方法 ====================
|
||||||
@@ -293,4 +305,80 @@ public class FaceStatusManager {
|
|||||||
log.debug("批量删除模板渲染状态缓存: faceId={}, count={}", faceId, count);
|
log.debug("批量删除模板渲染状态缓存: faceId={}, count={}", faceId, count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 拼图素材版本相关方法 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记拼图素材版本(记录当前的图片源数量)
|
||||||
|
* 在拼图生成成功后调用,用于后续判断素材是否变化
|
||||||
|
*
|
||||||
|
* @param faceId 人脸ID
|
||||||
|
* @param puzzleTemplateId 拼图模板ID(全局唯一)
|
||||||
|
* @param sourceCount 当前的图片源数量
|
||||||
|
*/
|
||||||
|
public void markPuzzleSourceVersion(Long faceId, Long puzzleTemplateId, int sourceCount) {
|
||||||
|
if (faceId == null || puzzleTemplateId == null) {
|
||||||
|
log.warn("标记拼图素材版本参数为空: faceId={}, puzzleTemplateId={}", faceId, puzzleTemplateId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String key = faceId + ":" + puzzleTemplateId;
|
||||||
|
puzzleSourceVersionCache.put(key, sourceCount);
|
||||||
|
log.debug("标记拼图素材版本: faceId={}, puzzleTemplateId={}, sourceCount={}", faceId, puzzleTemplateId, sourceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断拼图素材是否发生变化
|
||||||
|
* 通过比较当前的图片源数量与缓存中记录的数量
|
||||||
|
*
|
||||||
|
* @param faceId 人脸ID
|
||||||
|
* @param puzzleTemplateId 拼图模板ID(全局唯一)
|
||||||
|
* @param currentSourceCount 当前的图片源数量
|
||||||
|
* @return true=素材已变化(需要重新生成),false=素材未变化(可以跳过生成)
|
||||||
|
*/
|
||||||
|
public boolean isPuzzleSourceChanged(Long faceId, Long puzzleTemplateId, int currentSourceCount) {
|
||||||
|
if (faceId == null || puzzleTemplateId == null) {
|
||||||
|
log.warn("判断拼图素材变化参数为空: faceId={}, puzzleTemplateId={}", faceId, puzzleTemplateId);
|
||||||
|
return true; // 参数不合法时默认认为有变化
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = faceId + ":" + puzzleTemplateId;
|
||||||
|
Integer cachedCount = puzzleSourceVersionCache.getIfPresent(key);
|
||||||
|
|
||||||
|
if (cachedCount == null) {
|
||||||
|
// 缓存不存在,认为有变化(首次生成或缓存过期)
|
||||||
|
log.debug("拼图素材版本缓存不存在,需要生成: faceId={}, puzzleTemplateId={}", faceId, puzzleTemplateId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean changed = !cachedCount.equals(currentSourceCount);
|
||||||
|
if (changed) {
|
||||||
|
log.debug("拼图素材已变化: faceId={}, puzzleTemplateId={}, cachedCount={}, currentCount={}",
|
||||||
|
faceId, puzzleTemplateId, cachedCount, currentSourceCount);
|
||||||
|
} else {
|
||||||
|
log.debug("拼图素材未变化,可跳过生成: faceId={}, puzzleTemplateId={}, sourceCount={}",
|
||||||
|
faceId, puzzleTemplateId, currentSourceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使指定人脸的所有拼图素材版本缓存失效
|
||||||
|
* 当人脸的图片关联发生变化时调用(如人脸匹配后新增了关联)
|
||||||
|
*
|
||||||
|
* @param faceId 人脸ID
|
||||||
|
*/
|
||||||
|
public void invalidatePuzzleSourceVersion(Long faceId) {
|
||||||
|
if (faceId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String prefix = faceId + ":";
|
||||||
|
long count = puzzleSourceVersionCache.asMap().keySet().stream()
|
||||||
|
.filter(key -> key.startsWith(prefix))
|
||||||
|
.peek(puzzleSourceVersionCache::invalidate)
|
||||||
|
.count();
|
||||||
|
if (count > 0) {
|
||||||
|
log.debug("批量使拼图素材版本缓存失效: faceId={}, count={}", faceId, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.ycwl.basic.controller.mobile;
|
package com.ycwl.basic.controller.mobile;
|
||||||
|
|
||||||
import com.ycwl.basic.biz.OrderBiz;
|
import com.ycwl.basic.biz.OrderBiz;
|
||||||
import com.ycwl.basic.constant.SourceType;
|
|
||||||
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
|
import com.ycwl.basic.model.mobile.order.IsBuyRespVO;
|
||||||
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
|
import com.ycwl.basic.model.mobile.scenic.content.ContentPageVO;
|
||||||
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
import com.ycwl.basic.model.pc.face.entity.FaceEntity;
|
||||||
@@ -11,7 +10,7 @@ import com.ycwl.basic.pricing.dto.ProductItem;
|
|||||||
import com.ycwl.basic.pricing.enums.ProductType;
|
import com.ycwl.basic.pricing.enums.ProductType;
|
||||||
import com.ycwl.basic.pricing.service.IPriceCalculationService;
|
import com.ycwl.basic.pricing.service.IPriceCalculationService;
|
||||||
import com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity;
|
import com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity;
|
||||||
import com.ycwl.basic.puzzle.mapper.PuzzleGenerationRecordMapper;
|
import com.ycwl.basic.puzzle.repository.PuzzleRepository;
|
||||||
import com.ycwl.basic.repository.FaceRepository;
|
import com.ycwl.basic.repository.FaceRepository;
|
||||||
import com.ycwl.basic.service.printer.PrinterService;
|
import com.ycwl.basic.service.printer.PrinterService;
|
||||||
import com.ycwl.basic.utils.ApiResponse;
|
import com.ycwl.basic.utils.ApiResponse;
|
||||||
@@ -32,7 +31,7 @@ import java.util.stream.Collectors;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AppPuzzleController {
|
public class AppPuzzleController {
|
||||||
|
|
||||||
private final PuzzleGenerationRecordMapper recordMapper;
|
private final PuzzleRepository puzzleRepository;
|
||||||
private final FaceRepository faceRepository;
|
private final FaceRepository faceRepository;
|
||||||
private final IPriceCalculationService iPriceCalculationService;
|
private final IPriceCalculationService iPriceCalculationService;
|
||||||
private final PrinterService printerService;
|
private final PrinterService printerService;
|
||||||
@@ -46,7 +45,7 @@ public class AppPuzzleController {
|
|||||||
if (faceId == null) {
|
if (faceId == null) {
|
||||||
return ApiResponse.fail("faceId不能为空");
|
return ApiResponse.fail("faceId不能为空");
|
||||||
}
|
}
|
||||||
int count = recordMapper.countByFaceId(faceId);
|
int count = puzzleRepository.countRecordsByFaceId(faceId);
|
||||||
return ApiResponse.success(count);
|
return ApiResponse.success(count);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +57,7 @@ public class AppPuzzleController {
|
|||||||
if (faceId == null) {
|
if (faceId == null) {
|
||||||
return ApiResponse.fail("faceId不能为空");
|
return ApiResponse.fail("faceId不能为空");
|
||||||
}
|
}
|
||||||
List<PuzzleGenerationRecordEntity> records = recordMapper.listByFaceId(faceId);
|
List<PuzzleGenerationRecordEntity> records = puzzleRepository.getRecordsByFaceId(faceId);
|
||||||
List<ContentPageVO> result = records.stream()
|
List<ContentPageVO> result = records.stream()
|
||||||
.map(this::convertToContentPageVO)
|
.map(this::convertToContentPageVO)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
@@ -73,7 +72,7 @@ public class AppPuzzleController {
|
|||||||
if (recordId == null) {
|
if (recordId == null) {
|
||||||
return ApiResponse.fail("recordId不能为空");
|
return ApiResponse.fail("recordId不能为空");
|
||||||
}
|
}
|
||||||
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
|
PuzzleGenerationRecordEntity record = puzzleRepository.getRecordById(recordId);
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
return ApiResponse.fail("未找到对应的拼图记录");
|
return ApiResponse.fail("未找到对应的拼图记录");
|
||||||
}
|
}
|
||||||
@@ -89,7 +88,7 @@ public class AppPuzzleController {
|
|||||||
if (recordId == null) {
|
if (recordId == null) {
|
||||||
return ApiResponse.fail("recordId不能为空");
|
return ApiResponse.fail("recordId不能为空");
|
||||||
}
|
}
|
||||||
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
|
PuzzleGenerationRecordEntity record = puzzleRepository.getRecordById(recordId);
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
return ApiResponse.fail("未找到对应的拼图记录");
|
return ApiResponse.fail("未找到对应的拼图记录");
|
||||||
}
|
}
|
||||||
@@ -108,7 +107,7 @@ public class AppPuzzleController {
|
|||||||
if (recordId == null) {
|
if (recordId == null) {
|
||||||
return ApiResponse.fail("recordId不能为空");
|
return ApiResponse.fail("recordId不能为空");
|
||||||
}
|
}
|
||||||
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
|
PuzzleGenerationRecordEntity record = puzzleRepository.getRecordById(recordId);
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
return ApiResponse.fail("未找到对应的拼图记录");
|
return ApiResponse.fail("未找到对应的拼图记录");
|
||||||
}
|
}
|
||||||
@@ -142,7 +141,7 @@ public class AppPuzzleController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询拼图记录
|
// 查询拼图记录
|
||||||
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
|
PuzzleGenerationRecordEntity record = puzzleRepository.getRecordById(recordId);
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
return ApiResponse.fail("未找到对应的拼图记录");
|
return ApiResponse.fail("未找到对应的拼图记录");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ public class DeleteOldRelationsStage extends AbstractPipelineStage<FaceMatchingC
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MemberRelationRepository memberRelationRepository;
|
private MemberRelationRepository memberRelationRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private com.ycwl.basic.biz.FaceStatusManager faceStatusManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "DeleteOldRelations";
|
return "DeleteOldRelations";
|
||||||
@@ -60,6 +63,7 @@ public class DeleteOldRelationsStage extends AbstractPipelineStage<FaceMatchingC
|
|||||||
|
|
||||||
// 3. 清除缓存
|
// 3. 清除缓存
|
||||||
memberRelationRepository.clearSCacheByFace(faceId);
|
memberRelationRepository.clearSCacheByFace(faceId);
|
||||||
|
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
|
||||||
|
|
||||||
log.debug("人脸旧关系数据删除完成:faceId={}", faceId);
|
log.debug("人脸旧关系数据删除完成:faceId={}", faceId);
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ public class PersistRelationsStage extends AbstractPipelineStage<FaceMatchingCon
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MemberRelationRepository memberRelationRepository;
|
private MemberRelationRepository memberRelationRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private com.ycwl.basic.biz.FaceStatusManager faceStatusManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "PersistRelations";
|
return "PersistRelations";
|
||||||
@@ -87,6 +90,7 @@ public class PersistRelationsStage extends AbstractPipelineStage<FaceMatchingCon
|
|||||||
|
|
||||||
// 4. 清除缓存
|
// 4. 清除缓存
|
||||||
memberRelationRepository.clearSCacheByFace(faceId);
|
memberRelationRepository.clearSCacheByFace(faceId);
|
||||||
|
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
|
||||||
|
|
||||||
return StageResult.success(String.format("持久化了%d条关联关系", validFiltered.size()));
|
return StageResult.success(String.format("持久化了%d条关联关系", validFiltered.size()));
|
||||||
|
|
||||||
|
|||||||
@@ -218,6 +218,9 @@ public class PuzzleEdgeRenderTaskService {
|
|||||||
renderDurationMs
|
renderDurationMs
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 清除生成记录缓存(状态已更新)
|
||||||
|
puzzleRepository.clearRecordCache(record.getId(), record.getFaceId());
|
||||||
|
|
||||||
// 通知等待方任务完成
|
// 通知等待方任务完成
|
||||||
completeWaitFuture(taskId, TaskWaitResult.success(resultImageUrl));
|
completeWaitFuture(taskId, TaskWaitResult.success(resultImageUrl));
|
||||||
|
|
||||||
@@ -258,6 +261,9 @@ public class PuzzleEdgeRenderTaskService {
|
|||||||
}
|
}
|
||||||
recordMapper.updateFail(task.getRecordId(), errorMessage);
|
recordMapper.updateFail(task.getRecordId(), errorMessage);
|
||||||
|
|
||||||
|
// 清除生成记录缓存(状态已更新)
|
||||||
|
puzzleRepository.clearRecordCache(task.getRecordId(), task.getFaceId());
|
||||||
|
|
||||||
// 通知等待方任务失败
|
// 通知等待方任务失败
|
||||||
completeWaitFuture(taskId, TaskWaitResult.fail(errorMessage));
|
completeWaitFuture(taskId, TaskWaitResult.fail(errorMessage));
|
||||||
}
|
}
|
||||||
@@ -273,6 +279,7 @@ public class PuzzleEdgeRenderTaskService {
|
|||||||
List<Long> retryRecordIds = new ArrayList<>();
|
List<Long> retryRecordIds = new ArrayList<>();
|
||||||
Map<Long, String> failRecordMessages = new HashMap<>();
|
Map<Long, String> failRecordMessages = new HashMap<>();
|
||||||
Map<Long, String> failTaskMessages = new HashMap<>(); // taskId -> errorMessage
|
Map<Long, String> failTaskMessages = new HashMap<>(); // taskId -> errorMessage
|
||||||
|
Map<Long, Long> failRecordFaceIds = new HashMap<>(); // recordId -> faceId,用于缓存清除
|
||||||
|
|
||||||
synchronized (taskLock) {
|
synchronized (taskLock) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
@@ -303,6 +310,7 @@ public class PuzzleEdgeRenderTaskService {
|
|||||||
task.setUpdateTime(new Date(now));
|
task.setUpdateTime(new Date(now));
|
||||||
if (task.getRecordId() != null) {
|
if (task.getRecordId() != null) {
|
||||||
failRecordMessages.put(task.getRecordId(), errorMessage);
|
failRecordMessages.put(task.getRecordId(), errorMessage);
|
||||||
|
failRecordFaceIds.put(task.getRecordId(), task.getFaceId());
|
||||||
}
|
}
|
||||||
// 记录需要通知的任务
|
// 记录需要通知的任务
|
||||||
failTaskMessages.put(task.getId(), errorMessage);
|
failTaskMessages.put(task.getId(), errorMessage);
|
||||||
@@ -329,6 +337,9 @@ public class PuzzleEdgeRenderTaskService {
|
|||||||
|
|
||||||
for (Map.Entry<Long, String> entry : failRecordMessages.entrySet()) {
|
for (Map.Entry<Long, String> entry : failRecordMessages.entrySet()) {
|
||||||
recordMapper.updateFail(entry.getKey(), entry.getValue());
|
recordMapper.updateFail(entry.getKey(), entry.getValue());
|
||||||
|
// 清除生成记录缓存
|
||||||
|
Long faceId = failRecordFaceIds.get(entry.getKey());
|
||||||
|
puzzleRepository.clearRecordCache(entry.getKey(), faceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知等待方任务最终失败
|
// 通知等待方任务最终失败
|
||||||
|
|||||||
@@ -77,4 +77,15 @@ public interface PuzzleGenerationRecordMapper {
|
|||||||
PuzzleGenerationRecordEntity findByContentHash(@Param("templateId") Long templateId,
|
PuzzleGenerationRecordEntity findByContentHash(@Param("templateId") Long templateId,
|
||||||
@Param("contentHash") String contentHash,
|
@Param("contentHash") String contentHash,
|
||||||
@Param("scenicId") Long scenicId);
|
@Param("scenicId") Long scenicId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据人脸ID和模板ID查询最近的成功记录
|
||||||
|
* 用于素材版本缓存命中时快速返回历史结果
|
||||||
|
*
|
||||||
|
* @param faceId 人脸ID
|
||||||
|
* @param templateId 模板ID
|
||||||
|
* @return 最近的成功记录,如果不存在返回null
|
||||||
|
*/
|
||||||
|
PuzzleGenerationRecordEntity findLatestSuccessByFaceAndTemplate(@Param("faceId") Long faceId,
|
||||||
|
@Param("templateId") Long templateId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ package com.ycwl.basic.puzzle.repository;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.ycwl.basic.puzzle.entity.PuzzleElementEntity;
|
import com.ycwl.basic.puzzle.entity.PuzzleElementEntity;
|
||||||
|
import com.ycwl.basic.puzzle.entity.PuzzleGenerationRecordEntity;
|
||||||
import com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity;
|
import com.ycwl.basic.puzzle.entity.PuzzleTemplateEntity;
|
||||||
import com.ycwl.basic.puzzle.mapper.PuzzleElementMapper;
|
import com.ycwl.basic.puzzle.mapper.PuzzleElementMapper;
|
||||||
|
import com.ycwl.basic.puzzle.mapper.PuzzleGenerationRecordMapper;
|
||||||
import com.ycwl.basic.puzzle.mapper.PuzzleTemplateMapper;
|
import com.ycwl.basic.puzzle.mapper.PuzzleTemplateMapper;
|
||||||
import com.ycwl.basic.utils.JacksonUtil;
|
import com.ycwl.basic.utils.JacksonUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -26,6 +28,7 @@ public class PuzzleRepository {
|
|||||||
|
|
||||||
private final PuzzleTemplateMapper templateMapper;
|
private final PuzzleTemplateMapper templateMapper;
|
||||||
private final PuzzleElementMapper elementMapper;
|
private final PuzzleElementMapper elementMapper;
|
||||||
|
private final PuzzleGenerationRecordMapper recordMapper;
|
||||||
private final RedisTemplate<String, String> redisTemplate;
|
private final RedisTemplate<String, String> redisTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,17 +46,34 @@ public class PuzzleRepository {
|
|||||||
*/
|
*/
|
||||||
private static final String PUZZLE_ELEMENTS_BY_TEMPLATE_KEY = "puzzle:elements:templateId:%s";
|
private static final String PUZZLE_ELEMENTS_BY_TEMPLATE_KEY = "puzzle:elements:templateId:%s";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成记录缓存KEY(根据faceId)
|
||||||
|
*/
|
||||||
|
private static final String PUZZLE_RECORDS_BY_FACE_KEY = "puzzle:records:faceId:%s";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单条生成记录缓存KEY(根据recordId)
|
||||||
|
*/
|
||||||
|
private static final String PUZZLE_RECORD_BY_ID_KEY = "puzzle:record:id:%s";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存过期时间(小时)
|
* 缓存过期时间(小时)
|
||||||
*/
|
*/
|
||||||
private static final long CACHE_EXPIRE_HOURS = 24;
|
private static final long CACHE_EXPIRE_HOURS = 24;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成记录缓存过期时间(分钟)- 较短,因为可能频繁变化
|
||||||
|
*/
|
||||||
|
private static final long RECORD_CACHE_EXPIRE_MINUTES = 30;
|
||||||
|
|
||||||
public PuzzleRepository(
|
public PuzzleRepository(
|
||||||
PuzzleTemplateMapper templateMapper,
|
PuzzleTemplateMapper templateMapper,
|
||||||
PuzzleElementMapper elementMapper,
|
PuzzleElementMapper elementMapper,
|
||||||
|
PuzzleGenerationRecordMapper recordMapper,
|
||||||
RedisTemplate<String, String> redisTemplate) {
|
RedisTemplate<String, String> redisTemplate) {
|
||||||
this.templateMapper = templateMapper;
|
this.templateMapper = templateMapper;
|
||||||
this.elementMapper = elementMapper;
|
this.elementMapper = elementMapper;
|
||||||
|
this.recordMapper = recordMapper;
|
||||||
this.redisTemplate = redisTemplate;
|
this.redisTemplate = redisTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,4 +277,122 @@ public class PuzzleRepository {
|
|||||||
log.error("删除缓存失败: pattern={}", pattern, e);
|
log.error("删除缓存失败: pattern={}", pattern, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 生成记录缓存 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据人脸ID获取生成记录列表(优先从缓存读取)
|
||||||
|
*
|
||||||
|
* @param faceId 人脸ID
|
||||||
|
* @return 生成记录列表
|
||||||
|
*/
|
||||||
|
public List<PuzzleGenerationRecordEntity> getRecordsByFaceId(Long faceId) {
|
||||||
|
String cacheKey = String.format(PUZZLE_RECORDS_BY_FACE_KEY, faceId);
|
||||||
|
|
||||||
|
// 1. 尝试从缓存读取
|
||||||
|
Boolean hasKey = redisTemplate.hasKey(cacheKey);
|
||||||
|
if (Boolean.TRUE.equals(hasKey)) {
|
||||||
|
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
|
||||||
|
if (cacheValue != null) {
|
||||||
|
log.debug("从缓存读取生成记录列表: faceId={}", faceId);
|
||||||
|
return JacksonUtil.parseObject(cacheValue, new TypeReference<List<PuzzleGenerationRecordEntity>>() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 从数据库查询
|
||||||
|
List<PuzzleGenerationRecordEntity> records = recordMapper.listByFaceId(faceId);
|
||||||
|
|
||||||
|
// 3. 写入缓存
|
||||||
|
String json = JacksonUtil.toJSONString(records);
|
||||||
|
redisTemplate.opsForValue().set(cacheKey, json, RECORD_CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
||||||
|
log.debug("生成记录列表缓存写入: faceId={}, count={}", faceId, records.size());
|
||||||
|
|
||||||
|
return records;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据人脸ID获取生成记录数量(优先从缓存读取)
|
||||||
|
*
|
||||||
|
* @param faceId 人脸ID
|
||||||
|
* @return 记录数量
|
||||||
|
*/
|
||||||
|
public int countRecordsByFaceId(Long faceId) {
|
||||||
|
List<PuzzleGenerationRecordEntity> records = getRecordsByFaceId(faceId);
|
||||||
|
return records.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据记录ID获取单条生成记录(优先从缓存读取)
|
||||||
|
*
|
||||||
|
* @param recordId 记录ID
|
||||||
|
* @return 生成记录,不存在返回null
|
||||||
|
*/
|
||||||
|
public PuzzleGenerationRecordEntity getRecordById(Long recordId) {
|
||||||
|
String cacheKey = String.format(PUZZLE_RECORD_BY_ID_KEY, recordId);
|
||||||
|
|
||||||
|
// 1. 尝试从缓存读取
|
||||||
|
Boolean hasKey = redisTemplate.hasKey(cacheKey);
|
||||||
|
if (Boolean.TRUE.equals(hasKey)) {
|
||||||
|
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
|
||||||
|
if (cacheValue != null) {
|
||||||
|
log.debug("从缓存读取生成记录: recordId={}", recordId);
|
||||||
|
return JacksonUtil.parseObject(cacheValue, PuzzleGenerationRecordEntity.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 从数据库查询
|
||||||
|
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
|
||||||
|
if (record == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 写入缓存
|
||||||
|
String json = JacksonUtil.toJSONString(record);
|
||||||
|
redisTemplate.opsForValue().set(cacheKey, json, RECORD_CACHE_EXPIRE_MINUTES, TimeUnit.MINUTES);
|
||||||
|
log.debug("生成记录缓存写入: recordId={}", recordId);
|
||||||
|
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除人脸相关的生成记录缓存
|
||||||
|
* 在新拼图生成成功后调用
|
||||||
|
*
|
||||||
|
* @param faceId 人脸ID
|
||||||
|
*/
|
||||||
|
public void clearRecordCacheByFace(Long faceId) {
|
||||||
|
if (faceId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 清除列表缓存
|
||||||
|
String listKey = String.format(PUZZLE_RECORDS_BY_FACE_KEY, faceId);
|
||||||
|
redisTemplate.delete(listKey);
|
||||||
|
|
||||||
|
log.debug("清除人脸生成记录缓存: faceId={}", faceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除单条记录的缓存
|
||||||
|
*
|
||||||
|
* @param recordId 记录ID
|
||||||
|
*/
|
||||||
|
public void clearRecordCacheById(Long recordId) {
|
||||||
|
if (recordId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String cacheKey = String.format(PUZZLE_RECORD_BY_ID_KEY, recordId);
|
||||||
|
redisTemplate.delete(cacheKey);
|
||||||
|
log.debug("清除生成记录缓存: recordId={}", recordId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除单条记录缓存并同时清除关联的人脸缓存
|
||||||
|
*
|
||||||
|
* @param recordId 记录ID
|
||||||
|
* @param faceId 人脸ID
|
||||||
|
*/
|
||||||
|
public void clearRecordCache(Long recordId, Long faceId) {
|
||||||
|
clearRecordCacheById(recordId);
|
||||||
|
clearRecordCacheByFace(faceId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.ycwl.basic.puzzle.service.impl;
|
|||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.ycwl.basic.biz.FaceStatusManager;
|
||||||
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
|
import com.ycwl.basic.model.pc.mp.MpConfigEntity;
|
||||||
import com.ycwl.basic.puzzle.dto.PuzzleGenerateRequest;
|
import com.ycwl.basic.puzzle.dto.PuzzleGenerateRequest;
|
||||||
import com.ycwl.basic.puzzle.dto.PuzzleGenerateResponse;
|
import com.ycwl.basic.puzzle.dto.PuzzleGenerateResponse;
|
||||||
@@ -56,6 +57,7 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
private final PuzzleDuplicationDetector duplicationDetector;
|
private final PuzzleDuplicationDetector duplicationDetector;
|
||||||
private final PrinterService printerService;
|
private final PrinterService printerService;
|
||||||
private final PuzzleEdgeRenderTaskService puzzleEdgeRenderTaskService;
|
private final PuzzleEdgeRenderTaskService puzzleEdgeRenderTaskService;
|
||||||
|
private final FaceStatusManager faceStatusManager;
|
||||||
|
|
||||||
public PuzzleGenerateServiceImpl(
|
public PuzzleGenerateServiceImpl(
|
||||||
PuzzleRepository puzzleRepository,
|
PuzzleRepository puzzleRepository,
|
||||||
@@ -65,7 +67,8 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
@Lazy ScenicRepository scenicRepository,
|
@Lazy ScenicRepository scenicRepository,
|
||||||
@Lazy PuzzleDuplicationDetector duplicationDetector,
|
@Lazy PuzzleDuplicationDetector duplicationDetector,
|
||||||
@Lazy PrinterService printerService,
|
@Lazy PrinterService printerService,
|
||||||
PuzzleEdgeRenderTaskService puzzleEdgeRenderTaskService) {
|
PuzzleEdgeRenderTaskService puzzleEdgeRenderTaskService,
|
||||||
|
@Lazy FaceStatusManager faceStatusManager) {
|
||||||
this.puzzleRepository = puzzleRepository;
|
this.puzzleRepository = puzzleRepository;
|
||||||
this.recordMapper = recordMapper;
|
this.recordMapper = recordMapper;
|
||||||
this.imageRenderer = imageRenderer;
|
this.imageRenderer = imageRenderer;
|
||||||
@@ -74,6 +77,7 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
this.duplicationDetector = duplicationDetector;
|
this.duplicationDetector = duplicationDetector;
|
||||||
this.printerService = printerService;
|
this.printerService = printerService;
|
||||||
this.puzzleEdgeRenderTaskService = puzzleEdgeRenderTaskService;
|
this.puzzleEdgeRenderTaskService = puzzleEdgeRenderTaskService;
|
||||||
|
this.faceStatusManager = faceStatusManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -101,6 +105,32 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
}
|
}
|
||||||
Long resolvedScenicId = resolveScenicId(template, request.getScenicId());
|
Long resolvedScenicId = resolveScenicId(template, request.getScenicId());
|
||||||
|
|
||||||
|
// 2.5 素材版本缓存检查(减少数据库查询)
|
||||||
|
// 如果缓存存在,说明自上次成功生成后素材没有变化,可以直接复用历史记录
|
||||||
|
if (request.getFaceId() != null && !faceStatusManager.isPuzzleSourceChanged(request.getFaceId(), template.getId(), 0)) {
|
||||||
|
PuzzleGenerationRecordEntity cachedRecord = recordMapper.findLatestSuccessByFaceAndTemplate(
|
||||||
|
request.getFaceId(), template.getId());
|
||||||
|
if (cachedRecord != null) {
|
||||||
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
|
log.info("素材版本缓存命中,复用历史记录: faceId={}, templateId={}, recordId={}, imageUrl={}, duration={}ms",
|
||||||
|
request.getFaceId(), template.getId(), cachedRecord.getId(),
|
||||||
|
cachedRecord.getResultImageUrl(), duration);
|
||||||
|
return PuzzleGenerateResponse.success(
|
||||||
|
cachedRecord.getResultImageUrl(),
|
||||||
|
cachedRecord.getResultFileSize(),
|
||||||
|
cachedRecord.getResultWidth(),
|
||||||
|
cachedRecord.getResultHeight(),
|
||||||
|
(int) duration,
|
||||||
|
cachedRecord.getId(),
|
||||||
|
true,
|
||||||
|
cachedRecord.getId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 缓存存在但记录被删除,继续执行正常流程
|
||||||
|
log.debug("素材版本缓存存在但历史记录不存在,继续正常生成: faceId={}, templateId={}",
|
||||||
|
request.getFaceId(), template.getId());
|
||||||
|
}
|
||||||
|
|
||||||
// 3. 查询并排序元素
|
// 3. 查询并排序元素
|
||||||
List<PuzzleElementEntity> elements = puzzleRepository.getElementsByTemplateId(template.getId());
|
List<PuzzleElementEntity> elements = puzzleRepository.getElementsByTemplateId(template.getId());
|
||||||
if (elements.isEmpty()) {
|
if (elements.isEmpty()) {
|
||||||
@@ -124,6 +154,10 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
long duration = System.currentTimeMillis() - startTime;
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
log.info("检测到重复内容,复用历史记录: recordId={}, imageUrl={}, duration={}ms",
|
log.info("检测到重复内容,复用历史记录: recordId={}, imageUrl={}, duration={}ms",
|
||||||
duplicateRecord.getId(), duplicateRecord.getResultImageUrl(), duration);
|
duplicateRecord.getId(), duplicateRecord.getResultImageUrl(), duration);
|
||||||
|
// 标记素材版本缓存
|
||||||
|
if (request.getFaceId() != null) {
|
||||||
|
faceStatusManager.markPuzzleSourceVersion(request.getFaceId(), template.getId(), 0);
|
||||||
|
}
|
||||||
return PuzzleGenerateResponse.success(
|
return PuzzleGenerateResponse.success(
|
||||||
duplicateRecord.getResultImageUrl(),
|
duplicateRecord.getResultImageUrl(),
|
||||||
duplicateRecord.getResultFileSize(),
|
duplicateRecord.getResultFileSize(),
|
||||||
@@ -141,6 +175,9 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
record.setContentHash(contentHash);
|
record.setContentHash(contentHash);
|
||||||
recordMapper.insert(record);
|
recordMapper.insert(record);
|
||||||
|
|
||||||
|
// 清除生成记录缓存(新记录插入后列表和数量都会变化)
|
||||||
|
puzzleRepository.clearRecordCacheByFace(request.getFaceId());
|
||||||
|
|
||||||
// 8. 创建边缘渲染任务并等待完成
|
// 8. 创建边缘渲染任务并等待完成
|
||||||
Long taskId = puzzleEdgeRenderTaskService.createRenderTask(
|
Long taskId = puzzleEdgeRenderTaskService.createRenderTask(
|
||||||
record,
|
record,
|
||||||
@@ -164,6 +201,11 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
log.info("同步拼图边缘渲染完成: recordId={}, taskId={}, imageUrl={}, duration={}ms",
|
log.info("同步拼图边缘渲染完成: recordId={}, taskId={}, imageUrl={}, duration={}ms",
|
||||||
record.getId(), taskId, waitResult.getImageUrl(), duration);
|
record.getId(), taskId, waitResult.getImageUrl(), duration);
|
||||||
|
|
||||||
|
// 标记素材版本缓存(成功生成后)
|
||||||
|
if (request.getFaceId() != null) {
|
||||||
|
faceStatusManager.markPuzzleSourceVersion(request.getFaceId(), template.getId(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
// 重新查询记录获取完整信息(边缘渲染回调已更新)
|
// 重新查询记录获取完整信息(边缘渲染回调已更新)
|
||||||
PuzzleGenerationRecordEntity updatedRecord = recordMapper.getById(record.getId());
|
PuzzleGenerationRecordEntity updatedRecord = recordMapper.getById(record.getId());
|
||||||
if (updatedRecord != null && updatedRecord.getResultImageUrl() != null) {
|
if (updatedRecord != null && updatedRecord.getResultImageUrl() != null) {
|
||||||
@@ -212,6 +254,23 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
}
|
}
|
||||||
Long resolvedScenicId = resolveScenicId(template, request.getScenicId());
|
Long resolvedScenicId = resolveScenicId(template, request.getScenicId());
|
||||||
|
|
||||||
|
// 2.5 素材版本缓存检查(减少数据库查询)
|
||||||
|
// 如果缓存存在,说明自上次成功生成后素材没有变化,可以直接复用历史记录
|
||||||
|
if (request.getFaceId() != null && !faceStatusManager.isPuzzleSourceChanged(request.getFaceId(), template.getId(), 0)) {
|
||||||
|
PuzzleGenerationRecordEntity cachedRecord = recordMapper.findLatestSuccessByFaceAndTemplate(
|
||||||
|
request.getFaceId(), template.getId());
|
||||||
|
if (cachedRecord != null) {
|
||||||
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
|
log.info("素材版本缓存命中,复用历史记录: faceId={}, templateId={}, recordId={}, imageUrl={}, duration={}ms",
|
||||||
|
request.getFaceId(), template.getId(), cachedRecord.getId(),
|
||||||
|
cachedRecord.getResultImageUrl(), duration);
|
||||||
|
return cachedRecord.getId();
|
||||||
|
}
|
||||||
|
// 缓存存在但记录被删除,继续执行正常流程
|
||||||
|
log.debug("素材版本缓存存在但历史记录不存在,继续正常生成: faceId={}, templateId={}",
|
||||||
|
request.getFaceId(), template.getId());
|
||||||
|
}
|
||||||
|
|
||||||
// 3. 查询并排序元素
|
// 3. 查询并排序元素
|
||||||
List<PuzzleElementEntity> elements = puzzleRepository.getElementsByTemplateId(template.getId());
|
List<PuzzleElementEntity> elements = puzzleRepository.getElementsByTemplateId(template.getId());
|
||||||
if (elements.isEmpty()) {
|
if (elements.isEmpty()) {
|
||||||
@@ -235,6 +294,10 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
long duration = System.currentTimeMillis() - startTime;
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
log.info("检测到重复内容,复用历史记录: recordId={}, imageUrl={}, duration={}ms",
|
log.info("检测到重复内容,复用历史记录: recordId={}, imageUrl={}, duration={}ms",
|
||||||
duplicateRecord.getId(), duplicateRecord.getResultImageUrl(), duration);
|
duplicateRecord.getId(), duplicateRecord.getResultImageUrl(), duration);
|
||||||
|
// 标记素材版本缓存
|
||||||
|
if (request.getFaceId() != null) {
|
||||||
|
faceStatusManager.markPuzzleSourceVersion(request.getFaceId(), template.getId(), 0);
|
||||||
|
}
|
||||||
return duplicateRecord.getId();
|
return duplicateRecord.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,6 +306,9 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
record.setContentHash(contentHash);
|
record.setContentHash(contentHash);
|
||||||
recordMapper.insert(record);
|
recordMapper.insert(record);
|
||||||
|
|
||||||
|
// 清除生成记录缓存(新记录插入后列表和数量都会变化)
|
||||||
|
puzzleRepository.clearRecordCacheByFace(request.getFaceId());
|
||||||
|
|
||||||
// 8. 创建边缘渲染任务
|
// 8. 创建边缘渲染任务
|
||||||
Long taskId = puzzleEdgeRenderTaskService.createRenderTask(
|
Long taskId = puzzleEdgeRenderTaskService.createRenderTask(
|
||||||
record,
|
record,
|
||||||
@@ -253,6 +319,8 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
request.getQuality()
|
request.getQuality()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 异步任务:在回调成功后标记缓存(由边缘渲染服务在成功回调中处理)
|
||||||
|
// 这里只记录请求信息供后续使用
|
||||||
long duration = System.currentTimeMillis() - startTime;
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
log.info("异步拼图任务已进入边缘渲染队列: recordId={}, taskId={}, templateCode={}, duration={}ms",
|
log.info("异步拼图任务已进入边缘渲染队列: recordId={}, taskId={}, templateCode={}, duration={}ms",
|
||||||
record.getId(), taskId, request.getTemplateCode(), duration);
|
record.getId(), taskId, request.getTemplateCode(), duration);
|
||||||
@@ -331,6 +399,9 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
record.setContentHash(contentHash);
|
record.setContentHash(contentHash);
|
||||||
recordMapper.insert(record);
|
recordMapper.insert(record);
|
||||||
|
|
||||||
|
// 清除生成记录缓存(新记录插入后列表和数量都会变化)
|
||||||
|
puzzleRepository.clearRecordCacheByFace(request.getFaceId());
|
||||||
|
|
||||||
// 9. 执行核心生成逻辑
|
// 9. 执行核心生成逻辑
|
||||||
return doGenerateInternal(request, template, resolvedScenicId, record, startTime);
|
return doGenerateInternal(request, template, resolvedScenicId, record, startTime);
|
||||||
}
|
}
|
||||||
@@ -417,6 +488,9 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
(int) duration
|
(int) duration
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 清除生成记录缓存(状态已更新)
|
||||||
|
puzzleRepository.clearRecordCache(record.getId(), request.getFaceId());
|
||||||
|
|
||||||
log.info("拼图生成成功: recordId={}, originalUrl={}, finalUrl={}, duration={}ms",
|
log.info("拼图生成成功: recordId={}, originalUrl={}, finalUrl={}, duration={}ms",
|
||||||
record.getId(), originalImageUrl, finalImageUrl, duration);
|
record.getId(), originalImageUrl, finalImageUrl, duration);
|
||||||
|
|
||||||
@@ -450,6 +524,8 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("拼图生成失败: templateCode={}", request.getTemplateCode(), e);
|
log.error("拼图生成失败: templateCode={}", request.getTemplateCode(), e);
|
||||||
recordMapper.updateFail(record.getId(), e.getMessage());
|
recordMapper.updateFail(record.getId(), e.getMessage());
|
||||||
|
// 清除生成记录缓存(状态已更新)
|
||||||
|
puzzleRepository.clearRecordCache(record.getId(), request.getFaceId());
|
||||||
throw new RuntimeException("图片生成失败: " + e.getMessage(), e);
|
throw new RuntimeException("图片生成失败: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ public class SourceRepository {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private MemberRelationRepository memberRelationRepository;
|
private MemberRelationRepository memberRelationRepository;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
private com.ycwl.basic.biz.FaceStatusManager faceStatusManager;
|
||||||
|
@Autowired
|
||||||
private ScenicRepository scenicRepository;
|
private ScenicRepository scenicRepository;
|
||||||
|
|
||||||
public void addSource(SourceEntity source) {
|
public void addSource(SourceEntity source) {
|
||||||
@@ -81,6 +83,7 @@ public class SourceRepository {
|
|||||||
memberSource.setIsBuy(1);
|
memberSource.setIsBuy(1);
|
||||||
sourceMapper.updateRelation(memberSource);
|
sourceMapper.updateRelation(memberSource);
|
||||||
memberRelationRepository.clearSCacheByFace(faceId);
|
memberRelationRepository.clearSCacheByFace(faceId);
|
||||||
|
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
|
||||||
|
|
||||||
// 如果需要图像处理,对该faceId下的所有type=3的照片进行处理
|
// 如果需要图像处理,对该faceId下的所有type=3的照片进行处理
|
||||||
if (needsImageProcessing) {
|
if (needsImageProcessing) {
|
||||||
@@ -229,6 +232,7 @@ public class SourceRepository {
|
|||||||
memberSource.setIsBuy(0);
|
memberSource.setIsBuy(0);
|
||||||
sourceMapper.updateRelation(memberSource);
|
sourceMapper.updateRelation(memberSource);
|
||||||
memberRelationRepository.clearSCacheByFace(faceId);
|
memberRelationRepository.clearSCacheByFace(faceId);
|
||||||
|
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SourceEntity getSource(Long id) {
|
public SourceEntity getSource(Long id) {
|
||||||
|
|||||||
@@ -207,6 +207,8 @@ public class FaceServiceImpl implements FaceService {
|
|||||||
private FaceDetectLogAiCamService faceDetectLogAiCamService;
|
private FaceDetectLogAiCamService faceDetectLogAiCamService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private OrderRepository orderRepository;
|
private OrderRepository orderRepository;
|
||||||
|
@Autowired
|
||||||
|
private com.ycwl.basic.biz.FaceStatusManager faceStatusManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApiResponse<PageInfo<FaceRespVO>> pageQuery(FaceReqQuery faceReqQuery) {
|
public ApiResponse<PageInfo<FaceRespVO>> pageQuery(FaceReqQuery faceReqQuery) {
|
||||||
@@ -994,6 +996,7 @@ public class FaceServiceImpl implements FaceService {
|
|||||||
sourceMapper.deleteNotBuyFaceRelation(face.getMemberId(), faceId);
|
sourceMapper.deleteNotBuyFaceRelation(face.getMemberId(), faceId);
|
||||||
videoMapper.deleteNotBuyFaceRelations(face.getMemberId(), faceId);
|
videoMapper.deleteNotBuyFaceRelations(face.getMemberId(), faceId);
|
||||||
memberRelationRepository.clearSCacheByFace(faceId);
|
memberRelationRepository.clearSCacheByFace(faceId);
|
||||||
|
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
|
||||||
log.debug("人脸旧关系数据删除完成:faceId={}", faceId);
|
log.debug("人脸旧关系数据删除完成:faceId={}", faceId);
|
||||||
|
|
||||||
List<MemberSourceEntity> memberSourceEntityList = sourceRelationProcessor.processMemberSources(sampleListIds, face);
|
List<MemberSourceEntity> memberSourceEntityList = sourceRelationProcessor.processMemberSources(sampleListIds, face);
|
||||||
@@ -1016,6 +1019,7 @@ public class FaceServiceImpl implements FaceService {
|
|||||||
log.warn("没有有效的关联关系可创建: faceId={}, 原始数量={}", faceId, memberSourceEntityList.size());
|
log.warn("没有有效的关联关系可创建: faceId={}, 原始数量={}", faceId, memberSourceEntityList.size());
|
||||||
}
|
}
|
||||||
memberRelationRepository.clearSCacheByFace(faceId);
|
memberRelationRepository.clearSCacheByFace(faceId);
|
||||||
|
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
|
||||||
taskTaskService.autoCreateTaskByFaceId(faceId);
|
taskTaskService.autoCreateTaskByFaceId(faceId);
|
||||||
|
|
||||||
log.info("自定义人脸匹配完成:faceId={}, 匹配样本数={}, 关联源文件数={}, 免费数={}",
|
log.info("自定义人脸匹配完成:faceId={}, 匹配样本数={}, 关联源文件数={}, 免费数={}",
|
||||||
|
|||||||
@@ -341,6 +341,7 @@ public class FaceMatchingOrchestrator {
|
|||||||
|
|
||||||
// 清除缓存
|
// 清除缓存
|
||||||
memberRelationRepository.clearSCacheByFace(faceId);
|
memberRelationRepository.clearSCacheByFace(faceId);
|
||||||
|
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
|||||||
private ScenicService scenicService;
|
private ScenicService scenicService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private MemberRelationRepository memberRelationRepository;
|
private MemberRelationRepository memberRelationRepository;
|
||||||
|
@Autowired
|
||||||
|
private com.ycwl.basic.biz.FaceStatusManager faceStatusManager;
|
||||||
|
|
||||||
private IAcsClient getClient() {
|
private IAcsClient getClient() {
|
||||||
AliFaceBodyAdapter use = (AliFaceBodyAdapter) FaceBodyFactory.use();
|
AliFaceBodyAdapter use = (AliFaceBodyAdapter) FaceBodyFactory.use();
|
||||||
@@ -172,6 +174,7 @@ public class TaskFaceServiceImpl implements TaskFaceService {
|
|||||||
log.warn("没有有效的关联关系可创建: faceId={}, 原始数量={}", faceId, memberSourceEntityList.size());
|
log.warn("没有有效的关联关系可创建: faceId={}, 原始数量={}", faceId, memberSourceEntityList.size());
|
||||||
}
|
}
|
||||||
memberRelationRepository.clearSCacheByFace(faceId);
|
memberRelationRepository.clearSCacheByFace(faceId);
|
||||||
|
faceStatusManager.invalidatePuzzleSourceVersion(faceId);
|
||||||
List<FaceSampleEntity> faceSampleList = faceRepository.getFaceSampleList(faceId);
|
List<FaceSampleEntity> faceSampleList = faceRepository.getFaceSampleList(faceId);
|
||||||
List<Long> faceSampleIds = faceSampleList.stream()
|
List<Long> faceSampleIds = faceSampleList.stream()
|
||||||
.sorted(Comparator.comparing(FaceSampleEntity::getCreateAt).reversed())
|
.sorted(Comparator.comparing(FaceSampleEntity::getCreateAt).reversed())
|
||||||
|
|||||||
@@ -143,4 +143,15 @@
|
|||||||
LIMIT 1
|
LIMIT 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 根据人脸ID和模板ID查询最近的成功记录 -->
|
||||||
|
<select id="findLatestSuccessByFaceAndTemplate" resultMap="BaseResultMap">
|
||||||
|
SELECT <include refid="Base_Column_List"/>
|
||||||
|
FROM puzzle_generation_record
|
||||||
|
WHERE face_id = #{faceId}
|
||||||
|
AND template_id = #{templateId}
|
||||||
|
AND status = 1
|
||||||
|
ORDER BY create_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user