package com.qqchen.deploy.backend.deploy.service.impl;

import com.qqchen.deploy.backend.deploy.dto.*;
import com.qqchen.deploy.backend.deploy.entity.*;
import com.qqchen.deploy.backend.deploy.enums.BuildTypeEnum;
import com.qqchen.deploy.backend.deploy.repository.*;
import com.qqchen.deploy.backend.notification.entity.NotificationChannel;
import com.qqchen.deploy.backend.notification.entity.NotificationTemplate;
import com.qqchen.deploy.backend.notification.repository.INotificationChannelRepository;
import com.qqchen.deploy.backend.notification.repository.INotificationTemplateRepository;
import com.qqchen.deploy.backend.deploy.converter.TeamEnvironmentNotificationConfigConverter;
import com.qqchen.deploy.backend.deploy.service.IDeployService;
import com.qqchen.deploy.backend.framework.security.SecurityUtils;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.utils.JsonUtils;
import com.qqchen.deploy.backend.framework.utils.MapUtils;
import com.qqchen.deploy.backend.framework.utils.SpelExpressionResolver;
import com.qqchen.deploy.backend.system.entity.User;
import com.qqchen.deploy.backend.system.repository.IUserRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
import com.qqchen.deploy.backend.workflow.dto.WorkflowNodeLogDTO;
import com.qqchen.deploy.backend.workflow.dto.inputmapping.ApprovalInputMapping;
import com.qqchen.deploy.backend.workflow.dto.outputs.ApprovalOutputs;
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
import com.qqchen.deploy.backend.workflow.enums.WorkflowDefinitionStatusEnums;
import com.qqchen.deploy.backend.workflow.model.NodeContext;
import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository;
import com.qqchen.deploy.backend.workflow.service.IWorkflowInstanceService;
import com.qqchen.deploy.backend.workflow.service.IWorkflowNodeLogService;
import com.qqchen.deploy.backend.deploy.service.IDeployRecordService;
import com.qqchen.deploy.backend.deploy.repository.IDeployRecordRepository;
import com.qqchen.deploy.backend.deploy.entity.DeployRecord;
import com.qqchen.deploy.backend.deploy.enums.DeployRecordStatusEnums;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.*;

/**
 * 部署服务实现
 *
 * @author qqchen
 * @since 2025-11-02
 */
@Slf4j
@Service
public class DeployServiceImpl implements IDeployService {

    @Resource
    private IUserRepository userRepository;

    @Resource
    private ITeamMemberRepository teamMemberRepository;

    @Resource
    private ITeamRepository teamRepository;

    @Resource
    private ITeamEnvironmentConfigRepository teamEnvironmentConfigRepository;

    @Resource
    private IEnvironmentRepository environmentRepository;

    @Resource
    private ITeamApplicationRepository teamApplicationRepository;

    @Resource
    private IApplicationRepository applicationRepository;

    @Resource
    private IExternalSystemRepository externalSystemRepository;

    @Resource
    private IWorkflowDefinitionRepository workflowDefinitionRepository;

    @Resource
    private IWorkflowInstanceService workflowInstanceService;

    @Resource
    private IDeployRecordService deployRecordService;

    @Resource
    private IDeployRecordRepository deployRecordRepository;

    @Resource
    private ObjectMapper objectMapper;

    @Resource
    private TaskService taskService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private IWorkflowNodeLogService workflowNodeLogService;

    @Resource
    private ITeamEnvironmentNotificationConfigRepository teamEnvironmentNotificationConfigRepository;

    @Resource
    private INotificationChannelRepository notificationChannelRepository;

    @Resource
    private INotificationTemplateRepository notificationTemplateRepository;

    @Resource
    private TeamEnvironmentNotificationConfigConverter notificationConfigConverter;

    @Resource
    private IRepositoryProjectRepository repositoryProjectRepository;

    @Override
    public List<UserTeamDeployableDTO> getDeployableEnvironments() {
        Long currentUserId = SecurityUtils.getCurrentUserId();

        // 一次性查询并构建完整数据（避免重复查询）
        return batchQueryUserTeamDeployableData(currentUserId);
    }

