diff --git a/backend/.cursorignore b/backend/.cursorignore new file mode 100644 index 00000000..226624be --- /dev/null +++ b/backend/.cursorignore @@ -0,0 +1,2 @@ +# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) +target/* \ No newline at end of file diff --git a/backend/docs/团队管理功能开发指南.md b/backend/docs/团队管理功能开发指南.md deleted file mode 100644 index e014d185..00000000 --- a/backend/docs/团队管理功能开发指南.md +++ /dev/null @@ -1,1434 +0,0 @@ -# 团队管理功能开发指南 - -## 📋 目标 - -实现技术团队管理功能,支持: -- 产品线和团队的层级管理 -- 团队成员管理(支持跨部门) -- 团队与应用的分支级别关联(一个团队负责多个应用的特定分支) -- 团队-应用-环境的差异化配置 - -## 🎯 核心设计 - -### 关系层级 -``` -deploy_product_line (产品线) - ↓ (1:N) -deploy_team (开发团队) - ↓ (M:N with branch_pattern) -deploy_team_application (团队-应用关联,支持分支模式) - ↓ (关联到) -deploy_application (应用) - ↓ (M:N with team-specific config) -deploy_application_environment_config (应用-环境配置,支持团队差异化) - ↓ -deploy_environment (环境) -``` - -### 关键特性 -1. **产品线管理**: `deploy_project_group` 重命名为 `deploy_product_line`,更清晰表达业务含义 -2. **分支级别关联**: 团队可以负责应用的特定分支(如 `feature/*`, `release/*`) -3. **差异化配置**: 同一应用在同一环境,不同团队可以有不同的配置(如资源限制、环境变量) - -## 🎯 开发步骤 - -### Phase 1: 数据库设计和迁移 - -#### 步骤 1.1: 创建数据库迁移脚本 - -**文件**: `src/main/resources/db/changelog/changes/v1.0.2-team-management.sql` - -```sql --- ===================================================================== --- 团队管理功能表结构 v2.0 --- 关键改动: --- 1. deploy_project_group -> deploy_product_line(重命名) --- 2. deploy_team_application 增加 branch_pattern(支持分支级别管理) --- 3. 新增 deploy_application_environment_config(支持团队差异化配置) --- ===================================================================== - --- ===================================================================== --- 第一步:重命名产品线表 --- ===================================================================== - --- 重命名表名 -RENAME TABLE deploy_project_group TO deploy_product_line; - --- 重命名字段(保持语义一致) -ALTER TABLE deploy_product_line - CHANGE COLUMN project_group_code product_line_code VARCHAR(50) NOT NULL COMMENT '产品线编码', - CHANGE COLUMN project_group_name product_line_name VARCHAR(100) NOT NULL COMMENT '产品线名称', - CHANGE COLUMN project_group_desc product_line_desc VARCHAR(255) NULL COMMENT '产品线描述'; - --- 更新注释 -ALTER TABLE deploy_product_line COMMENT='产品线表(原项目组表)'; - --- ===================================================================== --- 第二步:创建团队表 --- ===================================================================== - --- 1. 技术团队表 -CREATE TABLE deploy_team ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - - -- 基本信息 - team_code VARCHAR(50) NOT NULL COMMENT '团队编码(如:USER_SERVICE_TEAM)', - team_name VARCHAR(100) NOT NULL COMMENT '团队名称(如:用户服务团队)', - team_desc VARCHAR(500) NULL COMMENT '团队描述', - - -- 产品线关联 - product_line_id BIGINT NOT NULL COMMENT '所属产品线ID', - - -- 团队负责人 - leader_id BIGINT NULL COMMENT '团队负责人ID', - - enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', - sort INT NOT NULL DEFAULT 0 COMMENT '排序号', - - -- 基础字段 - 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 '是否删除', - - UNIQUE INDEX uk_team_code (team_code), - INDEX idx_leader_id (leader_id), - INDEX idx_product_line_id (product_line_id), - - CONSTRAINT fk_team_leader FOREIGN KEY (leader_id) REFERENCES sys_user (id), - CONSTRAINT fk_team_product_line FOREIGN KEY (product_line_id) REFERENCES deploy_product_line (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci -COMMENT='技术团队表'; - --- 2. 团队成员表 -CREATE TABLE deploy_team_member ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - - team_id BIGINT NOT NULL COMMENT '团队ID', - user_id BIGINT NOT NULL COMMENT '用户ID', - - join_time DATETIME(6) NULL COMMENT '加入时间', - - -- 基础字段 - 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 '是否删除', - - UNIQUE INDEX uk_team_user (team_id, user_id), - INDEX idx_user_id (user_id), - - CONSTRAINT fk_team_member_team FOREIGN KEY (team_id) REFERENCES deploy_team (id), - CONSTRAINT fk_team_member_user FOREIGN KEY (user_id) REFERENCES sys_user (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci -COMMENT='团队成员表'; - --- 3. 团队应用关联表(支持分支模式) -CREATE TABLE deploy_team_application ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - - team_id BIGINT NOT NULL COMMENT '团队ID', - application_id BIGINT NOT NULL COMMENT '应用ID', - - -- ✅ 新增:分支模式匹配 - branch_pattern VARCHAR(255) NULL COMMENT '负责的分支模式(如:feature/*、release/*、master、develop)', - - responsibility VARCHAR(50) NOT NULL DEFAULT 'PRIMARY' COMMENT '职责类型:PRIMARY-主要负责,SECONDARY-协作支持', - - -- 基础字段 - 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 '是否删除', - - UNIQUE INDEX uk_team_app_branch (team_id, application_id, branch_pattern), - INDEX idx_application_id (application_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='团队应用关联表(支持分支级别管理)'; - --- 4. 应用环境配置表(支持团队差异化配置) -CREATE TABLE deploy_application_environment_config ( - id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID', - - application_id BIGINT NOT NULL COMMENT '应用ID', - environment_id BIGINT NOT NULL COMMENT '环境ID', - team_id BIGINT NULL COMMENT '团队ID(为NULL时表示默认配置,非NULL时表示团队专属配置)', - - -- 配置内容(JSON格式) - config JSON NULL COMMENT '环境配置(如:资源限制、环境变量、部署参数等)', - - enabled BIT NOT NULL DEFAULT 1 COMMENT '是否启用', - - -- 基础字段 - 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 '是否删除', - - UNIQUE INDEX uk_app_env_team (application_id, environment_id, team_id), - INDEX idx_environment_id (environment_id), - INDEX idx_team_id (team_id), - - CONSTRAINT fk_app_env_config_application FOREIGN KEY (application_id) REFERENCES deploy_application (id), - CONSTRAINT fk_app_env_config_environment FOREIGN KEY (environment_id) REFERENCES deploy_environment (id), - CONSTRAINT fk_app_env_config_team FOREIGN KEY (team_id) REFERENCES deploy_team (id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci -COMMENT='应用环境配置表(支持团队差异化配置)'; - --- ===================================================================== --- 第三步:清理旧表 --- ===================================================================== - --- 删除旧的产品线环境关联表 -DROP TABLE IF EXISTS deploy_project_group_environment; - --- ===================================================================== --- 第四步:调整 deploy_application 表 --- ===================================================================== - --- 更新产品线关联(如果存在旧的 project_group_id 字段) -ALTER TABLE deploy_application - DROP FOREIGN KEY IF EXISTS fk_application_project_group; - --- 删除冗余字段 -ALTER TABLE deploy_application - DROP COLUMN IF EXISTS project_group_id, - DROP COLUMN IF EXISTS repo_group_id, - DROP COLUMN IF EXISTS external_system_id; - --- ===================================================================== --- 第五步:优化 deploy_log 表 --- ===================================================================== - -ALTER TABLE deploy_log - DROP COLUMN IF EXISTS form_variables, - DROP COLUMN IF EXISTS deploy_variables, - ADD COLUMN deploy_branch VARCHAR(200) NULL COMMENT '部署分支', - ADD COLUMN team_id BIGINT NULL COMMENT '部署团队ID', - ADD COLUMN deploy_status VARCHAR(50) NULL COMMENT '部署状态:SUCCESS,FAILED,IN_PROGRESS', - ADD COLUMN start_time DATETIME(6) NULL COMMENT '开始时间', - ADD COLUMN end_time DATETIME(6) NULL COMMENT '结束时间', - ADD COLUMN error_message TEXT NULL COMMENT '错误信息', - ADD INDEX idx_team_id (team_id); -``` - -#### 步骤 1.2: 注册迁移脚本 - -**文件**: `src/main/resources/db/changelog/db.changelog-master.yaml` - -```yaml - - changeSet: - id: v1.0.2-team-management - author: qqchen - runOnChange: false - failOnError: true - comment: "团队管理功能表结构" - sqlFile: - path: db/changelog/changes/v1.0.2-team-management.sql - stripComments: false - splitStatements: true - endDelimiter: ";" - rollback: - - sql: "DROP TABLE IF EXISTS deploy_team_environment;" - - sql: "DROP TABLE IF EXISTS deploy_team_application;" - - sql: "DROP TABLE IF EXISTS deploy_team_member;" - - sql: "DROP TABLE IF EXISTS deploy_team;" -``` - ---- - -### Phase 2: 枚举类创建 - -#### 步骤 2.1: 创建团队类型枚举 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/enums/TeamTypeEnum.java` - -```java -package com.qqchen.deploy.backend.deploy.enums; - -import lombok.Getter; - -/** - * 团队类型枚举 - */ -@Getter -public enum TeamTypeEnum { - BACKEND("后端团队"), - FRONTEND("前端团队"), - FULLSTACK("全栈团队"), - OPS("运维团队"), - QA("测试团队"); - - private final String description; - - TeamTypeEnum(String description) { - this.description = description; - } -} -``` - -#### 步骤 2.2: 创建团队角色枚举 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/enums/TeamRoleEnum.java` - -```java -package com.qqchen.deploy.backend.deploy.enums; - -import lombok.Getter; - -/** - * 团队角色枚举 - */ -@Getter -public enum TeamRoleEnum { - LEADER("技术负责人"), - BACKEND_DEVELOPER("后端开发"), - FRONTEND_DEVELOPER("前端开发"), - TESTER("测试工程师"), - OPS("运维工程师"), - ARCHITECT("架构师"); - - private final String description; - - TeamRoleEnum(String description) { - this.description = description; - } -} -``` - -#### 步骤 2.3: 创建团队职责枚举 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/enums/TeamResponsibilityEnum.java` - -```java -package com.qqchen.deploy.backend.deploy.enums; - -import lombok.Getter; - -/** - * 团队应用职责类型枚举 - */ -@Getter -public enum TeamResponsibilityEnum { - PRIMARY("主要负责"), - SECONDARY("协作支持"); - - private final String description; - - TeamResponsibilityEnum(String description) { - this.description = description; - } -} -``` - ---- - -### Phase 3: 实体类创建 - -#### 步骤 3.1: 创建团队实体 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/entity/Team.java` - -```java -package com.qqchen.deploy.backend.deploy.entity; - -import com.qqchen.deploy.backend.deploy.enums.TeamTypeEnum; -import com.qqchen.deploy.backend.framework.domain.Entity; -import jakarta.persistence.*; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 技术团队实体 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@jakarta.persistence.Entity -@Table(name = "deploy_team") -public class Team extends Entity { - - @Column(name = "team_code", nullable = false, length = 50) - private String teamCode; - - @Column(name = "team_name", nullable = false, length = 100) - private String teamName; - - @Column(name = "team_desc", length = 500) - private String teamDesc; - - @Column(name = "product_line_id", nullable = false) - private Long productLineId; - - @Column(name = "leader_id") - private Long leaderId; - - @Column(name = "enabled", nullable = false) - private Boolean enabled = true; - - @Column(name = "sort", nullable = false) - private Integer sort = 0; -} -``` - -#### 步骤 3.2: 创建团队成员实体 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamMember.java` - -```java -package com.qqchen.deploy.backend.deploy.entity; - -import com.qqchen.deploy.backend.deploy.enums.TeamRoleEnum; -import com.qqchen.deploy.backend.framework.domain.Entity; -import jakarta.persistence.*; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.time.LocalDateTime; - -/** - * 团队成员实体 - */ -@Data -@EqualsAndHashCode(callSuper = true) -@jakarta.persistence.Entity -@Table(name = "deploy_team_member") -public class TeamMember extends Entity { - - @Column(name = "team_id", nullable = false) - private Long teamId; - - @Column(name = "user_id", nullable = false) - private Long userId; - - @Enumerated(EnumType.STRING) - @Column(name = "role_in_team", nullable = false, length = 50) - private TeamRoleEnum roleInTeam; - - @Column(name = "join_time") - private LocalDateTime joinTime; -} -``` - -#### 步骤 3.3: 创建团队应用关联实体 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/entity/TeamApplication.java` - -```java -package com.qqchen.deploy.backend.deploy.entity; - -import com.qqchen.deploy.backend.deploy.enums.TeamResponsibilityEnum; -import com.qqchen.deploy.backend.framework.domain.Entity; -import jakarta.persistence.*; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 团队应用关联实体(支持分支级别管理) - */ -@Data -@EqualsAndHashCode(callSuper = true) -@jakarta.persistence.Entity -@Table(name = "deploy_team_application") -public class TeamApplication extends Entity { - - @Column(name = "team_id", nullable = false) - private Long teamId; - - @Column(name = "application_id", nullable = false) - private Long applicationId; - - @Column(name = "branch_pattern", length = 255) - private String branchPattern; - - @Enumerated(EnumType.STRING) - @Column(name = "responsibility", nullable = false, length = 50) - private TeamResponsibilityEnum responsibility = TeamResponsibilityEnum.PRIMARY; -} -``` - -#### 步骤 3.4: 创建应用环境配置实体 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/entity/ApplicationEnvironmentConfig.java` - -```java -package com.qqchen.deploy.backend.deploy.entity; - -import com.qqchen.deploy.backend.framework.domain.Entity; -import com.vladmihalcea.hibernate.type.json.JsonType; -import jakarta.persistence.*; -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.hibernate.annotations.Type; - -import java.util.Map; - -/** - * 应用环境配置实体(支持团队差异化配置) - */ -@Data -@EqualsAndHashCode(callSuper = true) -@jakarta.persistence.Entity -@Table(name = "deploy_application_environment_config") -public class ApplicationEnvironmentConfig extends Entity { - - @Column(name = "application_id", nullable = false) - private Long applicationId; - - @Column(name = "environment_id", nullable = false) - private Long environmentId; - - @Column(name = "team_id") - private Long teamId; - - @Type(JsonType.class) - @Column(name = "config", columnDefinition = "json") - private Map config; - - @Column(name = "enabled", nullable = false) - private Boolean enabled = true; -} -``` - ---- - -### Phase 4: Repository 层 - -#### 步骤 4.1: 创建团队 Repository - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamRepository.java` - -```java -package com.qqchen.deploy.backend.deploy.repository; - -import com.qqchen.deploy.backend.deploy.entity.Team; -import com.qqchen.deploy.backend.framework.repository.IBaseRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface ITeamRepository extends IBaseRepository { - - /** - * 根据团队编码查询(忽略已删除) - */ - Optional findByTeamCodeAndDeletedFalse(String teamCode); - - /** - * 检查团队编码是否存在 - */ - boolean existsByTeamCodeAndDeletedFalse(String teamCode); -} -``` - -#### 步骤 4.2: 创建团队成员 Repository - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamMemberRepository.java` - -```java -package com.qqchen.deploy.backend.deploy.repository; - -import com.qqchen.deploy.backend.deploy.entity.TeamMember; -import com.qqchen.deploy.backend.framework.repository.IBaseRepository; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -@Repository -public interface ITeamMemberRepository extends IBaseRepository { - - /** - * 根据团队ID查询成员列表 - */ - List findByTeamIdAndDeletedFalse(Long teamId); - - /** - * 根据用户ID查询所属团队 - */ - List findByUserIdAndDeletedFalse(Long userId); - - /** - * 检查用户是否在团队中 - */ - boolean existsByTeamIdAndUserIdAndDeletedFalse(Long teamId, Long userId); - - /** - * 查询用户在团队中的记录 - */ - Optional findByTeamIdAndUserIdAndDeletedFalse(Long teamId, Long userId); -} -``` - -#### 步骤 4.3: 创建团队应用 Repository - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/repository/ITeamApplicationRepository.java` - -```java -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.stereotype.Repository; - -import java.util.List; - -@Repository -public interface ITeamApplicationRepository extends IBaseRepository { - - /** - * 根据团队ID查询关联的应用 - */ - List findByTeamIdAndDeletedFalse(Long teamId); - - /** - * 根据应用ID查询关联的团队 - */ - List findByApplicationIdAndDeletedFalse(Long applicationId); - - /** - * 检查团队和应用的关联是否存在 - */ - boolean existsByTeamIdAndApplicationIdAndDeletedFalse(Long teamId, Long applicationId); -} -``` - -#### 步骤 4.4: 创建应用环境配置 Repository - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/repository/IApplicationEnvironmentConfigRepository.java` - -```java -package com.qqchen.deploy.backend.deploy.repository; - -import com.qqchen.deploy.backend.deploy.entity.ApplicationEnvironmentConfig; -import com.qqchen.deploy.backend.framework.repository.IBaseRepository; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -@Repository -public interface IApplicationEnvironmentConfigRepository extends IBaseRepository { - - /** - * 根据应用ID查询环境配置 - */ - List findByApplicationIdAndDeletedFalse(Long applicationId); - - /** - * 根据应用ID和环境ID查询默认配置(团队ID为NULL) - */ - Optional findByApplicationIdAndEnvironmentIdAndTeamIdIsNullAndDeletedFalse( - Long applicationId, Long environmentId); - - /** - * 根据应用ID、环境ID和团队ID查询团队专属配置 - */ - Optional findByApplicationIdAndEnvironmentIdAndTeamIdAndDeletedFalse( - Long applicationId, Long environmentId, Long teamId); - - /** - * 检查配置是否存在 - */ - boolean existsByApplicationIdAndEnvironmentIdAndTeamIdAndDeletedFalse( - Long applicationId, Long environmentId, Long teamId); -} -``` - ---- - -### Phase 5: DTO 和 Query - -#### 步骤 5.1: 创建团队 DTO - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamDTO.java` - -```java -package com.qqchen.deploy.backend.deploy.dto; - -import com.qqchen.deploy.backend.deploy.enums.TeamTypeEnum; -import com.qqchen.deploy.backend.framework.dto.BaseDTO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.List; - -@Data -@EqualsAndHashCode(callSuper = true) -@Schema(description = "团队信息") -public class TeamDTO extends BaseDTO { - - @Schema(description = "团队编码", example = "USER_SERVICE_TEAM") - private String teamCode; - - @Schema(description = "团队名称", example = "用户服务团队") - private String teamName; - - @Schema(description = "团队描述") - private String teamDesc; - - @Schema(description = "所属产品线ID") - private Long productLineId; - - @Schema(description = "负责人ID") - private Long leaderId; - - @Schema(description = "是否启用") - private Boolean enabled; - - @Schema(description = "排序号") - private Integer sort; - - // 扩展字段(用于展示) - @Schema(description = "产品线名称") - private String productLineName; - - @Schema(description = "团队成员列表") - private List members; - - @Schema(description = "关联应用列表") - private List applicationIds; -} -``` - -#### 步骤 5.2: 创建团队成员 DTO - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/dto/TeamMemberDTO.java` - -```java -package com.qqchen.deploy.backend.deploy.dto; - -import com.qqchen.deploy.backend.deploy.enums.TeamRoleEnum; -import com.qqchen.deploy.backend.framework.dto.BaseDTO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.time.LocalDateTime; - -@Data -@EqualsAndHashCode(callSuper = true) -@Schema(description = "团队成员信息") -public class TeamMemberDTO extends BaseDTO { - - @Schema(description = "团队ID") - private Long teamId; - - @Schema(description = "用户ID") - private Long userId; - - @Schema(description = "团队角色") - private TeamRoleEnum roleInTeam; - - @Schema(description = "加入时间") - private LocalDateTime joinTime; - - // 扩展字段 - @Schema(description = "用户名") - private String username; - - @Schema(description = "真实姓名") - private String realName; - - @Schema(description = "邮箱") - private String email; -} -``` - -#### 步骤 5.3: 创建查询对象 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/query/TeamQuery.java` - -```java -package com.qqchen.deploy.backend.deploy.query; - -import com.qqchen.deploy.backend.deploy.enums.TeamTypeEnum; -import com.qqchen.deploy.backend.framework.query.BaseQuery; -import com.qqchen.deploy.backend.framework.query.QueryField; -import com.qqchen.deploy.backend.framework.query.QueryType; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Data -@EqualsAndHashCode(callSuper = true) -@Schema(description = "团队查询条件") -public class TeamQuery extends BaseQuery { - - @QueryField(field = "teamName", type = QueryType.LIKE) - @Schema(description = "团队名称(模糊查询)") - private String teamName; - - @QueryField(field = "teamCode", type = QueryType.LIKE) - @Schema(description = "团队编码(模糊查询)") - private String teamCode; - - @QueryField(field = "productLineId") - @Schema(description = "产品线ID") - private Long productLineId; - - @QueryField(field = "leaderId") - @Schema(description = "负责人ID") - private Long leaderId; - - @QueryField(field = "enabled") - @Schema(description = "是否启用") - private Boolean enabled; -} -``` - ---- - -### Phase 6: Converter - -#### 步骤 6.1: 创建团队 Converter - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamConverter.java` - -```java -package com.qqchen.deploy.backend.deploy.converter; - -import com.qqchen.deploy.backend.deploy.dto.TeamDTO; -import com.qqchen.deploy.backend.deploy.entity.Team; -import com.qqchen.deploy.backend.framework.converter.BaseConverter; -import org.mapstruct.Mapper; - -@Mapper(config = BaseConverter.class) -public interface TeamConverter extends BaseConverter { -} -``` - -#### 步骤 6.2: 创建团队成员 Converter - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/converter/TeamMemberConverter.java` - -```java -package com.qqchen.deploy.backend.deploy.converter; - -import com.qqchen.deploy.backend.deploy.dto.TeamMemberDTO; -import com.qqchen.deploy.backend.deploy.entity.TeamMember; -import com.qqchen.deploy.backend.framework.converter.BaseConverter; -import org.mapstruct.Mapper; - -@Mapper(config = BaseConverter.class) -public interface TeamMemberConverter extends BaseConverter { -} -``` - ---- - -### Phase 7: Service 层 - -#### 步骤 7.1: 创建团队 Service 接口 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/service/ITeamService.java` - -```java -package com.qqchen.deploy.backend.deploy.service; - -import com.qqchen.deploy.backend.deploy.dto.TeamDTO; -import com.qqchen.deploy.backend.deploy.dto.TeamMemberDTO; -import com.qqchen.deploy.backend.deploy.entity.Team; -import com.qqchen.deploy.backend.deploy.query.TeamQuery; -import com.qqchen.deploy.backend.framework.service.IBaseService; - -import java.util.List; - -public interface ITeamService extends IBaseService { - - /** - * 添加团队成员 - */ - void addMember(Long teamId, Long userId, String roleInTeam); - - /** - * 移除团队成员 - */ - void removeMember(Long teamId, Long userId); - - /** - * 获取团队成员列表 - */ - List getTeamMembers(Long teamId); - - /** - * 关联应用(支持分支模式) - * @param teamId 团队ID - * @param applicationId 应用ID - * @param branchPattern 分支模式(如:feature/*、release/*、master) - * @param responsibility 职责类型 - */ - void bindApplication(Long teamId, Long applicationId, String branchPattern, String responsibility); - - /** - * 解绑应用 - */ - void unbindApplication(Long teamId, Long applicationId, String branchPattern); - - /** - * 创建应用环境配置 - * @param applicationId 应用ID - * @param environmentId 环境ID - * @param teamId 团队ID(可选,NULL表示默认配置) - * @param config 配置内容 - */ - void createEnvironmentConfig(Long applicationId, Long environmentId, Long teamId, Map config); - - /** - * 更新应用环境配置 - */ - void updateEnvironmentConfig(Long configId, Map config); - - /** - * 删除应用环境配置 - */ - void deleteEnvironmentConfig(Long configId); -} -``` - -#### 步骤 7.2: 创建团队 Service 实现 - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/service/impl/TeamServiceImpl.java` - -```java -package com.qqchen.deploy.backend.deploy.service.impl; - -import com.qqchen.deploy.backend.deploy.converter.TeamConverter; -import com.qqchen.deploy.backend.deploy.converter.TeamMemberConverter; -import com.qqchen.deploy.backend.deploy.dto.TeamDTO; -import com.qqchen.deploy.backend.deploy.dto.TeamMemberDTO; -import com.qqchen.deploy.backend.deploy.entity.*; -import com.qqchen.deploy.backend.deploy.enums.TeamResponsibilityEnum; -import com.qqchen.deploy.backend.deploy.enums.TeamRoleEnum; -import com.qqchen.deploy.backend.deploy.query.TeamQuery; -import com.qqchen.deploy.backend.deploy.repository.*; -import com.qqchen.deploy.backend.deploy.service.ITeamService; -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 com.qqchen.deploy.backend.system.repository.IUserRepository; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.stream.Collectors; - -@Slf4j -@Service -public class TeamServiceImpl extends BaseServiceImpl - implements ITeamService { - - @Resource - private ITeamRepository teamRepository; - - @Resource - private ITeamMemberRepository teamMemberRepository; - - @Resource - private ITeamApplicationRepository teamApplicationRepository; - - @Resource - private IApplicationEnvironmentConfigRepository applicationEnvironmentConfigRepository; - - @Resource - private IUserRepository userRepository; - - @Resource - private TeamConverter teamConverter; - - @Resource - private TeamMemberConverter teamMemberConverter; - - @Override - @Transactional - public TeamDTO create(TeamDTO dto) { - // 检查团队编码唯一性 - if (teamRepository.existsByTeamCodeAndDeletedFalse(dto.getTeamCode())) { - throw new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"团队编码已存在: " + dto.getTeamCode()}); - } - - return super.create(dto); - } - - @Override - @Transactional - public void addMember(Long teamId, Long userId, String roleInTeam) { - // 检查团队是否存在 - teamRepository.findById(teamId) - .orElseThrow(() -> new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"团队不存在"})); - - // 检查用户是否存在 - userRepository.findById(userId) - .orElseThrow(() -> new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"用户不存在"})); - - // 检查是否已经是成员 - if (teamMemberRepository.existsByTeamIdAndUserIdAndDeletedFalse(teamId, userId)) { - throw new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"用户已经是团队成员"}); - } - - // 创建成员记录 - TeamMember member = new TeamMember(); - member.setTeamId(teamId); - member.setUserId(userId); - member.setRoleInTeam(TeamRoleEnum.valueOf(roleInTeam)); - member.setJoinTime(LocalDateTime.now()); - - teamMemberRepository.save(member); - log.info("添加团队成员: teamId={}, userId={}, role={}", teamId, userId, roleInTeam); - } - - @Override - @Transactional - public void removeMember(Long teamId, Long userId) { - TeamMember member = teamMemberRepository.findByTeamIdAndUserIdAndDeletedFalse(teamId, userId) - .orElseThrow(() -> new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"用户不是团队成员"})); - - member.setDeleted(true); - teamMemberRepository.save(member); - log.info("移除团队成员: teamId={}, userId={}", teamId, userId); - } - - @Override - public List getTeamMembers(Long teamId) { - List members = teamMemberRepository.findByTeamIdAndDeletedFalse(teamId); - return members.stream() - .map(teamMemberConverter::toDto) - .collect(Collectors.toList()); - } - - @Override - @Transactional - public void bindApplication(Long teamId, Long applicationId, String branchPattern, String responsibility) { - // 检查团队-应用-分支的组合是否已存在 - List existingBindings = teamApplicationRepository - .findByTeamIdAndDeletedFalse(teamId).stream() - .filter(ta -> ta.getApplicationId().equals(applicationId) - && Objects.equals(ta.getBranchPattern(), branchPattern)) - .collect(Collectors.toList()); - - if (!existingBindings.isEmpty()) { - throw new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"团队已关联该应用的指定分支模式"}); - } - - TeamApplication teamApp = new TeamApplication(); - teamApp.setTeamId(teamId); - teamApp.setApplicationId(applicationId); - teamApp.setBranchPattern(branchPattern); - teamApp.setResponsibility(TeamResponsibilityEnum.valueOf(responsibility)); - - teamApplicationRepository.save(teamApp); - log.info("团队关联应用: teamId={}, applicationId={}, branchPattern={}, responsibility={}", - teamId, applicationId, branchPattern, responsibility); - } - - @Override - @Transactional - public void unbindApplication(Long teamId, Long applicationId, String branchPattern) { - List teamApps = teamApplicationRepository - .findByTeamIdAndDeletedFalse(teamId).stream() - .filter(ta -> ta.getApplicationId().equals(applicationId) - && Objects.equals(ta.getBranchPattern(), branchPattern)) - .collect(Collectors.toList()); - - teamApps.forEach(ta -> { - ta.setDeleted(true); - teamApplicationRepository.save(ta); - }); - - log.info("团队解绑应用: teamId={}, applicationId={}, branchPattern={}", - teamId, applicationId, branchPattern); - } - - @Override - @Transactional - public void createEnvironmentConfig(Long applicationId, Long environmentId, Long teamId, Map config) { - // 检查配置是否已存在 - if (applicationEnvironmentConfigRepository.existsByApplicationIdAndEnvironmentIdAndTeamIdAndDeletedFalse( - applicationId, environmentId, teamId)) { - throw new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"该应用环境配置已存在"}); - } - - ApplicationEnvironmentConfig envConfig = new ApplicationEnvironmentConfig(); - envConfig.setApplicationId(applicationId); - envConfig.setEnvironmentId(environmentId); - envConfig.setTeamId(teamId); - envConfig.setConfig(config); - envConfig.setEnabled(true); - - applicationEnvironmentConfigRepository.save(envConfig); - log.info("创建环境配置: applicationId={}, environmentId={}, teamId={}", - applicationId, environmentId, teamId); - } - - @Override - @Transactional - public void updateEnvironmentConfig(Long configId, Map config) { - ApplicationEnvironmentConfig envConfig = applicationEnvironmentConfigRepository.findById(configId) - .orElseThrow(() -> new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"环境配置不存在"})); - - envConfig.setConfig(config); - applicationEnvironmentConfigRepository.save(envConfig); - log.info("更新环境配置: configId={}", configId); - } - - @Override - @Transactional - public void deleteEnvironmentConfig(Long configId) { - ApplicationEnvironmentConfig envConfig = applicationEnvironmentConfigRepository.findById(configId) - .orElseThrow(() -> new BusinessException(ResponseCode.SYSTEM_ERROR, - new Object[]{"环境配置不存在"})); - - envConfig.setDeleted(true); - applicationEnvironmentConfigRepository.save(envConfig); - log.info("删除环境配置: configId={}", configId); - } -} -``` - ---- - -### Phase 8: Controller 层 - -#### 步骤 8.1: 创建团队管理 Controller - -**文件**: `src/main/java/com/qqchen/deploy/backend/deploy/api/TeamApiController.java` - -```java -package com.qqchen.deploy.backend.deploy.api; - -import com.qqchen.deploy.backend.deploy.dto.TeamDTO; -import com.qqchen.deploy.backend.deploy.dto.TeamMemberDTO; -import com.qqchen.deploy.backend.deploy.entity.Team; -import com.qqchen.deploy.backend.deploy.query.TeamQuery; -import com.qqchen.deploy.backend.deploy.service.ITeamService; -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 lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@Slf4j -@RestController -@RequestMapping("/api/v1/team") -@Tag(name = "团队管理", description = "技术团队管理相关接口") -public class TeamApiController extends BaseController { - - @Resource - private ITeamService teamService; - - @Operation(summary = "添加团队成员") - @PostMapping("/{teamId}/member") - public Response addMember( - @Parameter(description = "团队ID") @PathVariable Long teamId, - @Parameter(description = "用户ID") @RequestParam Long userId, - @Parameter(description = "团队角色") @RequestParam String roleInTeam - ) { - teamService.addMember(teamId, userId, roleInTeam); - return Response.success(); - } - - @Operation(summary = "移除团队成员") - @DeleteMapping("/{teamId}/member/{userId}") - public Response removeMember( - @Parameter(description = "团队ID") @PathVariable Long teamId, - @Parameter(description = "用户ID") @PathVariable Long userId - ) { - teamService.removeMember(teamId, userId); - return Response.success(); - } - - @Operation(summary = "获取团队成员列表") - @GetMapping("/{teamId}/members") - public Response> getMembers( - @Parameter(description = "团队ID") @PathVariable Long teamId - ) { - return Response.success(teamService.getTeamMembers(teamId)); - } - - @Operation(summary = "关联应用(支持分支模式)") - @PostMapping("/{teamId}/application") - public Response bindApplication( - @Parameter(description = "团队ID") @PathVariable Long teamId, - @Parameter(description = "应用ID") @RequestParam Long applicationId, - @Parameter(description = "分支模式(如:feature/*、release/*、master)") @RequestParam(required = false) String branchPattern, - @Parameter(description = "职责类型") @RequestParam String responsibility - ) { - teamService.bindApplication(teamId, applicationId, branchPattern, responsibility); - return Response.success(); - } - - @Operation(summary = "解绑应用") - @DeleteMapping("/{teamId}/application/{applicationId}") - public Response unbindApplication( - @Parameter(description = "团队ID") @PathVariable Long teamId, - @Parameter(description = "应用ID") @PathVariable Long applicationId, - @Parameter(description = "分支模式") @RequestParam(required = false) String branchPattern - ) { - teamService.unbindApplication(teamId, applicationId, branchPattern); - return Response.success(); - } - - @Operation(summary = "创建应用环境配置") - @PostMapping("/environment-config") - public Response createEnvironmentConfig( - @Parameter(description = "应用ID") @RequestParam Long applicationId, - @Parameter(description = "环境ID") @RequestParam Long environmentId, - @Parameter(description = "团队ID(可选,NULL表示默认配置)") @RequestParam(required = false) Long teamId, - @Parameter(description = "配置内容") @RequestBody Map config - ) { - teamService.createEnvironmentConfig(applicationId, environmentId, teamId, config); - return Response.success(); - } - - @Operation(summary = "更新应用环境配置") - @PutMapping("/environment-config/{configId}") - public Response updateEnvironmentConfig( - @Parameter(description = "配置ID") @PathVariable Long configId, - @Parameter(description = "配置内容") @RequestBody Map config - ) { - teamService.updateEnvironmentConfig(configId, config); - return Response.success(); - } - - @Operation(summary = "删除应用环境配置") - @DeleteMapping("/environment-config/{configId}") - public Response deleteEnvironmentConfig( - @Parameter(description = "配置ID") @PathVariable Long configId - ) { - teamService.deleteEnvironmentConfig(configId); - return Response.success(); - } -} -``` - ---- - -### Phase 9: 国际化消息 - -**文件**: `src/main/resources/messages.properties` - -```properties -# 团队管理相关错误码 -team.code.exists=团队编码{0}已存在 -team.not.found=团队不存在 -team.member.exists=用户已经是团队成员 -team.member.not.found=用户不是团队成员 -team.application.bound=团队已关联该应用 -team.environment.granted=团队已拥有该环境访问权限 -``` - ---- - -### Phase 10: 测试 - -#### 步骤 10.1: 单元测试 - -**文件**: `src/test/java/com/qqchen/deploy/backend/deploy/service/TeamServiceTest.java` - -```java -package com.qqchen.deploy.backend.deploy.service; - -import com.qqchen.deploy.backend.deploy.dto.TeamDTO; -import com.qqchen.deploy.backend.deploy.enums.TeamTypeEnum; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.transaction.annotation.Transactional; - -import static org.junit.jupiter.api.Assertions.*; - -@SpringBootTest -@Transactional -class TeamServiceTest { - - @Autowired - private ITeamService teamService; - - @Test - void testCreateTeam() { - TeamDTO dto = new TeamDTO(); - dto.setTeamCode("TEST_TEAM"); - dto.setTeamName("测试团队"); - dto.setTeamType(TeamTypeEnum.FULLSTACK); - dto.setBusinessDomain("测试域"); - dto.setEnabled(true); - - TeamDTO created = teamService.create(dto); - assertNotNull(created.getId()); - assertEquals("TEST_TEAM", created.getTeamCode()); - } - - @Test - void testAddMember() { - // 准备数据 - TeamDTO team = createTestTeam(); - Long userId = 1L; // 假设存在的用户ID - - // 添加成员 - teamService.addMember(team.getId(), userId, "DEVELOPER"); - - // 验证 - var members = teamService.getTeamMembers(team.getId()); - assertEquals(1, members.size()); - } - - private TeamDTO createTestTeam() { - TeamDTO dto = new TeamDTO(); - dto.setTeamCode("TEST_" + System.currentTimeMillis()); - dto.setTeamName("测试团队"); - dto.setTeamType(TeamTypeEnum.BACKEND); - dto.setEnabled(true); - return teamService.create(dto); - } -} -``` - ---- - -## 🚀 执行步骤 - -### 1. 运行数据库迁移 -```bash -# 启动应用,Liquibase 会自动执行迁移脚本 -mvn spring-boot:run -``` - -### 2. 编译代码 -```bash -mvn clean compile -``` - -### 3. 运行测试 -```bash -mvn test -``` - -### 4. 验证 API -访问 Swagger UI: `http://localhost:8080/swagger-ui.html` - ---- - -## ✅ 检查清单 - -- [ ] 数据库迁移脚本已创建并注册 -- [ ] 枚举类创建完成 -- [ ] 实体类创建完成 -- [ ] Repository 创建完成 -- [ ] DTO 和 Query 创建完成 -- [ ] Converter 创建完成 -- [ ] Service 接口和实现创建完成 -- [ ] Controller 创建完成 -- [ ] 国际化消息配置完成 -- [ ] 单元测试编写完成 -- [ ] 代码编译通过 -- [ ] API 测试通过 - ---- - -## 📌 注意事项 - -1. **外键约束**: 确保 `sys_user` 和 `sys_department` 表存在 -2. **枚举值**: 前端需要与后端枚举值保持一致 -3. **软删除**: 所有删除操作使用软删除(`deleted=1`) -4. **事务管理**: 涉及多表操作的方法需要加 `@Transactional` -5. **日志记录**: 关键操作需要记录详细日志 - ---- - -## 🎯 后续扩展 - -1. **分支匹配算法优化**: 支持更复杂的分支模式匹配(如正则表达式、优先级规则) -2. **环境配置模板**: 为不同环境类型提供配置模板,简化配置创建 -3. **配置继承机制**: 支持团队配置继承默认配置,只需覆盖差异部分 -4. **部署权限控制**: 基于团队-应用-分支-环境的细粒度权限管理 -5. **团队工作负载统计**: 统计团队负责的应用数量、部署频率等指标 -6. **分支负责人自动通知**: 当某分支有部署需求时,自动通知对应团队 -7. **产品线仪表盘**: 展示产品线下所有团队的工作概况 - ---- - -## 📖 使用场景示例 - -### 场景1:多团队协作开发同一应用 - -**背景**: `user-service` 应用由多个团队协作开发 -- **用户团队**: 负责 `feature/user-*` 分支 -- **支付团队**: 负责 `feature/payment-*` 分支 -- **核心团队**: 负责 `master` 和 `release/*` 分支 - -**配置**: -```sql --- 用户团队关联 -INSERT INTO deploy_team_application (team_id, application_id, branch_pattern, responsibility) -VALUES (1, 100, 'feature/user-*', 'PRIMARY'); - --- 支付团队关联 -INSERT INTO deploy_team_application (team_id, application_id, branch_pattern, responsibility) -VALUES (2, 100, 'feature/payment-*', 'PRIMARY'); - --- 核心团队关联 -INSERT INTO deploy_team_application (team_id, application_id, branch_pattern, responsibility) -VALUES (3, 100, 'master', 'PRIMARY'); - -INSERT INTO deploy_team_application (team_id, application_id, branch_pattern, responsibility) -VALUES (3, 100, 'release/*', 'PRIMARY'); -``` - -### 场景2:团队在不同环境使用不同配置 - -**背景**: `order-service` 在测试环境和生产环境,不同团队有不同的资源配置需求 - -**配置**: -```sql --- 默认测试环境配置(所有团队共享) -INSERT INTO deploy_application_environment_config (application_id, environment_id, team_id, config) -VALUES (200, 1, NULL, '{"replicas": 2, "memory": "512Mi"}'); - --- 团队A在测试环境的专属配置(需要更多资源) -INSERT INTO deploy_application_environment_config (application_id, environment_id, team_id, config) -VALUES (200, 1, 10, '{"replicas": 3, "memory": "1Gi", "debug": true}'); - --- 生产环境默认配置 -INSERT INTO deploy_application_environment_config (application_id, environment_id, team_id, config) -VALUES (200, 2, NULL, '{"replicas": 5, "memory": "2Gi", "cpu": "1000m"}'); -``` - -**查询逻辑**: -```java -// 获取团队专属配置,如果不存在则使用默认配置 -Optional teamConfig = - configRepository.findByApplicationIdAndEnvironmentIdAndTeamIdAndDeletedFalse(appId, envId, teamId); - -if (teamConfig.isEmpty()) { - teamConfig = configRepository.findByApplicationIdAndEnvironmentIdAndTeamIdIsNullAndDeletedFalse(appId, envId); -} -``` - diff --git a/backend/docs/国产化适配项目方案-AI提示词.md b/backend/docs/国产化适配项目方案-AI提示词.md deleted file mode 100644 index ce3e9458..00000000 --- a/backend/docs/国产化适配项目方案-AI提示词.md +++ /dev/null @@ -1,1099 +0,0 @@ -# 国产化适配项目方案编写 - AI提示词 - -## 角色定位 -你是一位资深的企业级Java项目架构师和项目管理专家,精通Spring Cloud微服务架构和国产化适配改造。请根据以下信息,为我编写一份专业、详尽的《XXX系统国产化适配改造项目方案》。 - ---- - -## 一、项目基础信息 - -### 1.1 项目背景 -- **项目性质**:国产化信创适配改造项目,作为正式项目立项管理 -- **时间周期**:2025年10月25日 - 2026年1月31日(约3个月,98天) -- **团队配置**:8人兼职团队 - * 邓骐辰 - 项目协调(项目经理) - * 汤峰岷 - 架构师(技术负责人) - * 路宽 - 引擎开发工程师 - * TBD - ETL开发工程师 - * 彭川 - 应用开发经理 - * 刘铁瑞 - 测试经理 - * 田伟 - 产品经理 - * 杨帆 - 运维工程师 - -### 1.2 系统规模 -- **微服务模块数量**:约12个Spring Cloud微服务 -- **系统分层**: - * ETL层(数据抽取转换加载) - * 引擎层(核心计算引擎) - * 应用层(业务应用服务) - -### 1.3 现有技术栈 -- **微服务框架**:Spring Cloud -- **注册/配置中心**:Nacos -- **缓存**:Redis -- **数据库**:MySQL/Oracle -- **Web容器**:Tomcat(内嵌) -- **内存网格**:Apache Ignite(待确认) -- **求解器**:待确认型号 - -### 1.4 国产化目标技术栈 -- **注册/配置中心**:TongNCS(东方通消息中间件) -- **Web容器**:TongWeb(东方通应用服务器) -- **缓存**:TongRDS(东方通缓存服务) -- **数据库**:达梦数据库DM8 + 高斯数据库openGauss -- **Web服务器**:Tengine(阿里云定制Nginx) -- **操作系统**:麒麟OS - ---- - -## 二、核心问题与挑战 - -### 2.1 代码分支混乱问题(重点) -- **现状**:存在7-8套代码分支,不同功能分散在不同分支 -- **问题**: - * 功能分支、环境分支、客户定制分支混杂 - * 代码合并冲突风险高 - * 缺乏统一的代码基线版本 -- **目标**:合并出两套清晰分支 - * **baseline基线版本**:统一的主干功能版本 - * **localization国产化版本**:基于baseline的国产化适配版本 - -### 2.2 技术挑战 -1. **全面中间件替换**:5类中间件同时替换,技术风险高 -2. **兼职团队协调**:所有成员兼职,资源协调难度大 -3. **时间紧迫**:3个月完成12个微服务适配 -4. **未知技术风险**: - - Ignite内存网格的国产化替代方案未定 - - 求解器ARM架构兼容性未知 - - 第三方依赖库兼容性未知 - -### 2.3 方案编写定位 -- **受众**:技术领导、公司管理层、项目团队成员 -- **目标**: - * 体现充分的前期准备和技术调研 - * 展示深度的技术方案设计 - * 证明风险可控、方案可行 - * 提供可落地执行的详细计划 -- **风格**:专业严谨、逻辑清晰、数据支撑、可操作性强 - ---- - -## 三、方案文档结构要求 - -请按以下结构编写完整的项目方案文档(Word/Markdown格式): - -### 第一章:项目概述(战略高度) - -#### 1.1 项目背景与意义 -``` -要求: -- 阐述国家信创政策背景(2+8+N信创体系) -- 说明企业数字化转型与技术自主可控的重要性 -- 强调项目的战略意义和必要性 -- 篇幅:1-2页 -``` - -#### 1.2 项目目标与范围 -``` -要求: -- 明确的适配改造目标(量化指标) - * 功能兼容性达到100% - * 性能损耗≤5% - * 系统可用性≥99.5% -- 技术范围界定: - * 列出12个微服务模块清单及职责描述 - * 明确改造的中间件范围 - * 说明数据迁移范围 -- 明确不包含的内容(项目边界) -- 输出成果清单 -``` - -#### 1.3 项目成功标准 -``` -要求: -- 功能完整性标准(核心业务流程验证通过) -- 性能基准标准(响应时间、吞吐量对比) -- 稳定性标准(连续运行时长、错误率) -- 验收标准(UAT测试通过率) -``` - ---- - -### 第二章:现状分析(体现充分调研) - -#### 2.1 系统架构现状分析 -``` -要求: -1. 绘制当前系统架构图(三层架构示意图) -2. 提供12个微服务模块清单: - 模块名称 | 所属层次 | 核心职责 | 技术栈 | 代码规模 - --------|---------|---------|--------|---------- - control-panel-server | 应用层 | ... | Spring Boot 2.x | 约XXX行 - planner-workbench-server | 应用层 | ... | ... | ... - (依次列出其他模块) - -3. 绘制技术组件依赖关系图 -4. 分析当前架构的优势与不足 -``` - -#### 2.2 代码分支现状深度分析(核心重点) -``` -要求: -1. **分支清单与分类** - 分支名称 | 分支类型 | 用途 | 最后更新时间 | 领先/落后主分支提交数 | 独有功能 - --------|---------|------|-------------|---------------------|---------- - develop | 开发主分支 | ... | 2025-10-20 | - | - - feature/xxx | 功能分支 | ... | ... | +50/-30 | 功能A、B - env/test | 环境分支 | ... | ... | +20/-10 | 测试环境配置 - customer/xxx | 定制分支 | ... | ... | +100/-50 | 客户定制功能 - (列出所有7-8个分支) - -2. **分支差异分析** - - 使用Git命令分析分支差异: - git log --oneline --graph --all - - 统计每个分支的独有提交数 - - 识别关键功能差异 - - 评估代码冲突风险(高/中/低) - -3. **代码质量现状** - - 总代码行数统计 - - 代码重复率分析 - - 技术债务评估(TODO、FIXME统计) - - 单元测试覆盖率现状 - -4. **合并风险评估** - - 预估冲突文件数量 - - 识别高风险模块 - - 评估合并工作量(人天) -``` - -#### 2.3 技术栈适配影响分析 -``` -要求:对每个待替换组件进行详细分析 - -##### 2.3.1 Nacos → TongNCS 适配分析 -- 功能对比表(服务注册、配置管理、健康检查等) -- 代码影响范围: - * 配置文件修改点(bootstrap.yml) - * 依赖包替换(pom.xml) - * 代码改造点(注解、API调用) -- 影响的微服务模块清单 -- 预估改造工作量:XX人天 - -##### 2.3.2 Redis → TongRDS 适配分析 -(同上结构) - -##### 2.3.3 MySQL/Oracle → 达梦/高斯 适配分析 -- SQL语法差异重点分析: - * 分页语法(LIMIT vs ROWNUM) - * 日期函数差异 - * 字符串函数差异 - * 存储过程/函数适配 -- 数据迁移影响分析: - * 表结构迁移复杂度 - * 数据量统计 - * 迁移时间预估 -- ORM框架适配(MyBatis/JPA/QueryDSL) - -##### 2.3.4 Tomcat → TongWeb 适配分析 -(同上结构) - -##### 2.3.5 第三方依赖库兼容性分析 -- 梳理pom.xml中的第三方依赖 -- 识别潜在不兼容的库 -- 提供替代方案 -``` - -#### 2.4 风险识别与预评估 -``` -要求: -1. 技术风险矩阵(使用表格展示) - 风险项 | 概率 | 影响程度 | 风险等级 | 应对策略 - ------|------|---------|---------|---------- - Ignite替代方案不成熟 | 高 | 高 | 高 | 提前POC验证 - 数据库迁移数据丢失 | 中 | 高 | 高 | 完整备份+灰度验证 - (列出10-15个风险项) - -2. 进度风险分析 -3. 资源风险分析(兼职团队协调) -4. 质量风险分析 -``` - ---- - -### 第三章:技术解决方案(体现技术深度) - -#### 3.1 代码分支合并策略(核心重点章节) -``` -要求:这是方案的核心亮点,需要详细设计 - -##### 3.1.1 合并策略总体设计 -1. **目标分支架构** - ``` - master (锁定,仅用于发布) - ├── baseline (基线版本主分支) - │ ├── baseline-develop (基线开发分支) - │ └── baseline-hotfix/* (基线热修复分支) - └── localization (国产化版本主分支) - ├── localization-develop (国产化开发分支) - ├── localization-feature/* (国产化功能分支) - └── localization-hotfix/* (国产化热修复分支) - ``` - -2. **合并原则** - - 以develop分支为合并基准 - - 功能完整性优先原则 - - 代码质量优先原则 - - 充分测试验证原则 - -##### 3.1.2 分支合并详细步骤 - -**阶段一:分支分析与准备(1周)** -```bash -# 步骤1:备份所有分支 -git branch -a > branches_backup.txt -for branch in $(git branch -a | grep remotes | grep -v HEAD); do - git checkout -b backup/${branch##*/} $branch -done - -# 步骤2:分析分支差异 -git log --oneline --graph --decorate --all > git_history.txt - -# 步骤3:识别每个分支的独有提交 -for branch in feature/xxx env/test customer/xxx; do - git log baseline-develop..$branch --oneline > diff_$branch.txt -done -``` - -**阶段二:创建baseline基线分支(1周)** -```bash -# 步骤1:基于当前主develop创建baseline分支 -git checkout develop -git pull origin develop -git checkout -b baseline-main -git push origin baseline-main - -# 步骤2:合并核心功能分支 -# 按优先级合并功能分支,每次合并后进行编译测试 -git merge --no-ff feature/core-function-1 -mvn clean compile -# 如果编译失败,解决冲突后继续 -git merge --no-ff feature/core-function-2 -mvn clean compile - -# 步骤3:剔除环境特定配置 -# 将环境配置外部化到配置中心 - -# 步骤4:剔除客户定制功能 -# 通过feature-toggle方式保留钩子,但默认关闭 -``` - -**阶段三:创建localization国产化分支(1周)** -```bash -# 基于baseline创建国产化分支 -git checkout baseline-main -git checkout -b localization-main -git push origin localization-main - -# 创建国产化开发分支 -git checkout -b localization-develop -``` - -##### 3.1.3 冲突解决机制 -1. **冲突分类处理策略** - - 配置文件冲突:选择最新版本,手动合并差异配置 - - 业务代码冲突:召开技术评审会,选择最优实现 - - 依赖冲突:统一升级到最新稳定版本 - -2. **冲突解决流程** - ``` - 发现冲突 → 冲突分类 → 责任人分配 → 解决方案评审 → 实施解决 → 代码审查 → 测试验证 - ``` - -3. **冲突记录模板** - ```markdown - ## 冲突记录 #001 - - 冲突文件:src/main/java/com/xxx/Service.java - - 冲突分支:feature/A vs feature/B - - 冲突类型:业务逻辑冲突 - - 负责人:张三 - - 解决方案:采用feature/A的实现,补充feature/B的异常处理 - - 审查人:李四 - - 状态:已解决 - ``` - -##### 3.1.4 代码评审流程 -- 所有合并必须经过Code Review -- 评审检查清单: - * 代码风格规范 - * 业务逻辑正确性 - * 单元测试覆盖率 - * 性能影响评估 - * 安全漏洞检查 - -##### 3.1.5 合并后的分支管理策略 -1. **双主干并行维护策略** - - baseline分支:维护原有技术栈版本,用于非国产化客户 - - localization分支:国产化适配版本,向上合并baseline的bug修复 - -2. **代码同步机制** - ```bash - # 定期将baseline的bug修复合并到localization - git checkout localization-develop - git merge baseline-develop - ``` - -3. **分支保护规则** - - baseline-main和localization-main设置为保护分支 - - 禁止直接推送,必须通过PR/MR - - 至少2人审查通过才能合并 - -##### 3.1.6 回滚预案 -- 每次合并前打tag标记 -- 保留原有分支至少3个月 -- 提供快速回滚脚本 - -##### 3.1.7 工作量与进度安排 -- 分支分析与准备:1周(5人天) -- baseline分支合并:1周(10人天) -- localization分支创建:0.5周(3人天) -- 测试验证:1.5周(8人天) -- 总计:4周,26人天 -``` - -#### 3.2 中间件适配技术方案(需收集互联网经验) -``` -要求:对每个中间件提供详细适配方案,并引用互联网实际案例 - -##### 3.2.1 Nacos → TongNCS 适配方案 - -**1. TongNCS技术特性介绍** -- 产品介绍:东方通TongNCS是企业级分布式配置与服务治理平台 -- 核心功能:服务注册发现、配置管理、服务路由、负载均衡 -- 与Nacos的关系:TongNCS兼容部分Nacos API - -**2. 功能对比分析** -| 功能项 | Nacos | TongNCS | 兼容性 | 适配难度 | -|--------|-------|---------|--------|----------| -| 服务注册 | ✓ | ✓ | 高 | 低 | -| 服务发现 | ✓ | ✓ | 高 | 低 | -| 配置管理 | ✓ | ✓ | 中 | 中 | -| 配置热更新 | ✓ | ✓ | 中 | 中 | -| 命名空间 | ✓ | ✓ | 高 | 低 | -| 多环境管理 | ✓ | ✓ | 高 | 低 | -| 权限控制 | ✓ | ✓ | 中 | 中 | -| API兼容性 | - | 部分兼容 | 中 | 中 | - -**3. 配置迁移方案** -```yaml -# Nacos配置示例(修改前) -spring: - cloud: - nacos: - discovery: - server-addr: 192.168.1.100:8848 - namespace: dev - group: DEFAULT_GROUP - config: - server-addr: 192.168.1.100:8848 - file-extension: yml - namespace: dev - group: DEFAULT_GROUP - -# TongNCS配置示例(修改后) -spring: - cloud: - tongncs: - discovery: - server-addr: 192.168.1.200:8848 - namespace: dev - group: DEFAULT_GROUP - config: - server-addr: 192.168.1.200:8848 - file-extension: yml - namespace: dev - group: DEFAULT_GROUP -``` - -**4. 代码改造点清单** -- Maven依赖替换: - ```xml - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - - - - - com.tongtech - tongncs-spring-cloud-starter - x.x.x - - ``` - -- 配置文件修改(bootstrap.yml) -- 如使用Nacos Java SDK的直接调用代码需要适配 - -**5. 互联网已知适配经验(重要)** -请收集并引用: -- 某银行核心系统Nacos迁移TongNCS案例 -- 某政务云平台信创改造经验 -- 东方通官方迁移指南文档 -- 技术社区的实践经验分享 - -示例格式: -``` -【案例1】某股份制银行核心交易系统国产化改造 -- 系统规模:20+微服务 -- 迁移周期:2个月 -- 关键经验: - * TongNCS在高并发场景下的心跳间隔需要调优 - * 配置中心的加密配置需要重新设置 - * 建议先迁移非核心服务进行验证 -- 参考资料:[链接] - -【案例2】某省政务服务平台信创适配 -- 遇到的坑:TongNCS某版本与Spring Cloud 2.x兼容性问题 -- 解决方案:升级TongNCS到x.x版本 -- 参考资料:[链接] -``` - -**6. 潜在问题与解决方案** -| 问题 | 影响 | 解决方案 | -|------|------|----------| -| API兼容性差异 | 中 | 封装适配层,统一接口 | -| 性能差异 | 中 | 调优参数,压测验证 | -| 监控告警迁移 | 低 | 对接Prometheus | - -**7. 改造工作量评估** -- 依赖包替换:0.5人天/服务 × 12服务 = 6人天 -- 配置文件修改:0.5人天/服务 × 12服务 = 6人天 -- 代码适配(如有SDK调用):2人天/服务 × 3服务 = 6人天 -- 联调测试:1人天/服务 × 12服务 = 12人天 -- **总计:30人天** - ---- - -##### 3.2.2 Redis → TongRDS 适配方案 - -**1. TongRDS技术特性** -(按相同结构展开) - -**2. 功能对比** -| 功能项 | Redis | TongRDS | 兼容性 | 适配难度 | -|--------|-------|---------|--------|----------| -| 数据结构 | String/Hash/List/Set/ZSet | 全支持 | 高 | 低 | -| 持久化 | RDB/AOF | 支持 | 高 | 低 | -| 主从复制 | ✓ | ✓ | 高 | 低 | -| 哨兵模式 | ✓ | ✓ | 中 | 中 | -| 集群模式 | ✓ | ✓ | 中 | 中 | -| Lua脚本 | ✓ | 部分支持 | 中 | 中 | -| 发布订阅 | ✓ | ✓ | 高 | 低 | - -**3. 配置迁移** -(提供配置示例对比) - -**4. 代码改造点** -(列出具体改造点) - -**5. 互联网已知经验** -(收集实际案例) - -**6. 潜在问题与解决方案** - -**7. 工作量评估:XX人天** - ---- - -##### 3.2.3 MySQL/Oracle → 达梦DM8/高斯openGauss 适配方案 - -**1. 数据库技术特性对比** -| 特性 | MySQL | Oracle | 达梦DM8 | 高斯openGauss | -|------|-------|--------|---------|---------------| -| SQL标准 | SQL-2003 | SQL:2016 | SQL-2003 | SQL:2011 | -| 存储过程 | ✓ | ✓ | ✓ | ✓ | -| 触发器 | ✓ | ✓ | ✓ | ✓ | -| 分区表 | ✓ | ✓ | ✓ | ✓ | -| 物化视图 | ✗ | ✓ | ✓ | ✓ | - -**2. SQL语法差异分析(重点)** - -**2.1 分页语法差异** -```sql --- MySQL分页 -SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 20; - --- Oracle分页 -SELECT * FROM ( - SELECT A.*, ROWNUM RN FROM ( - SELECT * FROM user ORDER BY id - ) A WHERE ROWNUM <= 30 -) WHERE RN > 20; - --- 达梦DM8分页(兼容MySQL和Oracle语法) -SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 20; -- MySQL方式 -SELECT * FROM user ORDER BY id OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY; -- 标准SQL - --- 高斯openGauss分页(兼容PostgreSQL) -SELECT * FROM user ORDER BY id LIMIT 10 OFFSET 20; -``` - -**2.2 日期函数差异** -```sql --- MySQL -NOW(), DATE_FORMAT(date, '%Y-%m-%d') - --- Oracle -SYSDATE, TO_CHAR(date, 'YYYY-MM-DD') - --- 达梦DM8(兼容Oracle) -SYSDATE, TO_CHAR(date, 'YYYY-MM-DD') - --- 高斯openGauss(兼容PostgreSQL) -NOW(), TO_CHAR(date, 'YYYY-MM-DD') -``` - -**2.3 字符串函数差异** -(列出常用函数对比) - -**2.4 自增主键差异** -(列出序列、IDENTITY等差异) - -**3. 数据迁移方案** - -**3.1 Schema迁移** -- 使用达梦DTS工具或官方迁移工具 -- 手动核对表结构定义 -- 索引、约束、触发器迁移检查清单 - -**3.2 数据迁移** -- 全量数据迁移策略 -- 增量数据同步策略 -- 数据一致性校验方案 - -**3.3 迁移时间预估** -| 数据库 | 表数量 | 数据量 | 预估迁移时间 | -|--------|--------|--------|-------------| -| 业务库1 | 50 | 10GB | 2小时 | -| 业务库2 | 30 | 5GB | 1小时 | - -**4. ORM框架适配** - -**4.1 MyBatis适配** -```xml - - - - - -``` - -**4.2 JPA/Hibernate适配** -- Dialect配置修改 -- 方言差异处理 - -**4.3 QueryDSL适配** -- 数据库方言配置 -- 自定义函数注册 - -**5. 性能优化建议** -- 索引优化策略 -- 执行计划分析 -- SQL调优checklist - -**6. 互联网已知经验** -(收集Oracle→达梦、MySQL→高斯的实际案例) - -示例: -``` -【案例1】某保险公司核心业务系统Oracle迁移达梦 -- 数据库规模:200+表,500GB数据 -- 迁移周期:3个月 -- 关键经验: - * 存储过程需要逐个改造和测试 - * 达梦的优化器与Oracle有差异,需要重新调优 - * CLOB/BLOB字段迁移需要特别注意 - * 建议使用达梦的Oracle兼容模式 - -【案例2】某互联网公司MySQL迁移高斯 -- 遇到的坑:字符集编码问题 -- 解决方案:统一使用UTF8MB4 -``` - -**7. 工作量评估** -- Schema迁移:5人天 -- 数据迁移:3人天 -- SQL语句适配:15人天(假设100+处SQL需要修改) -- 存储过程改造:10人天 -- ORM框架适配:5人天 -- 性能调优:10人天 -- 测试验证:15人天 -- **总计:63人天** - ---- - -##### 3.2.4 Tomcat → TongWeb 适配方案 - -**1. TongWeb技术特性** -(按相同结构展开) - -**2. 功能对比** - -**3. 部署配置变更** -- Spring Boot内嵌容器改为外置容器 -- 打包方式从jar改为war -- 启动参数调整 - -**4. 代码改造点** -```java -// 修改Application主类 -@SpringBootApplication -public class Application extends SpringBootServletInitializer { - - @Override - protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { - return application.sources(Application.class); - } - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} -``` - -**5. 互联网已知经验** -(收集实际案例) - -**6. 工作量评估:XX人天** - ---- - -##### 3.2.5 其他适配项 - -**1. Tengine部署方案** -(Nginx配置迁移) - -**2. 麒麟OS环境适配** -- JDK选择(OpenJDK vs 毕升JDK) -- 系统依赖库检查 - -**3. 第三方依赖库兼容性处理** -(列出需要替换的库) - -``` - -#### 3.3 技术选型与替代方案 - -##### 3.3.1 Ignite内存网格替代方案 -``` -要求: -1. Ignite功能分析 -2. 替代方案对比: - | 方案 | 优点 | 缺点 | 适配成本 | 推荐指数 | - |------|------|------|----------|----------| - | Hazelcast | ... | ... | 高 | ⭐⭐⭐ | - | Redis Cluster | ... | ... | 中 | ⭐⭐⭐⭐ | - | TongRDS集群 | ... | ... | 低 | ⭐⭐⭐⭐⭐ | - | Apache Geode | ... | ... | 高 | ⭐⭐ | - -3. 技术验证POC计划 -4. 推荐方案及理由 -``` - -##### 3.3.2 求解器ARM兼容性方案 -``` -要求: -1. 当前求解器技术分析 -2. ARM架构兼容性调研 -3. 替代方案对比 -4. 技术决策建议 -``` - -#### 3.4 系统架构设计 - -##### 3.4.1 国产化后的目标架构图 -``` -要求: -- 绘制完整的系统架构图(使用工具绘制) -- 标注所有国产化组件 -- 说明架构变化点 -``` - -##### 3.4.2 部署架构图 -``` -要求: -- 绘制物理部署架构 -- 标注服务器配置 -- 说明网络规划 -``` - -##### 3.4.3 数据架构图 -``` -要求: -- 绘制数据流转图 -- 说明数据迁移路径 -``` - ---- - -### 第四章:实施计划(体现项目管理能力) - -#### 4.1 项目阶段与里程碑 -``` -要求: -基于提供的5阶段任务清单,细化WBS到3级,并制定详细进度计划 - -使用甘特图或表格形式展示: -阶段 | 里程碑 | 活动 | 子活动 | 负责人 | 工期 | 前置任务 | 计划开始 | 计划结束 | 交付物 ------|--------|------|--------|--------|------|----------|----------|----------|-------- -PHASE 1 | 项目启动 | 项目启动准备 | 项目团队组建 | 邓骐辰 | 1d | - | 2025-10-25 | 2025-10-25 | 项目启动会纪要 -... | ... | ... | ... | ... | ... | ... | ... | ... | ... - -关键路径标注: -- 识别关键路径任务 -- 标注时间缓冲 -- 说明并行任务安排 -``` - -#### 4.2 人力资源计划 -``` -要求: -1. 团队组织架构图 -2. 角色职责矩阵(RACI) - 任务 | 邓骐辰 | 汤峰岷 | 路宽 | TBD | 彭川 | 刘铁瑞 | 田伟 | 杨帆 - -----|--------|--------|------|-----|------|--------|------|------ - 分支合并 | A | R | C | C | C | I | I | I - 框架适配 | A | R | I | I | C | I | I | C - (R=负责, A=批准, C=咨询, I=知情) - -3. 人力资源甘特图 -4. 兼职人员协调机制 -``` - -#### 4.3 沟通管理计划 -``` -要求: -1. 会议机制 - - 每日站会:15分钟,同步进展和问题 - - 每周例会:1小时,项目回顾和计划 - - 里程碑评审:2小时,阶段性验收 - -2. 沟通渠道 - - 即时通讯:企业微信/钉钉 - - 项目管理:禅道/JIRA - - 文档协作:企业网盘 - -3. 汇报机制 - - 日报:每日提交 - - 周报:每周五提交 - - 月报:每月底提交 - - 里程碑报告:里程碑完成后3日内 -``` - -#### 4.4 质量管理计划 -``` -要求: -1. 代码质量标准 - - 代码规范:阿里巴巴Java开发手册 - - 单元测试覆盖率:≥70% - - 代码审查:必须进行 - -2. 测试策略 - - 单元测试:开发人员编写 - - 集成测试:开发+测试 - - 系统测试:测试团队 - - 性能测试:运维+测试 - - UAT测试:产品+业务 - -3. 质量检查点 - - 代码提交检查 - - 每日构建检查 - - 里程碑质量评审 -``` - ---- - -### 第五章:风险管理计划 - -#### 5.1 风险登记册 -``` -要求: -编制完整的风险登记册(15-20个风险项) - -风险ID | 风险描述 | 类别 | 概率 | 影响 | 风险等级 | 应对策略 | 责任人 | 应急预案 --------|---------|------|------|------|----------|----------|--------|---------- -R001 | Ignite替代方案技术验证失败 | 技术 | 高 | 高 | 高 | 提前POC,准备多个备选方案 | 汤峰岷 | 采用Redis Cluster方案 -R002 | 代码分支合并冲突严重 | 技术 | 中 | 高 | 高 | 详细分析后分阶段合并 | 邓骐辰 | 延长合并周期,增加人力 -R003 | 数据库迁移数据丢失 | 技术 | 中 | 高 | 高 | 完整备份+分批迁移 | 杨帆 | 回滚到备份版本 -... - -风险等级 = 概率 × 影响(高=3,中=2,低=1) -高风险:≥6,中风险:3-5,低风险:1-2 -``` - -#### 5.2 风险应对措施 -``` -要求: -针对高风险项制定详细应对措施,包括: -- 预防措施 -- 减轻措施 -- 应急预案 -- 责任人和时间表 -``` - ---- - -### 第六章:测试方案 - -#### 6.1 测试策略 -``` -要求: -说明各层次测试策略和范围 -``` - -#### 6.2 测试环境规划 -``` -要求: -| 环境 | 用途 | 配置 | 部署时间 | -|------|------|------|----------| -| DEV | 开发测试 | ... | 11月1日 | -| SIT | 集成测试 | ... | 11月15日 | -| UAT | 验收测试 | ... | 12月20日 | -``` - -#### 6.3 测试用例设计 -``` -要求: -- 功能测试用例框架 -- 性能测试场景设计 -- 兼容性测试矩阵 -``` - -#### 6.4 验收标准 -``` -要求: -明确的量化验收标准 -``` - ---- - -### 第七章:应急预案与回滚方案 - -#### 7.1 应急响应机制 -``` -要求: -- 问题分级标准 -- 应急响应流程 -- 升级机制 -``` - -#### 7.2 数据回滚方案 -``` -要求: -- 数据库回滚步骤 -- 配置回滚步骤 -- 代码回滚步骤 -``` - -#### 7.3 业务连续性保障 -``` -要求: -- 服务降级方案 -- 灰度发布策略 -``` - ---- - -### 第八章:成本预算 - -#### 8.1 硬件成本 -``` -要求: -| 项目 | 数量 | 单价 | 小计 | 备注 | -|------|------|------|------|------| -| 云服务器(8核16G) | 10台 | X元/月 | XX万 | 3个月 | -| 达梦数据库授权 | 2套 | X万 | XX万 | 永久授权 | -| TongWeb授权 | 10个 | X万 | XX万 | 永久授权 | -... -``` - -#### 8.2 人力成本 -``` -要求: -估算人力投入成本 -``` - -#### 8.3 总成本估算 -``` -要求: -汇总总成本,说明成本效益分析 -``` - ---- - -### 第九章:项目成果与知识沉淀 - -#### 9.1 预期交付成果 -``` -要求: -列出所有交付物清单 -- 国产化适配后的源代码 -- 数据库迁移脚本 -- 部署文档 -- 测试报告 -- 用户手册 -- 培训材料 -- 项目总结报告 -``` - -#### 9.2 知识沉淀计划 -``` -要求: -- 技术文档库建设 -- 最佳实践总结 -- 问题案例库 -- 培训体系建设 -``` - ---- - -### 第十章:总结与展望 - -#### 10.1 项目总结 -``` -要求: -总结本方案的核心要点和创新点 -``` - -#### 10.2 后续规划 -``` -要求: -说明国产化适配后的运维、优化计划 -``` - ---- - -## 四、附录要求 - -### 附录A:12个微服务模块清单 -``` -要求: -详细列出12个微服务的信息: -模块名称 | 所属层次 | 核心职责 | 技术栈 | 代码规模 | 外部依赖 | 改造优先级 -``` - -### 附录B:技术调研报告 -``` -要求: -提供各中间件的详细技术调研报告 -``` - -### 附录C:代码分支详细分析报告 -``` -要求: -提供Git分支分析的详细数据 -``` - -### 附录D:术语表 -``` -要求: -解释文档中的专业术语和缩写 -``` - -### 附录E:参考资料 -``` -要求: -列出所有参考的互联网资料、官方文档、技术博客等 -``` - ---- - -## 五、文档格式要求 - -### 5.1 整体要求 -- **格式**:Word文档(.docx)或Markdown(可导出PDF) -- **篇幅**:60-80页(A4纸) -- **字体**: - * 标题:黑体,一级标题18pt,二级16pt,三级14pt - * 正文:宋体,12pt - * 代码:Consolas,10pt -- **行距**:1.5倍 -- **页边距**:上下2.54cm,左右3.17cm - -### 5.2 图表要求 -- 所有架构图使用专业绘图工具(draw.io、Visio、PlantUML) -- 图表必须清晰可读,统一风格 -- 图表必须有编号和标题 -- 复杂图表提供图例说明 - -### 5.3 代码示例要求 -- 使用代码高亮 -- 提供必要的注释 -- 展示前后对比(修改前vs修改后) - -### 5.4 数据支撑要求 -- 工作量评估必须有明确数据支撑 -- 风险评估必须量化 -- 成本预算必须详细 - ---- - -## 六、方案编写提示 - -### 6.1 体现"用心良苦"的要点 -1. **充分的前期调研** - - 详尽的代码分支分析(用数据说话) - - 全面的技术栈影响评估 - - 深入的互联网经验收集 - -2. **深度的技术方案** - - 代码分支合并的详细步骤(可直接执行) - - 中间件适配的完整方案(有实际案例支撑) - - 多维度的风险评估和应对 - -3. **严谨的项目管理** - - 科学的WBS分解 - - 合理的进度安排 - - 完善的质量保障机制 - -4. **可落地的执行计划** - - 每个任务都有明确的负责人 - - 每个阶段都有清晰的交付物 - - 每个风险都有应急预案 - -### 6.2 避免的问题 -- 避免空洞的理论,要有实际数据和案例 -- 避免过于乐观的估算,要预留风险缓冲 -- 避免忽略细节,要考虑实施中的各种问题 -- 避免文档过于冗长,要突出重点 - -### 6.3 重点突出的章节 -1. **代码分支合并策略**(核心亮点,占方案15-20%篇幅) -2. **中间件适配方案**(技术深度体现,占方案30-35%篇幅) -3. **风险管理计划**(体现风险意识,占方案10-15%篇幅) -4. **实施计划**(体现项目管理能力,占方案15-20%篇幅) - ---- - -## 七、请开始编写 - -请严格按照以上要求,编写一份完整、专业、可落地的《XXX系统国产化适配改造项目方案》。 - -**特别强调**: -1. 代码分支合并策略必须详细到可以直接执行的程度 -2. 中间件适配方案必须包含互联网实际案例(至少每个中间件2个案例) -3. 所有工作量评估必须有数据支撑 -4. 所有风险必须有应对措施 -5. 方案必须体现充分准备和技术深度 - -请使用Markdown格式输出完整方案文档。 - diff --git a/backend/src/main/java/com/qqchen/deploy/backend/schedule/aspect/JobMonitorAspect.java b/backend/src/main/java/com/qqchen/deploy/backend/schedule/aspect/JobMonitorAspect.java index 53c7189f..b9132269 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/schedule/aspect/JobMonitorAspect.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/schedule/aspect/JobMonitorAspect.java @@ -2,9 +2,11 @@ package com.qqchen.deploy.backend.schedule.aspect; import com.qqchen.deploy.backend.schedule.annotation.MonitoredJob; import com.qqchen.deploy.backend.schedule.dto.JobStatusDTO; +import com.qqchen.deploy.backend.schedule.entity.ScheduleJob; import com.qqchen.deploy.backend.schedule.entity.ScheduleJobLog; import com.qqchen.deploy.backend.schedule.enums.JobStatusEnum; import com.qqchen.deploy.backend.schedule.repository.IScheduleJobLogRepository; +import com.qqchen.deploy.backend.schedule.repository.IScheduleJobRepository; import com.qqchen.deploy.backend.schedule.service.JobProgressReporter; import com.qqchen.deploy.backend.schedule.service.JobStatusRedisService; import jakarta.annotation.Resource; @@ -13,6 +15,10 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; +import org.quartz.CronTrigger; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerKey; import org.springframework.stereotype.Component; import java.io.PrintWriter; @@ -20,7 +26,9 @@ import java.io.StringWriter; import java.net.InetAddress; import java.time.Duration; import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.Arrays; +import java.util.Date; import java.util.stream.Collectors; /** @@ -43,6 +51,12 @@ public class JobMonitorAspect { @Resource private IScheduleJobLogRepository jobLogRepository; + @Resource + private IScheduleJobRepository jobRepository; + + @Resource + private Scheduler scheduler; + @Around("@annotation(monitoredJob)") public Object monitor(ProceedingJoinPoint joinPoint, MonitoredJob monitoredJob) throws Throwable { Long jobId = monitoredJob.jobId(); @@ -75,16 +89,22 @@ public class JobMonitorAspect { // 4. 保存成功日志到数据库 saveLog(jobId, jobName, beanName, methodName, startTime, JobStatusEnum.SUCCESS, "任务执行成功", null); + // 5. 更新任务统计信息(成功) + updateJobStatistics(jobId, true); + return result; } catch (Throwable e) { - // 5. 记录失败状态到Redis + // 6. 记录失败状态到Redis log.error("任务执行失败: jobId={}", jobId, e); saveStatus(jobId, jobName, "FAIL", null, "任务执行失败: " + e.getMessage(), startTime); - // 6. 保存失败日志到数据库 + // 7. 保存失败日志到数据库 saveLog(jobId, jobName, beanName, methodName, startTime, JobStatusEnum.FAIL, "任务执行失败", e); + // 8. 更新任务统计信息(失败) + updateJobStatistics(jobId, false); + throw e; } finally { @@ -148,6 +168,63 @@ public class JobMonitorAspect { } } + /** + * 更新任务统计信息 + * + * @param jobId 任务ID + * @param success 是否执行成功 + */ + private void updateJobStatistics(Long jobId, boolean success) { + try { + ScheduleJob job = jobRepository.findById(jobId).orElse(null); + if (job == null) { + log.warn("更新任务统计失败:任务不存在,jobId={}", jobId); + return; + } + + // 更新执行次数 + Integer executeCount = job.getExecuteCount(); + job.setExecuteCount(executeCount == null ? 1 : executeCount + 1); + + // 更新成功/失败次数 + if (success) { + Integer successCount = job.getSuccessCount(); + job.setSuccessCount(successCount == null ? 1 : successCount + 1); + } else { + Integer failCount = job.getFailCount(); + job.setFailCount(failCount == null ? 1 : failCount + 1); + } + + // 更新上次执行时间 + job.setLastExecuteTime(LocalDateTime.now()); + + // 更新下次执行时间(从Quartz Trigger获取) + try { + TriggerKey triggerKey = TriggerKey.triggerKey("trigger_" + jobId, "DEFAULT"); + CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey); + if (trigger != null) { + Date nextFireTime = trigger.getNextFireTime(); + if (nextFireTime != null) { + job.setNextExecuteTime(LocalDateTime.ofInstant( + nextFireTime.toInstant(), + ZoneId.systemDefault() + )); + } + } + } catch (SchedulerException e) { + log.warn("获取下次执行时间失败:jobId={}", jobId, e); + } + + // 保存更新 + jobRepository.save(job); + log.info("任务统计信息已更新:jobId={}, executeCount={}, successCount={}, failCount={}", + jobId, job.getExecuteCount(), job.getSuccessCount(), job.getFailCount()); + + } catch (Exception e) { + log.error("更新任务统计失败:jobId={}", jobId, e); + } + } + /** * 获取异常堆栈信息 */ diff --git a/backend/src/main/java/com/qqchen/deploy/backend/schedule/converter/ScheduleJobConverter.java b/backend/src/main/java/com/qqchen/deploy/backend/schedule/converter/ScheduleJobConverter.java index f42196fb..40032cda 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/schedule/converter/ScheduleJobConverter.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/schedule/converter/ScheduleJobConverter.java @@ -4,13 +4,61 @@ import com.qqchen.deploy.backend.framework.converter.BaseConverter; import com.qqchen.deploy.backend.schedule.dto.ScheduleJobDTO; import com.qqchen.deploy.backend.schedule.entity.ScheduleJob; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.Mappings; /** * 定时任务Converter * + * 说明: + * 1. executeCount/successCount/failCount/lastExecuteTime 是后台自动管理的统计字段 + * 2. 创建时必须ignore,让Entity的默认值(=0)生效 + * 3. 更新时必须ignore,防止前端覆盖这些字段 + * * @author qichen */ @Mapper(config = BaseConverter.class) public interface ScheduleJobConverter extends BaseConverter { + + /** + * 覆盖父类的toEntity方法,忽略后台管理的统计字段 + * 让Entity的默认值(executeCount=0等)正常生效 + */ + @Override + @Mappings({ + @Mapping(target = "id", source = "id"), + @Mapping(target = "createTime", source = "createTime"), + @Mapping(target = "createBy", source = "createBy"), + @Mapping(target = "updateTime", source = "updateTime"), + @Mapping(target = "updateBy", source = "updateBy"), + @Mapping(target = "version", source = "version"), + @Mapping(target = "deleted", source = "deleted"), + @Mapping(target = "executeCount", ignore = true), + @Mapping(target = "successCount", ignore = true), + @Mapping(target = "failCount", ignore = true), + @Mapping(target = "lastExecuteTime", ignore = true) + }) + ScheduleJob toEntity(ScheduleJobDTO dto); + + /** + * 覆盖父类的updateEntity方法,忽略所有不应该被前端修改的字段 + * 注意:覆盖父类方法时,父类的@Mapping注解会失效,必须重新声明 + */ + @Override + @Mappings({ + @Mapping(target = "id", ignore = true), + @Mapping(target = "createTime", ignore = true), + @Mapping(target = "createBy", ignore = true), + @Mapping(target = "updateTime", ignore = true), + @Mapping(target = "updateBy", ignore = true), + @Mapping(target = "version", ignore = true), + @Mapping(target = "deleted", ignore = true), + @Mapping(target = "executeCount", ignore = true), + @Mapping(target = "successCount", ignore = true), + @Mapping(target = "failCount", ignore = true), + @Mapping(target = "lastExecuteTime", ignore = true) + }) + void updateEntity(@MappingTarget ScheduleJob entity, ScheduleJobDTO dto); } diff --git a/backend/src/main/java/com/qqchen/deploy/backend/schedule/service/impl/ScheduleJobServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/schedule/service/impl/ScheduleJobServiceImpl.java index b9e62192..270bbb03 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/schedule/service/impl/ScheduleJobServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/schedule/service/impl/ScheduleJobServiceImpl.java @@ -72,6 +72,9 @@ public class ScheduleJobServiceImpl extends BaseServiceImpl