You've already forked FrameTour-BE
fix(puzzle): 解决拼图生成服务重复内容检测问题
- 添加对正在生成中记录的状态检查,避免并发重复写入 - 实现等待机制处理相同内容正在生成的情况 - 优化数据库查询逻辑,同时匹配成功和生成中的记录 - 仅对成功记录标记素材版本缓存,避免生成中记录失败时的错误标记 - 更新日志输出包含记录状态信息以便调试 - 添加超时机制确保系统稳定性
This commit is contained in:
@@ -141,23 +141,50 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
template.getId(), contentHash, resolvedScenicId
|
template.getId(), contentHash, resolvedScenicId
|
||||||
);
|
);
|
||||||
if (duplicateRecord != null) {
|
if (duplicateRecord != null) {
|
||||||
long duration = System.currentTimeMillis() - startTime;
|
if (duplicateRecord.getStatus() == 1) {
|
||||||
log.info("检测到重复内容,复用历史记录: recordId={}, imageUrl={}, duration={}ms",
|
// 已有成功记录,直接复用
|
||||||
duplicateRecord.getId(), duplicateRecord.getResultImageUrl(), duration);
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
// 标记素材版本缓存
|
log.info("检测到重复内容,复用历史记录: recordId={}, imageUrl={}, duration={}ms",
|
||||||
if (request.getFaceId() != null) {
|
duplicateRecord.getId(), duplicateRecord.getResultImageUrl(), duration);
|
||||||
faceStatusManager.markPuzzleSourceVersion(request.getFaceId(), template.getId(), 0);
|
// 标记素材版本缓存
|
||||||
|
if (request.getFaceId() != null) {
|
||||||
|
faceStatusManager.markPuzzleSourceVersion(request.getFaceId(), template.getId(), 0);
|
||||||
|
}
|
||||||
|
return PuzzleGenerateResponse.success(
|
||||||
|
duplicateRecord.getResultImageUrl(),
|
||||||
|
duplicateRecord.getResultFileSize(),
|
||||||
|
duplicateRecord.getResultWidth(),
|
||||||
|
duplicateRecord.getResultHeight(),
|
||||||
|
(int) duration,
|
||||||
|
duplicateRecord.getId(),
|
||||||
|
true,
|
||||||
|
duplicateRecord.getId()
|
||||||
|
);
|
||||||
|
} else if (duplicateRecord.getStatus() == 0) {
|
||||||
|
// 相同内容正在生成中,等待完成后复用
|
||||||
|
log.info("检测到相同内容正在生成中,等待完成: recordId={}", duplicateRecord.getId());
|
||||||
|
PuzzleGenerationRecordEntity completedRecord = waitForRecordCompletion(duplicateRecord.getId(), 30_000);
|
||||||
|
if (completedRecord != null && completedRecord.getStatus() == 1) {
|
||||||
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
|
log.info("等待生成中记录完成,复用结果: recordId={}, imageUrl={}, duration={}ms",
|
||||||
|
completedRecord.getId(), completedRecord.getResultImageUrl(), duration);
|
||||||
|
if (request.getFaceId() != null) {
|
||||||
|
faceStatusManager.markPuzzleSourceVersion(request.getFaceId(), template.getId(), 0);
|
||||||
|
}
|
||||||
|
return PuzzleGenerateResponse.success(
|
||||||
|
completedRecord.getResultImageUrl(),
|
||||||
|
completedRecord.getResultFileSize(),
|
||||||
|
completedRecord.getResultWidth(),
|
||||||
|
completedRecord.getResultHeight(),
|
||||||
|
(int) duration,
|
||||||
|
completedRecord.getId(),
|
||||||
|
true,
|
||||||
|
completedRecord.getId()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 超时或失败,兜底创建新记录
|
||||||
|
log.warn("等待生成中记录超时或失败,创建新记录: originalRecordId={}", duplicateRecord.getId());
|
||||||
}
|
}
|
||||||
return PuzzleGenerateResponse.success(
|
|
||||||
duplicateRecord.getResultImageUrl(),
|
|
||||||
duplicateRecord.getResultFileSize(),
|
|
||||||
duplicateRecord.getResultWidth(),
|
|
||||||
duplicateRecord.getResultHeight(),
|
|
||||||
(int) duration,
|
|
||||||
duplicateRecord.getId(),
|
|
||||||
true,
|
|
||||||
duplicateRecord.getId()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. 创建生成记录
|
// 7. 创建生成记录
|
||||||
@@ -290,10 +317,10 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
);
|
);
|
||||||
if (duplicateRecord != null) {
|
if (duplicateRecord != null) {
|
||||||
long duration = System.currentTimeMillis() - startTime;
|
long duration = System.currentTimeMillis() - startTime;
|
||||||
log.info("检测到重复内容,复用历史记录: recordId={}, imageUrl={}, duration={}ms",
|
log.info("检测到重复内容,复用历史记录: recordId={}, status={}, imageUrl={}, duration={}ms",
|
||||||
duplicateRecord.getId(), duplicateRecord.getResultImageUrl(), duration);
|
duplicateRecord.getId(), duplicateRecord.getStatus(), duplicateRecord.getResultImageUrl(), duration);
|
||||||
// 标记素材版本缓存
|
// 仅成功记录才标记素材版本缓存(生成中的记录可能会失败)
|
||||||
if (request.getFaceId() != null) {
|
if (request.getFaceId() != null && duplicateRecord.getStatus() == 1) {
|
||||||
faceStatusManager.markPuzzleSourceVersion(request.getFaceId(), template.getId(), 0);
|
faceStatusManager.markPuzzleSourceVersion(request.getFaceId(), template.getId(), 0);
|
||||||
}
|
}
|
||||||
return duplicateRecord.getId();
|
return duplicateRecord.getId();
|
||||||
@@ -326,6 +353,33 @@ public class PuzzleGenerateServiceImpl implements IPuzzleGenerateService {
|
|||||||
return record.getId();
|
return record.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待生成中的记录完成
|
||||||
|
* 轮询数据库直到记录状态变为非生成中(成功或失败),或超时返回null
|
||||||
|
*
|
||||||
|
* @param recordId 记录ID
|
||||||
|
* @param timeoutMs 超时时间(毫秒)
|
||||||
|
* @return 完成后的记录,超时返回null
|
||||||
|
*/
|
||||||
|
private PuzzleGenerationRecordEntity waitForRecordCompletion(Long recordId, long timeoutMs) {
|
||||||
|
long deadline = System.currentTimeMillis() + timeoutMs;
|
||||||
|
while (System.currentTimeMillis() < deadline) {
|
||||||
|
PuzzleGenerationRecordEntity record = recordMapper.getById(recordId);
|
||||||
|
if (record == null || record.getStatus() != 0) {
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(500);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
log.warn("等待记录完成被中断: recordId={}", recordId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.warn("等待记录完成超时: recordId={}, timeoutMs={}", recordId, timeoutMs);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验请求参数
|
* 校验请求参数
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -128,15 +128,15 @@
|
|||||||
WHERE id = #{id}
|
WHERE id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<!-- 根据内容哈希查询历史记录(用于去重) -->
|
<!-- 根据内容哈希查询历史记录(用于去重,同时匹配成功和生成中的记录,防止并发重复写入) -->
|
||||||
<select id="findByContentHash" resultMap="BaseResultMap">
|
<select id="findByContentHash" resultMap="BaseResultMap">
|
||||||
SELECT <include refid="Base_Column_List"/>
|
SELECT <include refid="Base_Column_List"/>
|
||||||
FROM puzzle_generation_record
|
FROM puzzle_generation_record
|
||||||
WHERE template_id = #{templateId}
|
WHERE template_id = #{templateId}
|
||||||
AND content_hash = #{contentHash}
|
AND content_hash = #{contentHash}
|
||||||
AND scenic_id = #{scenicId}
|
AND scenic_id = #{scenicId}
|
||||||
AND status = 1
|
AND (status = 1 OR (status = 0 AND create_time > DATE_SUB(NOW(), INTERVAL 5 MINUTE)))
|
||||||
ORDER BY create_time DESC
|
ORDER BY status DESC, create_time DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user