You've already forked FrameTour-BE
feat(puzzle): 实现拼图生成去重机制
- 新增内容哈希计算逻辑,基于元素内容生成SHA256哈希用于去重判断 - 添加重复图片检测功能,当所有IMAGE元素使用相同URL时抛出异常 - 实现历史记录查询接口,根据模板ID、内容哈希和景区ID查找重复记录 - 扩展生成响应对象,增加isDuplicate和originalRecordId字段标识复用情况 - 更新数据库实体和Mapper,新增content_hash、is_duplicate等字段支持去重 - 添加完整的单元测试和集成测试,覆盖去重检测、哈希计算等核心逻辑 - 引入DuplicateImageException和PuzzleBizException异常类完善错误处理
This commit is contained in:
@@ -14,6 +14,7 @@ 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.service.IPuzzleGenerateService;
|
||||
import com.ycwl.basic.puzzle.util.PuzzleDuplicationDetector;
|
||||
import com.ycwl.basic.puzzle.util.PuzzleImageRenderer;
|
||||
import com.ycwl.basic.repository.ScenicRepository;
|
||||
import com.ycwl.basic.storage.StorageFactory;
|
||||
@@ -53,6 +54,7 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
||||
private final PuzzleImageRenderer imageRenderer;
|
||||
private final PuzzleElementFillEngine fillEngine;
|
||||
private final ScenicRepository scenicRepository;
|
||||
private final PuzzleDuplicationDetector duplicationDetector;
|
||||
|
||||
@Override
|
||||
public PuzzleGenerateResponse generate(PuzzleGenerateRequest request) {
|
||||
@@ -90,19 +92,51 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
||||
// 4. 准备dynamicData(合并自动填充和手动数据)
|
||||
Map<String, String> finalDynamicData = buildDynamicData(template, request, resolvedScenicId, elements);
|
||||
|
||||
// 5. 创建生成记录
|
||||
// 5. 执行重复图片检测
|
||||
// 如果所有IMAGE元素使用相同URL,抛出DuplicateImageException
|
||||
duplicationDetector.detectDuplicateImages(finalDynamicData, elements);
|
||||
|
||||
// 6. 计算内容哈希
|
||||
String contentHash = duplicationDetector.calculateContentHash(finalDynamicData);
|
||||
|
||||
// 7. 查询历史记录(去重核心逻辑)
|
||||
PuzzleGenerationRecordEntity duplicateRecord = duplicationDetector.findDuplicateRecord(
|
||||
template.getId(), contentHash, resolvedScenicId);
|
||||
|
||||
if (duplicateRecord != null) {
|
||||
// 发现重复内容,直接返回历史记录
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
log.info("检测到重复内容,复用历史记录: recordId={}, imageUrl={}, duration={}ms",
|
||||
duplicateRecord.getId(), duplicateRecord.getResultImageUrl(), duration);
|
||||
|
||||
// 直接返回历史图片URL(语义化生成成功)
|
||||
return PuzzleGenerateResponse.success(
|
||||
duplicateRecord.getResultImageUrl(),
|
||||
duplicateRecord.getResultFileSize(),
|
||||
duplicateRecord.getResultWidth(),
|
||||
duplicateRecord.getResultHeight(),
|
||||
(int) duration,
|
||||
duplicateRecord.getId(),
|
||||
true, // isDuplicate=true
|
||||
duplicateRecord.getId() // originalRecordId(复用时指向自己)
|
||||
);
|
||||
}
|
||||
|
||||
// 8. 没有历史记录,创建新的生成记录
|
||||
PuzzleGenerationRecordEntity record = createRecord(template, request, resolvedScenicId);
|
||||
record.setContentHash(contentHash);
|
||||
record.setIsDuplicate(false);
|
||||
recordMapper.insert(record);
|
||||
|
||||
try {
|
||||
// 5. 渲染图片
|
||||
// 9. 渲染图片
|
||||
BufferedImage resultImage = imageRenderer.render(template, elements, finalDynamicData);
|
||||
|
||||
// 5. 上传到OSS
|
||||
// 10. 上传到OSS
|
||||
String imageUrl = uploadImage(resultImage, template.getCode(), request.getOutputFormat(), request.getQuality());
|
||||
log.info("图片上传成功: url={}", imageUrl);
|
||||
|
||||
// 6. 更新记录为成功
|
||||
// 11. 更新记录为成功
|
||||
long duration = (int) (System.currentTimeMillis() - startTime);
|
||||
long fileSize = estimateFileSize(resultImage, request.getOutputFormat());
|
||||
recordMapper.updateSuccess(
|
||||
@@ -114,7 +148,7 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
||||
(int) duration
|
||||
);
|
||||
|
||||
log.info("拼图生成成功: recordId={}, imageUrl={}, duration={}ms",
|
||||
log.info("拼图生成成功(新生成): recordId={}, imageUrl={}, duration={}ms",
|
||||
record.getId(), imageUrl, duration);
|
||||
|
||||
return PuzzleGenerateResponse.success(
|
||||
@@ -123,7 +157,9 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
||||
resultImage.getWidth(),
|
||||
resultImage.getHeight(),
|
||||
(int) duration,
|
||||
record.getId()
|
||||
record.getId(),
|
||||
false, // isDuplicate=false
|
||||
null // originalRecordId=null
|
||||
);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
Reference in New Issue
Block a user