增加构建通知

This commit is contained in:
dengqichen 2025-11-14 14:35:46 +08:00
parent 566425869d
commit 3a7492ac91
4 changed files with 99 additions and 102 deletions

View File

@ -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;

View File

@ -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<Long> 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<TeamApplication> allTeamApps = teamApplicationRepository.findByTeamIdIn(teamIds);
// 8. 提取所有需要的ID集合
Set<Long> allEnvIds = new HashSet<>();
Set<Long> appIds = new HashSet<>();
@ -218,16 +218,16 @@ public class DeployServiceImpl implements IDeployService {
// 10. 批量查询团队环境配置获取团队配置的环境
List<TeamEnvironmentConfig> teamEnvConfigs = teamEnvironmentConfigRepository.findByTeamIdIn(teamIds);
Map<String, TeamEnvironmentConfig> teamEnvConfigMap = teamEnvConfigs.stream()
.collect(toMap(c -> c.getTeamId() + "_" + c.getEnvironmentId(), c -> c));
.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));
@ -291,9 +291,9 @@ public class DeployServiceImpl implements IDeployService {
// 17. 批量查询审批人信息
Set<Long> 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<Long, User> 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<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();
// 遍历所有团队配置的环境有应用就显示应用没应用就显示空列表
for (Environment env : envMap.values()) {
// 获取该环境的应用列表没有则为空列表
List<TeamApplication> 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<UserDeployableTeamEnvironmentApproverDTO> 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<Long, DeployStatisticsDTO> queryDeployStatistics(List<Long> teamApplicationIds) {
Map<Long, DeployStatisticsDTO> statisticsMap = new HashMap<>();
List<Object[]> statisticsList = deployRecordRepository.findDeployStatisticsByTeamApplicationIds(teamApplicationIds);
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();
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<Long, DeployRecord> queryLatestRecords(List<Long> teamApplicationIds) {
List<DeployRecord> latestRecords = deployRecordRepository.findLatestDeployRecordsByTeamApplicationIds(teamApplicationIds);
List<DeployRecord> latestRecords = deployRecordRepository.findLatestDeployRecordsByTeamApplicationIds(teamApplicationIds);
Map<Long, DeployRecord> 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<DeployApprovalTaskDTO> getMyApprovalTasks(Long teamId, Long environmentId, List<String> 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<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()) {
@ -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 {
* <p> NodeContext inputs 字段中解析审批配置
*
* @param variables 流程变量 Map
* @param nodeId 审批节点ID
* @param nodeId 审批节点ID
* @return ApprovalInputMapping 对象解析失败返回 null
*/
private ApprovalInputMapping extractApprovalInputMapping(Map<String, Object> variables, String nodeId) {
@ -1069,9 +1069,9 @@ public class DeployServiceImpl implements IDeployService {
if (nodeData instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> nodeDataMap = (Map<String, Object>) nodeData;
NodeContext<ApprovalInputMapping, ApprovalOutputs> nodeContext =
NodeContext<ApprovalInputMapping, ApprovalOutputs> 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<WorkflowNodeLogDTO> 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<DeployNodeLogDTO.LogEntry> logEntries = logs.stream()
.map(log -> {
@ -1141,7 +1141,7 @@ public class DeployServiceImpl implements IDeployService {
return entry;
})
.collect(Collectors.toList());
result.setLogs(logEntries);
}

View File

@ -91,8 +91,7 @@ public class JenkinsBuildDelegate extends BaseNodeDelegate<JenkinsBuildInputMapp
JenkinsBuildResponse buildDetails = jenkinsServiceIntegration.getBuildDetails(externalSystem, jobName, buildInfo.getBuildNumber());
// 打印调试信息
log.info("Build details - changeSets: {}, artifacts: {}",
buildDetails.getChangeSets(), buildDetails.getArtifacts());
log.info("Build details - changeSets: {}, artifacts: {}", buildDetails.getChangeSets(), buildDetails.getArtifacts());
// 6. 设置输出结果执行到这里说明构建成功
// 直接修改预初始化的 output 对象

View File

@ -37,7 +37,7 @@ public class NotificationNodeDelegate extends BaseNodeDelegate<NotificationInput
@Resource
private INotificationChannelRepository notificationChannelRepository;
@Resource
private INotificationTemplateRepository notificationTemplateRepository;
@ -48,45 +48,39 @@ public class NotificationNodeDelegate extends BaseNodeDelegate<NotificationInput
logWarn(String.format("Notification delegate parameter verification failed - channelId: %s, templateId: %s", input.getChannelId(), input.getNotificationTemplateId()));
return;
}
try {
// 2. 查询渠道和模板信息
NotificationChannel channel = notificationChannelRepository.findById(input.getChannelId()).orElseThrow(() -> 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<NotificationInput
} catch (Exception e) {
log.warn("解析企业微信模板配置失败,使用默认消息类型: {}", e.getMessage());
}
// 默认使用TEXT类型
return WeworkMessageTypeEnum.TEXT;
}