From 2ab9ebff70a74c02e794c7ca5a9606bc7cbe0eea Mon Sep 17 00:00:00 2001 From: dengqichen Date: Fri, 31 Oct 2025 15:07:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deploy/api/TeamConfigApiController.java | 50 ++++++++++++++++ .../deploy/converter/TeamConfigConverter.java | 17 ++++++ .../backend/deploy/dto/EnvironmentDTO.java | 6 ++ .../deploy/dto/TeamApplicationDTO.java | 10 ++++ .../backend/deploy/dto/TeamConfigDTO.java | 35 +++++++++++ .../deploy/backend/deploy/dto/TeamDTO.java | 5 +- .../deploy/entity/TeamApplication.java | 23 ++++++++ .../backend/deploy/entity/TeamConfig.java | 59 +++++++++++++++++++ .../deploy/query/TeamApplicationQuery.java | 7 +++ .../backend/deploy/query/TeamConfigQuery.java | 24 ++++++++ .../ITeamApplicationRepository.java | 52 ++++++++++++++-- .../repository/ITeamConfigRepository.java | 34 +++++++++++ .../deploy/service/ITeamConfigService.java | 24 ++++++++ .../service/impl/ApplicationServiceImpl.java | 6 +- .../service/impl/EnvironmentServiceImpl.java | 47 +++++++++++++-- .../impl/TeamApplicationServiceImpl.java | 5 +- .../service/impl/TeamConfigServiceImpl.java | 42 +++++++++++++ .../deploy/service/impl/TeamServiceImpl.java | 39 ++++++++++-- .../backend/framework/enums/ResponseCode.java | 1 + .../db/changelog/changes/v1.0.0-data.sql | 8 +++ .../db/changelog/changes/v1.0.0-schema.sql | 32 +++++++++- .../src/main/resources/messages.properties | 4 ++ 22 files changed, 507 insertions(+), 23 deletions(-) create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamConfigApiController.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConfigConverter.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamConfigDTO.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamConfig.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamConfigQuery.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamConfigService.java create mode 100644 backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamConfigServiceImpl.java diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamConfigApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamConfigApiController.java new file mode 100644 index 00000000..f454e54f --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/api/TeamConfigApiController.java @@ -0,0 +1,50 @@ +package com.qqchen.deploy.backend.deploy.api; + +import com.qqchen.deploy.backend.deploy.dto.TeamConfigDTO; +import com.qqchen.deploy.backend.deploy.entity.TeamConfig; +import com.qqchen.deploy.backend.deploy.query.TeamConfigQuery; +import com.qqchen.deploy.backend.deploy.service.ITeamConfigService; +import com.qqchen.deploy.backend.framework.api.Response; +import com.qqchen.deploy.backend.framework.controller.BaseController; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 团队配置API控制器 + * + * @author qqchen + * @since 2025-10-31 + */ +@Slf4j +@RestController +@RequestMapping("/api/v1/team-configs") +@Tag(name = "团队配置管理", description = "团队配置的增删改查接口") +public class TeamConfigApiController extends BaseController { + + @Resource + private ITeamConfigService teamConfigService; + + @Operation(summary = "根据团队ID获取配置", description = "获取指定团队的配置信息") + @GetMapping("/team/{teamId}") + public Response getByTeamId( + @Parameter(description = "团队ID", required = true) @PathVariable Long teamId + ) { + return Response.success(teamConfigService.getByTeamId(teamId)); + } + + @Override + protected void exportData(HttpServletResponse response, List data) { + // TODO: 实现导出功能 + } +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConfigConverter.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConfigConverter.java new file mode 100644 index 00000000..76cd4dc1 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConfigConverter.java @@ -0,0 +1,17 @@ +package com.qqchen.deploy.backend.deploy.converter; + +import com.qqchen.deploy.backend.deploy.dto.TeamConfigDTO; +import com.qqchen.deploy.backend.deploy.entity.TeamConfig; +import com.qqchen.deploy.backend.framework.converter.BaseConverter; +import org.mapstruct.Mapper; + +/** + * 团队配置转换器 + * + * @author qqchen + * @since 2025-10-31 + */ +@Mapper(config = BaseConverter.class) +public interface TeamConfigConverter extends BaseConverter { +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/EnvironmentDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/EnvironmentDTO.java index 27bfcbc3..c4293118 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/EnvironmentDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/EnvironmentDTO.java @@ -35,4 +35,10 @@ public class EnvironmentDTO extends BaseDTO { @Schema(description = "排序号") @NotNull(message = "排序号不能为空") private Integer sort; + + @Schema(description = "使用该环境的团队数量(去重)") + private Long teamCount; + + @Schema(description = "该环境关联的应用数量(去重)") + private Long applicationCount; } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamApplicationDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamApplicationDTO.java index 3ae45bc7..0a50ffcb 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamApplicationDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamApplicationDTO.java @@ -19,6 +19,13 @@ public class TeamApplicationDTO extends BaseDTO { @NotNull(message = "应用ID不能为空") private Long applicationId; + @Schema(description = "环境ID", required = true) + @NotNull(message = "环境ID不能为空") + private Long environmentId; + + @Schema(description = "分支名称", example = "develop") + private String branch; + @Schema(description = "团队名称") private String teamName; @@ -27,5 +34,8 @@ public class TeamApplicationDTO extends BaseDTO { @Schema(description = "应用编码") private String applicationCode; + + @Schema(description = "环境名称") + private String environmentName; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamConfigDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamConfigDTO.java new file mode 100644 index 00000000..6d7c640d --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamConfigDTO.java @@ -0,0 +1,35 @@ +package com.qqchen.deploy.backend.deploy.dto; + +import com.qqchen.deploy.backend.framework.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 团队配置DTO + * + * @author qqchen + * @since 2025-10-31 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "团队配置信息") +public class TeamConfigDTO extends BaseDTO { + + @Schema(description = "团队ID", required = true) + @NotNull(message = "团队ID不能为空") + private Long teamId; + + @Schema(description = "允许访问的环境ID列表", example = "[1, 2, 3]") + private List allowedEnvironmentIds; + + @Schema(description = "环境是否需要审批(与allowedEnvironmentIds位置对应)", example = "[false, false, true]") + private List environmentApprovalRequired; + + @Schema(description = "各环境的审批人列表(与allowedEnvironmentIds位置对应,无审批人用null)", example = "[null, null, [1, 4]]") + private List approverUserIds; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java index 28e20e5a..ef5bc30c 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java @@ -40,7 +40,10 @@ public class TeamDTO extends BaseDTO { @Schema(description = "成员数量") private Long memberCount; - @Schema(description = "应用数量") + @Schema(description = "该团队可访问的环境数量(去重)") + private Long environmentCount; + + @Schema(description = "该团队的应用配置数量(不去重,同一应用在不同环境算多个)") private Long applicationCount; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java index 40c05cbe..49166094 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java @@ -1,5 +1,6 @@ package com.qqchen.deploy.backend.deploy.entity; +import com.qqchen.deploy.backend.framework.annotation.LogicDelete; import com.qqchen.deploy.backend.framework.domain.Entity; import jakarta.persistence.Column; import jakarta.persistence.Table; @@ -8,17 +9,39 @@ import lombok.EqualsAndHashCode; /** * 团队应用关联实体 + * + * @author qqchen + * @since 2025-10-31 */ @Data @EqualsAndHashCode(callSuper = true) @jakarta.persistence.Entity @Table(name = "deploy_team_application") +@LogicDelete(false) public class TeamApplication extends Entity { + /** + * 团队ID + */ @Column(name = "team_id", nullable = false) private Long teamId; + /** + * 应用ID + */ @Column(name = "application_id", nullable = false) private Long applicationId; + + /** + * 环境ID + */ + @Column(name = "environment_id", nullable = false) + private Long environmentId; + + /** + * 分支名称 + */ + @Column(name = "branch", length = 100) + private String branch; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamConfig.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamConfig.java new file mode 100644 index 00000000..5da3fea2 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamConfig.java @@ -0,0 +1,59 @@ +package com.qqchen.deploy.backend.deploy.entity; + +import com.qqchen.deploy.backend.framework.annotation.LogicDelete; +import com.qqchen.deploy.backend.framework.domain.Entity; +import com.vladmihalcea.hibernate.type.json.JsonType; +import jakarta.persistence.Column; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.annotations.Type; + +import java.util.List; + +/** + * 团队配置实体 + * + * @author qqchen + * @since 2025-10-31 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@jakarta.persistence.Entity +@Table(name = "deploy_team_config") +@LogicDelete +public class TeamConfig extends Entity { + + /** + * 团队ID + */ + @Column(name = "team_id", nullable = false) + private Long teamId; + + /** + * 允许访问的环境ID列表 + */ + @Type(JsonType.class) + @Column(name = "allowed_environment_ids", columnDefinition = "JSON") + private List allowedEnvironmentIds; + + /** + * 环境是否需要审批(boolean数组,与allowedEnvironmentIds位置对应) + * 例如:allowedEnvironmentIds=[1,2,3], environmentApprovalRequired=[false,false,true] + * 表示环境1和2不需要审批,环境3需要审批 + */ + @Type(JsonType.class) + @Column(name = "environment_approval_required", columnDefinition = "JSON") + private List environmentApprovalRequired; + + /** + * 各环境的审批人列表(数组,与allowedEnvironmentIds位置对应) + * 元素可以是:null(无审批人)或审批人ID数组 + * 例如:allowedEnvironmentIds=[1,2,3], approverUserIds=[null,null,[1,4]] + * 表示环境1和2无审批人,环境3的审批人是用户1和4 + */ + @Type(JsonType.class) + @Column(name = "approver_user_ids", columnDefinition = "JSON") + private List approverUserIds; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamApplicationQuery.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamApplicationQuery.java index 85de5ff0..e6d3582e 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamApplicationQuery.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamApplicationQuery.java @@ -8,6 +8,9 @@ import lombok.EqualsAndHashCode; /** * 团队应用关联查询条件 + * + * @author qqchen + * @since 2025-10-31 */ @Data @EqualsAndHashCode(callSuper = true) @@ -21,5 +24,9 @@ public class TeamApplicationQuery extends BaseQuery { @QueryField(field = "applicationId") @Schema(description = "应用ID") private Long applicationId; + + @QueryField(field = "environmentId") + @Schema(description = "环境ID") + private Long environmentId; } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamConfigQuery.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamConfigQuery.java new file mode 100644 index 00000000..1cb8da13 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/query/TeamConfigQuery.java @@ -0,0 +1,24 @@ +package com.qqchen.deploy.backend.deploy.query; + +import com.qqchen.deploy.backend.framework.annotation.QueryField; +import com.qqchen.deploy.backend.framework.query.BaseQuery; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 团队配置查询条件 + * + * @author qqchen + * @since 2025-10-31 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "团队配置查询条件") +public class TeamConfigQuery extends BaseQuery { + + @QueryField(field = "teamId") + @Schema(description = "团队ID") + private Long teamId; +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java index 858c083f..a0572c80 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java @@ -2,17 +2,25 @@ package com.qqchen.deploy.backend.deploy.repository; import com.qqchen.deploy.backend.deploy.entity.TeamApplication; import com.qqchen.deploy.backend.framework.repository.IBaseRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import java.util.Optional; +/** + * 团队应用关联Repository + * + * @author qqchen + * @since 2025-10-31 + */ @Repository public interface ITeamApplicationRepository extends IBaseRepository { /** - * 统计团队关联的应用数量 + * 统计团队的应用配置数量(不去重,物理删除无需deleted条件) */ - Long countByTeamIdAndDeletedFalse(Long teamId); + Long countByTeamId(Long teamId); /** * 统计应用被哪些团队关联 @@ -20,13 +28,45 @@ public interface ITeamApplicationRepository extends IBaseRepository findByTeamIdAndApplicationIdAndDeletedFalse(Long teamId, Long applicationId); + Optional findByTeamIdAndApplicationIdAndEnvironmentIdAndDeletedFalse(Long teamId, Long applicationId, Long environmentId); + + /** + * 统计使用指定环境的不同团队数量(去重 team_id) + * 使用原生查询:SELECT COUNT(DISTINCT team_id) FROM deploy_team_application WHERE environment_id = ? + */ + @Query("SELECT COUNT(DISTINCT ta.teamId) FROM TeamApplication ta WHERE ta.environmentId = :environmentId") + Long countDistinctTeamIdByEnvironmentId(@Param("environmentId") Long environmentId); + + /** + * 统计指定环境关联的不同应用数量(去重 application_id) + * 使用原生查询:SELECT COUNT(DISTINCT application_id) FROM deploy_team_application WHERE environment_id = ? + */ + @Query("SELECT COUNT(DISTINCT ta.applicationId) FROM TeamApplication ta WHERE ta.environmentId = :environmentId") + Long countDistinctApplicationIdByEnvironmentId(@Param("environmentId") Long environmentId); + + /** + * 统计指定团队可访问的不同环境数量(去重 environment_id) + */ + @Query("SELECT COUNT(DISTINCT ta.environmentId) FROM TeamApplication ta WHERE ta.teamId = :teamId") + Long countDistinctEnvironmentIdByTeamId(@Param("teamId") Long teamId); + + /** + * 统计指定团队关联的不同应用数量(去重 application_id) + */ + @Query("SELECT COUNT(DISTINCT ta.applicationId) FROM TeamApplication ta WHERE ta.teamId = :teamId") + Long countDistinctApplicationIdByTeamId(@Param("teamId") Long teamId); + + /** + * 统计指定应用被多少个不同团队使用(去重 team_id) + */ + @Query("SELECT COUNT(DISTINCT ta.teamId) FROM TeamApplication ta WHERE ta.applicationId = :applicationId") + Long countDistinctTeamIdByApplicationId(@Param("applicationId") Long applicationId); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java new file mode 100644 index 00000000..549dd4fd --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamConfigRepository.java @@ -0,0 +1,34 @@ +package com.qqchen.deploy.backend.deploy.repository; + +import com.qqchen.deploy.backend.deploy.entity.TeamConfig; +import com.qqchen.deploy.backend.framework.repository.IBaseRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +/** + * 团队配置Repository + * + * @author qqchen + * @since 2025-10-31 + */ +@Repository +public interface ITeamConfigRepository extends IBaseRepository { + + /** + * 根据团队ID查询配置 + * + * @param teamId 团队ID + * @return 团队配置 + */ + Optional findByTeamIdAndDeletedFalse(Long teamId); + + /** + * 检查团队配置是否存在 + * + * @param teamId 团队ID + * @return 是否存在 + */ + boolean existsByTeamIdAndDeletedFalse(Long teamId); +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamConfigService.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamConfigService.java new file mode 100644 index 00000000..24c8f972 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamConfigService.java @@ -0,0 +1,24 @@ +package com.qqchen.deploy.backend.deploy.service; + +import com.qqchen.deploy.backend.deploy.dto.TeamConfigDTO; +import com.qqchen.deploy.backend.deploy.entity.TeamConfig; +import com.qqchen.deploy.backend.deploy.query.TeamConfigQuery; +import com.qqchen.deploy.backend.framework.service.IBaseService; + +/** + * 团队配置Service接口 + * + * @author qqchen + * @since 2025-10-31 + */ +public interface ITeamConfigService extends IBaseService { + + /** + * 根据团队ID获取配置 + * + * @param teamId 团队ID + * @return 团队配置 + */ + TeamConfigDTO getByTeamId(Long teamId); +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/ApplicationServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/ApplicationServiceImpl.java index 7baefb22..adbcf771 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/ApplicationServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/ApplicationServiceImpl.java @@ -63,9 +63,9 @@ public class ApplicationServiceImpl extends BaseServiceImpl application.setRepositoryProject(repositoryProjectConverter.toDto(repositoryProject))); } - // 统计该应用被多少个团队关联 - Long teamCount = teamApplicationRepository.countByApplicationIdAndDeletedFalse(application.getId()); - application.setTeamCount(teamCount); + // 统计该应用被多少个不同团队使用(去重 team_id) + Long teamCount = teamApplicationRepository.countDistinctTeamIdByApplicationId(application.getId()); + application.setTeamCount(teamCount != null ? teamCount : 0L); }).collect(toList()); return new PageImpl<>(result, page.getPageable(), page.getTotalElements()); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java index d2211063..65c76bc4 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/EnvironmentServiceImpl.java @@ -6,9 +6,12 @@ import com.qqchen.deploy.backend.deploy.entity.Environment; import com.qqchen.deploy.backend.deploy.entity.QEnvironment; import com.qqchen.deploy.backend.deploy.query.EnvironmentQuery; import com.qqchen.deploy.backend.deploy.repository.IEnvironmentRepository; +import com.qqchen.deploy.backend.deploy.repository.ITeamApplicationRepository; import com.qqchen.deploy.backend.deploy.service.IEnvironmentService; import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl; import com.querydsl.jpa.impl.JPAQueryFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; import org.springframework.stereotype.Service; import jakarta.annotation.Resource; import jakarta.persistence.EntityManager; @@ -32,12 +35,46 @@ public class EnvironmentServiceImpl extends BaseServiceImpl page(EnvironmentQuery query) { + Page page = super.page(query); + + // 填充统计数据 + List content = page.getContent().stream() + .peek(env -> { + // 统计使用该环境的团队数量(去重) + Long teamCount = teamApplicationRepository.countDistinctTeamIdByEnvironmentId(env.getId()); + env.setTeamCount(teamCount != null ? teamCount : 0L); + + // 统计该环境关联的应用数量(去重) + Long appCount = teamApplicationRepository.countDistinctApplicationIdByEnvironmentId(env.getId()); + env.setApplicationCount(appCount != null ? appCount : 0L); + }) + .collect(Collectors.toList()); + + return new PageImpl<>(content, page.getPageable(), page.getTotalElements()); + } + + @Override + public List findAll(EnvironmentQuery query) { + List list = super.findAll(query); + + // 填充统计数据 + return list.stream() + .peek(env -> { + // 统计使用该环境的团队数量(去重) + Long teamCount = teamApplicationRepository.countDistinctTeamIdByEnvironmentId(env.getId()); + env.setTeamCount(teamCount != null ? teamCount : 0L); + + // 统计该环境关联的应用数量(去重) + Long appCount = teamApplicationRepository.countDistinctApplicationIdByEnvironmentId(env.getId()); + env.setApplicationCount(appCount != null ? appCount : 0L); + }) + .collect(Collectors.toList()); + } @Override public List getProjectEnvironments(Long projectGroupId) { diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java index e23a36e2..66c78ed0 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamApplicationServiceImpl.java @@ -44,8 +44,9 @@ public class TeamApplicationServiceImpl extends BaseServiceImpl + implements ITeamConfigService { + + @Resource + private ITeamConfigRepository teamConfigRepository; + + @Resource + private TeamConfigConverter teamConfigConverter; + + @Override + public TeamConfigDTO getByTeamId(Long teamId) { + TeamConfig config = teamConfigRepository.findByTeamIdAndDeletedFalse(teamId) + .orElseThrow(() -> new BusinessException(ResponseCode.TEAM_CONFIG_NOT_FOUND, new Object[]{teamId})); + return teamConfigConverter.toDto(config); + } +} + diff --git a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamServiceImpl.java index 4309f7e8..d4ab1ae4 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamServiceImpl.java @@ -58,7 +58,7 @@ public class TeamServiceImpl extends BaseServiceImpl 0) { throw new BusinessException(ResponseCode.TEAM_HAS_APPLICATIONS); } @@ -70,17 +70,46 @@ public class TeamServiceImpl extends BaseServiceImpl page(TeamQuery query) { Page page = super.page(query); - // 统计每个团队的成员数量和应用数量 + // 统计每个团队的成员数量、环境数量和应用配置数量 List content = page.getContent().stream() .peek(team -> { + // 统计团队成员数量 Long memberCount = teamMemberRepository.countByTeamIdAndDeletedFalse(team.getId()); - Long applicationCount = teamApplicationRepository.countByTeamIdAndDeletedFalse(team.getId()); - team.setMemberCount(memberCount); - team.setApplicationCount(applicationCount); + team.setMemberCount(memberCount != null ? memberCount : 0L); + + // 统计团队可访问的环境数量(去重) + Long environmentCount = teamApplicationRepository.countDistinctEnvironmentIdByTeamId(team.getId()); + team.setEnvironmentCount(environmentCount != null ? environmentCount : 0L); + + // 统计团队的应用配置数量(不去重,同一应用在不同环境算多个) + Long applicationCount = teamApplicationRepository.countByTeamId(team.getId()); + team.setApplicationCount(applicationCount != null ? applicationCount : 0L); }) .collect(Collectors.toList()); return new PageImpl<>(content, page.getPageable(), page.getTotalElements()); } + + @Override + public List findAll(TeamQuery query) { + List list = super.findAll(query); + + // 统计每个团队的成员数量、环境数量和应用配置数量 + return list.stream() + .peek(team -> { + // 统计团队成员数量 + Long memberCount = teamMemberRepository.countByTeamIdAndDeletedFalse(team.getId()); + team.setMemberCount(memberCount != null ? memberCount : 0L); + + // 统计团队可访问的环境数量(去重) + Long environmentCount = teamApplicationRepository.countDistinctEnvironmentIdByTeamId(team.getId()); + team.setEnvironmentCount(environmentCount != null ? environmentCount : 0L); + + // 统计团队的应用配置数量(不去重,同一应用在不同环境算多个) + Long applicationCount = teamApplicationRepository.countByTeamId(team.getId()); + team.setApplicationCount(applicationCount != null ? applicationCount : 0L); + }) + .collect(Collectors.toList()); + } } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java index dbddc79d..7d77e285 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java @@ -198,6 +198,7 @@ public enum ResponseCode { TEAM_MEMBER_ALREADY_EXISTS(2925, "team.member.already.exists"), TEAM_APPLICATION_NOT_FOUND(2926, "team.application.not.found"), TEAM_APPLICATION_ALREADY_EXISTS(2927, "team.application.already.exists"), + TEAM_CONFIG_NOT_FOUND(2928, "team.config.not.found"), // 服务器管理相关错误码 (2950-2969) SERVER_NOT_FOUND(2950, "server.not.found"), diff --git a/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql b/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql index ef7da1bf..15e2c9aa 100644 --- a/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql +++ b/backend/src/main/resources/db/changelog/changes/v1.0.0-data.sql @@ -870,6 +870,14 @@ VALUES (2, 4, 'ops_manager', '负责人', NOW(), 'admin', NOW(), 'admin', NOW(), 1, 0), (2, 2, 'it_manager', '运维', NOW(), 'admin', NOW(), 'admin', NOW(), 1, 0); +-- 初始化团队配置数据 +INSERT INTO deploy_team_config (team_id, allowed_environment_ids, environment_approval_required, approver_user_ids, create_by, create_time, update_by, update_time, version, deleted) +VALUES +-- 平台研发团队配置(可访问开发和测试环境,都不需要审批) +(1, '[1, 2]', '[false, false]', '[null, null]', 'admin', NOW(), 'admin', NOW(), 1, 0), +-- DevOps团队配置(可访问所有环境,生产环境需要审批,审批人是用户1和4) +(2, '[1, 2, 3]', '[false, false, true]', '[null, null, [1, 4]]', 'admin', NOW(), 'admin', NOW(), 1, 0); + -- ==================================================================== -- 定时任务初始化数据 -- ==================================================================== diff --git a/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql b/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql index 59c9bc16..887b21ad 100644 --- a/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql +++ b/backend/src/main/resources/db/changelog/changes/v1.0.0-schema.sql @@ -815,12 +815,42 @@ CREATE TABLE deploy_team_application team_id BIGINT NOT NULL COMMENT '团队ID', application_id BIGINT NOT NULL COMMENT '应用ID', + environment_id BIGINT NOT NULL COMMENT '环境ID', + branch VARCHAR(100) NULL COMMENT '分支名称', - UNIQUE INDEX uk_team_app (team_id, application_id), + UNIQUE INDEX uk_team_app_env (team_id, application_id, environment_id), + INDEX idx_team (team_id), INDEX idx_application (application_id), + INDEX idx_environment (environment_id), CONSTRAINT fk_team_app_team FOREIGN KEY (team_id) REFERENCES deploy_team(id), CONSTRAINT fk_team_app_application FOREIGN KEY (application_id) REFERENCES deploy_application(id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队应用关联表'; + +-- 团队配置表 +CREATE TABLE deploy_team_config +( + id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', + create_by VARCHAR(100) NULL COMMENT '创建人', + create_time DATETIME(6) NULL COMMENT '创建时间', + update_by VARCHAR(100) NULL COMMENT '更新人', + update_time DATETIME(6) NULL COMMENT '更新时间', + version INT NOT NULL DEFAULT 1 COMMENT '版本号', + deleted BIT NOT NULL DEFAULT 0 COMMENT '是否删除', + + team_id BIGINT NOT NULL COMMENT '团队ID', + + -- 环境权限配置 + allowed_environment_ids JSON NULL COMMENT '团队可访问的环境ID列表,如:[1, 2, 3]', + + -- 审批配置(与allowed_environment_ids位置对应) + environment_approval_required JSON NULL COMMENT '各环境是否需要审批(boolean数组),如:[false, false, true]', + approver_user_ids JSON NULL COMMENT '各环境的审批人列表(数组,无审批人用null),如:[null, null, [1, 4]]', + + UNIQUE INDEX uk_team (team_id), + INDEX idx_deleted (deleted), + CONSTRAINT fk_team_config_team FOREIGN KEY (team_id) REFERENCES deploy_team(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='团队配置表'; + CREATE TABLE deploy_log ( -- 基础字段 diff --git a/backend/src/main/resources/messages.properties b/backend/src/main/resources/messages.properties index 9da2f5c2..5c5d82bb 100644 --- a/backend/src/main/resources/messages.properties +++ b/backend/src/main/resources/messages.properties @@ -41,6 +41,7 @@ data.already.exists=数据已存在:{0} data.in.use=数据正在使用中,无法删除 entity.not.found.id=找不到ID为{0}的实体 entity.not.found.message={0} + entity.not.found.name.id=找不到ID为{1}的{0} # 用户相关 (2000-2099) @@ -224,3 +225,6 @@ schedule.job.category.in.use=任务分类正在使用中,无法删除 # 任务日志错误 (2830-2839) schedule.job.log.not.found=任务执行日志不存在 schedule.job.executor.not.found=找不到任务执行器:{0} + +# 团队配置相关错误消息 (2920-2949) +team.config.not.found=团队ID为 {0} 的配置不存在