ChapterSplitAsyncListener.java
package com.yumu.noveltranslator.domain.service;
import com.yumu.noveltranslator.domain.model.CollabChapterTask;
import com.yumu.noveltranslator.domain.model.CollabProject;
import com.yumu.noveltranslator.domain.model.Document;
import com.yumu.noveltranslator.enums.ChapterTaskStatus;
import com.yumu.noveltranslator.enums.CollabProjectStatus;
import com.yumu.noveltranslator.enums.TranslationStatus;
import com.yumu.noveltranslator.domain.event.ChapterSplitEvent;
import com.yumu.noveltranslator.domain.service.CollabStateMachine;
import com.yumu.noveltranslator.port.out.CollaborationRepositoryPort;
import com.yumu.noveltranslator.port.out.DocumentRepositoryPort;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
/**
* 章节拆分异步监听器
* 接收 ChapterSplitEvent 事件,按批次异步插入章节,完成后激活项目。
*/
@Component
@RequiredArgsConstructor
@Slf4j
public class ChapterSplitAsyncListener {
private static final int BATCH_SIZE = 50;
private final CollaborationRepositoryPort collaborationRepository;
private final DocumentRepositoryPort documentRepository;
private final CollabStateMachine collabStateMachine;
@Async("chapterSplitExecutor")
@EventListener
public void onChapterSplitEvent(ChapterSplitEvent event) {
Long projectId = event.getProjectId();
List<String> chapters = event.getChapters();
log.info("开始异步插入章节: projectId={}, totalChapters={}", projectId, chapters.size());
try {
int totalBatches = (int) Math.ceil((double) chapters.size() / BATCH_SIZE);
for (int i = 0; i < chapters.size(); i += BATCH_SIZE) {
int end = Math.min(i + BATCH_SIZE, chapters.size());
List<String> batch = chapters.subList(i, end);
int batchIndex = (i / BATCH_SIZE) + 1;
log.debug("插入章节批次: projectId={}, batch={}/{}", projectId, batchIndex, totalBatches);
insertChapterBatch(event, batch, i);
}
// 所有章节批次插入完成,激活项目
activateProject(projectId, event.getDocumentId());
log.info("章节异步插入完成: projectId={}, totalChapters={}", projectId, chapters.size());
} catch (Exception e) {
log.error("章节异步插入失败: projectId={}, error={}", projectId, e.getMessage(), e);
}
}
@Transactional
protected void insertChapterBatch(ChapterSplitEvent event, List<String> batch, int startIndex) {
Long projectId = event.getProjectId();
for (int i = 0; i < batch.size(); i++) {
String chapterText = batch.get(i);
CollabChapterTask chapter = new CollabChapterTask();
chapter.setProjectId(projectId);
chapter.setChapterNumber(startIndex + i + 1);
chapter.setTitle("第 " + (startIndex + i + 1) + " 章");
chapter.setSourceText(chapterText);
chapter.setTargetText(null);
chapter.setStatus(ChapterTaskStatus.UNASSIGNED.getValue());
chapter.setProgress(0);
chapter.setSourceWordCount(chapterText.length());
collaborationRepository.saveChapterTask(chapter);
}
log.debug("批次插入成功: projectId={}, batchStart={}, count={}", projectId, startIndex + 1, batch.size());
}
@Transactional
protected void activateProject(Long projectId, Long documentId) {
CollabProject project = collaborationRepository.findProjectById(projectId).orElse(null);
if (project == null) {
log.error("激活项目失败: 项目不存在, projectId={}", projectId);
return;
}
// 通过状态机将项目从 DRAFT 转换到 ACTIVE
collabStateMachine.transitionProject(project, CollabProjectStatus.ACTIVE);
project.setProgress(0);
collaborationRepository.updateProject(project);
// 更新文档状态为已完成
Document doc = documentRepository.findById(documentId).orElse(null);
if (doc != null) {
doc.setStatus(TranslationStatus.COMPLETED.getValue());
doc.setUpdateTime(LocalDateTime.now());
documentRepository.update(doc);
}
log.info("项目已激活: projectId={}", projectId);
}
}