feat(导图): 防并发生成 + 增量合并 + mergeTrees 工具

- MindMapTreeTool.mergeTrees() 按标准化标题合并新旧树节点
- StandardMindServiceImpl 防并发生成(ConcurrentHashMap 锁)
- incrementalGenerate() 保留用户编辑节点 + 追加新节点
- regenerate 端点接受 mode 参数(full/incremental)
This commit is contained in:
2026-07-05 15:19:03 +08:00
parent a636e516d1
commit 3c3f682b2b
4 changed files with 132 additions and 2 deletions
@@ -20,6 +20,8 @@ import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
/**
@@ -44,9 +46,13 @@ public class StandardMindMapServiceImpl implements StandardMindMapService {
private static final String GENERATOR_BUILTIN = "BUILTIN";
private static final String GENERATOR_USER = "USER";
private static final String GENERATOR_USER_MERGE = "USER_MERGE";
private static final String MATCH_STATUS_MATCHED = "MATCHED";
private static final String MATCH_STATUS_MISSED = "MISSED";
/** 防并发生成:taskNum → 是否正在生成中 */
private final ConcurrentHashMap<String, AtomicBoolean> generatingTasks = new ConcurrentHashMap<>();
// ============ 查询与生成 ============
@Override
@@ -59,7 +65,57 @@ public class StandardMindMapServiceImpl implements StandardMindMapService {
@Override
public ReviewStandardMindMapEntity regenerate(String taskNum) throws NotFindEntitiesException, OperationFailedException {
ensureTaskExists(taskNum);
return doGenerate(taskNum);
// 防并发生成:同一任务正在生成时直接返回当前实体
AtomicBoolean lock = generatingTasks.computeIfAbsent(taskNum, k -> new AtomicBoolean(false));
if (!lock.compareAndSet(false, true)) {
ReviewStandardMindMapEntity existing = queryByTaskNum(taskNum);
log.warn("任务[{}]正在生成中,跳过重复请求", taskNum);
return existing;
}
try {
return doGenerate(taskNum);
} finally {
lock.set(false);
generatingTasks.remove(taskNum);
}
}
@Override
@Transactional
public ReviewStandardMindMapEntity incrementalGenerate(String taskNum) throws NotFindEntitiesException, OperationFailedException {
ensureTaskExists(taskNum);
ReviewStandardMindMapEntity existing = queryByTaskNum(taskNum);
if (existing == null) {
throw new NotFindEntitiesException("标准思维导图尚不存在,无法增量更新");
}
// 防并发生成
AtomicBoolean lock = generatingTasks.computeIfAbsent(taskNum, k -> new AtomicBoolean(false));
if (!lock.compareAndSet(false, true)) {
log.warn("任务[{}]正在生成中,跳过重复增量请求", taskNum);
return existing;
}
try {
ReviewStandardMindMapEntity fresh = doGenerate(taskNum);
// 合并新旧树:保留用户编辑过的节点,追加新节点
MindMapNode oldRoot = MindMapTreeTool.fromJson(existing.getContent(), objectMapper);
MindMapNode newRoot = MindMapTreeTool.fromJson(fresh.getContent(), objectMapper);
MindMapNode merged = MindMapTreeTool.mergeTrees(oldRoot, newRoot);
String mergedJson = MindMapTreeTool.toJson(merged, objectMapper);
String mergedOutline = MindMapTreeTool.toFullOutline(merged);
existing.setContent(mergedJson);
existing.setOutline(mergedOutline);
existing.setTitle(merged.getTitle() != null ? merged.getTitle() : "思维导图");
existing.setGenerator(GENERATOR_USER_MERGE);
existing.setGeneratorVersion(fresh.getGeneratorVersion());
existing.setSummary("增量更新,共 " + MindMapTreeTool.countNodes(merged) + " 个节点");
existing.setGeneratedTime(LocalDateTime.now());
standardMindMapMapper.updateById(existing);
return existing;
} finally {
lock.set(false);
generatingTasks.remove(taskNum);
}
}
@Override