From 62dec088fd82dcd53d08eac234a4dc38dba5a4cb Mon Sep 17 00:00:00 2001 From: dengqichen Date: Sun, 2 Nov 2025 13:47:09 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9B=A2=E9=98=9Fapp=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BB=91=E5=AE=9A=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deploy/api/DeployApiController.java | 40 ++ .../deploy/api/TeamMemberApiController.java | 17 +- .../backend/deploy/dto/ApproverDTO.java | 25 ++ .../deploy/dto/DeployableApplicationDTO.java | 52 +++ .../deploy/dto/DeployableEnvironmentDTO.java | 45 ++ .../backend/deploy/dto/TeamDeployableDTO.java | 36 ++ .../backend/deploy/dto/UserDeployableDTO.java | 30 ++ .../ITeamApplicationRepository.java | 8 + .../repository/ITeamConfigRepository.java | 10 + .../repository/ITeamMemberRepository.java | 6 + .../deploy/service/IDeployService.java | 20 + .../service/impl/DeployServiceImpl.java | 392 ++++++++++++++++++ 12 files changed, 674 insertions(+), 7 deletions(-) create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/api/DeployApiController.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/ApproverDTO.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableApplicationDTO.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableEnvironmentDTO.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDeployableDTO.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/UserDeployableDTO.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/service/IDeployService.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/DeployApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/DeployApiController.java new file mode 100644 index 00000000..2f4d0a53 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/DeployApiController.java @@ -0,0 +1,40 @@ +package com.qqchen.deploy.backend.deploy.api; + +import com.qqchen.deploy.backend.deploy.dto.UserDeployableDTO; +import com.qqchen.deploy.backend.deploy.service.IDeployService; +import com.qqchen.deploy.backend.framework.api.Response; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 部署管理API控制器 + * + * @author qqchen + * @since 2025-11-02 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/deploy") +@Tag(name = "部署管理", description = "部署相关接口") +public class DeployApiController { + + @Resource + private IDeployService deployService; + + /** + * 获取当前用户可部署的环境和应用 + */ + @Operation(summary = "获取可部署的环境", description = "获取当前登录用户在各团队中可部署的环境和应用列表") + @GetMapping("/environments") + @PreAuthorize("isAuthenticated()") + public Response getDeployableEnvironments() { + return Response.success(deployService.getDeployableEnvironments()); + } +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamMemberApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamMemberApiController.java index c36fd0e9..77a0f546 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamMemberApiController.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamMemberApiController.java @@ -11,6 +11,9 @@ import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -30,23 +33,23 @@ public class TeamMemberApiController extends BaseController create(TeamMemberDTO dto) { + public Response create(@Validated @RequestBody TeamMemberDTO dto) { return super.create(dto); } @Override - public Response update(Long aLong, TeamMemberDTO dto) { - return super.update(aLong, dto); + public Response update(@PathVariable Long id, @Validated @RequestBody TeamMemberDTO dto) { + return super.update(id, dto); } @Override - public Response delete(Long aLong) { - return super.delete(aLong); + public Response delete(@PathVariable Long id) { + return super.delete(id); } @Override - public Response findById(Long aLong) { - return super.findById(aLong); + public Response findById(@PathVariable Long id) { + return super.findById(id); } @Override diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/ApproverDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/ApproverDTO.java new file mode 100644 index 00000000..5279132f --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/ApproverDTO.java @@ -0,0 +1,25 @@ +package com.qqchen.deploy.backend.deploy.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 审批人DTO + * + * @author qqchen + * @since 2025-11-02 + */ +@Data +@Schema(description = "审批人信息") +public class ApproverDTO { + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "用户名") + private String username; + + @Schema(description = "真实姓名") + private String realName; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableApplicationDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableApplicationDTO.java new file mode 100644 index 00000000..eeddcf27 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableApplicationDTO.java @@ -0,0 +1,52 @@ +package com.qqchen.deploy.backend.deploy.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * 可部署应用DTO + * + * @author qqchen + * @since 2025-11-02 + */ +@Data +@Schema(description = "可部署应用信息") +public class DeployableApplicationDTO { + + @Schema(description = "团队应用关联ID") + private Long teamApplicationId; + + @Schema(description = "应用ID") + private Long applicationId; + + @Schema(description = "应用编码") + private String applicationCode; + + @Schema(description = "应用名称") + private String applicationName; + + @Schema(description = "应用描述") + private String applicationDesc; + + @Schema(description = "分支名称") + private String branch; + + @Schema(description = "部署系统ID(Jenkins系统)") + private Long deploySystemId; + + @Schema(description = "部署系统名称") + private String deploySystemName; + + @Schema(description = "部署任务ID(Jenkins Job)") + private String deployJob; + + @Schema(description = "工作流定义ID") + private Long workflowDefinitionId; + + @Schema(description = "工作流定义名称") + private String workflowDefinitionName; + + @Schema(description = "工作流流程标识(processKey)") + private String workflowDefinitionKey; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableEnvironmentDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableEnvironmentDTO.java new file mode 100644 index 00000000..005f0f86 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployableEnvironmentDTO.java @@ -0,0 +1,45 @@ +package com.qqchen.deploy.backend.deploy.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 可部署环境DTO + * + * @author qqchen + * @since 2025-11-02 + */ +@Data +@Schema(description = "可部署环境信息") +public class DeployableEnvironmentDTO { + + @Schema(description = "环境ID") + private Long environmentId; + + @Schema(description = "环境编码") + private String environmentCode; + + @Schema(description = "环境名称") + private String environmentName; + + @Schema(description = "环境描述") + private String environmentDesc; + + @Schema(description = "是否启用") + private Boolean enabled; + + @Schema(description = "排序号") + private Integer sort; + + @Schema(description = "是否需要审批") + private Boolean requiresApproval; + + @Schema(description = "审批人列表") + private List approvers; + + @Schema(description = "可部署应用列表") + private List applications; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDeployableDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDeployableDTO.java new file mode 100644 index 00000000..2d2d1cf3 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDeployableDTO.java @@ -0,0 +1,36 @@ +package com.qqchen.deploy.backend.deploy.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 团队可部署环境DTO + * + * @author qqchen + * @since 2025-11-02 + */ +@Data +@Schema(description = "团队可部署环境信息") +public class TeamDeployableDTO { + + @Schema(description = "团队ID") + private Long teamId; + + @Schema(description = "团队编码") + private String teamCode; + + @Schema(description = "团队名称") + private String teamName; + + @Schema(description = "用户在团队中的角色") + private String teamRole; + + @Schema(description = "团队描述") + private String description; + + @Schema(description = "可部署环境列表") + private List environments; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/UserDeployableDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/UserDeployableDTO.java new file mode 100644 index 00000000..bc1dae55 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/UserDeployableDTO.java @@ -0,0 +1,30 @@ +package com.qqchen.deploy.backend.deploy.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 用户可部署环境DTO + * + * @author qqchen + * @since 2025-11-02 + */ +@Data +@Schema(description = "用户可部署环境信息") +public class UserDeployableDTO { + + @Schema(description = "用户ID") + private Long userId; + + @Schema(description = "用户名") + private String username; + + @Schema(description = "真实姓名") + private String realName; + + @Schema(description = "用户所属团队列表") + private List teams; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java index a38ca38a..88c8a74b 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java @@ -81,5 +81,13 @@ public interface ITeamApplicationRepository extends IBaseRepository countDistinctTeamIdByApplicationIds(@Param("applicationIds") Collection applicationIds); + + /** + * 批量根据团队ID查询应用配置 + * + * @param teamIds 团队ID集合 + * @return 团队应用配置列表 + */ + List findByTeamIdIn(Collection teamIds); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java index 549dd4fd..1684e610 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java @@ -4,6 +4,8 @@ import com.qqchen.deploy.backend.deploy.entity.TeamConfig; import com.qqchen.deploy.backend.framework.repository.IBaseRepository; import org.springframework.stereotype.Repository; +import java.util.Collection; +import java.util.List; import java.util.Optional; /** @@ -30,5 +32,13 @@ public interface ITeamConfigRepository extends IBaseRepository * @return 是否存在 */ boolean existsByTeamIdAndDeletedFalse(Long teamId); + + /** + * 批量根据团队ID查询配置 + * + * @param teamIds 团队ID集合 + * @return 团队配置列表 + */ + List findByTeamIdIn(Collection teamIds); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamMemberRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamMemberRepository.java index 91d513b3..c0d242a0 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamMemberRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamMemberRepository.java @@ -4,6 +4,7 @@ import com.qqchen.deploy.backend.deploy.entity.TeamMember; import com.qqchen.deploy.backend.framework.repository.IBaseRepository; import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; @Repository @@ -23,5 +24,10 @@ public interface ITeamMemberRepository extends IBaseRepository * 根据团队ID和用户ID查询 */ Optional findByTeamIdAndUserIdAndDeletedFalse(Long teamId, Long userId); + + /** + * 根据用户ID查询所有团队成员记录 + */ + List findByUserIdAndDeletedFalse(Long userId); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/IDeployService.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/IDeployService.java new file mode 100644 index 00000000..a699359d --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/IDeployService.java @@ -0,0 +1,20 @@ +package com.qqchen.deploy.backend.deploy.service; + +import com.qqchen.deploy.backend.deploy.dto.UserDeployableDTO; + +/** + * 部署服务接口 + * + * @author qqchen + * @since 2025-11-02 + */ +public interface IDeployService { + + /** + * 获取当前用户可部署的环境和应用 + * + * @return 用户可部署环境信息 + */ + UserDeployableDTO getDeployableEnvironments(); +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java new file mode 100644 index 00000000..ec41d338 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/DeployServiceImpl.java @@ -0,0 +1,392 @@ +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.repository.*; +import com.qqchen.deploy.backend.deploy.service.IDeployService; +import com.qqchen.deploy.backend.framework.security.SecurityUtils; +import com.qqchen.deploy.backend.system.entity.User; +import com.qqchen.deploy.backend.system.repository.IUserRepository; +import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition; +import com.qqchen.deploy.backend.workflow.repository.IWorkflowDefinitionRepository; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +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 ITeamConfigRepository teamConfigRepository; + + @Resource + private IEnvironmentRepository environmentRepository; + + @Resource + private ITeamApplicationRepository teamApplicationRepository; + + @Resource + private IApplicationRepository applicationRepository; + + @Resource + private IExternalSystemRepository externalSystemRepository; + + @Resource + private IWorkflowDefinitionRepository workflowDefinitionRepository; + + @Override + @Transactional(readOnly = true) + public UserDeployableDTO getDeployableEnvironments() { + // 1. 获取当前登录用户 + Long currentUserId = SecurityUtils.getCurrentUserId(); + User user = userRepository.findById(currentUserId) + .orElseThrow(() -> new RuntimeException("用户不存在")); + + // 2. 查询用户所属的团队 + List teamMembers = teamMemberRepository.findByUserIdAndDeletedFalse(currentUserId); + if (teamMembers.isEmpty()) { + log.info("用户 {} 未加入任何团队", user.getUsername()); + return buildEmptyResult(user); + } + + List teamIds = teamMembers.stream() + .map(TeamMember::getTeamId) + .collect(toList()); + + // 3. 批量查询团队信息 + Map teamMap = teamRepository.findAllById(teamIds) + .stream().collect(toMap(Team::getId, t -> t)); + + // 4. 批量查询团队配置 + Map configMap = teamConfigRepository.findByTeamIdIn(teamIds) + .stream().collect(toMap(TeamConfig::getTeamId, c -> c)); + + // 5. 收集所有环境ID + Set allEnvIds = configMap.values().stream() + .filter(c -> c.getAllowedEnvironmentIds() != null) + .flatMap(c -> c.getAllowedEnvironmentIds().stream()) + .collect(Collectors.toSet()); + + if (allEnvIds.isEmpty()) { + log.info("用户 {} 所属团队未配置任何环境", user.getUsername()); + return buildEmptyResult(user); + } + + // 6. 批量查询环境信息 + Map envMap = environmentRepository.findAllById(allEnvIds) + .stream().collect(toMap(Environment::getId, e -> e)); + + // 7. 批量查询所有团队的应用配置 + List allTeamApps = teamApplicationRepository.findByTeamIdIn(teamIds); + Map> teamAppsMap = allTeamApps.stream() + .collect(groupingBy(TeamApplication::getTeamId)); + + // 8. 批量查询应用信息 + Set appIds = allTeamApps.stream() + .map(TeamApplication::getApplicationId) + .collect(Collectors.toSet()); + final Map appMap; + if (!appIds.isEmpty()) { + appMap = applicationRepository.findAllById(appIds) + .stream().collect(toMap(Application::getId, a -> a)); + } else { + appMap = Collections.emptyMap(); + } + + // 9. 批量查询部署系统 + Set systemIds = allTeamApps.stream() + .map(TeamApplication::getDeploySystemId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + final Map systemMap; + if (!systemIds.isEmpty()) { + systemMap = externalSystemRepository.findAllById(systemIds) + .stream().collect(toMap(ExternalSystem::getId, s -> s)); + } else { + systemMap = Collections.emptyMap(); + } + + // 10. 批量查询工作流定义 + Set workflowIds = allTeamApps.stream() + .map(TeamApplication::getWorkflowDefinitionId) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + final Map workflowMap; + if (!workflowIds.isEmpty()) { + workflowMap = workflowDefinitionRepository.findAllById(workflowIds) + .stream().collect(toMap(WorkflowDefinition::getId, w -> w)); + } else { + workflowMap = Collections.emptyMap(); + } + + // 11. 批量查询审批人信息 + Set approverUserIds = configMap.values().stream() + .filter(c -> c.getApproverUserIds() != null) + .flatMap(c -> c.getApproverUserIds().stream()) + .filter(obj -> obj instanceof List) + .flatMap(obj -> ((List) obj).stream()) + .filter(id -> id instanceof Number) + .map(id -> ((Number) id).longValue()) + .collect(Collectors.toSet()); + final Map approverMap; + if (!approverUserIds.isEmpty()) { + approverMap = userRepository.findAllById(approverUserIds) + .stream().collect(toMap(User::getId, u -> u)); + } else { + approverMap = Collections.emptyMap(); + } + + // 12. 组装团队数据 + Map teamMemberMap = teamMembers.stream() + .collect(toMap(TeamMember::getTeamId, tm -> tm)); + + List teamDTOs = teamIds.stream() + .map(teamId -> buildTeamDTO( + teamId, + teamMap, + teamMemberMap, + configMap, + envMap, + teamAppsMap, + appMap, + systemMap, + workflowMap, + approverMap + )) + .filter(Objects::nonNull) + .collect(toList()); + + // 13. 组装最终结果 + UserDeployableDTO result = new UserDeployableDTO(); + result.setUserId(user.getId()); + result.setUsername(user.getUsername()); + result.setRealName(user.getNickname()); + result.setTeams(teamDTOs); + + log.info("用户 {} 可部署环境数据查询完成,包含 {} 个团队", user.getUsername(), teamDTOs.size()); + return result; + } + + /** + * 构建空结果 + */ + private UserDeployableDTO buildEmptyResult(User user) { + UserDeployableDTO result = new UserDeployableDTO(); + result.setUserId(user.getId()); + result.setUsername(user.getUsername()); + result.setRealName(user.getNickname()); + result.setTeams(Collections.emptyList()); + return result; + } + + /** + * 构建团队DTO + */ + private TeamDeployableDTO buildTeamDTO( + Long teamId, + Map teamMap, + Map teamMemberMap, + Map configMap, + Map envMap, + Map> teamAppsMap, + Map appMap, + Map systemMap, + Map workflowMap, + Map approverMap + ) { + Team team = teamMap.get(teamId); + if (team == null) { + return null; + } + + TeamMember member = teamMemberMap.get(teamId); + TeamConfig config = configMap.get(teamId); + + TeamDeployableDTO teamDTO = new TeamDeployableDTO(); + teamDTO.setTeamId(team.getId()); + teamDTO.setTeamCode(team.getTeamCode()); + teamDTO.setTeamName(team.getTeamName()); + teamDTO.setDescription(team.getDescription()); + teamDTO.setTeamRole(member != null ? member.getRoleInTeam() : null); + + // 组装环境数据 + if (config != null && config.getAllowedEnvironmentIds() != null && !config.getAllowedEnvironmentIds().isEmpty()) { + List allowedEnvIds = config.getAllowedEnvironmentIds(); + List teamApps = teamAppsMap.getOrDefault(teamId, Collections.emptyList()); + + // 按环境分组应用 + Map> appsByEnv = teamApps.stream() + .collect(groupingBy(TeamApplication::getEnvironmentId)); + + List envDTOs = new ArrayList<>(); + for (int i = 0; i < allowedEnvIds.size(); i++) { + Long envId = allowedEnvIds.get(i); + Environment env = envMap.get(envId); + if (env == null) { + continue; + } + + DeployableEnvironmentDTO envDTO = buildEnvironmentDTO( + env, + config, + i, + appsByEnv.getOrDefault(envId, Collections.emptyList()), + appMap, + systemMap, + workflowMap, + approverMap + ); + + envDTOs.add(envDTO); + } + + envDTOs.sort(Comparator.comparing(DeployableEnvironmentDTO::getSort)); + teamDTO.setEnvironments(envDTOs); + } else { + teamDTO.setEnvironments(Collections.emptyList()); + } + + return teamDTO; + } + + /** + * 构建环境DTO + */ + private DeployableEnvironmentDTO buildEnvironmentDTO( + Environment env, + TeamConfig config, + int envIndex, + List teamApps, + Map appMap, + Map systemMap, + Map workflowMap, + Map approverMap + ) { + DeployableEnvironmentDTO envDTO = new DeployableEnvironmentDTO(); + envDTO.setEnvironmentId(env.getId()); + envDTO.setEnvironmentCode(env.getEnvCode()); + envDTO.setEnvironmentName(env.getEnvName()); + envDTO.setEnvironmentDesc(env.getEnvDesc()); + envDTO.setEnabled(env.getEnabled()); + envDTO.setSort(env.getSort()); + + // 审批配置 + if (config.getEnvironmentApprovalRequired() != null && envIndex < config.getEnvironmentApprovalRequired().size()) { + envDTO.setRequiresApproval(config.getEnvironmentApprovalRequired().get(envIndex)); + } else { + envDTO.setRequiresApproval(false); + } + + // 审批人 + if (config.getApproverUserIds() != null && envIndex < config.getApproverUserIds().size()) { + Object approverObj = config.getApproverUserIds().get(envIndex); + if (approverObj instanceof List) { + List approverList = (List) approverObj; + List approverDTOs = approverList.stream() + .filter(id -> id instanceof Number) + .map(id -> buildApproverDTO(((Number) id).longValue(), approverMap)) + .filter(Objects::nonNull) + .collect(toList()); + envDTO.setApprovers(approverDTOs); + } else { + envDTO.setApprovers(Collections.emptyList()); + } + } else { + envDTO.setApprovers(Collections.emptyList()); + } + + // 应用列表 + List appDTOs = teamApps.stream() + .map(ta -> buildApplicationDTO(ta, appMap, systemMap, workflowMap)) + .filter(Objects::nonNull) + .collect(toList()); + + envDTO.setApplications(appDTOs); + + return envDTO; + } + + /** + * 构建审批人DTO + */ + private ApproverDTO buildApproverDTO(Long userId, Map approverMap) { + User user = approverMap.get(userId); + if (user == null) { + return null; + } + + ApproverDTO dto = new ApproverDTO(); + dto.setUserId(user.getId()); + dto.setUsername(user.getUsername()); + dto.setRealName(user.getNickname()); + return dto; + } + + /** + * 构建应用DTO + */ + private DeployableApplicationDTO buildApplicationDTO( + TeamApplication ta, + Map appMap, + Map systemMap, + Map workflowMap + ) { + Application app = appMap.get(ta.getApplicationId()); + if (app == null) { + return null; + } + + DeployableApplicationDTO dto = new DeployableApplicationDTO(); + dto.setTeamApplicationId(ta.getId()); + dto.setApplicationId(app.getId()); + dto.setApplicationCode(app.getAppCode()); + dto.setApplicationName(app.getAppName()); + dto.setApplicationDesc(app.getAppDesc()); + dto.setBranch(ta.getBranch()); + + // 部署系统 + dto.setDeploySystemId(ta.getDeploySystemId()); + if (ta.getDeploySystemId() != null) { + ExternalSystem system = systemMap.get(ta.getDeploySystemId()); + dto.setDeploySystemName(system != null ? system.getName() : null); + } + + // 部署任务 + dto.setDeployJob(ta.getDeployJob()); + + // 工作流定义 + dto.setWorkflowDefinitionId(ta.getWorkflowDefinitionId()); + if (ta.getWorkflowDefinitionId() != null) { + WorkflowDefinition workflow = workflowMap.get(ta.getWorkflowDefinitionId()); + if (workflow != null) { + dto.setWorkflowDefinitionName(workflow.getName()); + dto.setWorkflowDefinitionKey(workflow.getKey()); + } + } + + return dto; + } +} +