打印了JENKINS节点日志
This commit is contained in:
parent
bb1faa1495
commit
e34248f3ea
@ -1,68 +0,0 @@
|
||||
package com.qqchen.deploy.backend.deploy.converter;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.dto.ApproverDTO;
|
||||
import com.qqchen.deploy.backend.system.entity.User;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 审批人转换器
|
||||
*
|
||||
* <p>职责:User → ApproverDTO 的转换
|
||||
* <p>遵循单一职责原则(SRP),只负责审批人相关的转换逻辑
|
||||
*
|
||||
* @author qqchen
|
||||
* @since 2025-11-06
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
@Component
|
||||
public interface ApproverConverter {
|
||||
|
||||
/**
|
||||
* User 转换为 ApproverDTO
|
||||
*
|
||||
* @param user 用户实体
|
||||
* @return 审批人DTO
|
||||
*/
|
||||
@Mappings({
|
||||
@Mapping(source = "id", target = "userId"),
|
||||
@Mapping(source = "username", target = "username"),
|
||||
@Mapping(source = "nickname", target = "realName")
|
||||
})
|
||||
ApproverDTO toDTO(User user);
|
||||
|
||||
/**
|
||||
* 批量转换 User 列表
|
||||
*
|
||||
* @param users 用户列表
|
||||
* @return 审批人DTO列表
|
||||
*/
|
||||
List<ApproverDTO> toDTOList(List<User> users);
|
||||
|
||||
/**
|
||||
* 从用户ID列表和用户Map构建审批人列表
|
||||
* <p>常用场景:从 TeamEnvironmentConfig.approverUserIds 构建审批人列表
|
||||
*
|
||||
* @param userIds 用户ID列表
|
||||
* @param userMap 用户Map(key=userId, value=User)
|
||||
* @return 审批人DTO列表(自动过滤 Map 中不存在的用户)
|
||||
*/
|
||||
default List<ApproverDTO> fromUserIds(List<Long> userIds, Map<Long, User> userMap) {
|
||||
if (userIds == null || userIds.isEmpty() || userMap == null || userMap.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return userIds.stream()
|
||||
.map(userMap::get) // 从 Map 获取 User
|
||||
.filter(user -> user != null) // 过滤 null(Map 中不存在的用户)
|
||||
.map(this::toDTO) // 转换为 ApproverDTO
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,169 +0,0 @@
|
||||
package com.qqchen.deploy.backend.deploy.converter;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployRecordSummaryDTO;
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployStatisticsDTO;
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployableApplicationDTO;
|
||||
import com.qqchen.deploy.backend.deploy.entity.*;
|
||||
import com.qqchen.deploy.backend.deploy.enums.DeployRecordStatusEnums;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 可部署应用转换器
|
||||
*
|
||||
* <p>职责:多源数据 → DeployableApplicationDTO 的转换
|
||||
* <p>遵循单一职责原则(SRP),只负责可部署应用相关的转换逻辑
|
||||
*
|
||||
* <p>数据源:
|
||||
* <ul>
|
||||
* <li>TeamApplication - 团队应用关联信息</li>
|
||||
* <li>Application - 应用基础信息</li>
|
||||
* <li>ExternalSystem - 部署系统信息(Jenkins)</li>
|
||||
* <li>WorkflowDefinition - 工作流定义</li>
|
||||
* <li>DeployStatistics - 部署统计</li>
|
||||
* <li>DeployRecord - 部署记录</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author qqchen
|
||||
* @since 2025-11-06
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
@Component
|
||||
public interface DeployableApplicationConverter {
|
||||
|
||||
/**
|
||||
* 基础转换:TeamApplication + Application → DeployableApplicationDTO
|
||||
* <p>只处理简单字段映射
|
||||
*/
|
||||
@Mappings({
|
||||
@Mapping(source = "teamApplication.id", target = "teamApplicationId"),
|
||||
@Mapping(source = "application.id", target = "applicationId"),
|
||||
@Mapping(source = "application.appCode", target = "applicationCode"),
|
||||
@Mapping(source = "application.appName", target = "applicationName"),
|
||||
@Mapping(source = "application.appDesc", target = "applicationDesc"),
|
||||
@Mapping(source = "teamApplication.branch", target = "branch"),
|
||||
@Mapping(source = "teamApplication.deploySystemId", target = "deploySystemId"),
|
||||
@Mapping(source = "teamApplication.deployJob", target = "deployJob"),
|
||||
@Mapping(source = "teamApplication.workflowDefinitionId", target = "workflowDefinitionId"),
|
||||
@Mapping(target = "deploySystemName", ignore = true),
|
||||
@Mapping(target = "workflowDefinitionName", ignore = true),
|
||||
@Mapping(target = "workflowDefinitionKey", ignore = true),
|
||||
@Mapping(target = "deployStatistics", ignore = true),
|
||||
@Mapping(target = "isDeploying", ignore = true),
|
||||
@Mapping(target = "recentDeployRecords", ignore = true)
|
||||
})
|
||||
DeployableApplicationDTO toBaseDTO(TeamApplication teamApplication, Application application);
|
||||
|
||||
/**
|
||||
* 设置部署系统信息
|
||||
*
|
||||
* @param dto 目标DTO
|
||||
* @param system 部署系统(Jenkins)
|
||||
*/
|
||||
default void applyDeploySystem(DeployableApplicationDTO dto, ExternalSystem system) {
|
||||
if (system != null) {
|
||||
dto.setDeploySystemName(system.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置工作流定义信息
|
||||
*
|
||||
* @param dto 目标DTO
|
||||
* @param workflow 工作流定义
|
||||
*/
|
||||
default void applyWorkflowDefinition(DeployableApplicationDTO dto, WorkflowDefinition workflow) {
|
||||
if (workflow != null) {
|
||||
dto.setWorkflowDefinitionName(workflow.getName());
|
||||
dto.setWorkflowDefinitionKey(workflow.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置部署统计信息和部署状态
|
||||
*
|
||||
* @param dto 目标DTO
|
||||
* @param statistics 部署统计
|
||||
* @param latestRecord 最新部署记录(用于判断是否正在部署)
|
||||
*/
|
||||
default void applyDeployStatistics(DeployableApplicationDTO dto, DeployStatisticsDTO statistics, DeployRecord latestRecord) {
|
||||
if (statistics != null) {
|
||||
dto.setDeployStatistics(statistics);
|
||||
|
||||
// 判断是否正在部署中
|
||||
if (latestRecord != null) {
|
||||
DeployRecordStatusEnums status = latestRecord.getStatus();
|
||||
dto.setIsDeploying(status == DeployRecordStatusEnums.CREATED || status == DeployRecordStatusEnums.RUNNING);
|
||||
} else {
|
||||
dto.setIsDeploying(false);
|
||||
}
|
||||
} else {
|
||||
// 没有部署记录,设置默认空统计
|
||||
dto.setDeployStatistics(createEmptyStatistics());
|
||||
dto.setIsDeploying(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最近部署记录列表
|
||||
*
|
||||
* @param dto 目标DTO
|
||||
* @param recentRecordSummaries 最近部署记录摘要列表
|
||||
*/
|
||||
default void setRecentDeployRecords(DeployableApplicationDTO dto, List<DeployRecordSummaryDTO> recentRecordSummaries) {
|
||||
dto.setRecentDeployRecords(recentRecordSummaries != null ? recentRecordSummaries : List.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空的部署统计(用于没有部署记录的应用)
|
||||
*/
|
||||
default DeployStatisticsDTO createEmptyStatistics() {
|
||||
DeployStatisticsDTO statistics = new DeployStatisticsDTO();
|
||||
statistics.setTotalCount(0L);
|
||||
statistics.setSuccessCount(0L);
|
||||
statistics.setFailedCount(0L);
|
||||
statistics.setRunningCount(0L);
|
||||
return statistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* 完整转换:从上下文构建 DeployableApplicationDTO
|
||||
* <p>封装所有转换逻辑,简化 Service 层调用
|
||||
*
|
||||
* @param teamApplication 团队应用关联
|
||||
* @param application 应用实体
|
||||
* @param deploySystem 部署系统(可选)
|
||||
* @param workflow 工作流定义(可选)
|
||||
* @param statistics 部署统计(可选)
|
||||
* @param latestRecord 最新部署记录(可选)
|
||||
* @param recentRecordSummaries 最近部署记录摘要列表(可选)
|
||||
* @return 完整的可部署应用DTO
|
||||
*/
|
||||
default DeployableApplicationDTO toFullDTO(
|
||||
TeamApplication teamApplication,
|
||||
Application application,
|
||||
ExternalSystem deploySystem,
|
||||
WorkflowDefinition workflow,
|
||||
DeployStatisticsDTO statistics,
|
||||
DeployRecord latestRecord,
|
||||
List<DeployRecordSummaryDTO> recentRecordSummaries
|
||||
) {
|
||||
// 1. 基础转换
|
||||
DeployableApplicationDTO dto = toBaseDTO(teamApplication, application);
|
||||
|
||||
// 2. 应用扩展信息
|
||||
applyDeploySystem(dto, deploySystem);
|
||||
applyWorkflowDefinition(dto, workflow);
|
||||
applyDeployStatistics(dto, statistics, latestRecord);
|
||||
setRecentDeployRecords(dto, recentRecordSummaries);
|
||||
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,94 +0,0 @@
|
||||
package com.qqchen.deploy.backend.deploy.converter;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.dto.ApproverDTO;
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployableApplicationDTO;
|
||||
import com.qqchen.deploy.backend.deploy.dto.DeployableEnvironmentDTO;
|
||||
import com.qqchen.deploy.backend.deploy.entity.Environment;
|
||||
import com.qqchen.deploy.backend.deploy.entity.TeamEnvironmentConfig;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 可部署环境转换器
|
||||
*
|
||||
* <p>采用 MapStruct + 自定义方法混合模式:
|
||||
* <ul>
|
||||
* <li>简单字段映射:交给 MapStruct 自动生成</li>
|
||||
* <li>复杂业务逻辑:使用 @AfterMapping 或自定义方法</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author qqchen
|
||||
* @since 2025-11-06
|
||||
*/
|
||||
@Mapper(componentModel = "spring")
|
||||
@Component
|
||||
public interface DeployableEnvironmentConverter {
|
||||
|
||||
/**
|
||||
* 基础转换:Environment → DeployableEnvironmentDTO
|
||||
* <p>仅处理简单字段映射
|
||||
*/
|
||||
@Mappings({
|
||||
@Mapping(source = "id", target = "environmentId"),
|
||||
@Mapping(source = "envCode", target = "environmentCode"),
|
||||
@Mapping(source = "envName", target = "environmentName"),
|
||||
@Mapping(source = "envDesc", target = "environmentDesc"),
|
||||
@Mapping(target = "requiresApproval", ignore = true),
|
||||
@Mapping(target = "approvers", ignore = true),
|
||||
@Mapping(target = "notificationEnabled", ignore = true),
|
||||
@Mapping(target = "notificationChannelId", ignore = true),
|
||||
@Mapping(target = "requireCodeReview", ignore = true),
|
||||
@Mapping(target = "remark", ignore = true),
|
||||
@Mapping(target = "applications", ignore = true)
|
||||
})
|
||||
DeployableEnvironmentDTO toBaseDTO(Environment environment);
|
||||
|
||||
/**
|
||||
* 合并配置信息到 DTO
|
||||
* <p>从 TeamEnvironmentConfig 补充配置字段
|
||||
*
|
||||
* @param dto 目标 DTO
|
||||
* @param config 团队环境配置
|
||||
*/
|
||||
default void applyConfig(DeployableEnvironmentDTO dto, TeamEnvironmentConfig config) {
|
||||
if (config != null) {
|
||||
dto.setRequiresApproval(config.getApprovalRequired() != null ? config.getApprovalRequired() : false);
|
||||
dto.setRequireCodeReview(config.getRequireCodeReview() != null ? config.getRequireCodeReview() : false);
|
||||
dto.setNotificationEnabled(config.getNotificationEnabled() != null ? config.getNotificationEnabled() : true);
|
||||
dto.setNotificationChannelId(config.getNotificationChannelId());
|
||||
dto.setRemark(config.getRemark());
|
||||
} else {
|
||||
// 默认值
|
||||
dto.setRequiresApproval(false);
|
||||
dto.setRequireCodeReview(false);
|
||||
dto.setNotificationEnabled(true);
|
||||
dto.setApprovers(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置审批人列表
|
||||
*
|
||||
* @param dto 目标 DTO
|
||||
* @param approvers 审批人列表
|
||||
*/
|
||||
default void setApprovers(DeployableEnvironmentDTO dto, List<ApproverDTO> approvers) {
|
||||
dto.setApprovers(approvers != null ? approvers : Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置应用列表
|
||||
*
|
||||
* @param dto 目标 DTO
|
||||
* @param applications 应用列表
|
||||
*/
|
||||
default void setApplications(DeployableEnvironmentDTO dto, List<DeployableApplicationDTO> applications) {
|
||||
dto.setApplications(applications != null ? applications : Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部署执行请求
|
||||
*
|
||||
@ -95,7 +97,7 @@ public class DeployExecuteRequest {
|
||||
private Boolean required;
|
||||
|
||||
@Schema(description = "审批人ID列表(逗号分隔)")
|
||||
private String userIds;
|
||||
private List<String> userNames;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
package com.qqchen.deploy.backend.deploy.dto;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.enums.DeployRecordStatusEnums;
|
||||
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 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;
|
||||
|
||||
@Schema(description = "部署统计信息")
|
||||
private DeployStatisticsDTO deployStatistics;
|
||||
|
||||
@Schema(description = "是否正在部署中")
|
||||
private Boolean isDeploying;
|
||||
|
||||
@Schema(description = "最近部署记录列表(最多10条)")
|
||||
private List<DeployRecordSummaryDTO> recentDeployRecords;
|
||||
}
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
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<ApproverDTO> approvers;
|
||||
|
||||
@Schema(description = "是否启用部署通知")
|
||||
private Boolean notificationEnabled;
|
||||
|
||||
@Schema(description = "通知渠道ID")
|
||||
private Long notificationChannelId;
|
||||
|
||||
@Schema(description = "是否要求代码审查")
|
||||
private Boolean requireCodeReview;
|
||||
|
||||
@Schema(description = "备注信息")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "可部署应用列表")
|
||||
private List<DeployableApplicationDTO> applications;
|
||||
}
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
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<DeployableEnvironmentDTO> environments;
|
||||
}
|
||||
|
||||
@ -50,5 +50,13 @@ public class UserDeployableTeamEnvironmentDTO {
|
||||
|
||||
@Schema(description = "可部署应用列表")
|
||||
private List<UserDeployableTeamEnvironmentApplicationDTO> applications;
|
||||
|
||||
// ==================== 当前用户权限 ====================
|
||||
|
||||
@Schema(description = "当前用户是否可以发起部署")
|
||||
private Boolean canDeploy;
|
||||
|
||||
@Schema(description = "当前用户是否可以审批部署")
|
||||
private Boolean canApprove;
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
package com.qqchen.deploy.backend.deploy.service.impl;
|
||||
|
||||
import com.qqchen.deploy.backend.deploy.converter.ApproverConverter;
|
||||
import com.qqchen.deploy.backend.deploy.converter.DeployableApplicationConverter;
|
||||
import com.qqchen.deploy.backend.deploy.converter.DeployableEnvironmentConverter;
|
||||
import com.qqchen.deploy.backend.deploy.dto.*;
|
||||
import com.qqchen.deploy.backend.deploy.entity.*;
|
||||
import com.qqchen.deploy.backend.deploy.repository.*;
|
||||
@ -11,6 +8,7 @@ import com.qqchen.deploy.backend.framework.security.SecurityUtils;
|
||||
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
|
||||
import com.qqchen.deploy.backend.framework.exception.BusinessException;
|
||||
import com.qqchen.deploy.backend.framework.utils.JsonUtils;
|
||||
import com.qqchen.deploy.backend.framework.utils.MapUtils;
|
||||
import com.qqchen.deploy.backend.framework.utils.SpelExpressionResolver;
|
||||
import com.qqchen.deploy.backend.system.entity.User;
|
||||
import com.qqchen.deploy.backend.system.repository.IUserRepository;
|
||||
@ -18,7 +16,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceDTO;
|
||||
import com.qqchen.deploy.backend.workflow.dto.WorkflowInstanceStartRequest;
|
||||
import com.qqchen.deploy.backend.workflow.dto.inputmapping.ApprovalInputMapping;
|
||||
import com.qqchen.deploy.backend.workflow.dto.inputmapping.JenkinsBuildInputMapping;
|
||||
import com.qqchen.deploy.backend.workflow.dto.outputs.ApprovalOutputs;
|
||||
import com.qqchen.deploy.backend.workflow.entity.WorkflowDefinition;
|
||||
import com.qqchen.deploy.backend.workflow.model.NodeContext;
|
||||
@ -30,11 +27,11 @@ import com.qqchen.deploy.backend.deploy.entity.DeployRecord;
|
||||
import com.qqchen.deploy.backend.deploy.enums.DeployRecordStatusEnums;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
|
||||
import org.flowable.engine.RuntimeService;
|
||||
import org.flowable.engine.TaskService;
|
||||
import org.flowable.engine.runtime.ProcessInstance;
|
||||
import org.flowable.task.api.Task;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@ -102,17 +99,8 @@ public class DeployServiceImpl implements IDeployService {
|
||||
@Resource
|
||||
private RuntimeService runtimeService;
|
||||
|
||||
@Resource
|
||||
private DeployableEnvironmentConverter deployableEnvironmentConverter;
|
||||
|
||||
@Resource
|
||||
private ApproverConverter approverConverter;
|
||||
|
||||
@Resource
|
||||
private DeployableApplicationConverter deployableApplicationConverter;
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<UserTeamDeployableDTO> getDeployableEnvironments() {
|
||||
Long currentUserId = SecurityUtils.getCurrentUserId();
|
||||
|
||||
@ -249,7 +237,7 @@ public class DeployServiceImpl implements IDeployService {
|
||||
List<UserTeamDeployableDTO> result = new ArrayList<>();
|
||||
for (Long teamId : teamIds) {
|
||||
UserTeamDeployableDTO teamDTO = buildUserTeamDeployableDTO(
|
||||
teamId, teamMap, ownerMap, membersByTeam, memberUserMap,
|
||||
currentUserId, teamId, teamMap, ownerMap, membersByTeam, memberUserMap,
|
||||
teamAppsMap, envMap, appMap, systemMap, workflowMap,
|
||||
teamEnvConfigMap, approverMap,
|
||||
statisticsMap, latestRecordMap, recentRecordsMap
|
||||
@ -267,6 +255,7 @@ public class DeployServiceImpl implements IDeployService {
|
||||
* 构建单个团队的可部署数据DTO
|
||||
*/
|
||||
private UserTeamDeployableDTO buildUserTeamDeployableDTO(
|
||||
Long currentUserId,
|
||||
Long teamId,
|
||||
Map<Long, Team> teamMap,
|
||||
Map<Long, User> ownerMap,
|
||||
@ -342,6 +331,7 @@ public class DeployServiceImpl implements IDeployService {
|
||||
}
|
||||
|
||||
UserDeployableTeamEnvironmentDTO envDTO = buildUserDeployableTeamEnvironmentDTO(
|
||||
currentUserId, team.getOwnerId(),
|
||||
teamId, env, entry.getValue(),
|
||||
appMap, systemMap, workflowMap,
|
||||
teamEnvConfigMap, approverMap,
|
||||
@ -364,6 +354,8 @@ public class DeployServiceImpl implements IDeployService {
|
||||
* 构建单个环境的可部署数据DTO
|
||||
*/
|
||||
private UserDeployableTeamEnvironmentDTO buildUserDeployableTeamEnvironmentDTO(
|
||||
Long currentUserId,
|
||||
Long teamOwnerId,
|
||||
Long teamId,
|
||||
Environment env,
|
||||
List<TeamApplication> teamApps,
|
||||
@ -430,6 +422,18 @@ public class DeployServiceImpl implements IDeployService {
|
||||
.collect(Collectors.toList());
|
||||
dto.setApplications(applications);
|
||||
|
||||
// ==================== 设置当前用户权限 ====================
|
||||
|
||||
// 1. 团队成员都可以发起部署
|
||||
dto.setCanDeploy(true);
|
||||
|
||||
// 2. 判断是否可以审批(团队负责人 或 审批人列表中的任意一人)
|
||||
List<Long> approverUserIds = config != null && config.getApproverUserIds() != null
|
||||
? config.getApproverUserIds()
|
||||
: Collections.emptyList();
|
||||
boolean canApprove = hasApprovalPermission(currentUserId, teamOwnerId, approverUserIds);
|
||||
dto.setCanApprove(canApprove);
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
@ -696,9 +700,9 @@ public class DeployServiceImpl implements IDeployService {
|
||||
String currentUsername = SecurityUtils.getCurrentUsername();
|
||||
log.info("查询用户 {} 的部署审批任务", currentUsername);
|
||||
|
||||
// 2. 查询用户的所有待办任务(模仿 ApprovalTaskService.getMyTasks)
|
||||
// 2. 查询用户的所有待办任务(包括候选人和分配人,支持或签模式)
|
||||
List<Task> tasks = taskService.createTaskQuery()
|
||||
.taskAssignee(currentUsername)
|
||||
.taskCandidateOrAssigned(currentUsername)
|
||||
.orderByTaskCreateTime()
|
||||
.desc()
|
||||
.list();
|
||||
@ -721,6 +725,9 @@ public class DeployServiceImpl implements IDeployService {
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 批量查询团队信息(解决 N+1 查询问题)
|
||||
enrichTeamInfo(result);
|
||||
|
||||
log.info("用户 {} 共有 {} 个部署审批任务", currentUsername, result.size());
|
||||
return result;
|
||||
}
|
||||
@ -740,19 +747,8 @@ public class DeployServiceImpl implements IDeployService {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 检查是否为部署流程(通过 deploy 变量判断)
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> deployContext = (Map<String, Object>) variables.get("deploy");
|
||||
|
||||
if (deployContext == null) {
|
||||
log.debug("任务 {} 不包含部署上下文,跳过", task.getId());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. 获取 BusinessKey(需要通过 RuntimeService 查询)
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
|
||||
.processInstanceId(task.getProcessInstanceId())
|
||||
.singleResult();
|
||||
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
|
||||
|
||||
if (processInstance == null) {
|
||||
log.debug("流程实例 {} 已结束,跳过", task.getProcessInstanceId());
|
||||
@ -766,15 +762,9 @@ public class DeployServiceImpl implements IDeployService {
|
||||
}
|
||||
|
||||
// 4. 查询部署记录
|
||||
Optional<DeployRecord> deployRecordOpt = deployRecordRepository
|
||||
.findByBusinessKeyAndDeletedFalse(businessKey);
|
||||
|
||||
if (deployRecordOpt.isEmpty()) {
|
||||
log.warn("找不到业务标识为 {} 的部署记录", businessKey);
|
||||
return null;
|
||||
}
|
||||
|
||||
DeployRecord deployRecord = deployRecordOpt.get();
|
||||
DeployRecord deployRecord = deployRecordRepository.findByBusinessKeyAndDeletedFalse(businessKey)
|
||||
.orElseThrow(() -> new BusinessException(ResponseCode.DEPLOY_RECORD_NOT_FOUND,
|
||||
new Object[]{businessKey}));
|
||||
|
||||
// 5. 构建 DTO
|
||||
DeployApprovalTaskDTO dto = new DeployApprovalTaskDTO();
|
||||
@ -799,37 +789,30 @@ public class DeployServiceImpl implements IDeployService {
|
||||
ZoneId.systemDefault()));
|
||||
}
|
||||
|
||||
// 5.2 审批配置信息(从流程变量获取)
|
||||
dto.setApprovalTitle((String) variables.get("approvalTitle"));
|
||||
dto.setApprovalContent((String) variables.get("approvalContent"));
|
||||
dto.setApprovalMode((String) variables.get("approvalMode"));
|
||||
dto.setAllowDelegate((Boolean) variables.get("allowDelegate"));
|
||||
dto.setAllowAddSign((Boolean) variables.get("allowAddSign"));
|
||||
dto.setRequireComment((Boolean) variables.get("requireComment"));
|
||||
|
||||
// 5.3 部署业务上下文信息
|
||||
dto.setDeployRecordId(deployRecord.getId());
|
||||
dto.setBusinessKey(businessKey);
|
||||
dto.setTeamId(getLongValue(deployContext, "teamId"));
|
||||
dto.setApplicationId(getLongValue(deployContext, "applicationId"));
|
||||
dto.setApplicationCode((String) deployContext.get("applicationCode"));
|
||||
dto.setApplicationName((String) deployContext.get("applicationName"));
|
||||
dto.setEnvironmentId(getLongValue(deployContext, "environmentId"));
|
||||
dto.setEnvironmentCode((String) deployContext.get("environmentCode"));
|
||||
dto.setEnvironmentName((String) deployContext.get("environmentName"));
|
||||
dto.setDeployBy((String) deployContext.get("by"));
|
||||
dto.setDeployRemark((String) deployContext.get("remark"));
|
||||
dto.setDeployStartTime(deployRecord.getStartTime());
|
||||
|
||||
// 5.4 查询团队名称
|
||||
Long teamId = dto.getTeamId();
|
||||
if (teamId != null) {
|
||||
teamRepository.findById(teamId).ifPresent(team ->
|
||||
dto.setTeamName(team.getTeamName())
|
||||
);
|
||||
// 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
|
||||
? approvalInputs.getApprovalMode().name()
|
||||
: null);
|
||||
dto.setAllowDelegate(approvalInputs.getAllowDelegate());
|
||||
dto.setAllowAddSign(approvalInputs.getAllowAddSign());
|
||||
dto.setRequireComment(approvalInputs.getRequireComment());
|
||||
}
|
||||
|
||||
// 5.5 计算待审批时长
|
||||
// 5.3 部署业务上下文信息(从 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);
|
||||
|
||||
// 5.4 计算待审批时长(团队名称通过批量查询填充)
|
||||
if (dto.getCreateTime() != null) {
|
||||
long pendingMillis = Duration.between(dto.getCreateTime(), LocalDateTime.now()).toMillis();
|
||||
dto.setPendingDuration(pendingMillis);
|
||||
@ -841,30 +824,6 @@ public class DeployServiceImpl implements IDeployService {
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 Long 值
|
||||
*/
|
||||
private Long getLongValue(Map<String, Object> map, String key) {
|
||||
Object value = map.get(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return ((Integer) value).longValue();
|
||||
}
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Long.parseLong((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无法将 {} 转换为 Long: {}", key, value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@ -873,31 +832,29 @@ public class DeployServiceImpl implements IDeployService {
|
||||
log.info("开始处理部署审批: taskId={}, result={}", taskId, request.getResult());
|
||||
|
||||
try {
|
||||
// 1. 验证任务是否存在
|
||||
// 1. 获取当前用户
|
||||
String currentUsername = SecurityUtils.getCurrentUsername();
|
||||
|
||||
// 2. 验证任务是否存在 && 当前用户是否有权限审批
|
||||
// taskCandidateOrAssigned 支持所有审批模式:
|
||||
// - SINGLE 模式: 检查 assignee
|
||||
// - ANY/ALL 模式: 检查 candidateUsers
|
||||
Task task = taskService.createTaskQuery()
|
||||
.taskId(taskId)
|
||||
.taskCandidateOrAssigned(currentUsername)
|
||||
.singleResult();
|
||||
|
||||
if (task == null) {
|
||||
log.error("审批任务不存在: taskId={}", taskId);
|
||||
throw new BusinessException(ResponseCode.DATA_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 2. 获取当前用户
|
||||
String currentUsername = SecurityUtils.getCurrentUsername();
|
||||
log.debug("审批人: {}, 任务负责人: {}", currentUsername, task.getAssignee());
|
||||
|
||||
// 3. 验证审批权限
|
||||
if (!currentUsername.equals(task.getAssignee())) {
|
||||
log.error("无权审批该任务: taskId={}, 当前用户={}, 任务负责人={}",
|
||||
taskId, currentUsername, task.getAssignee());
|
||||
log.error("审批任务不存在或无权审批: taskId={}, 当前用户={}", taskId, currentUsername);
|
||||
throw new BusinessException(ResponseCode.PERMISSION_DENIED);
|
||||
}
|
||||
|
||||
// 4. 获取节点ID并更新 NodeContext(与 ApprovalTaskService 保持一致)
|
||||
log.debug("审批人: {}, 任务负责人: {}", currentUsername, task.getAssignee());
|
||||
|
||||
// 3. 获取节点ID并更新 NodeContext(与 ApprovalTaskService 保持一致)
|
||||
String nodeId = task.getTaskDefinitionKey();
|
||||
|
||||
// 5. 读取现有 NodeContext
|
||||
// 4. 读取现有 NodeContext
|
||||
Object nodeDataObj = taskService.getVariable(task.getId(), nodeId);
|
||||
NodeContext<ApprovalInputMapping, ApprovalOutputs> nodeContext;
|
||||
if (nodeDataObj instanceof Map) {
|
||||
@ -909,7 +866,7 @@ public class DeployServiceImpl implements IDeployService {
|
||||
nodeContext = new NodeContext<>();
|
||||
}
|
||||
|
||||
// 6. 创建审批结果(只设置核心字段,其他由 ApprovalEndExecutionListener 自动装配)
|
||||
// 5. 创建审批结果(只设置核心字段,其他由 ApprovalEndExecutionListener 自动装配)
|
||||
ApprovalOutputs outputs = new ApprovalOutputs();
|
||||
outputs.setApprovalResult(request.getResult());
|
||||
outputs.setApprover(currentUsername);
|
||||
@ -917,18 +874,18 @@ public class DeployServiceImpl implements IDeployService {
|
||||
outputs.setApprovalComment(request.getComment());
|
||||
// ✅ 不需要手动设置 status 和 approvalDuration,监听器会自动装配
|
||||
|
||||
// 7. 设置到 NodeContext
|
||||
// 6. 设置到 NodeContext
|
||||
nodeContext.setOutputs(outputs);
|
||||
|
||||
// 8. 保存回流程变量
|
||||
// 7. 保存回流程变量
|
||||
taskService.setVariable(task.getId(), nodeId, nodeContext.toMap(objectMapper));
|
||||
|
||||
// 9. 添加任务评论(供历史查询)
|
||||
// 8. 添加任务评论(供历史查询)
|
||||
if (request.getComment() != null) {
|
||||
taskService.addComment(taskId, task.getProcessInstanceId(), request.getComment());
|
||||
}
|
||||
|
||||
// 10. 完成任务(触发 ApprovalEndExecutionListener,监听器会自动装配其他字段)
|
||||
// 9. 完成任务(触发 ApprovalEndExecutionListener,监听器会自动装配其他字段)
|
||||
taskService.complete(taskId);
|
||||
|
||||
log.info("部署审批已提交,后续节点将异步执行: taskId={}, result={}, comment={}",
|
||||
@ -942,5 +899,106 @@ public class DeployServiceImpl implements IDeployService {
|
||||
throw new BusinessException(ResponseCode.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
/**
|
||||
* 批量查询并填充团队信息(解决 N+1 查询问题)
|
||||
*
|
||||
* @param dtoList 待填充的 DTO 列表
|
||||
*/
|
||||
private void enrichTeamInfo(List<DeployApprovalTaskDTO> dtoList) {
|
||||
if (dtoList == null || dtoList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 收集所有 teamIds
|
||||
Set<Long> teamIds = dtoList.stream()
|
||||
.map(DeployApprovalTaskDTO::getTeamId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
if (teamIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 批量查询团队信息
|
||||
Map<Long, Team> teamMap = teamRepository.findAllById(teamIds).stream()
|
||||
.collect(Collectors.toMap(Team::getId, team -> team));
|
||||
|
||||
// 3. 填充团队名称
|
||||
for (DeployApprovalTaskDTO dto : dtoList) {
|
||||
Long teamId = dto.getTeamId();
|
||||
if (teamId != null) {
|
||||
Team team = teamMap.get(teamId);
|
||||
if (team != null) {
|
||||
dto.setTeamName(team.getTeamName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从流程变量中提取审批节点的 ApprovalInputMapping
|
||||
* <p>从 NodeContext 的 inputs 字段中解析审批配置
|
||||
*
|
||||
* @param variables 流程变量 Map
|
||||
* @param nodeId 审批节点ID
|
||||
* @return ApprovalInputMapping 对象,解析失败返回 null
|
||||
*/
|
||||
private ApprovalInputMapping extractApprovalInputMapping(Map<String, Object> variables, String nodeId) {
|
||||
try {
|
||||
// 1. 从 variables 中获取节点数据
|
||||
Object nodeData = variables.get(nodeId);
|
||||
if (nodeData == null) {
|
||||
log.warn("流程变量中没有找到节点 {} 的数据", nodeId);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 解析为 NodeContext
|
||||
if (nodeData instanceof Map) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> nodeDataMap = (Map<String, Object>) nodeData;
|
||||
NodeContext<ApprovalInputMapping, ApprovalOutputs> nodeContext =
|
||||
NodeContext.fromMap(nodeDataMap, ApprovalInputMapping.class, ApprovalOutputs.class, objectMapper);
|
||||
|
||||
// 3. 返回 inputMapping
|
||||
return nodeContext.getInputMapping();
|
||||
}
|
||||
|
||||
log.warn("节点 {} 的数据格式不正确,无法解析为 NodeContext", nodeId);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("提取审批节点 {} 的 ApprovalInputMapping 失败", nodeId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否有审批权限
|
||||
* <p>权限规则:团队负责人 或 审批人列表中的任意一人
|
||||
*
|
||||
* @param currentUserId 当前用户ID
|
||||
* @param teamOwnerId 团队负责人ID
|
||||
* @param approverUserIds 审批人ID列表
|
||||
* @return true-有权限, false-无权限
|
||||
*/
|
||||
private boolean hasApprovalPermission(Long currentUserId, Long teamOwnerId, List<Long> approverUserIds) {
|
||||
if (currentUserId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 1. 是团队负责人
|
||||
if (currentUserId.equals(teamOwnerId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 2. 在审批人列表中
|
||||
if (approverUserIds != null && !approverUserIds.isEmpty()) {
|
||||
return approverUserIds.contains(currentUserId);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,236 @@
|
||||
package com.qqchen.deploy.backend.framework.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Map 工具类
|
||||
* <p>提供从 Map 中安全获取和转换各种类型值的工具方法
|
||||
*
|
||||
* @author qqchen
|
||||
* @since 2025-11-06
|
||||
*/
|
||||
@Slf4j
|
||||
public class MapUtils {
|
||||
|
||||
private MapUtils() {
|
||||
throw new UnsupportedOperationException("Utility class cannot be instantiated");
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 Long 值
|
||||
* <p>支持类型转换:Long、Integer、String
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @return Long 值,转换失败或不存在返回 null
|
||||
*/
|
||||
public static Long getLongValue(Map<String, Object> map, String key) {
|
||||
if (map == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = map.get(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
if (value instanceof Integer) {
|
||||
return ((Integer) value).longValue();
|
||||
}
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Long.parseLong((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无法将 key={} 的值转换为 Long: {}", key, value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
|
||||
log.warn("不支持的 Long 类型转换,key={}, type={}", key, value.getClass().getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 Integer 值
|
||||
* <p>支持类型转换:Integer、Long、String
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @return Integer 值,转换失败或不存在返回 null
|
||||
*/
|
||||
public static Integer getIntegerValue(Map<String, Object> map, String key) {
|
||||
if (map == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = map.get(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof Integer) {
|
||||
return (Integer) value;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return ((Long) value).intValue();
|
||||
}
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Integer.parseInt((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无法将 key={} 的值转换为 Integer: {}", key, value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).intValue();
|
||||
}
|
||||
|
||||
log.warn("不支持的 Integer 类型转换,key={}, type={}", key, value.getClass().getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 String 值
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @return String 值,不存在返回 null
|
||||
*/
|
||||
public static String getStringValue(Map<String, Object> map, String key) {
|
||||
if (map == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = map.get(key);
|
||||
return value != null ? value.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 Boolean 值
|
||||
* <p>支持类型转换:Boolean、String("true"/"false")
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @return Boolean 值,转换失败或不存在返回 null
|
||||
*/
|
||||
public static Boolean getBooleanValue(Map<String, Object> map, String key) {
|
||||
if (map == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = map.get(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof Boolean) {
|
||||
return (Boolean) value;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return Boolean.parseBoolean((String) value);
|
||||
}
|
||||
|
||||
log.warn("不支持的 Boolean 类型转换,key={}, type={}", key, value.getClass().getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 Double 值
|
||||
* <p>支持类型转换:Double、Float、Integer、Long、String、BigDecimal
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @return Double 值,转换失败或不存在返回 null
|
||||
*/
|
||||
public static Double getDoubleValue(Map<String, Object> map, String key) {
|
||||
if (map == null || key == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object value = map.get(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (value instanceof Double) {
|
||||
return (Double) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).doubleValue();
|
||||
}
|
||||
if (value instanceof String) {
|
||||
try {
|
||||
return Double.parseDouble((String) value);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("无法将 key={} 的值转换为 Double: {}", key, value);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("不支持的 Double 类型转换,key={}, type={}", key, value.getClass().getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 Long 值(带默认值)
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @param defaultValue 默认值
|
||||
* @return Long 值,转换失败或不存在返回默认值
|
||||
*/
|
||||
public static Long getLongValue(Map<String, Object> map, String key, Long defaultValue) {
|
||||
Long value = getLongValue(map, key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 Integer 值(带默认值)
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @param defaultValue 默认值
|
||||
* @return Integer 值,转换失败或不存在返回默认值
|
||||
*/
|
||||
public static Integer getIntegerValue(Map<String, Object> map, String key, Integer defaultValue) {
|
||||
Integer value = getIntegerValue(map, key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 String 值(带默认值)
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @param defaultValue 默认值
|
||||
* @return String 值,不存在返回默认值
|
||||
*/
|
||||
public static String getStringValue(Map<String, Object> map, String key, String defaultValue) {
|
||||
String value = getStringValue(map, key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 Map 中安全获取 Boolean 值(带默认值)
|
||||
*
|
||||
* @param map 数据 Map
|
||||
* @param key 键名
|
||||
* @param defaultValue 默认值
|
||||
* @return Boolean 值,转换失败或不存在返回默认值
|
||||
*/
|
||||
public static Boolean getBooleanValue(Map<String, Object> map, String key, Boolean defaultValue) {
|
||||
Boolean value = getBooleanValue(map, key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user