增加构建通知

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
*
* <p>扩展了审批任务的基本信息增加部署相关的业务上下文
* <p>结构化的审批任务信息包含审批任务部署记录关联实体等信息
* <p>用于部署审批列表让审批人员快速了解审批内容
*
* @author qqchen
@ -18,7 +18,35 @@ import java.time.LocalDateTime;
@Schema(description = "部署审批任务信息")
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")
private String taskId;
@ -35,7 +63,7 @@ public class DeployApprovalTaskDTO {
@Schema(description = "流程定义ID")
private String processDefinitionId;
@Schema(description = "审批人")
@Schema(description = "审批人用户名")
private String assignee;
@Schema(description = "创建时间")
@ -44,6 +72,9 @@ public class DeployApprovalTaskDTO {
@Schema(description = "到期时间")
private LocalDateTime dueDate;
@Schema(description = "待审批时长(毫秒)- 从任务创建到现在的时长")
private Long pendingDuration;
@Schema(description = "审批标题")
private String approvalTitle;
@ -61,49 +92,97 @@ public class DeployApprovalTaskDTO {
@Schema(description = "是否必须填写意见")
private Boolean requireComment;
}
// ============ 部署业务上下文信息 ============
/**
* 部署记录信息
*/
@Data
@Schema(description = "部署记录信息")
public static class DeployRecordInfo {
@Schema(description = "部署记录ID")
private Long deployRecordId;
private Long id;
@Schema(description = "业务标识UUID")
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 = "部署备注")
private String deployRemark;
private String remark;
@Schema(description = "部署开始时间")
private LocalDateTime deployStartTime;
@Schema(description = "待审批时长(毫秒)- 从任务创建到现在的时长")
private Long pendingDuration;
private LocalDateTime startTime;
}
/**
* 团队信息
*/
@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 = "部署备注")
private String deployRemark;
private String deployUser;
/**
* Jenkins配置
*/

View File

@ -755,9 +755,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.getDeployRemark());
deployRecordService.createDeployRecord(workflowInstance.getId(), businessKey, request.getTeamApplicationId(), request.getTeamId(), request.getApplicationId(), request.getEnvironmentId(), request.getDeployUser(), request.getDeployRemark());
log.info("部署记录已创建: businessKey={}, workflowInstanceId={}", businessKey, workflowInstance.getId());
}
@ -820,20 +818,21 @@ public class DeployServiceImpl implements IDeployService {
}
}
// 4. 批量查询团队信息解决 N+1 查询问题
// 4. 批量查询团队信息和用户信息解决 N+1 查询问题
enrichTeamInfo(result);
enrichDeployUserInfo(result);
// 5. 后置筛选按团队和环境过滤
if (teamId != null) {
result = result.stream()
.filter(task -> teamId.equals(task.getTeamId()))
.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 -> environmentId.equals(task.getEnvironmentId()))
.filter(task -> task.getEnvironment() != null && environmentId.equals(task.getEnvironment().getId()))
.collect(Collectors.toList());
log.debug("按环境ID筛选后剩余 {} 个任务", result.size());
}
@ -879,56 +878,79 @@ public class DeployServiceImpl implements IDeployService {
// 5. 构建 DTO
DeployApprovalTaskDTO dto = new DeployApprovalTaskDTO();
// 5.1 审批任务基本信息
dto.setTaskId(task.getId());
dto.setTaskName(task.getName());
dto.setTaskDescription(task.getDescription());
dto.setProcessInstanceId(task.getProcessInstanceId());
dto.setProcessDefinitionId(task.getProcessDefinitionId());
dto.setAssignee(task.getAssignee());
// 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) {
dto.setCreateTime(LocalDateTime.ofInstant(
LocalDateTime createTime = LocalDateTime.ofInstant(
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) {
dto.setDueDate(LocalDateTime.ofInstant(
approvalTask.setDueDate(LocalDateTime.ofInstant(
task.getDueDate().toInstant(),
ZoneId.systemDefault()));
}
// 5.2 审批配置信息从审批节点的 NodeContext 中获取 ApprovalInputMapping
// 5.2 填充审批配置信息从审批节点的 NodeContext 中获取 ApprovalInputMapping
String nodeId = task.getTaskDefinitionKey();
ApprovalInputMapping approvalInputs = extractApprovalInputMapping(variables, nodeId);
if (approvalInputs != null) {
dto.setApprovalTitle(approvalInputs.getApprovalTitle());
dto.setApprovalContent(approvalInputs.getApprovalContent());
dto.setApprovalMode(approvalInputs.getApprovalMode() != null
approvalTask.setApprovalTitle(approvalInputs.getApprovalTitle());
approvalTask.setApprovalContent(approvalInputs.getApprovalContent());
approvalTask.setApprovalMode(approvalInputs.getApprovalMode() != null
? approvalInputs.getApprovalMode().name()
: null);
dto.setAllowDelegate(approvalInputs.getAllowDelegate());
dto.setAllowAddSign(approvalInputs.getAllowAddSign());
dto.setRequireComment(approvalInputs.getRequireComment());
approvalTask.setAllowDelegate(approvalInputs.getAllowDelegate());
approvalTask.setAllowAddSign(approvalInputs.getAllowAddSign());
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);
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) {
long pendingMillis = Duration.between(dto.getCreateTime(), LocalDateTime.now()).toMillis();
dto.setPendingDuration(pendingMillis);
}
// 应用信息
DeployApprovalTaskDTO.ApplicationInfo application = new DeployApprovalTaskDTO.ApplicationInfo();
application.setId(deployRequest.getApplicationId());
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);
return dto;
@ -1017,6 +1039,11 @@ public class DeployServiceImpl implements IDeployService {
*
* @param dtoList 待填充的 DTO 列表
*/
/**
* 批量查询并填充团队信息
*
* @param dtoList 待填充的 DTO 列表
*/
private void enrichTeamInfo(List<DeployApprovalTaskDTO> dtoList) {
if (dtoList == null || dtoList.isEmpty()) {
return;
@ -1024,7 +1051,9 @@ public class DeployServiceImpl implements IDeployService {
// 1. 收集所有 teamIds
Set<Long> teamIds = dtoList.stream()
.map(DeployApprovalTaskDTO::getTeamId)
.map(DeployApprovalTaskDTO::getTeam)
.filter(Objects::nonNull)
.map(DeployApprovalTaskDTO.TeamInfo::getId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
@ -1038,16 +1067,75 @@ public class DeployServiceImpl implements IDeployService {
// 3. 填充团队名称
for (DeployApprovalTaskDTO dto : dtoList) {
Long teamId = dto.getTeamId();
if (teamId != null) {
Team team = teamMap.get(teamId);
if (dto.getTeam() != null && dto.getTeam().getId() != null) {
Team team = teamMap.get(dto.getTeam().getId());
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
* <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 org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@Repository
@ -14,6 +16,11 @@ public interface IUserRepository extends IBaseRepository<User, Long> {
*/
Optional<User> findByUsernameAndDeletedFalse(String username);
/**
* 批量根据用户名查找未删除的用户用于批量查询
*/
List<User> findByUsernameInAndDeletedFalse(Collection<String> usernames);
/**
* 检查用户名是否存在包括已删除的记录
*/