    /**
     * 批量查询用户团队可部署数据（完整版，直接构建新DTO）
     * <p>优化：一次性完成所有查询，避免重复查询团队信息
     *
     * @param currentUserId 当前用户ID
     * @return 用户团队可部署数据列表
     */
    private List<UserTeamDeployableDTO> batchQueryUserTeamDeployableData(Long currentUserId) {
        // 1. 查询用户所属团队（作为成员 + 作为负责人）
        List<TeamMember> userTeamMembers = teamMemberRepository.findByUserIdAndDeletedFalse(currentUserId);
        List<Team> ownedTeams = teamRepository.findByOwnerIdAndDeletedFalse(currentUserId);

        // 2. 合并团队ID并去重
        Set<Long> teamIdSet = new HashSet<>();
        teamIdSet.addAll(userTeamMembers.stream().map(TeamMember::getTeamId).collect(Collectors.toList()));
        teamIdSet.addAll(ownedTeams.stream().map(Team::getId).collect(Collectors.toList()));

        if (teamIdSet.isEmpty()) {
            log.info("用户 {} 未加入任何团队且不是任何团队的负责人", currentUserId);
            return Collections.emptyList();
        }

        List<Long> teamIds = new ArrayList<>(teamIdSet);

        // 3. 批量查询团队基础信息（复用已查询的数据）
        Map<Long, Team> teamMap = new HashMap<>();
        ownedTeams.forEach(team -> teamMap.put(team.getId(), team));
        // 补充查询作为成员但不是负责人的团队
        Set<Long> missingTeamIds = teamIds.stream()
            .filter(id -> !teamMap.containsKey(id))
            .collect(Collectors.toSet());
        if (!missingTeamIds.isEmpty()) {
            teamRepository.findAllById(missingTeamIds).forEach(team -> teamMap.put(team.getId(), team));
        }

        // 4. 批量查询团队负责人信息
        Set<Long> ownerIds = teamMap.values().stream()
            .map(Team::getOwnerId)
            .filter(id -> id != null)
            .collect(Collectors.toSet());
        Map<Long, User> ownerMap = !ownerIds.isEmpty()
            ? userRepository.findAllById(ownerIds).stream().collect(toMap(User::getId, u -> u))
            : Collections.emptyMap();

        // 5. 批量查询团队成员
        List<TeamMember> allMembers = teamMemberRepository.findByTeamIdInAndDeletedFalse(teamIds);
        Map<Long, List<TeamMember>> membersByTeam = allMembers.stream()
            .collect(Collectors.groupingBy(TeamMember::getTeamId));

        // 6. 批量查询成员用户信息
        Set<Long> memberUserIds = allMembers.stream()
            .map(TeamMember::getUserId)
            .collect(Collectors.toSet());
        Map<Long, User> memberUserMap = !memberUserIds.isEmpty()
            ? userRepository.findAllById(memberUserIds).stream().collect(toMap(User::getId, u -> u))
            : Collections.emptyMap();

        // 7. 批量查询所有团队的应用配置
        List<TeamApplication> allTeamApps = teamApplicationRepository.findByTeamIdIn(teamIds);

        // 8. 提取所有需要的ID集合
        Set<Long> allEnvIds = new HashSet<>();
        Set<Long> appIds = new HashSet<>();
        Set<Long> systemIds = new HashSet<>();
        Set<Long> workflowIds = new HashSet<>();
        List<Long> teamApplicationIds = new ArrayList<>();
        
        // 收集源和目标Git项目查询条件
        record GitProjectKey(Long systemId, Long repoProjectId) {}
        Set<GitProjectKey> gitProjectKeys = new HashSet<>();

        for (TeamApplication ta : allTeamApps) {
            teamApplicationIds.add(ta.getId());
            allEnvIds.add(ta.getEnvironmentId());
            appIds.add(ta.getApplicationId());
            if (ta.getDeploySystemId() != null) {
                systemIds.add(ta.getDeploySystemId());
            }
            if (ta.getWorkflowDefinitionId() != null) {
                workflowIds.add(ta.getWorkflowDefinitionId());
            }
            // 收集源Git系统和项目
            if (ta.getSourceGitSystemId() != null) {
                systemIds.add(ta.getSourceGitSystemId());
                if (ta.getSourceGitProjectId() != null) {
                    gitProjectKeys.add(new GitProjectKey(ta.getSourceGitSystemId(), ta.getSourceGitProjectId()));
                }
            }
            // 收集目标Git系统和项目
            if (ta.getTargetGitSystemId() != null) {
                systemIds.add(ta.getTargetGitSystemId());
                if (ta.getTargetGitProjectId() != null) {
                    gitProjectKeys.add(new GitProjectKey(ta.getTargetGitSystemId(), ta.getTargetGitProjectId()));
                }
            }
        }

        // 9. 应用配置按团队分组
        Map<Long, List<TeamApplication>> teamAppsMap = allTeamApps.stream()
            .collect(groupingBy(TeamApplication::getTeamId));

        // 10. 批量查询团队环境配置（获取团队配置的环境）
        List<TeamEnvironmentConfig> teamEnvConfigs = teamEnvironmentConfigRepository.findByTeamIdIn(teamIds);
        Map<String, TeamEnvironmentConfig> teamEnvConfigMap = teamEnvConfigs.stream()
            .collect(toMap(c -> c.getTeamId() + "_" + c.getEnvironmentId(), c -> c));

        // 从团队环境配置中提取环境ID
        Set<Long> teamConfiguredEnvIds = teamEnvConfigs.stream()
            .map(TeamEnvironmentConfig::getEnvironmentId)
            .collect(Collectors.toSet());

        // 合并应用配置中的环境ID和团队配置的环境ID
        allEnvIds.addAll(teamConfiguredEnvIds);

        // 批量查询环境信息
        Map<Long, Environment> envMap = environmentRepository.findAllById(allEnvIds).stream()
            .collect(toMap(Environment::getId, e -> e));

        // 11. 批量查询应用信息
        Map<Long, Application> appMap = !appIds.isEmpty()
            ? applicationRepository.findAllById(appIds).stream().collect(toMap(Application::getId, a -> a))
            : Collections.emptyMap();

        // 12. 批量查询部署系统（包含源/目标Git系统）
        Map<Long, ExternalSystem> systemMap = !systemIds.isEmpty()
            ? externalSystemRepository.findAllById(systemIds).stream().collect(toMap(ExternalSystem::getId, s -> s))
            : Collections.emptyMap();

        // 12.5. 批量查询Git项目信息（按 systemId + repoProjectId）
        Map<String, RepositoryProject> gitProjectMap = new HashMap<>();
        for (GitProjectKey key : gitProjectKeys) {
            repositoryProjectRepository.findByRepoProjectIdAndExternalSystemIdAndDeletedFalse(
                key.repoProjectId(), key.systemId()
            ).ifPresent(project -> 
                gitProjectMap.put(key.systemId() + "_" + key.repoProjectId(), project)
            );
        }

        // 13. 批量查询工作流定义
        Map<Long, WorkflowDefinition> workflowMap = !workflowIds.isEmpty()
            ? workflowDefinitionRepository.findAllById(workflowIds).stream().collect(toMap(WorkflowDefinition::getId, w -> w))
            : Collections.emptyMap();

        // 14. 批量查询通知配置
        Map<String, TeamEnvironmentNotificationConfig> notificationConfigMap = new HashMap<>();
        if (!teamIds.isEmpty() && !allEnvIds.isEmpty()) {
            List<TeamEnvironmentNotificationConfig> notificationConfigs =
                teamEnvironmentNotificationConfigRepository.findByTeamIdInAndEnvironmentIdIn(new HashSet<>(teamIds), allEnvIds);

            notificationConfigs.forEach(nc -> {
                String key = nc.getTeamId() + "_" + nc.getEnvironmentId();
                notificationConfigMap.put(key, nc);
            });
        }

        // 15. 批量查询通知渠道信息
        Set<Long> channelIds = notificationConfigMap.values().stream()
            .map(TeamEnvironmentNotificationConfig::getNotificationChannelId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Map<Long, NotificationChannel> channelMap = new HashMap<>();
        if (!channelIds.isEmpty()) {
            notificationChannelRepository.findAllById(channelIds).forEach(channel ->
                channelMap.put(channel.getId(), channel)
            );
        }

        // 16. 批量查询通知模板信息
        Set<Long> templateIds = notificationConfigMap.values().stream()
            .flatMap(config -> Stream.of(
                config.getPreApprovalNotificationTemplateId(),
                config.getBuildNotificationTemplateId()
            ))
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

        Map<Long, NotificationTemplate> templateMap = new HashMap<>();
        if (!templateIds.isEmpty()) {
            notificationTemplateRepository.findAllById(templateIds).forEach(template ->
                templateMap.put(template.getId(), template)
            );
        }

        // 17. 批量查询审批人信息
        Set<Long> approverUserIds = teamEnvConfigs.stream()
            .filter(c -> c.getApproverUserIds() != null)
            .flatMap(c -> c.getApproverUserIds().stream())
            .collect(Collectors.toSet());
        Map<Long, User> approverMap = !approverUserIds.isEmpty()
            ? userRepository.findAllById(approverUserIds).stream().collect(toMap(User::getId, u -> u))
            : Collections.emptyMap();

        // 15. 批量查询动态数据（部署统计、最近记录）
        Map<Long, DeployStatisticsDTO> statisticsMap = queryDeployStatistics(teamApplicationIds);
        Map<Long, DeployRecord> latestRecordMap = queryLatestRecords(teamApplicationIds);
        Map<Long, List<DeployRecordSummaryDTO>> recentRecordsMap = queryRecentRecords(teamApplicationIds);

        // 17. 为每个团队组装完整数据
        List<UserTeamDeployableDTO> result = new ArrayList<>();
        for (Long teamId : teamIds) {
            UserTeamDeployableDTO teamDTO = buildUserTeamDeployableDTO(
                currentUserId, teamId, teamMap, ownerMap, membersByTeam, memberUserMap,
                teamAppsMap, envMap, appMap, systemMap, gitProjectMap, workflowMap,
                teamEnvConfigMap, approverMap,
                notificationConfigMap, channelMap, templateMap,
                statisticsMap, latestRecordMap, recentRecordsMap
            );
            if (teamDTO != null) {
                result.add(teamDTO);
            }
        }

        log.debug("批量查询用户团队可部署数据完成: 团队数={}", result.size());
        return result;
    }

    /**
     * 构建单个团队的可部署数据DTO
     */
    private UserTeamDeployableDTO buildUserTeamDeployableDTO(
        Long currentUserId,
        Long teamId,
        Map<Long, Team> teamMap,
        Map<Long, User> ownerMap,
        Map<Long, List<TeamMember>> membersByTeam,
        Map<Long, User> memberUserMap,
        Map<Long, List<TeamApplication>> teamAppsMap,
        Map<Long, Environment> envMap,
        Map<Long, Application> appMap,
        Map<Long, ExternalSystem> systemMap,
        Map<String, RepositoryProject> gitProjectMap,
        Map<Long, WorkflowDefinition> workflowMap,
        Map<String, TeamEnvironmentConfig> teamEnvConfigMap,
        Map<Long, User> approverMap,
        Map<String, TeamEnvironmentNotificationConfig> notificationConfigMap,
        Map<Long, NotificationChannel> channelMap,
        Map<Long, NotificationTemplate> templateMap,
        Map<Long, DeployStatisticsDTO> statisticsMap,
        Map<Long, DeployRecord> latestRecordMap,
        Map<Long, List<DeployRecordSummaryDTO>> recentRecordsMap
    ) {
        Team team = teamMap.get(teamId);
        if (team == null) {
            return null;
        }

        UserTeamDeployableDTO dto = new UserTeamDeployableDTO();
        dto.setTeamId(team.getId());
        dto.setTeamCode(team.getTeamCode());
        dto.setTeamName(team.getTeamName());
        dto.setDescription(team.getDescription());

        // 设置团队负责人
        if (team.getOwnerId() != null) {
            dto.setOwnerId(team.getOwnerId());
            User owner = ownerMap.get(team.getOwnerId());
            if (owner != null) {
                dto.setOwnerName(owner.getNickname());
            }
        }

        // 设置团队成员
        List<TeamMember> members = membersByTeam.get(teamId);
        if (members != null && !members.isEmpty()) {
            List<UserDeployableTeamMemberDTO> memberDTOs = members.stream()
                .map(member -> {
                    User user = memberUserMap.get(member.getUserId());
                    if (user == null) {
                        return null;
                    }
                    UserDeployableTeamMemberDTO memberDTO = new UserDeployableTeamMemberDTO();
                    memberDTO.setUserId(user.getId());
                    memberDTO.setUsername(user.getUsername());
                    memberDTO.setRealName(user.getNickname());
                    memberDTO.setRoleInTeam(member.getRoleInTeam());
                    return memberDTO;
                })
                .filter(m -> m != null)
                .collect(Collectors.toList());
            dto.setMembers(memberDTOs);
        } else {
            dto.setMembers(Collections.emptyList());
        }

        // 构建环境列表
        List<TeamApplication> teamApps = teamAppsMap.get(teamId);
        List<UserDeployableTeamEnvironmentDTO> environments = new ArrayList<>();

        // 按环境分组应用（如果有应用的话）
        Map<Long, List<TeamApplication>> appsByEnv = (teamApps != null && !teamApps.isEmpty())
            ? teamApps.stream().collect(groupingBy(TeamApplication::getEnvironmentId))
            : Collections.emptyMap();

        // 获取当前团队配置的环境ID列表
        Set<Long> currentTeamEnvIds = teamEnvConfigMap.values().stream()
            .filter(config -> config.getTeamId().equals(teamId))
            .map(TeamEnvironmentConfig::getEnvironmentId)
            .collect(Collectors.toSet());

        // 只遍历当前团队配置的环境
        for (Long envId : currentTeamEnvIds) {
            Environment env = envMap.get(envId);
            if (env == null) {
                continue; // 环境不存在，跳过
            }
            
            // 获取该环境的应用列表（没有则为空列表）
            List<TeamApplication> envApps = appsByEnv.getOrDefault(env.getId(), Collections.emptyList());

            UserDeployableTeamEnvironmentDTO envDTO = buildUserDeployableTeamEnvironmentDTO(
                currentUserId, team.getOwnerId(),
                teamId, env, envApps,
                appMap, systemMap, gitProjectMap, workflowMap,
                teamEnvConfigMap, approverMap,
                notificationConfigMap, channelMap, templateMap,
                statisticsMap, latestRecordMap, recentRecordsMap
            );
            environments.add(envDTO);
        }

        // 按sort排序
        environments.sort(Comparator.comparingInt(e -> e.getSort() != null ? e.getSort() : Integer.MAX_VALUE));
        dto.setEnvironments(environments);

        return dto;
    }

    /**
     * 构建单个环境的可部署数据DTO
     */
    private UserDeployableTeamEnvironmentDTO buildUserDeployableTeamEnvironmentDTO(
        Long currentUserId,
        Long teamOwnerId,
        Long teamId,
        Environment env,
        List<TeamApplication> teamApps,
        Map<Long, Application> appMap,
        Map<Long, ExternalSystem> systemMap,
        Map<String, RepositoryProject> gitProjectMap,
        Map<Long, WorkflowDefinition> workflowMap,
        Map<String, TeamEnvironmentConfig> teamEnvConfigMap,
        Map<Long, User> approverMap,
        Map<String, TeamEnvironmentNotificationConfig> notificationConfigMap,
        Map<Long, NotificationChannel> channelMap,
        Map<Long, NotificationTemplate> templateMap,
        Map<Long, DeployStatisticsDTO> statisticsMap,
        Map<Long, DeployRecord> latestRecordMap,
        Map<Long, List<DeployRecordSummaryDTO>> recentRecordsMap
    ) {
        UserDeployableTeamEnvironmentDTO dto = new UserDeployableTeamEnvironmentDTO();
        dto.setEnvironmentId(env.getId());
        dto.setEnvironmentCode(env.getEnvCode());
        dto.setEnvironmentName(env.getEnvName());
        dto.setEnvironmentDesc(env.getEnvDesc());
        dto.setEnabled(env.getEnabled());
        dto.setSort(env.getSort());

        // 获取团队环境配置
        String configKey = teamId + "_" + env.getId();
        TeamEnvironmentConfig config = teamEnvConfigMap.get(configKey);
        if (config != null) {
            boolean requiresApproval = config.getApprovalRequired() != null ? config.getApprovalRequired() : false;

            dto.setRequiresApproval(requiresApproval);
            dto.setRequireCodeReview(config.getRequireCodeReview() != null ? config.getRequireCodeReview() : false);

            // 兜底逻辑：只有需要审批时才返回审批人列表
            if (requiresApproval && config.getApproverUserIds() != null && !config.getApproverUserIds().isEmpty()) {
                List<UserDeployableTeamEnvironmentApproverDTO> approvers = config.getApproverUserIds().stream()
                    .map(userId -> {
                        User user = approverMap.get(userId);
                        if (user == null) {
                            return null;
                        }
                        UserDeployableTeamEnvironmentApproverDTO approverDTO = new UserDeployableTeamEnvironmentApproverDTO();
                        approverDTO.setUserId(user.getId());
                        approverDTO.setUsername(user.getUsername());
                        approverDTO.setRealName(user.getNickname());
                        return approverDTO;
                    })
                    .filter(a -> a != null)
                    .collect(Collectors.toList());
                dto.setApprovers(approvers);
            } else {
                dto.setApprovers(Collections.emptyList());
            }

            // 构建通知配置 - 使用MapStruct转换器
            TeamEnvironmentNotificationConfig notificationConfig = notificationConfigMap.get(configKey);
            UserTeamEnvironmentNotificationConfigDTO notificationConfigDTO;
            if (notificationConfig != null) {
                notificationConfigDTO = notificationConfigConverter.toUserDTO(notificationConfig, channelMap, templateMap);
            } else {
                // 没有配置时返回默认值（开关字段为false）
                notificationConfigDTO = createDefaultNotificationConfigDTO();
            }
            dto.setNotificationConfig(notificationConfigDTO);
        } else {
            dto.setRequiresApproval(false);
            dto.setRequireCodeReview(false);
            dto.setApprovers(Collections.emptyList());
            // 没有配置时返回默认值（开关字段为false）
            dto.setNotificationConfig(createDefaultNotificationConfigDTO());
        }

        // 构建应用列表
        List<UserDeployableTeamEnvironmentApplicationDTO> applications = teamApps.stream()
            .map(teamApp -> buildUserDeployableTeamEnvironmentApplicationDTO(
                teamApp, appMap, systemMap, gitProjectMap, workflowMap,
                statisticsMap, latestRecordMap, recentRecordsMap
            ))
            .filter(app -> app != null)
            .collect(Collectors.toList());
        dto.setApplications(applications);

        // ==================== 设置当前用户权限 ====================

        // 1. 团队成员都可以发起部署
        dto.setCanDeploy(true);

        // 2. 判断是否可以审批（团队负责人 或 审批人列表中的任意一人）
        List<Long> approverUserIds = config != null && config.getApproverUserIds() != null
            ? config.getApproverUserIds()
            : Collections.emptyList();
        boolean canApprove = hasApprovalPermission(currentUserId, teamOwnerId, approverUserIds);
        dto.setCanApprove(canApprove);

        return dto;
    }

    /**
     * 构建单个应用的可部署数据DTO
     */
    private UserDeployableTeamEnvironmentApplicationDTO buildUserDeployableTeamEnvironmentApplicationDTO(
        TeamApplication teamApp,
        Map<Long, Application> appMap,
        Map<Long, ExternalSystem> systemMap,
        Map<String, RepositoryProject> gitProjectMap,
        Map<Long, WorkflowDefinition> workflowMap,
        Map<Long, DeployStatisticsDTO> statisticsMap,
        Map<Long, DeployRecord> latestRecordMap,
        Map<Long, List<DeployRecordSummaryDTO>> recentRecordsMap
    ) {
        Application app = appMap.get(teamApp.getApplicationId());
        if (app == null) {
            return null;
        }

        UserDeployableTeamEnvironmentApplicationDTO dto = new UserDeployableTeamEnvironmentApplicationDTO();
        dto.setTeamApplicationId(teamApp.getId());
        dto.setApplicationId(app.getId());
        dto.setApplicationCode(app.getAppCode());
        dto.setApplicationName(app.getAppName());
        dto.setApplicationDesc(app.getAppDesc());
        dto.setLanguage(app.getLanguage());
        dto.setBuildType(teamApp.getBuildType());

        // 设置源Git配置
        dto.setSourceGitSystemId(teamApp.getSourceGitSystemId());
        dto.setSourceGitProjectId(teamApp.getSourceGitProjectId());
        dto.setSourceBranch(teamApp.getSourceBranch());
        if (teamApp.getSourceGitSystemId() != null) {
            ExternalSystem sourceGitSystem = systemMap.get(teamApp.getSourceGitSystemId());
            if (sourceGitSystem != null) {
                dto.setSourceGitSystemName(sourceGitSystem.getName());
            }
            // 填充源Git项目名称
            if (teamApp.getSourceGitProjectId() != null) {
                String key = teamApp.getSourceGitSystemId() + "_" + teamApp.getSourceGitProjectId();
                RepositoryProject project = gitProjectMap.get(key);
                if (project != null) {
                    dto.setSourceGitProjectName(project.getName());
                }
            }
        }

        // 设置目标Git配置
        dto.setTargetGitSystemId(teamApp.getTargetGitSystemId());
        dto.setTargetGitProjectId(teamApp.getTargetGitProjectId());
        dto.setTargetBranch(teamApp.getTargetBranch());
        if (teamApp.getTargetGitSystemId() != null) {
            ExternalSystem targetGitSystem = systemMap.get(teamApp.getTargetGitSystemId());
            if (targetGitSystem != null) {
                dto.setTargetGitSystemName(targetGitSystem.getName());
            }
            // 填充目标Git项目名称
            if (teamApp.getTargetGitProjectId() != null) {
                String key = teamApp.getTargetGitSystemId() + "_" + teamApp.getTargetGitProjectId();
                RepositoryProject project = gitProjectMap.get(key);
                if (project != null) {
                    dto.setTargetGitProjectName(project.getName());
                }
            }
        }

        // 设置部署系统信息
        if (teamApp.getDeploySystemId() != null) {
            dto.setDeploySystemId(teamApp.getDeploySystemId());
            ExternalSystem system = systemMap.get(teamApp.getDeploySystemId());
            if (system != null) {
                dto.setDeploySystemName(system.getName());
            }
            dto.setDeployJob(teamApp.getDeployJob());
        }

        // 设置工作流定义信息
        if (teamApp.getWorkflowDefinitionId() != null) {
            dto.setWorkflowDefinitionId(teamApp.getWorkflowDefinitionId());
            WorkflowDefinition workflow = workflowMap.get(teamApp.getWorkflowDefinitionId());
            if (workflow != null) {
                dto.setWorkflowDefinitionName(workflow.getName());
                dto.setWorkflowDefinitionKey(workflow.getKey());
                if (workflow.getFormDefinitionId() != null) {
                    dto.setWorkflowDefinitionFormId(workflow.getFormDefinitionId());
                }
            }
        }

        // 设置部署统计信息（动态数据）
        Long teamAppId = teamApp.getId();
        DeployRecord latestRecord = latestRecordMap.get(teamAppId);
        DeployStatisticsDTO stats = statisticsMap.get(teamAppId);
        if (stats != null) {
            // 从最新记录中补充 lastDeployBy 和 latestStatus
            if (latestRecord != null) {
                stats.setLastDeployBy(latestRecord.getDeployBy());
                stats.setLatestStatus(latestRecord.getStatus());
            }
            dto.setDeployStatistics(stats);

            // 设置是否正在部署
            if (latestRecord != null) {
                DeployRecordStatusEnums status = latestRecord.getStatus();
                dto.setIsDeploying(status == DeployRecordStatusEnums.CREATED || status == DeployRecordStatusEnums.RUNNING);
            } else {
                dto.setIsDeploying(false);
            }
        } else {
            // 没有部署记录，设置默认值
            dto.setDeployStatistics(createEmptyStatistics());
            dto.setIsDeploying(false);
        }

        // 设置最近部署记录
        List<DeployRecordSummaryDTO> recentRecords = recentRecordsMap.get(teamAppId);
        dto.setRecentDeployRecords(recentRecords != null ? recentRecords : Collections.emptyList());

        return dto;
    }

    /**
     * 创建空的部署统计
     */
    private DeployStatisticsDTO createEmptyStatistics() {
        DeployStatisticsDTO statistics = new DeployStatisticsDTO();
        statistics.setTotalCount(0L);
        statistics.setSuccessCount(0L);
        statistics.setFailedCount(0L);
        statistics.setRunningCount(0L);
        return statistics;
    }

    /**
     * 批量查询部署统计
     */
    private Map<Long, DeployStatisticsDTO> queryDeployStatistics(List<Long> teamApplicationIds) {
        Map<Long, DeployStatisticsDTO> statisticsMap = new HashMap<>();
        List<Object[]> statisticsList = deployRecordRepository.findDeployStatisticsByTeamApplicationIds(teamApplicationIds);

        for (Object[] row : statisticsList) {
            Long teamApplicationId = (Long) row[0];
            Long totalCount = ((Number) row[1]).longValue();
            Long successCount = ((Number) row[2]).longValue();
            Long failedCount = ((Number) row[3]).longValue();
            Long runningCount = ((Number) row[4]).longValue();

            LocalDateTime lastDeployTime = null;
            if (row[5] != null) {
                if (row[5] instanceof Timestamp) {
                    lastDeployTime = ((Timestamp) row[5]).toLocalDateTime();
                } else if (row[5] instanceof LocalDateTime) {
                    lastDeployTime = (LocalDateTime) row[5];
                }
            }

            DeployStatisticsDTO stats = new DeployStatisticsDTO();
            stats.setTotalCount(totalCount);
            stats.setSuccessCount(successCount);
            stats.setFailedCount(failedCount);
            stats.setRunningCount(runningCount);
            stats.setLastDeployTime(lastDeployTime);
            statisticsMap.put(teamApplicationId, stats);
        }

        return statisticsMap;
    }

    /**
     * 批量查询最新部署记录
     */
    private Map<Long, DeployRecord> queryLatestRecords(List<Long> teamApplicationIds) {
        List<DeployRecord> latestRecords = deployRecordRepository.findLatestDeployRecordsByTeamApplicationIds(teamApplicationIds);
        Map<Long, DeployRecord> latestRecordMap = latestRecords.stream()
            .collect(toMap(DeployRecord::getTeamApplicationId, r -> r));

        // 更新统计信息中的最新状态和部署人
        latestRecordMap.forEach((teamAppId, record) -> {
            // 这里可以添加额外的处理逻辑
        });

        return latestRecordMap;
    }

    /**
     * 批量查询最近部署记录
     */
    private Map<Long, List<DeployRecordSummaryDTO>> queryRecentRecords(List<Long> teamApplicationIds) {
        List<DeployRecord> recentRecords = deployRecordRepository.findRecentDeployRecordsByTeamApplicationIds(teamApplicationIds, 10);

        return recentRecords.stream()
            .collect(groupingBy(
                DeployRecord::getTeamApplicationId,
                Collectors.mapping(
                    this::buildDeployRecordSummary,
                    Collectors.toList()
                )
            ));
    }


    /**
     * 构建部署记录摘要DTO
     */
    private DeployRecordSummaryDTO buildDeployRecordSummary(DeployRecord record) {
        DeployRecordSummaryDTO summary = new DeployRecordSummaryDTO();
        summary.setId(record.getId());
        summary.setStatus(record.getStatus());
        summary.setDeployBy(record.getDeployBy());
        summary.setStartTime(record.getStartTime());
        summary.setEndTime(record.getEndTime());
        summary.setDeployRemark(record.getDeployRemark());

        // 计算持续时间（毫秒）
        if (record.getStartTime() != null && record.getEndTime() != null) {
            // 已完成的记录：计算实际持续时间
            long duration = java.time.Duration.between(record.getStartTime(), record.getEndTime()).toMillis();
            summary.setDuration(duration);
        } else if (record.getStartTime() != null && isRunningStatus(record.getStatus())) {
            // 正在运行的记录：计算到目前为止的持续时间
            long duration = java.time.Duration.between(record.getStartTime(), LocalDateTime.now()).toMillis();
            summary.setDuration(duration);
        }
        // 等待审批或其他未开始的状态：不设置 duration（保持为 null）

        return summary;
    }

    /**
     * 判断是否为运行中状态
     */
    private boolean isRunningStatus(DeployRecordStatusEnums status) {
        return status == DeployRecordStatusEnums.RUNNING
            || status == DeployRecordStatusEnums.CREATED;
    }

    /**
     * 创建默认的通知配置DTO（开关字段默认为false）
     */
    private UserTeamEnvironmentNotificationConfigDTO createDefaultNotificationConfigDTO() {
        UserTeamEnvironmentNotificationConfigDTO dto = new UserTeamEnvironmentNotificationConfigDTO();
        dto.setPreApprovalNotificationEnabled(false);
        dto.setBuildNotificationEnabled(false);
        dto.setBuildFailureFileEnabled(false);
        return dto;
    }

    @Override
    @Transactional
    public DeployResultDTO executeDeploy(DeployExecuteRequest request) {
        log.info("开始执行部署: teamApplicationId={}, applicationCode={}, environmentCode={}, buildType={}", 
            request.getTeamApplicationId(), request.getApplicationCode(), request.getEnvironmentCode(), request.getBuildType());

        // 0. 根据构建类型校验Jenkins配置
        validateJenkinsConfig(request);

        // 1. 查询工作流定义
        TeamApplication teamApp = teamApplicationRepository.findById(request.getTeamApplicationId()).orElseThrow(() -> new BusinessException(ResponseCode.TEAM_APPLICATION_NOT_FOUND));

        WorkflowDefinition workflowDefinition = workflowDefinitionRepository.findById(teamApp.getWorkflowDefinitionId()).orElseThrow(() -> new BusinessException(ResponseCode.DEPLOY_WORKFLOW_NOT_CONFIGURED));

        // 2. 检查工作流是否已发布
        if (workflowDefinition.getStatus() != WorkflowDefinitionStatusEnums.PUBLISHED) {
            throw new BusinessException(ResponseCode.WORKFLOW_NOT_PUBLISHED);
        }

        // 2. 生成业务标识（使用前端传入的 executionNo）
        String businessKey = UUID.randomUUID().toString();

        log.debug("工作流变量: jenkins={}, approval={}, notification={}", request.getJenkins(), request.getApproval(), request.getNotification());

        // 4. 启动工作流
        WorkflowInstanceDTO workflowInstance = startWorkflow(workflowDefinition, businessKey, JsonUtils.toMap(request));

        // 5. 创建部署记录
        createDeployRecord(request, workflowInstance, businessKey);

        // 6. 返回结果
        return buildDeployResult(workflowInstance, businessKey, request);
    }

    /**
     * 启动工作流
     */
    private WorkflowInstanceDTO startWorkflow(WorkflowDefinition workflowDefinition, String businessKey, Map<String, Object> variables) {
        WorkflowInstanceStartRequest workflowRequest = new WorkflowInstanceStartRequest();
        workflowRequest.setProcessKey(workflowDefinition.getKey());
        workflowRequest.setBusinessKey(businessKey);
        workflowRequest.setVariables(variables);
        WorkflowInstanceDTO workflowInstance = workflowInstanceService.startWorkflow(workflowRequest);
        log.info("部署流程已启动: businessKey={}, processInstanceId={}", businessKey, workflowInstance.getProcessInstanceId());
        return workflowInstance;
    }

    /**
     * 创建部署记录
     */
    private void createDeployRecord(DeployExecuteRequest request, WorkflowInstanceDTO workflowInstance, String businessKey) {
        deployRecordService.createDeployRecord(workflowInstance.getId(), businessKey, request.getTeamApplicationId(), request.getTeamId(), request.getApplicationId(), request.getEnvironmentId(), request.getDeployUser(), request.getDeployRemark());
        log.info("部署记录已创建: businessKey={}, workflowInstanceId={}", businessKey, workflowInstance.getId());
    }

    /**
     * 构建部署结果DTO
     */
    private DeployResultDTO buildDeployResult(WorkflowInstanceDTO workflowInstance, String businessKey, DeployExecuteRequest request) {
        DeployResultDTO result = new DeployResultDTO();
        result.setWorkflowInstanceId(workflowInstance.getId());
        result.setBusinessKey(businessKey);
        result.setProcessInstanceId(workflowInstance.getProcessInstanceId());
        result.setStatus(workflowInstance.getStatus().name());
        log.info("部署启动成功: businessKey={}, application={}, environment={}", businessKey, request.getApplicationCode(), request.getEnvironmentCode());
        return result;
    }

    /**
     * 根据构建类型校验Jenkins配置
     * <p>当buildType为JENKINS时，jenkins配置必填
     */
    private void validateJenkinsConfig(DeployExecuteRequest request) {
        if (request.getBuildType() == BuildTypeEnum.JENKINS) {
            if (request.getJenkins() == null) {
                throw new BusinessException(ResponseCode.DEPLOY_JENKINS_CONFIG_MISSING);
            }
            if (request.getJenkins().getServerId() == null) {
                throw new BusinessException(ResponseCode.INVALID_PARAM, new Object[]{"Jenkins服务器ID不能为空"});
            }
            if (request.getJenkins().getJobName() == null || request.getJenkins().getJobName().isBlank()) {
                throw new BusinessException(ResponseCode.INVALID_PARAM, new Object[]{"Jenkins任务名称不能为空"});
            }
        }
    }

    @Override
    public List<DeployApprovalTaskDTO> getMyApprovalTasks(Long teamId, Long environmentId, List<String> workflowDefinitionKeys) {
        // 1. 获取当前登录用户
        String currentUsername = SecurityUtils.getCurrentUsername();
        log.info("查询用户 {} 的部署审批任务, teamId={}, environmentId={}, workflowDefinitionKeys={}",
            currentUsername, teamId, environmentId, workflowDefinitionKeys);

        // 2. 查询用户的部署工作流待办任务（支持多个工作流）
        TaskQuery taskQuery = taskService.createTaskQuery()
            .taskCandidateOrAssigned(currentUsername)
            .orderByTaskCreateTime()
            .desc();

        // 如果指定了工作流定义Key列表，则精确筛选这些工作流的所有待审批任务
        if (workflowDefinitionKeys != null && !workflowDefinitionKeys.isEmpty()) {
            // 过滤掉空字符串
            List<String> validKeys = workflowDefinitionKeys.stream()
                .filter(key -> key != null && !key.trim().isEmpty())
                .collect(Collectors.toList());

            if (!validKeys.isEmpty()) {
                taskQuery.processDefinitionKeyIn(validKeys);
                log.debug("按工作流定义Key列表筛选: {}", validKeys);
            }
        }

        List<Task> tasks = taskQuery.list();

        if (tasks.isEmpty()) {
            log.debug("用户 {} 当前没有待办审批任务", currentUsername);
            return Collections.emptyList();
        }

        // 3. 过滤出部署相关的任务并转换为DTO
        List<DeployApprovalTaskDTO> result = new ArrayList<>();
        for (Task task : tasks) {
            try {
                DeployApprovalTaskDTO dto = convertToDeployApprovalDTO(task);
                if (dto != null) {
                    result.add(dto);
                }
            } catch (Exception e) {
                log.warn("转换审批任务失败: taskId={}, error={}", task.getId(), e.getMessage());
            }
        }

        // 4. 批量查询团队信息和用户信息（解决 N+1 查询问题）
        enrichTeamInfo(result);
        enrichDeployUserInfo(result);

        // 5. 后置筛选：按团队和环境过滤
        if (teamId != null) {
            result = result.stream()
                .filter(task -> task.getTeam() != null && teamId.equals(task.getTeam().getId()))
                .collect(Collectors.toList());
            log.debug("按团队ID筛选后剩余 {} 个任务", result.size());
        }

        if (environmentId != null) {
            result = result.stream()
                .filter(task -> task.getEnvironment() != null && environmentId.equals(task.getEnvironment().getId()))
                .collect(Collectors.toList());
            log.debug("按环境ID筛选后剩余 {} 个任务", result.size());
        }

        log.info("用户 {} 共有 {} 个部署审批任务（筛选后）", currentUsername, result.size());
        return result;
    }

    /**
     * 转换 Flowable Task 为部署审批 DTO
     * <p>模仿 ApprovalTaskService.convertToDTO 方法
     *
     * @param task Flowable 任务
     * @return 部署审批DTO，如果不是部署任务则返回 null
     */
    private DeployApprovalTaskDTO convertToDeployApprovalDTO(Task task) {
        // 1. 获取流程变量
        Map<String, Object> variables = taskService.getVariables(task.getId());
        if (variables == null || variables.isEmpty()) {
            log.debug("任务 {} 没有流程变量，跳过", task.getId());
            return null;
        }

        // 3. 获取 BusinessKey（需要通过 RuntimeService 查询）
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();

        if (processInstance == null) {
            log.debug("流程实例 {} 已结束，跳过", task.getProcessInstanceId());
            return null;
        }

        String businessKey = processInstance.getBusinessKey();
        if (businessKey == null) {
            log.debug("任务 {} 的流程实例没有 businessKey，跳过", task.getId());
            return null;
        }

        // 4. 查询部署记录
        DeployRecord deployRecord = deployRecordRepository.findByBusinessKeyAndDeletedFalse(businessKey)
            .orElseThrow(() -> new BusinessException(ResponseCode.DEPLOY_RECORD_NOT_FOUND,
                new Object[] {businessKey}));

        // 5. 构建 DTO
        DeployApprovalTaskDTO dto = new DeployApprovalTaskDTO();

        // 5.1 构建审批任务信息
        DeployApprovalTaskDTO.ApprovalTaskInfo approvalTask = new DeployApprovalTaskDTO.ApprovalTaskInfo();
        approvalTask.setTaskId(task.getId());
        approvalTask.setTaskName(task.getName());
        approvalTask.setTaskDescription(task.getDescription());
        approvalTask.setProcessInstanceId(task.getProcessInstanceId());
        approvalTask.setProcessDefinitionId(task.getProcessDefinitionId());
        approvalTask.setAssignee(task.getAssignee());

        if (task.getCreateTime() != null) {
            LocalDateTime createTime = LocalDateTime.ofInstant(
                task.getCreateTime().toInstant(),
                ZoneId.systemDefault());
            approvalTask.setCreateTime(createTime);
            
            // 计算待审批时长
            long pendingMillis = Duration.between(createTime, LocalDateTime.now()).toMillis();
            approvalTask.setPendingDuration(pendingMillis);
        }

        if (task.getDueDate() != null) {
            approvalTask.setDueDate(LocalDateTime.ofInstant(
                task.getDueDate().toInstant(),
                ZoneId.systemDefault()));
        }

        // 5.2 填充审批配置信息（从审批节点的 NodeContext 中获取 ApprovalInputMapping）
        String nodeId = task.getTaskDefinitionKey();
        ApprovalInputMapping approvalInputs = extractApprovalInputMapping(variables, nodeId);
        if (approvalInputs != null) {
            approvalTask.setApprovalTitle(approvalInputs.getApprovalTitle());
            approvalTask.setApprovalContent(approvalInputs.getApprovalContent());
            approvalTask.setApprovalMode(approvalInputs.getApprovalMode() != null
                ? approvalInputs.getApprovalMode().name()
                : null);
            approvalTask.setAllowDelegate(approvalInputs.getAllowDelegate());
            approvalTask.setAllowAddSign(approvalInputs.getAllowAddSign());
            approvalTask.setRequireComment(approvalInputs.getRequireComment());
        }
        dto.setApprovalTask(approvalTask);

        // 5.3 构建部署记录信息
        DeployApprovalTaskDTO.DeployRecordInfo deployRecordInfo = new DeployApprovalTaskDTO.DeployRecordInfo();
        deployRecordInfo.setId(deployRecord.getId());
        deployRecordInfo.setBusinessKey(businessKey);
        deployRecordInfo.setRemark(deployRecord.getDeployRemark());
        deployRecordInfo.setStartTime(deployRecord.getStartTime());
        dto.setDeployRecord(deployRecordInfo);

        // 5.4 构建关联实体信息（从 variables 还原 DeployExecuteRequest）
        DeployExecuteRequest deployRequest = JsonUtils.fromMap(variables, DeployExecuteRequest.class);
        
        // 团队信息
        DeployApprovalTaskDTO.TeamInfo team = new DeployApprovalTaskDTO.TeamInfo();
        team.setId(deployRequest.getTeamId());
        // 团队名称通过批量查询填充（见 enrichTeamInfo 方法）
        dto.setTeam(team);
        
        // 应用信息
        DeployApprovalTaskDTO.ApplicationInfo application = new DeployApprovalTaskDTO.ApplicationInfo();
        application.setId(deployRequest.getApplicationId());
        application.setCode(deployRequest.getApplicationCode());
        application.setName(deployRequest.getApplicationName());
        dto.setApplication(application);
        
        // 环境信息
        DeployApprovalTaskDTO.EnvironmentInfo environment = new DeployApprovalTaskDTO.EnvironmentInfo();
        environment.setId(deployRequest.getEnvironmentId());
        environment.setCode(deployRequest.getEnvironmentCode());
        environment.setName(deployRequest.getEnvironmentName());
        dto.setEnvironment(environment);

        // 5.5 自动解析 EL 表达式（如 ${deploy.applicationName}）
        SpelExpressionResolver.resolveObject(dto, variables);

        return dto;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeApproval(DeployApprovalCompleteRequest request) {
        String taskId = request.getTaskId();
        log.info("开始处理部署审批: taskId={}, result={}", taskId, request.getResult());

        try {
            // 1. 获取当前用户
            String currentUsername = SecurityUtils.getCurrentUsername();

            // 2. 验证任务是否存在 && 当前用户是否有权限审批
            // taskCandidateOrAssigned 支持所有审批模式：
            //   - SINGLE 模式: 检查 assignee
            //   - ANY/ALL 模式: 检查 candidateUsers
            Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .taskCandidateOrAssigned(currentUsername)
                .singleResult();

            if (task == null) {
                log.error("审批任务不存在或无权审批: taskId={}, 当前用户={}", taskId, currentUsername);
                throw new BusinessException(ResponseCode.PERMISSION_DENIED);
            }

            log.debug("审批人: {}, 任务负责人: {}", currentUsername, task.getAssignee());

            // 3. 获取节点ID并更新 NodeContext（与 ApprovalTaskService 保持一致）
            String nodeId = task.getTaskDefinitionKey();

            // 4. 读取现有 NodeContext
            Object nodeDataObj = taskService.getVariable(task.getId(), nodeId);
            NodeContext<ApprovalInputMapping, ApprovalOutputs> nodeContext;
            if (nodeDataObj instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> nodeDataMap = (Map<String, Object>) nodeDataObj;
                nodeContext = NodeContext.fromMap(nodeDataMap,
                    ApprovalInputMapping.class, ApprovalOutputs.class, objectMapper);
            } else {
                nodeContext = new NodeContext<>();
            }

            // 5. 创建审批结果（只设置核心字段，其他由 ApprovalEndExecutionListener 自动装配）
            ApprovalOutputs outputs = new ApprovalOutputs();
            outputs.setApprovalResult(request.getResult());
            outputs.setApprover(currentUsername);
            outputs.setApprovalTime(LocalDateTime.now());
            outputs.setApprovalComment(request.getComment());
            // ✅ 不需要手动设置 status 和 approvalDuration，监听器会自动装配

            // 6. 设置到 NodeContext
            nodeContext.setOutputs(outputs);

            // 7. 保存回流程变量
            taskService.setVariable(task.getId(), nodeId, nodeContext.toMap(objectMapper));

            // 8. 添加任务评论（供历史查询）
            if (request.getComment() != null) {
                taskService.addComment(taskId, task.getProcessInstanceId(), request.getComment());
            }

            // 9. 完成任务（触发 ApprovalEndExecutionListener，监听器会自动装配其他字段）
            taskService.complete(taskId);

            log.info("部署审批已提交，后续节点将异步执行: taskId={}, result={}, comment={}",
                taskId, request.getResult(), request.getComment());

        } catch (BusinessException e) {
            log.error("审批失败: taskId={}, error={}", taskId, e.getMessage());
            throw e;
        } catch (Exception e) {
            log.error("审批处理异常: taskId={}", taskId, e);
            throw new BusinessException(ResponseCode.ERROR);
        }
    }

    // ==================== 工具方法 ====================

    /**
     * 批量查询并填充团队信息（解决 N+1 查询问题）
     *
     * @param dtoList 待填充的 DTO 列表
     */
    /**
     * 批量查询并填充团队信息
     *
     * @param dtoList 待填充的 DTO 列表
     */
    private void enrichTeamInfo(List<DeployApprovalTaskDTO> dtoList) {
        if (dtoList == null || dtoList.isEmpty()) {
            return;
        }

        // 1. 收集所有 teamIds
        Set<Long> teamIds = dtoList.stream()
            .map(DeployApprovalTaskDTO::getTeam)
            .filter(Objects::nonNull)
            .map(DeployApprovalTaskDTO.TeamInfo::getId)
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

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

        // 2. 批量查询团队信息
        Map<Long, Team> teamMap = teamRepository.findAllById(teamIds).stream()
            .collect(Collectors.toMap(Team::getId, team -> team));

        // 3. 填充团队名称
        for (DeployApprovalTaskDTO dto : dtoList) {
            if (dto.getTeam() != null && dto.getTeam().getId() != null) {
                Team team = teamMap.get(dto.getTeam().getId());
                if (team != null) {
                    dto.getTeam().setName(team.getTeamName());
                }
            }
        }
    }

    /**
     * 批量查询并填充部署发起人信息
     *
     * @param dtoList 待填充的 DTO 列表
     */
    private void enrichDeployUserInfo(List<DeployApprovalTaskDTO> dtoList) {
        if (dtoList == null || dtoList.isEmpty()) {
            return;
        }

        // 1. 收集所有 deployBy 用户名
        Set<String> usernames = dtoList.stream()
            .map(DeployApprovalTaskDTO::getDeployRecord)
            .filter(Objects::nonNull)
            .map(record -> {
                // 从原始 DeployRecord 获取 deployBy（需要通过 deployRecordId 查询）
                if (record.getId() != null) {
                    return deployRecordRepository.findById(record.getId())
                        .map(DeployRecord::getDeployBy)
                        .orElse(null);
                }
                return null;
            })
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());

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

        // 2. 批量查询用户信息
        List<User> users = userRepository.findByUsernameInAndDeletedFalse(usernames);
        Map<String, User> userMap = users.stream()
            .collect(Collectors.toMap(User::getUsername, u -> u));

        // 3. 填充用户信息
        for (DeployApprovalTaskDTO dto : dtoList) {
            if (dto.getDeployRecord() != null && dto.getDeployRecord().getId() != null) {
                deployRecordRepository.findById(dto.getDeployRecord().getId())
                    .ifPresent(deployRecord -> {
                        String username = deployRecord.getDeployBy();
                        if (username != null) {
                            User user = userMap.get(username);
                            if (user != null) {
                                DeployApprovalTaskDTO.UserSimpleInfo userInfo = new DeployApprovalTaskDTO.UserSimpleInfo();
                                userInfo.setUsername(user.getUsername());
                                userInfo.setNickname(user.getNickname());
                                userInfo.setEmail(user.getEmail());
                                userInfo.setPhone(user.getPhone());
                                if (user.getDepartment() != null) {
                                    userInfo.setDepartmentName(user.getDepartment().getName());
                                }
                                dto.setDeployUser(userInfo);
                            }
                        }
                    });
            }
        }
    }

    /**
     * 从流程变量中提取审批节点的 ApprovalInputMapping
     * <p>从 NodeContext 的 inputs 字段中解析审批配置
     *
     * @param variables 流程变量 Map
     * @param nodeId    审批节点ID
     * @return ApprovalInputMapping 对象，解析失败返回 null
     */
    private ApprovalInputMapping extractApprovalInputMapping(Map<String, Object> variables, String nodeId) {
        try {
            // 1. 从 variables 中获取节点数据
            Object nodeData = variables.get(nodeId);
            if (nodeData == null) {
                log.warn("流程变量中没有找到节点 {} 的数据", nodeId);
                return null;
            }

            // 2. 解析为 NodeContext
            if (nodeData instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> nodeDataMap = (Map<String, Object>) nodeData;
                NodeContext<ApprovalInputMapping, ApprovalOutputs> nodeContext =
                    NodeContext.fromMap(nodeDataMap, ApprovalInputMapping.class, ApprovalOutputs.class, objectMapper);

                // 3. 返回 inputMapping
                return nodeContext.getInputMapping();
            }

            log.warn("节点 {} 的数据格式不正确，无法解析为 NodeContext", nodeId);
            return null;
        } catch (Exception e) {
            log.error("提取审批节点 {} 的 ApprovalInputMapping 失败", nodeId, e);
            return null;
        }
    }

    /**
     * 判断用户是否有审批权限
     * <p>权限规则：团队负责人 或 审批人列表中的任意一人
     *
     * @param currentUserId   当前用户ID
     * @param teamOwnerId     团队负责人ID
     * @param approverUserIds 审批人ID列表
     * @return true-有权限, false-无权限
     */
    private boolean hasApprovalPermission(Long currentUserId, Long teamOwnerId, List<Long> approverUserIds) {
        if (currentUserId == null) {
            return false;
        }

        // 1. 是团队负责人
        if (currentUserId.equals(teamOwnerId)) {
            return true;
        }

        // 2. 在审批人列表中
        if (approverUserIds != null && !approverUserIds.isEmpty()) {
            return approverUserIds.contains(currentUserId);
        }

        return false;
    }

    @Override
    public DeployNodeLogDTO getNodeLogs(String processInstanceId, String nodeId) {
        DeployNodeLogDTO result = new DeployNodeLogDTO();
        result.setProcessInstanceId(processInstanceId);
        result.setNodeId(nodeId);

        // 查询日志
        List<WorkflowNodeLogDTO> logs = workflowNodeLogService.getNodeLogs(processInstanceId, nodeId);

        if (logs.isEmpty()) {
            // 判断是过期还是还没有日志
            result.setExpired(true);
            result.setMessage("日志已过期（超过7天）或不存在");
            result.setLogs(Collections.emptyList());
        } else {
            result.setExpired(false);
            result.setMessage("查询成功");

            // 转换为 DTO
            List<DeployNodeLogDTO.LogEntry> logEntries = logs.stream()
                .map(log -> {
                    DeployNodeLogDTO.LogEntry entry = new DeployNodeLogDTO.LogEntry();
                    entry.setSequenceId(log.getSequenceId());
                    entry.setTimestamp(log.getTimestamp());
                    entry.setLevel(log.getLevel().name());
                    entry.setSource(log.getSource().name());
                    entry.setMessage(log.getMessage());
                    return entry;
                })
                .collect(Collectors.toList());

            result.setLogs(logEntries);
        }

        return result;
    }
}

