增加构建通知

This commit is contained in:
dengqichen 2025-11-15 11:24:45 +08:00
parent c09d82e7b5
commit 88f5ec1d5c
4 changed files with 277 additions and 101 deletions

View File

@ -8,7 +8,7 @@ import java.time.LocalDateTime;
/** /**
* 部署审批任务DTO * 部署审批任务DTO
* *
* <p>扩展了审批任务的基本信息增加部署相关的业务上下文 * <p>结构化的审批任务信息包含审批任务部署记录关联实体等信息
* <p>用于部署审批列表让审批人员快速了解审批内容 * <p>用于部署审批列表让审批人员快速了解审批内容
* *
* @author qqchen * @author qqchen
@ -18,7 +18,35 @@ import java.time.LocalDateTime;
@Schema(description = "部署审批任务信息") @Schema(description = "部署审批任务信息")
public class DeployApprovalTaskDTO { public class DeployApprovalTaskDTO {
// ============ 审批任务基本信息 ============ // ========== 审批任务信息 ==========
@Schema(description = "审批任务信息")
private ApprovalTaskInfo approvalTask;
// ========== 部署记录信息 ==========
@Schema(description = "部署记录信息")
private DeployRecordInfo deployRecord;
// ========== 关联实体信息 ==========
@Schema(description = "团队信息")
private TeamInfo team;
@Schema(description = "应用信息")
private ApplicationInfo application;
@Schema(description = "环境信息")
private EnvironmentInfo environment;
@Schema(description = "发起人信息")
private UserSimpleInfo deployUser;
// ========== 嵌套类定义 ==========
/**
* 审批任务信息
*/
@Data
@Schema(description = "审批任务信息")
public static class ApprovalTaskInfo {
@Schema(description = "任务ID") @Schema(description = "任务ID")
private String taskId; private String taskId;
@ -35,7 +63,7 @@ public class DeployApprovalTaskDTO {
@Schema(description = "流程定义ID") @Schema(description = "流程定义ID")
private String processDefinitionId; private String processDefinitionId;
@Schema(description = "审批人") @Schema(description = "审批人用户名")
private String assignee; private String assignee;
@Schema(description = "创建时间") @Schema(description = "创建时间")
@ -44,6 +72,9 @@ public class DeployApprovalTaskDTO {
@Schema(description = "到期时间") @Schema(description = "到期时间")
private LocalDateTime dueDate; private LocalDateTime dueDate;
@Schema(description = "待审批时长(毫秒)- 从任务创建到现在的时长")
private Long pendingDuration;
@Schema(description = "审批标题") @Schema(description = "审批标题")
private String approvalTitle; private String approvalTitle;
@ -61,49 +92,97 @@ public class DeployApprovalTaskDTO {
@Schema(description = "是否必须填写意见") @Schema(description = "是否必须填写意见")
private Boolean requireComment; private Boolean requireComment;
}
// ============ 部署业务上下文信息 ============ /**
* 部署记录信息
*/
@Data
@Schema(description = "部署记录信息")
public static class DeployRecordInfo {
@Schema(description = "部署记录ID") @Schema(description = "部署记录ID")
private Long deployRecordId; private Long id;
@Schema(description = "业务标识UUID") @Schema(description = "业务标识UUID")
private String businessKey; private String businessKey;
@Schema(description = "团队ID")
private Long teamId;
@Schema(description = "团队名称")
private String teamName;
@Schema(description = "应用ID")
private Long applicationId;
@Schema(description = "应用编码")
private String applicationCode;
@Schema(description = "应用名称")
private String applicationName;
@Schema(description = "环境ID")
private Long environmentId;
@Schema(description = "环境编码")
private String environmentCode;
@Schema(description = "环境名称")
private String environmentName;
@Schema(description = "发起人")
private String deployBy;
@Schema(description = "部署备注") @Schema(description = "部署备注")
private String deployRemark; private String remark;
@Schema(description = "部署开始时间") @Schema(description = "部署开始时间")
private LocalDateTime deployStartTime; private LocalDateTime startTime;
}
@Schema(description = "待审批时长(毫秒)- 从任务创建到现在的时长") /**
private Long pendingDuration; * 团队信息
*/
@Data
@Schema(description = "团队信息")
public static class TeamInfo {
@Schema(description = "团队ID")
private Long id;
@Schema(description = "团队名称")
private String name;
}
/**
* 应用信息
*/
@Data
@Schema(description = "应用信息")
public static class ApplicationInfo {
@Schema(description = "应用ID")
private Long id;
@Schema(description = "应用编码")
private String code;
@Schema(description = "应用名称")
private String name;
}
/**
* 环境信息
*/
@Data
@Schema(description = "环境信息")
public static class EnvironmentInfo {
@Schema(description = "环境ID")
private Long id;
@Schema(description = "环境编码")
private String code;
@Schema(description = "环境名称")
private String name;
}
/**
* 用户简要信息
*/
@Data
@Schema(description = "用户简要信息")
public static class UserSimpleInfo {
@Schema(description = "用户名")
private String username;
@Schema(description = "昵称")
private String nickname;
@Schema(description = "邮箱")
private String email;
@Schema(description = "手机号")
private String phone;
@Schema(description = "部门名称")
private String departmentName;
}
} }

View File

@ -72,6 +72,8 @@ public class DeployExecuteRequest {
@Schema(description = "部署备注") @Schema(description = "部署备注")
private String deployRemark; private String deployRemark;
private String deployUser;
/** /**
* Jenkins配置 * Jenkins配置
*/ */

View File

@ -755,9 +755,7 @@ public class DeployServiceImpl implements IDeployService {
* 创建部署记录 * 创建部署记录
*/ */
private void createDeployRecord(DeployExecuteRequest request, WorkflowInstanceDTO workflowInstance, String businessKey) { 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(), request.getDeployUser(), request.getDeployRemark());
deployRecordService.createDeployRecord(workflowInstance.getId(), businessKey, request.getTeamApplicationId(), request.getTeamId(), request.getApplicationId(), request.getEnvironmentId(), currentUsername, request.getDeployRemark());
log.info("部署记录已创建: businessKey={}, workflowInstanceId={}", businessKey, workflowInstance.getId()); log.info("部署记录已创建: businessKey={}, workflowInstanceId={}", businessKey, workflowInstance.getId());
} }
@ -820,20 +818,21 @@ public class DeployServiceImpl implements IDeployService {
} }
} }
// 4. 批量查询团队信息解决 N+1 查询问题 // 4. 批量查询团队信息和用户信息解决 N+1 查询问题
enrichTeamInfo(result); enrichTeamInfo(result);
enrichDeployUserInfo(result);
// 5. 后置筛选按团队和环境过滤 // 5. 后置筛选按团队和环境过滤
if (teamId != null) { if (teamId != null) {
result = result.stream() result = result.stream()
.filter(task -> teamId.equals(task.getTeamId())) .filter(task -> task.getTeam() != null && teamId.equals(task.getTeam().getId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
log.debug("按团队ID筛选后剩余 {} 个任务", result.size()); log.debug("按团队ID筛选后剩余 {} 个任务", result.size());
} }
if (environmentId != null) { if (environmentId != null) {
result = result.stream() result = result.stream()
.filter(task -> environmentId.equals(task.getEnvironmentId())) .filter(task -> task.getEnvironment() != null && environmentId.equals(task.getEnvironment().getId()))
.collect(Collectors.toList()); .collect(Collectors.toList());
log.debug("按环境ID筛选后剩余 {} 个任务", result.size()); log.debug("按环境ID筛选后剩余 {} 个任务", result.size());
} }
@ -879,56 +878,79 @@ public class DeployServiceImpl implements IDeployService {
// 5. 构建 DTO // 5. 构建 DTO
DeployApprovalTaskDTO dto = new DeployApprovalTaskDTO(); DeployApprovalTaskDTO dto = new DeployApprovalTaskDTO();
// 5.1 审批任务基本信息 // 5.1 构建审批任务信息
dto.setTaskId(task.getId()); DeployApprovalTaskDTO.ApprovalTaskInfo approvalTask = new DeployApprovalTaskDTO.ApprovalTaskInfo();
dto.setTaskName(task.getName()); approvalTask.setTaskId(task.getId());
dto.setTaskDescription(task.getDescription()); approvalTask.setTaskName(task.getName());
dto.setProcessInstanceId(task.getProcessInstanceId()); approvalTask.setTaskDescription(task.getDescription());
dto.setProcessDefinitionId(task.getProcessDefinitionId()); approvalTask.setProcessInstanceId(task.getProcessInstanceId());
dto.setAssignee(task.getAssignee()); approvalTask.setProcessDefinitionId(task.getProcessDefinitionId());
approvalTask.setAssignee(task.getAssignee());
if (task.getCreateTime() != null) { if (task.getCreateTime() != null) {
dto.setCreateTime(LocalDateTime.ofInstant( LocalDateTime createTime = LocalDateTime.ofInstant(
task.getCreateTime().toInstant(), task.getCreateTime().toInstant(),
ZoneId.systemDefault())); ZoneId.systemDefault());
approvalTask.setCreateTime(createTime);
// 计算待审批时长
long pendingMillis = Duration.between(createTime, LocalDateTime.now()).toMillis();
approvalTask.setPendingDuration(pendingMillis);
} }
if (task.getDueDate() != null) { if (task.getDueDate() != null) {
dto.setDueDate(LocalDateTime.ofInstant( approvalTask.setDueDate(LocalDateTime.ofInstant(
task.getDueDate().toInstant(), task.getDueDate().toInstant(),
ZoneId.systemDefault())); ZoneId.systemDefault()));
} }
// 5.2 审批配置信息从审批节点的 NodeContext 中获取 ApprovalInputMapping // 5.2 填充审批配置信息从审批节点的 NodeContext 中获取 ApprovalInputMapping
String nodeId = task.getTaskDefinitionKey(); String nodeId = task.getTaskDefinitionKey();
ApprovalInputMapping approvalInputs = extractApprovalInputMapping(variables, nodeId); ApprovalInputMapping approvalInputs = extractApprovalInputMapping(variables, nodeId);
if (approvalInputs != null) { if (approvalInputs != null) {
dto.setApprovalTitle(approvalInputs.getApprovalTitle()); approvalTask.setApprovalTitle(approvalInputs.getApprovalTitle());
dto.setApprovalContent(approvalInputs.getApprovalContent()); approvalTask.setApprovalContent(approvalInputs.getApprovalContent());
dto.setApprovalMode(approvalInputs.getApprovalMode() != null approvalTask.setApprovalMode(approvalInputs.getApprovalMode() != null
? approvalInputs.getApprovalMode().name() ? approvalInputs.getApprovalMode().name()
: null); : null);
dto.setAllowDelegate(approvalInputs.getAllowDelegate()); approvalTask.setAllowDelegate(approvalInputs.getAllowDelegate());
dto.setAllowAddSign(approvalInputs.getAllowAddSign()); approvalTask.setAllowAddSign(approvalInputs.getAllowAddSign());
dto.setRequireComment(approvalInputs.getRequireComment()); approvalTask.setRequireComment(approvalInputs.getRequireComment());
} }
dto.setApprovalTask(approvalTask);
// 5.3 部署业务上下文信息 variables 还原 DeployExecuteRequest // 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); DeployExecuteRequest deployRequest = JsonUtils.fromMap(variables, DeployExecuteRequest.class);
dto.setDeployRecordId(deployRecord.getId());
dto.setBusinessKey(businessKey);
dto.setDeployStartTime(deployRecord.getStartTime());
// 使用 BeanUtils 批量复制同名字段 // 团队信息
BeanUtils.copyProperties(deployRequest, dto); DeployApprovalTaskDTO.TeamInfo team = new DeployApprovalTaskDTO.TeamInfo();
team.setId(deployRequest.getTeamId());
// 团队名称通过批量查询填充 enrichTeamInfo 方法
dto.setTeam(team);
// 5.4 计算待审批时长团队名称通过批量查询填充 // 应用信息
if (dto.getCreateTime() != null) { DeployApprovalTaskDTO.ApplicationInfo application = new DeployApprovalTaskDTO.ApplicationInfo();
long pendingMillis = Duration.between(dto.getCreateTime(), LocalDateTime.now()).toMillis(); application.setId(deployRequest.getApplicationId());
dto.setPendingDuration(pendingMillis); application.setCode(deployRequest.getApplicationCode());
} application.setName(deployRequest.getApplicationName());
dto.setApplication(application);
// 5.6 自动解析 EL 表达式 ${deploy.applicationName} // 环境信息
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); SpelExpressionResolver.resolveObject(dto, variables);
return dto; return dto;
@ -1017,6 +1039,11 @@ public class DeployServiceImpl implements IDeployService {
* *
* @param dtoList 待填充的 DTO 列表 * @param dtoList 待填充的 DTO 列表
*/ */
/**
* 批量查询并填充团队信息
*
* @param dtoList 待填充的 DTO 列表
*/
private void enrichTeamInfo(List<DeployApprovalTaskDTO> dtoList) { private void enrichTeamInfo(List<DeployApprovalTaskDTO> dtoList) {
if (dtoList == null || dtoList.isEmpty()) { if (dtoList == null || dtoList.isEmpty()) {
return; return;
@ -1024,7 +1051,9 @@ public class DeployServiceImpl implements IDeployService {
// 1. 收集所有 teamIds // 1. 收集所有 teamIds
Set<Long> teamIds = dtoList.stream() Set<Long> teamIds = dtoList.stream()
.map(DeployApprovalTaskDTO::getTeamId) .map(DeployApprovalTaskDTO::getTeam)
.filter(Objects::nonNull)
.map(DeployApprovalTaskDTO.TeamInfo::getId)
.filter(Objects::nonNull) .filter(Objects::nonNull)
.collect(Collectors.toSet()); .collect(Collectors.toSet());
@ -1038,16 +1067,75 @@ public class DeployServiceImpl implements IDeployService {
// 3. 填充团队名称 // 3. 填充团队名称
for (DeployApprovalTaskDTO dto : dtoList) { for (DeployApprovalTaskDTO dto : dtoList) {
Long teamId = dto.getTeamId(); if (dto.getTeam() != null && dto.getTeam().getId() != null) {
if (teamId != null) { Team team = teamMap.get(dto.getTeam().getId());
Team team = teamMap.get(teamId);
if (team != null) { if (team != null) {
dto.setTeamName(team.getTeamName()); 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 * 从流程变量中提取审批节点的 ApprovalInputMapping
* <p> NodeContext inputs 字段中解析审批配置 * <p> NodeContext inputs 字段中解析审批配置

View File

@ -4,6 +4,8 @@ import com.qqchen.deploy.backend.system.entity.User;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository; import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
import java.util.Optional; import java.util.Optional;
@Repository @Repository
@ -14,6 +16,11 @@ public interface IUserRepository extends IBaseRepository<User, Long> {
*/ */
Optional<User> findByUsernameAndDeletedFalse(String username); Optional<User> findByUsernameAndDeletedFalse(String username);
/**
* 批量根据用户名查找未删除的用户用于批量查询
*/
List<User> findByUsernameInAndDeletedFalse(Collection<String> usernames);
/** /**
* 检查用户名是否存在包括已删除的记录 * 检查用户名是否存在包括已删除的记录
*/ */