# MultiAgentTranslationService.java

    package com.yumu.noveltranslator.domain.service;
    import com.yumu.noveltranslator.application.service.RagTranslationApplicationService;

    import com.yumu.noveltranslator.domain.model.AiGlossary;
    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.domain.model.Glossary;
    import com.yumu.noveltranslator.domain.model.TranslationTask;
    import com.yumu.noveltranslator.enums.ChapterTaskStatus;
    import com.yumu.noveltranslator.enums.CollabProjectStatus;
    import com.yumu.noveltranslator.enums.TranslationMode;
    import com.yumu.noveltranslator.enums.TranslationStatus;
    import com.yumu.noveltranslator.port.out.TeamTranslationPort;
    import com.yumu.noveltranslator.domain.service.TranslationPipeline;
    import com.yumu.noveltranslator.domain.service.CollabStateMachine;
    import com.yumu.noveltranslator.port.out.CollaborationRepositoryPort;
    import com.yumu.noveltranslator.port.out.DocumentRepositoryPort;
    import com.yumu.noveltranslator.port.out.TranslationRepositoryPort;
    import com.yumu.noveltranslator.port.out.TranslationCachePort;
    import com.yumu.noveltranslator.port.out.GlossaryRepositoryPort;
    import com.yumu.noveltranslator.util.ExternalResponseUtil;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Service;

    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.time.LocalDateTime;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Map;
    import java.util.Optional;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.stream.Collectors;
    import jakarta.annotation.PostConstruct;
    import jakarta.annotation.PreDestroy;

    /**
     * 多 Agent 协作翻译服务
     * 使用 AI 翻译团队（Agentscope 多 Agent）进行章节翻译
     *
     * 架构：Java = 编排器（缓存 + 实体一致性 + 占位符保护），Python = AI 翻译外包
     * 接入能力：三级缓存 + 实体一致性 + 术语表 + 占位符保护 + 章节拼接
     */
    @Service
    @RequiredArgsConstructor
    @Slf4j
    public class MultiAgentTranslationService {

        private static final int MAX_CONCURRENT_AGENTS = 5;
        private static final int MAX_RETRY_COUNT = 3;
        private static final long RETRY_BASE_DELAY_MS = 2000;
        private static final long RETRY_MAX_DELAY_MS = 30000;

        /**
         * 虚拟线程执行器，用于并发翻译章节（统一生命周期管理）
         */
        private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

        /**
         * 内存映射跟踪章节重试次数（服务重启后由 init() 从 DB 恢复）
         * 使用 AtomicInteger 保证并发安全的读-改-写
         */
        private final Map<Long, AtomicInteger> retryCounterMap = new ConcurrentHashMap<>();

        private final CollaborationRepositoryPort collabPort;
        private final DocumentRepositoryPort documentPort;
        private final TranslationRepositoryPort translationPort;
        private final TeamTranslationPort teamTranslationPort;
        private final TranslationCachePort cachePort;
        private final EntityConsistencyService entityConsistencyService;
        private final GlossaryRepositoryPort glossaryPort;
        private final RagTranslationApplicationService ragTranslationService;
        private final AiGlossaryService aiGlossaryService;
        private final TranslationPostProcessingService postProcessingService;
        private final CollabStateMachine collabStateMachine;

        /**
         * 启动时从数据库恢复重试计数
         */
        @PostConstruct
        public void init() {
            try {
                List<CollabChapterTask> chaptersWithRetries = collabPort.findChaptersWithRetryCountGreaterThan(0);
                for (CollabChapterTask chapter : chaptersWithRetries) {
                    retryCounterMap.put(chapter.getId(), new AtomicInteger(chapter.getRetryCount()));
                }
                if (!chaptersWithRetries.isEmpty()) {
                    log.info("从数据库恢复 {} 个章节的重试计数", chaptersWithRetries.size());
                }
            } catch (Exception e) {
                log.warn("初始化重试计数失败: {}", e.getMessage());
            }
        }

        /**
         * 服务关闭时优雅停止翻译线程池
         */
        @PreDestroy
        public void shutdown() {
            executor.shutdown();
            try {
                if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
                    log.warn("翻译线程池 30 秒内未关闭，强制执行 shutdownNow");
                    executor.shutdownNow();
                }
            } catch (InterruptedException e) {
                executor.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }

        /**
         * 启动多 Agent 协作翻译
         */
        public void startMultiAgentTranslation(Long projectId) {
            // 恢复上次中断的翻译（TRANSLATING 状态的章节回退到 UNASSIGNED）
            recoverStuckChapters(projectId);

            List<CollabChapterTask> chapters = collabPort.findChapterTasksByProjectIdAndStatus(
                    projectId, ChapterTaskStatus.UNASSIGNED.getValue());

            if (chapters.isEmpty()) {
                log.info("项目没有待翻译的章节: projectId={}", projectId);
                return;
            }

            CollabProject project = collabPort.findProjectById(projectId).orElse(null);
            if (project == null) {
                log.error("项目不存在: projectId={}", projectId);
                return;
            }

            log.info("启动多 Agent 翻译: projectId={}, chapters={}", projectId, chapters.size());

            // 更新关联文档状态为处理中
            if (project.getDocumentId() != null) {
                Document doc = documentPort.findById(project.getDocumentId()).orElse(null);
                if (doc != null) {
                    doc.setStatus(TranslationStatus.PROCESSING.getValue());
                    doc.setUpdateTime(LocalDateTime.now());
                    documentPort.update(doc);
                }
            }

            CountDownLatch latch = new CountDownLatch(chapters.size());
            AtomicInteger completed = new AtomicInteger(0);
            AtomicInteger failed = new AtomicInteger(0);
            Semaphore agentSemaphore = new Semaphore(MAX_CONCURRENT_AGENTS);

            for (CollabChapterTask chapter : chapters) {
                executor.submit(() -> {
                    try {
                        agentSemaphore.acquire();
                        try {
                            boolean success = translateChapter(chapter, project);
                            if (success) {
                                completed.incrementAndGet();
                            } else {
                                failed.incrementAndGet();
                            }
                            log.info("Agent 翻译完成章节: chapterId={}, progress={}/{}",
                                    chapter.getId(), completed.get(), chapters.size());
                        } finally {
                            agentSemaphore.release();
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        failed.incrementAndGet();
                        log.error("翻译章节被中断: chapterId={}", chapter.getId());
                    } catch (Exception e) {
                        failed.incrementAndGet();
                        log.error("Agent 翻译章节异常: chapterId={}", chapter.getId(), e);
                    } finally {
                        latch.countDown();
                    }
                });
            }

            // 异步等待所有 Agent 完成，完成后更新项目进度并拼接完整文档
            executor.submit(() -> {
                try {
                    latch.await();
                    log.info("多 Agent 翻译全部完成: projectId={}, success={}, failed={}",
                            projectId, completed.get(), failed.get());
                    updateProjectProgress(projectId);
                    assembleCompleteDocument(projectId);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error("等待翻译完成被中断: projectId={}", projectId);
                }
            });
        }

        /**
         * 单个 Agent 翻译一个章节
         * 走 TranslationPipeline 完整管线（L1缓存→L2 RAG→L3实体一致性→L4团队翻译）
         * 翻译完成标记为 SUBMITTED（待审校状态）
         *
         * @return true 翻译成功, false 翻译失败
         */
        private boolean translateChapter(CollabChapterTask chapter, CollabProject project) {
            Long chapterId = chapter.getId();
            String sourceText = chapter.getSourceText();

            if (sourceText == null || sourceText.trim().isEmpty()) {
                log.warn("章节内容为空，跳过: chapterId={}", chapterId);
                return false;
            }

            // 更新章节状态为翻译中
            collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.TRANSLATING);
            chapter.setAssignedTime(LocalDateTime.now());
            chapter.setProgress(50);
            collabPort.updateChapterTask(chapter);

            String sourceLang = project.getSourceLang();
            String targetLang = project.getTargetLang();

            try {
                // 获取 userId（RAG 需要用户上下文）
                Long userId = chapter.getAssigneeId();
                if (userId == null) {
                    CollabProject proj = collabPort.findProjectById(chapter.getProjectId()).orElse(null);
                    if (proj != null) {
                        userId = proj.getOwnerId();
                    }
                }

                // 加载术语表
                List<Glossary> glossaryTerms = loadGlossaryTermsForProject(userId, sourceText);

                // 加载 AI 术语表（仅已确认的术语，优先级低于用户术语表）
                List<AiGlossary> aiGlossaryTerms = aiGlossaryService.getProjectGlossary(project.getId());
                List<Glossary> aiGlossaryGlossaryTerms = aiGlossaryTerms.stream()
                        .filter(t -> sourceText.contains(t.getSourceWord()))
                        .map(t -> {
                            Glossary g = new Glossary();
                            g.setSourceWord(t.getSourceWord());
                            g.setTargetWord(t.getTargetWord());
                            return g;
                        })
                        .toList();

                // 合并：用户术语表优先，AI 术语表补充
                if (!aiGlossaryGlossaryTerms.isEmpty()) {
                    Set<String> userTermKeys = glossaryTerms.stream()
                            .map(Glossary::getSourceWord)
                            .collect(Collectors.toSet());
                    for (Glossary aiTerm : aiGlossaryGlossaryTerms) {
                        if (!userTermKeys.contains(aiTerm.getSourceWord())) {
                            glossaryTerms.add(aiTerm);
                        }
                    }
                    log.info("AI 术语表注入: {} 个术语（过滤后）", aiGlossaryGlossaryTerms.size());
                }

                // 推导小说类型
                String novelType = deriveNovelType(project);

                // 构建 Pipeline 并执行完整管线（L1→L2→L3→L4=Team）
                TranslationPipeline pipeline = new TranslationPipeline(
                        cachePort, ragTranslationService, entityConsistencyService,
                        null, postProcessingService, teamTranslationPort, userId, chapter.getProjectId().toString(), glossaryTerms);

                String translated = pipeline.executeTeam(
                        sourceText, sourceLang, targetLang, TranslationMode.TEAM, novelType, glossaryTerms);

                if (translated == null || translated.trim().isEmpty()) {
                    throw new RuntimeException("AI 翻译团队返回结果为空");
                }

                // 维护 AI 术语表：提取实体并保存
                try {
                    List<String> entities = entityConsistencyService.extractEntitiesSegmented(sourceText, targetLang);
                    if (!entities.isEmpty()) {
                        Map<String, String> entityTranslations = entityConsistencyService.translateEntities(entities, targetLang);
                        String contextExcerpt = sourceText.length() > 200 ? sourceText.substring(0, 200) : sourceText;
                        for (Map.Entry<String, String> entry : entityTranslations.entrySet()) {
                            aiGlossaryService.addTerm(
                                    project.getId(),
                                    entry.getKey(),
                                    entry.getValue(),
                                    contextExcerpt,
                                    null,
                                    chapter.getId()
                            );
                        }
                        log.info("AI 术语表维护完成: chapterId={}, 提取{}个实体", chapterId, entityTranslations.size());
                    }
                } catch (Exception e) {
                    log.warn("AI 术语表维护失败: {}", e.getMessage());
                }

                // 标记为 SUBMITTED（待审校）
                applyTranslationResult(chapter, sourceText, translated, false);

                log.info("章节翻译完成（待审校）: chapterId={}, 原文{}字, 译文{}字",
                        chapterId, sourceText.length(), translated.length());
                return true;

            } catch (Exception e) {
                log.error("章节翻译异常: chapterId={}, error={}", chapterId, e.getMessage(), e);
                int retryCount = getRetryCount(chapter);
                if (retryCount >= MAX_RETRY_COUNT) {
                    log.error("章节 {} 已达到最大重试次数 {}，停止自动重试", chapterId, MAX_RETRY_COUNT);
                    // 经 REVIEWING 中间状态转入 REJECTED
                    collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.REVIEWING);
                    collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.REJECTED);
                    chapter.setReviewComment("翻译异常（已重试 " + retryCount + " 次）: " + e.getMessage());
                } else {
                    incrementRetryCount(chapter);
                    int newRetry = retryCount + 1;
                    chapter.setRetryCount(newRetry);
                    collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.UNASSIGNED);
                    chapter.setReviewComment("翻译异常，等待重试（第 " + newRetry + " 次）: " + e.getMessage());
                    // 指数退避，避免立即重试
                    sleepWithBackoff(retryCount, e);
                }
                collabPort.updateChapterTask(chapter);
                return false;
            }
        }

        /**
         * 恢复上次中断的翻译：将 TRANSLATING 状态的章节回退到 UNASSIGNED
         */
        private void recoverStuckChapters(Long projectId) {
            List<CollabChapterTask> stuckChapters = collabPort.findChapterTasksByProjectIdAndStatus(
                    projectId, ChapterTaskStatus.TRANSLATING.getValue());

            if (stuckChapters.isEmpty()) {
                return;
            }

            log.info("发现 {} 个翻译中断的章节，回退到待翻译状态", stuckChapters.size());
            for (CollabChapterTask chapter : stuckChapters) {
                // 检查重试次数
                int retryCount = getRetryCount(chapter);
                if (retryCount >= MAX_RETRY_COUNT) {
                    log.warn("章节 {} 已重试 {} 次，跳过自动翻译", chapter.getId(), retryCount);
                    continue;
                }
                // 增加重试计数并回退状态
                incrementRetryCount(chapter);
                collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.UNASSIGNED);
                chapter.setReviewComment("翻译中断，自动重试（第 " + (retryCount + 2) + " 次）");
                chapter.setRetryCount(retryCount + 1);
                collabPort.updateChapterTask(chapter);
                log.info("章节 {} 回退到 UNASSIGNED，准备重试", chapter.getId());
            }
        }

        /**
         * 获取章节重试次数（原子读）
         */
        private int getRetryCount(CollabChapterTask chapter) {
            AtomicInteger counter = retryCounterMap.get(chapter.getId());
            return counter == null ? 0 : counter.get();
        }

        /**
         * 增加章节重试次数（原子递增）
         */
        private void incrementRetryCount(CollabChapterTask chapter) {
            retryCounterMap.computeIfAbsent(chapter.getId(), k -> new AtomicInteger(0)).incrementAndGet();
        }

        /**
         * 定时清理卡在 TRANSLATING 状态的章节（每 5 分钟执行一次）
         * 使用 CAS 单行更新保证并发安全，不依赖事务
         */
        @Scheduled(fixedRate = 300000)
        public void cleanupStuckChapters() {
            LocalDateTime cutoff = LocalDateTime.now().minusMinutes(30);
            List<CollabChapterTask> stuckChapters = collabPort.findChapterTasksByStatusAndUpdateTimeBefore(
                    ChapterTaskStatus.TRANSLATING.getValue(), cutoff);

            if (stuckChapters.isEmpty()) {
                return;
            }

            String expectedStatus = ChapterTaskStatus.TRANSLATING.getValue();
            log.info("定时清理: 发现 {} 个卡住超过 30 分钟的章节", stuckChapters.size());
            for (CollabChapterTask chapter : stuckChapters) {
                int retryCount = getRetryCount(chapter);
                if (retryCount >= MAX_RETRY_COUNT) {
                    // CAS 直接转到 REJECTED
                    int rows = collabPort.casRejectChapter(
                            chapter.getId(), expectedStatus, retryCount,
                            "翻译超时（已重试 " + retryCount + " 次），自动终止");
                    if (rows > 0) {
                        chapter.setStatus(ChapterTaskStatus.REJECTED.getValue());
                        chapter.setRetryCount(retryCount);
                        log.info("定时清理: 章节 {} 已达到最大重试次数，标记为 REJECTED", chapter.getId());
                    } else {
                        log.debug("定时清理: 章节 {} 状态已被其他线程修改，跳过", chapter.getId());
                    }
                } else {
                    incrementRetryCount(chapter);
                    int newRetry = retryCount + 1;
                    String comment = "翻译超时，自动重试（第 " + newRetry + " 次）";
                    int rows = collabPort.casResetChapterToUnassigned(
                            chapter.getId(), expectedStatus, newRetry, comment);
                    if (rows > 0) {
                        chapter.setStatus(ChapterTaskStatus.UNASSIGNED.getValue());
                        chapter.setRetryCount(newRetry);
                        chapter.setReviewComment(comment);
                        log.info("定时清理: 章节 {} 回退到 UNASSIGNED", chapter.getId());
                    } else {
                        // CAS 失败，回退内存计数避免虚高
                        AtomicInteger counter = retryCounterMap.get(chapter.getId());
                        if (counter != null && counter.get() > 0) {
                            counter.decrementAndGet();
                        }
                        log.debug("定时清理: 章节 {} 状态已被其他线程修改，跳过", chapter.getId());
                    }
                }
            }
        }

        /**
         * 指数退避等待，避免重试风暴
         */
        private void sleepWithBackoff(int retryCount, Exception e) {
            long delay;
            String msg = e.getMessage();
            boolean isRateLimited = msg != null && (msg.contains("429") || msg.contains("rate limit") || msg.contains("Too Many Requests"));

            if (isRateLimited) {
                delay = Math.max(5000, RETRY_BASE_DELAY_MS * (1L << retryCount));
            } else {
                long baseDelay = RETRY_BASE_DELAY_MS * (1L << retryCount);
                long jitter = (long) (Math.random() * baseDelay * 0.5);
                delay = baseDelay + jitter;
            }
            delay = Math.min(delay, RETRY_MAX_DELAY_MS);
            try {
                Thread.sleep(delay);
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }

        /**
         * 加载项目所有者的术语表中在原文中出现的词条
         */
        private List<Glossary> loadGlossaryTermsForProject(Long userId, String sourceText) {
            if (userId == null) {
                return List.of();
            }

            try {
                List<Glossary> allTerms = glossaryPort.findActiveGlossaryByUserId(userId);

                // 过滤只保留在原文中出现的术语
                return allTerms.stream()
                        .filter(term -> term.getSourceWord() != null && sourceText.contains(term.getSourceWord()))
                        .toList();
            } catch (Exception e) {
                log.warn("加载术语表失败: {}", e.getMessage());
                return List.of();
            }
        }

        /**
         * 从项目信息推导小说类型
         */
        private String deriveNovelType(CollabProject project) {
            if (project == null) {
                return "daily";
            }

            String description = project.getDescription();
            if (description == null || description.isBlank()) {
                return "daily";
            }

            // 简单的关键词匹配推导类型
            String lower = description.toLowerCase();
            if (lower.contains("fantasy") || lower.contains("奇幻") || lower.contains("玄幻")) {
                return "fantasy";
            }
            if (lower.contains("romance") || lower.contains("言情") || lower.contains("恋爱")) {
                return "romance";
            }
            if (lower.contains("scifi") || lower.contains("科幻")) {
                return "scifi";
            }
            if (lower.contains("mystery") || lower.contains("悬疑") || lower.contains("推理")) {
                return "mystery";
            }
            if (lower.contains("horror") || lower.contains("恐怖")) {
                return "horror";
            }
            if (lower.contains("daily") || lower.contains("日常") || lower.contains("生活")) {
                return "daily";
            }

            return "daily";
        }

        /**
         * 应用翻译结果到章节实体
         */
        private void applyTranslationResult(CollabChapterTask chapter, String sourceText, String translated, boolean fromCache) {
            chapter.setTargetText(translated);
            chapter.setTargetWordCount(translated.length());
            chapter.setSourceWordCount(sourceText.length());

            if (fromCache) {
                // 缓存命中：经 SUBMITTED → REVIEWING → APPROVED → COMPLETED 完成自动审核
                collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.SUBMITTED);
                collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.REVIEWING);
                collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.APPROVED);
                chapter.setProgress(100);
                chapter.setCompletedTime(LocalDateTime.now());
                chapter.setSubmittedTime(LocalDateTime.now());
                collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.COMPLETED);
            } else {
                // AI 新翻译：标记为 SUBMITTED（待审校）
                collabStateMachine.transitionChapter(chapter, ChapterTaskStatus.SUBMITTED);
                chapter.setProgress(80);
                chapter.setSubmittedTime(LocalDateTime.now());
            }

            collabPort.updateChapterTask(chapter);
        }

        /**
         * 更新项目整体进度
         */
        private void updateProjectProgress(Long projectId) {
            List<CollabChapterTask> tasks = collabPort.findChapterTasksByProjectId(projectId);
            if (tasks.isEmpty()) return;

            long completed = tasks.stream()
                    .filter(t -> ChapterTaskStatus.COMPLETED.getValue().equals(t.getStatus()))
                    .count();

            int progress = (int) Math.round((double) completed / tasks.size() * 100);

            CollabProject project = collabPort.findProjectById(projectId).orElse(null);
            if (project != null) {
                project.setProgress(progress);
                if (progress == 100) {
                    try {
                        collabStateMachine.transitionProject(project, CollabProjectStatus.COMPLETED);
                    } catch (IllegalStateException e) {
                        log.debug("项目无法转换为 COMPLETED 状态（当前状态不允许）: projectId={}", projectId);
                    }
                }
                collabPort.updateProject(project);
            }
        }

        /**
         * 所有章节翻译完成后，按 chapterNumber 顺序拼接完整翻译文档
         */
        private void assembleCompleteDocument(Long projectId) {
            // 按 chapterNumber 升序获取所有章节
            List<CollabChapterTask> chapters = collabPort.findChapterTasksByProjectId(projectId);
            if (chapters.isEmpty()) return;

            chapters.sort(Comparator.comparingInt(CollabChapterTask::getChapterNumber));

            // 拼接完整译文（不添加合成章节标记，保留原文结构）
            StringBuilder fullText = new StringBuilder();
            for (CollabChapterTask chapter : chapters) {
                if (chapter.getTargetText() != null && !chapter.getTargetText().isEmpty()) {
                    fullText.append(chapter.getTargetText());
                    if (!chapter.getTargetText().endsWith("\n")) {
                        fullText.append("\n");
                    }
                }
            }

            if (fullText.length() == 0) return;

            // 通过项目关联的 documentId 直接获取 Document
            CollabProject project = collabPort.findProjectById(projectId).orElse(null);
            if (project == null || project.getDocumentId() == null) {
                log.warn("项目未关联 Document，无法保存完整译文: projectId={}", projectId);
                return;
            }

            Document matchedDoc = documentPort.findById(project.getDocumentId()).orElse(null);
            if (matchedDoc == null) {
                log.warn("关联的 Document 不存在: documentId={}", project.getDocumentId());
                return;
            }

            // 保存完整翻译文档到文件
            String translatedPath = ExternalResponseUtil.buildTranslatedPath(matchedDoc.getPath());
            try {
                Path path = Paths.get(translatedPath);
                Files.createDirectories(path.getParent());
                Files.write(path, fullText.toString().getBytes(StandardCharsets.UTF_8));
                log.info("完整翻译文档已保存: path={}, size={} bytes", translatedPath, fullText.length());
            } catch (IOException e) {
                log.error("保存完整翻译文档失败: {}", e.getMessage(), e);
            }

            // 创建/更新 TranslationTask
            TranslationTask task = translationPort.findTaskByDocumentId(matchedDoc.getId()).orElse(null);
            if (task == null) {
                task = new TranslationTask();
                task.setTaskId("task_" + System.currentTimeMillis() + "_" + projectId);
                task.setDocumentId(matchedDoc.getId());
                task.setUserId(project.getOwnerId());
                task.setSourceLang(project.getSourceLang());
                task.setTargetLang(project.getTargetLang());
                task.setType("document");
                task.setMode("team");
                task.setStatus("completed");
                translationPort.saveTask(task);
            } else {
                task.setStatus("completed");
                translationPort.updateTask(task);
            }
            log.info("团队模式翻译任务已更新: taskId={}, status=completed", task.getId());

            // 更新 Document 状态为 completed
            matchedDoc.setStatus("completed");
            matchedDoc.setTaskId(task.getTaskId());
            documentPort.update(matchedDoc);
            log.info("团队模式文档状态已更新: documentId={}, status=completed, taskId={}", matchedDoc.getId(), task.getTaskId());
        }
    }