diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployExecuteRequest.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployExecuteRequest.java index 356cb765..f2b9cbe2 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployExecuteRequest.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/DeployExecuteRequest.java @@ -6,6 +6,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; +import java.util.Date; import java.util.List; /** @@ -65,8 +66,11 @@ public class DeployExecuteRequest { @Valid private NotificationConfig notification; + @Schema(description = "执行时间", required = true) + private Date deployDate = new Date(); + @Schema(description = "部署备注") - private String remark; + private String deployRemark; /** * Jenkins配置 @@ -106,7 +110,7 @@ public class DeployExecuteRequest { @Data @Schema(description = "通知配置") public static class NotificationConfig { - + @Schema(description = "通知渠道ID") private Long notificationChannelId; 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 index 3fdfbaf9..c67cb1e6 100644 --- 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 @@ -107,7 +107,7 @@ public class DeployServiceImpl implements IDeployService { @Resource private RuntimeService runtimeService; - + @Resource private IWorkflowNodeLogService workflowNodeLogService; @@ -162,7 +162,7 @@ public class DeployServiceImpl implements IDeployService { // 补充查询作为成员但不是负责人的团队 Set missingTeamIds = teamIds.stream() .filter(id -> !teamMap.containsKey(id)) - .collect(Collectors.toSet()); + .collect(Collectors.toSet()); if (!missingTeamIds.isEmpty()) { teamRepository.findAllById(missingTeamIds).forEach(team -> teamMap.put(team.getId(), team)); } @@ -191,7 +191,7 @@ public class DeployServiceImpl implements IDeployService { // 7. 批量查询所有团队的应用配置 List allTeamApps = teamApplicationRepository.findByTeamIdIn(teamIds); - + // 8. 提取所有需要的ID集合 Set allEnvIds = new HashSet<>(); Set appIds = new HashSet<>(); @@ -218,16 +218,16 @@ public class DeployServiceImpl implements IDeployService { // 10. 批量查询团队环境配置(获取团队配置的环境) List teamEnvConfigs = teamEnvironmentConfigRepository.findByTeamIdIn(teamIds); Map teamEnvConfigMap = teamEnvConfigs.stream() - .collect(toMap(c -> c.getTeamId() + "_" + c.getEnvironmentId(), c -> c)); - + .collect(toMap(c -> c.getTeamId() + "_" + c.getEnvironmentId(), c -> c)); + // 从团队环境配置中提取环境ID Set teamConfiguredEnvIds = teamEnvConfigs.stream() .map(TeamEnvironmentConfig::getEnvironmentId) .collect(Collectors.toSet()); - + // 合并应用配置中的环境ID和团队配置的环境ID allEnvIds.addAll(teamConfiguredEnvIds); - + // 批量查询环境信息 Map envMap = environmentRepository.findAllById(allEnvIds).stream() .collect(toMap(Environment::getId, e -> e)); @@ -291,9 +291,9 @@ public class DeployServiceImpl implements IDeployService { // 17. 批量查询审批人信息 Set approverUserIds = teamEnvConfigs.stream() - .filter(c -> c.getApproverUserIds() != null) - .flatMap(c -> c.getApproverUserIds().stream()) - .collect(Collectors.toSet()); + .filter(c -> c.getApproverUserIds() != null) + .flatMap(c -> c.getApproverUserIds().stream()) + .collect(Collectors.toSet()); Map approverMap = !approverUserIds.isEmpty() ? userRepository.findAllById(approverUserIds).stream().collect(toMap(User::getId, u -> u)) : Collections.emptyMap(); @@ -392,17 +392,17 @@ public class DeployServiceImpl implements IDeployService { // 构建环境列表 List teamApps = teamAppsMap.get(teamId); List environments = new ArrayList<>(); - + // 按环境分组应用(如果有应用的话) Map> appsByEnv = (teamApps != null && !teamApps.isEmpty()) ? teamApps.stream().collect(groupingBy(TeamApplication::getEnvironmentId)) : Collections.emptyMap(); - + // 遍历所有团队配置的环境,有应用就显示应用,没应用就显示空列表 for (Environment env : envMap.values()) { // 获取该环境的应用列表(没有则为空列表) List envApps = appsByEnv.getOrDefault(env.getId(), Collections.emptyList()); - + UserDeployableTeamEnvironmentDTO envDTO = buildUserDeployableTeamEnvironmentDTO( currentUserId, team.getOwnerId(), teamId, env, envApps, @@ -455,10 +455,10 @@ public class DeployServiceImpl implements IDeployService { 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 approvers = config.getApproverUserIds().stream() @@ -479,10 +479,10 @@ public class DeployServiceImpl implements IDeployService { } else { dto.setApprovers(Collections.emptyList()); } - + // 构建通知配置 - 使用MapStruct转换器 TeamEnvironmentNotificationConfig notificationConfig = notificationConfigMap.get(configKey); - UserTeamEnvironmentNotificationConfigDTO notificationConfigDTO = + UserTeamEnvironmentNotificationConfigDTO notificationConfigDTO = notificationConfigConverter.toUserDTO(notificationConfig, channelMap, templateMap); dto.setNotificationConfig(notificationConfigDTO); } else { @@ -615,33 +615,33 @@ public class DeployServiceImpl implements IDeployService { */ private Map queryDeployStatistics(List teamApplicationIds) { Map statisticsMap = new HashMap<>(); - List statisticsList = deployRecordRepository.findDeployStatisticsByTeamApplicationIds(teamApplicationIds); + List 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(); + 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]; - } + 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); } + 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; } @@ -649,12 +649,12 @@ public class DeployServiceImpl implements IDeployService { * 批量查询最新部署记录 */ private Map queryLatestRecords(List teamApplicationIds) { - List latestRecords = deployRecordRepository.findLatestDeployRecordsByTeamApplicationIds(teamApplicationIds); + List latestRecords = deployRecordRepository.findLatestDeployRecordsByTeamApplicationIds(teamApplicationIds); Map latestRecordMap = latestRecords.stream() .collect(toMap(DeployRecord::getTeamApplicationId, r -> r)); - // 更新统计信息中的最新状态和部署人 - latestRecordMap.forEach((teamAppId, record) -> { + // 更新统计信息中的最新状态和部署人 + latestRecordMap.forEach((teamAppId, record) -> { // 这里可以添加额外的处理逻辑 }); @@ -756,7 +756,7 @@ public class DeployServiceImpl implements IDeployService { */ private void createDeployRecord(DeployExecuteRequest request, WorkflowInstanceDTO workflowInstance, String businessKey) { String currentUsername = SecurityUtils.getCurrentUsername(); - deployRecordService.createDeployRecord(workflowInstance.getId(), businessKey, request.getTeamApplicationId(), request.getTeamId(), request.getApplicationId(), request.getEnvironmentId(), currentUsername, request.getRemark()); + deployRecordService.createDeployRecord(workflowInstance.getId(), businessKey, request.getTeamApplicationId(), request.getTeamId(), request.getApplicationId(), request.getEnvironmentId(), currentUsername, request.getDeployRemark()); log.info("部署记录已创建: businessKey={}, workflowInstanceId={}", businessKey, workflowInstance.getId()); } @@ -778,7 +778,7 @@ public class DeployServiceImpl implements IDeployService { public List getMyApprovalTasks(Long teamId, Long environmentId, List workflowDefinitionKeys) { // 1. 获取当前登录用户 String currentUsername = SecurityUtils.getCurrentUsername(); - log.info("查询用户 {} 的部署审批任务, teamId={}, environmentId={}, workflowDefinitionKeys={}", + log.info("查询用户 {} 的部署审批任务, teamId={}, environmentId={}, workflowDefinitionKeys={}", currentUsername, teamId, environmentId, workflowDefinitionKeys); // 2. 查询用户的部署工作流待办任务(支持多个工作流) @@ -786,20 +786,20 @@ public class DeployServiceImpl implements IDeployService { .taskCandidateOrAssigned(currentUsername) .orderByTaskCreateTime() .desc(); - + // 如果指定了工作流定义Key列表,则精确筛选这些工作流的所有待审批任务 if (workflowDefinitionKeys != null && !workflowDefinitionKeys.isEmpty()) { // 过滤掉空字符串 List validKeys = workflowDefinitionKeys.stream() .filter(key -> key != null && !key.trim().isEmpty()) .collect(Collectors.toList()); - + if (!validKeys.isEmpty()) { taskQuery.processDefinitionKeyIn(validKeys); log.debug("按工作流定义Key列表筛选: {}", validKeys); } } - + List tasks = taskQuery.list(); if (tasks.isEmpty()) { @@ -830,7 +830,7 @@ public class DeployServiceImpl implements IDeployService { .collect(Collectors.toList()); log.debug("按团队ID筛选后剩余 {} 个任务", result.size()); } - + if (environmentId != null) { result = result.stream() .filter(task -> environmentId.equals(task.getEnvironmentId())) @@ -873,8 +873,8 @@ public class DeployServiceImpl implements IDeployService { // 4. 查询部署记录 DeployRecord deployRecord = deployRecordRepository.findByBusinessKeyAndDeletedFalse(businessKey) - .orElseThrow(() -> new BusinessException(ResponseCode.DEPLOY_RECORD_NOT_FOUND, - new Object[]{businessKey})); + .orElseThrow(() -> new BusinessException(ResponseCode.DEPLOY_RECORD_NOT_FOUND, + new Object[] {businessKey})); // 5. 构建 DTO DeployApprovalTaskDTO dto = new DeployApprovalTaskDTO(); @@ -905,8 +905,8 @@ public class DeployServiceImpl implements IDeployService { if (approvalInputs != null) { dto.setApprovalTitle(approvalInputs.getApprovalTitle()); dto.setApprovalContent(approvalInputs.getApprovalContent()); - dto.setApprovalMode(approvalInputs.getApprovalMode() != null - ? approvalInputs.getApprovalMode().name() + dto.setApprovalMode(approvalInputs.getApprovalMode() != null + ? approvalInputs.getApprovalMode().name() : null); dto.setAllowDelegate(approvalInputs.getAllowDelegate()); dto.setAllowAddSign(approvalInputs.getAllowAddSign()); @@ -918,7 +918,7 @@ public class DeployServiceImpl implements IDeployService { dto.setDeployRecordId(deployRecord.getId()); dto.setBusinessKey(businessKey); dto.setDeployStartTime(deployRecord.getStartTime()); - + // 使用 BeanUtils 批量复制同名字段 BeanUtils.copyProperties(deployRequest, dto); @@ -1053,7 +1053,7 @@ public class DeployServiceImpl implements IDeployService { *

从 NodeContext 的 inputs 字段中解析审批配置 * * @param variables 流程变量 Map - * @param nodeId 审批节点ID + * @param nodeId 审批节点ID * @return ApprovalInputMapping 对象,解析失败返回 null */ private ApprovalInputMapping extractApprovalInputMapping(Map variables, String nodeId) { @@ -1069,9 +1069,9 @@ public class DeployServiceImpl implements IDeployService { if (nodeData instanceof Map) { @SuppressWarnings("unchecked") Map nodeDataMap = (Map) nodeData; - NodeContext nodeContext = + NodeContext nodeContext = NodeContext.fromMap(nodeDataMap, ApprovalInputMapping.class, ApprovalOutputs.class, objectMapper); - + // 3. 返回 inputMapping return nodeContext.getInputMapping(); } @@ -1110,16 +1110,16 @@ public class DeployServiceImpl implements IDeployService { return false; } - + @Override public DeployNodeLogDTO getNodeLogs(String processInstanceId, String nodeId) { DeployNodeLogDTO result = new DeployNodeLogDTO(); result.setProcessInstanceId(processInstanceId); result.setNodeId(nodeId); - + // 查询日志 List logs = workflowNodeLogService.getNodeLogs(processInstanceId, nodeId); - + if (logs.isEmpty()) { // 判断是过期还是还没有日志 result.setExpired(true); @@ -1128,7 +1128,7 @@ public class DeployServiceImpl implements IDeployService { } else { result.setExpired(false); result.setMessage("查询成功"); - + // 转换为 DTO List logEntries = logs.stream() .map(log -> { @@ -1141,7 +1141,7 @@ public class DeployServiceImpl implements IDeployService { return entry; }) .collect(Collectors.toList()); - + result.setLogs(logEntries); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/JenkinsBuildDelegate.java b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/JenkinsBuildDelegate.java index 28564063..b2b09d7f 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/JenkinsBuildDelegate.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/workflow/delegate/JenkinsBuildDelegate.java @@ -91,8 +91,7 @@ public class JenkinsBuildDelegate extends BaseNodeDelegate new RuntimeException("通知渠道不存在: " + input.getChannelId())); - NotificationTemplate template = notificationTemplateRepository.findById(input.getNotificationTemplateId()).orElseThrow(() -> new RuntimeException("通知模板不存在: " + input.getNotificationTemplateId())); + // 2. 查询渠道和模板信息 + NotificationChannel channel = notificationChannelRepository.findById(input.getChannelId()).orElseThrow(() -> new RuntimeException("通知渠道不存在: " + input.getChannelId())); + NotificationTemplate template = notificationTemplateRepository.findById(input.getNotificationTemplateId()).orElseThrow(() -> new RuntimeException("通知模板不存在: " + input.getNotificationTemplateId())); - // 3. 构建SendNotificationRequest - SendNotificationRequest request = new SendNotificationRequest(); - request.setNotificationTemplateId(input.getNotificationTemplateId()); - request.setTemplateParams(execution.getVariables()); + // 3. 构建SendNotificationRequest + SendNotificationRequest request = new SendNotificationRequest(); + request.setNotificationTemplateId(input.getNotificationTemplateId()); + request.setTemplateParams(execution.getVariables()); - // 4. 根据渠道类型创建sendRequest,并从模板配置中获取参数 - switch (channel.getChannelType()) { - case WEWORK -> { - WeworkSendNotificationRequest weworkRequest = new WeworkSendNotificationRequest(); - weworkRequest.setChannelId(input.getChannelId()); - // 从模板配置中获取消息类型 - weworkRequest.setMessageType(getWeworkMessageType(template)); - request.setSendRequest(weworkRequest); - } - case EMAIL -> { - EmailSendNotificationRequest emailRequest = new EmailSendNotificationRequest(); - emailRequest.setChannelId(input.getChannelId()); - // 收件人从工作流变量获取 - emailRequest.setToReceivers(getEmailReceivers(execution, configs)); - // 其他配置(HTML格式等)由NotificationService根据模板配置自动设置 - request.setSendRequest(emailRequest); - } - default -> throw new RuntimeException("不支持的渠道类型: " + channel.getChannelType()); + // 4. 根据渠道类型创建sendRequest,并从模板配置中获取参数 + switch (channel.getChannelType()) { + case WEWORK -> { + WeworkSendNotificationRequest weworkRequest = new WeworkSendNotificationRequest(); + weworkRequest.setChannelId(input.getChannelId()); + // 从模板配置中获取消息类型 + weworkRequest.setMessageType(getWeworkMessageType(template)); + request.setSendRequest(weworkRequest); } - - // 5. 发送通知(NotificationService会处理模板渲染和详细配置) - notificationService.send(request); - - log.info("工作流通知发送成功 - 渠道ID: {}, 模板ID: {}", - input.getChannelId(), input.getNotificationTemplateId()); - } catch (Exception e) { - logError("工作流通知发送失败: " + e.getMessage()); - throw new RuntimeException("通知发送失败", e); + case EMAIL -> { + EmailSendNotificationRequest emailRequest = new EmailSendNotificationRequest(); + emailRequest.setChannelId(input.getChannelId()); + // 收件人从工作流变量获取 + emailRequest.setToReceivers(getEmailReceivers(execution, configs)); + // 其他配置(HTML格式等)由NotificationService根据模板配置自动设置 + request.setSendRequest(emailRequest); + } + default -> throw new RuntimeException("不支持的渠道类型: " + channel.getChannelType()); } + + // 5. 发送通知(NotificationService会处理模板渲染和详细配置) + notificationService.send(request); + + log.info("工作流通知发送成功 - 渠道ID: {}, 模板ID: {}", input.getChannelId(), input.getNotificationTemplateId()); } /** @@ -103,7 +97,7 @@ public class NotificationNodeDelegate extends BaseNodeDelegate