工作流修复

This commit is contained in:
dengqichen 2025-10-31 15:07:57 +08:00
parent e38fea962c
commit 2ab9ebff70
22 changed files with 507 additions and 23 deletions

View File

@ -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<TeamConfig, TeamConfigDTO, Long, TeamConfigQuery> {
@Resource
private ITeamConfigService teamConfigService;
@Operation(summary = "根据团队ID获取配置", description = "获取指定团队的配置信息")
@GetMapping("/team/{teamId}")
public Response<TeamConfigDTO> getByTeamId(
@Parameter(description = "团队ID", required = true) @PathVariable Long teamId
) {
return Response.success(teamConfigService.getByTeamId(teamId));
}
@Override
protected void exportData(HttpServletResponse response, List<TeamConfigDTO> data) {
// TODO: 实现导出功能
}
}

View File

@ -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<TeamConfig, TeamConfigDTO> {
}

View File

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

View File

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

View File

@ -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<Long> allowedEnvironmentIds;
@Schema(description = "环境是否需要审批与allowedEnvironmentIds位置对应", example = "[false, false, true]")
private List<Boolean> environmentApprovalRequired;
@Schema(description = "各环境的审批人列表与allowedEnvironmentIds位置对应无审批人用null", example = "[null, null, [1, 4]]")
private List<Object> approverUserIds;
}

View File

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

View File

@ -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<Long> {
/**
* 团队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;
}

View File

@ -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<Long> {
/**
* 团队ID
*/
@Column(name = "team_id", nullable = false)
private Long teamId;
/**
* 允许访问的环境ID列表
*/
@Type(JsonType.class)
@Column(name = "allowed_environment_ids", columnDefinition = "JSON")
private List<Long> 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<Boolean> 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<Object> approverUserIds;
}

View File

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

View File

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

View File

@ -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<TeamApplication, Long> {
/**
* 统计团队关联的应用数量
* 统计团队的应用配置数量不去重物理删除无需deleted条件
*/
Long countByTeamIdAndDeletedFalse(Long teamId);
Long countByTeamId(Long teamId);
/**
* 统计应用被哪些团队关联
@ -20,13 +28,45 @@ public interface ITeamApplicationRepository extends IBaseRepository<TeamApplicat
Long countByApplicationIdAndDeletedFalse(Long applicationId);
/**
* 检查团队是否已关联该应用
* 检查团队应用环境组合是否已存在
*/
boolean existsByTeamIdAndApplicationIdAndDeletedFalse(Long teamId, Long applicationId);
boolean existsByTeamIdAndApplicationIdAndEnvironmentIdAndDeletedFalse(Long teamId, Long applicationId, Long environmentId);
/**
* 根据团队ID和应用ID查询
* 根据团队ID应用ID和环境ID查询
*/
Optional<TeamApplication> findByTeamIdAndApplicationIdAndDeletedFalse(Long teamId, Long applicationId);
Optional<TeamApplication> 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);
}

View File

@ -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<TeamConfig, Long> {
/**
* 根据团队ID查询配置
*
* @param teamId 团队ID
* @return 团队配置
*/
Optional<TeamConfig> findByTeamIdAndDeletedFalse(Long teamId);
/**
* 检查团队配置是否存在
*
* @param teamId 团队ID
* @return 是否存在
*/
boolean existsByTeamIdAndDeletedFalse(Long teamId);
}

View File

@ -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<TeamConfig, TeamConfigDTO, TeamConfigQuery, Long> {
/**
* 根据团队ID获取配置
*
* @param teamId 团队ID
* @return 团队配置
*/
TeamConfigDTO getByTeamId(Long teamId);
}

View File

@ -63,9 +63,9 @@ public class ApplicationServiceImpl extends BaseServiceImpl<Application, Applica
repositoryProjectOptional.ifPresent(repositoryProject -> 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());
}

View File

@ -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<Environment, Environ
@Resource
private EntityManager entityManager;
@Resource
private ITeamApplicationRepository teamApplicationRepository;
// @Override
// @Transactional
// public EnvironmentDTO create(EnvironmentDTO dto) {
// return this.create(dto);
// }
@Override
public Page<EnvironmentDTO> page(EnvironmentQuery query) {
Page<EnvironmentDTO> page = super.page(query);
// 填充统计数据
List<EnvironmentDTO> 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<EnvironmentDTO> findAll(EnvironmentQuery query) {
List<EnvironmentDTO> 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<EnvironmentDTO> getProjectEnvironments(Long projectGroupId) {

View File

@ -44,8 +44,9 @@ public class TeamApplicationServiceImpl extends BaseServiceImpl<TeamApplication,
@Override
@Transactional
public TeamApplicationDTO create(TeamApplicationDTO dto) {
// 检查是否已关联
if (teamApplicationRepository.existsByTeamIdAndApplicationIdAndDeletedFalse(dto.getTeamId(), dto.getApplicationId())) {
// 检查团队+应用+环境组合是否已存在
if (teamApplicationRepository.existsByTeamIdAndApplicationIdAndEnvironmentIdAndDeletedFalse(
dto.getTeamId(), dto.getApplicationId(), dto.getEnvironmentId())) {
throw new BusinessException(ResponseCode.TEAM_APPLICATION_ALREADY_EXISTS);
}

View File

@ -0,0 +1,42 @@
package com.qqchen.deploy.backend.deploy.service.impl;
import com.qqchen.deploy.backend.deploy.converter.TeamConfigConverter;
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.repository.ITeamConfigRepository;
import com.qqchen.deploy.backend.deploy.service.ITeamConfigService;
import com.qqchen.deploy.backend.framework.annotation.ServiceType;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException;
import com.qqchen.deploy.backend.framework.service.impl.BaseServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 团队配置Service实现
*
* @author qqchen
* @since 2025-10-31
*/
@Slf4j
@Service
@ServiceType(ServiceType.Type.DATABASE)
public class TeamConfigServiceImpl extends BaseServiceImpl<TeamConfig, TeamConfigDTO, TeamConfigQuery, Long>
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);
}
}

View File

@ -58,7 +58,7 @@ public class TeamServiceImpl extends BaseServiceImpl<Team, TeamDTO, TeamQuery, L
}
// 检查是否有关联应用
Long applicationCount = teamApplicationRepository.countByTeamIdAndDeletedFalse(id);
Long applicationCount = teamApplicationRepository.countByTeamId(id);
if (applicationCount > 0) {
throw new BusinessException(ResponseCode.TEAM_HAS_APPLICATIONS);
}
@ -70,17 +70,46 @@ public class TeamServiceImpl extends BaseServiceImpl<Team, TeamDTO, TeamQuery, L
public Page<TeamDTO> page(TeamQuery query) {
Page<TeamDTO> page = super.page(query);
// 统计每个团队的成员数量和应用数量
// 统计每个团队的成员数量环境数量和应用配置数量
List<TeamDTO> 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<TeamDTO> findAll(TeamQuery query) {
List<TeamDTO> 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());
}
}

View File

@ -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"),

View File

@ -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);
-- ====================================================================
-- 定时任务初始化数据
-- ====================================================================

View File

@ -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
(
-- 基础字段

View File

@ -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} 的配置不存在