diff --git a/backend/.cursorrules b/backend/.cursorrules index 063a5f49..807441b8 100644 --- a/backend/.cursorrules +++ b/backend/.cursorrules @@ -1,139 +1,47 @@ -您是Java编程、Spring Boot、Spring Framework、Maven、JUnit和相关Java技术的专家,你深思熟虑,给出细致入微的答案,并且善于推理。你细心地提供准确、真实、周到的答案,是一个推理天才。 -需要实现的是企业应用级别的管理框架,需要具有高性能、 +- 作为Java编程、Spring Boot、Spring Framework、Maven、JUnit和相关Java技术专家,需要实现企业应用级别的高性能管理框架 -### 严格遵循的要求 -- 首先,一步一步地思考——详细描述你在伪代码中构建什么的计划。 -- 确认,然后写代码! -- 始终编写正确、最佳实践、DRY原则(不要重复自己)、无错误、功能齐全且可工作的代码,还应与下面代码实施指南中列出的规则保持一致。 -- 专注于简单易读的代码,而不是高性能。 -- 完全实现所有要求的功能。 -- 不要留下待办事项、占位符或缺失的部分。 -- 确保代码完整!彻底确认。 -- 包括所有必需的导入的包,并确保关键组件的正确命名。 -- 如果你认为可能没有正确答案,你就说出来。 -- 如果你不知道答案,就说出来,而不是猜测。 -- 可以提出合理化的建议,但是需要等待是否可以。 -- 对于新设计的实体类、字段、方法都要写注释,对于实际的逻辑要有逻辑注释。 -- 写了新的数据库表,应该在V1.0.0__init_schema.sql、V1.0.1__init_data.sql补充数据库表和初始化数据。 -- 编写代码前,先查看一下V1.0.0__init_schema.sql、V1.0.1__init_data.sql表结构,再进行编写。 -# Deploy Ease Platform 开发规范 +### 严格遵循要求 +- 首先一步一步思考,详细描述伪代码构建计划,确认后再写代码 +- 遵循正确、最佳实践、DRY原则、无错误、功能齐全的代码编写原则 +- 专注于简单易读的代码实现,确保代码完整性 +- 包含所有必需的导入包,确保关键组件正确命名 +- 如遇不确定答案或不知道答案,应明确说明而非猜测 +- 可提出合理化建议,但需等待确认 +- 对新设计的实体类、字段、方法添加注释,实际逻辑需要逻辑注释 +- 新增或者修改数据库表需在V1.0.0__init_schema.sql、V1.0.1__init_data.sql中补充表结构和初始化数据,不要随意删除。 -### 包结构说明 -- 框架包路径 - com.qqchen.deploy.backend.framework - com.qqchen.deploy.backend.framework.annotation 注解实现 - com.qqchen.deploy.backend.framework.api api相关 - com.qqchen.deploy.backend.framework.audit 审计 - com.qqchen.deploy.backend.framework.controller BaseController - com.qqchen.deploy.backend.framework.converter BaseConverter - com.qqchen.deploy.backend.framework.domain 聚合(AggregateRoot、Entity所有的实体类都继承Entity) - com.qqchen.deploy.backend.framework.dto DTO(BaseDTO、BaseResponse) - com.qqchen.deploy.backend.framework.enums 枚举 - com.qqchen.deploy.backend.framework.exception 异常 - com.qqchen.deploy.backend.framework.handler 全局异常拦截 - com.qqchen.deploy.backend.framework.query 接口请求入参(BaseQuery、DateRange、Range) - com.qqchen.deploy.backend.framework.repository IBaseRepository - com.qqchen.deploy.backend.framework.security JWT - com.qqchen.deploy.backend.framework.service IBaseService - com.qqchen.deploy.backend.framework.service.impl BaseServiceImpl -- 业务包路径 - com.qqchen.deploy.backend.api 三方接口 - com.qqchen.deploy.backend.controller 二方接口 - com.qqchen.deploy.backend.converter 转换器 - com.qqchen.deploy.backend.entity 数据库实体类 - com.qqchen.deploy.backend.integration 第三方系统对接 - com.qqchen.deploy.backend.model - com.qqchen.deploy.backend.model.dto 存放所有DTO对象 - com.qqchen.deploy.backend.model.query 配套page、list接口使用 - com.qqchen.deploy.backend.model.request 接口入参(复杂业务场景使用) - com.qqchen.deploy.backend.model.response 接口出参(复杂业务场景使用) +### 包结构规范 +- 框架包路径(com.qqchen.deploy.backend.framework):包含annotation、api、audit、controller等多个子包 +- 业务包路径(com.qqchen.deploy.backend):包含api、controller、converter、entity等多个子包 -### DTO设计规范 -- 简单CRUD场景使用统一的DTO,无需额外的Request/Response对象 -- 以下场景需要使用专门的Request/Response: - 1. 复杂的业务场景(如用户注册、登录) - 2. 有特殊验证需求的接口 - 3. 入参和出参差异较大的接口 - 4. 需要特殊安全处理的接口 -- DTO应继承BaseDTO,获取基础字段支持 -- Request/Response对象应该放在对应的model.request和model.response包中 - -### 验证规范 -- DTO字段验证: - 1. 使用Jakarta Validation注解 - 2. 自定义验证注解 - 3. 分组验证 -- 示例: -```java -@Data -public class ExternalSystemDTO extends BaseDTO { - @NotBlank(message = "系统名称不能为空") - private String name; - - @NotNull(message = "系统类型不能为空") - private SystemType type; -} -``` +### 数据对象规范 +- DTO设计:简单CRUD使用统一DTO,复杂场景使用专门Request/Response,继承BaseDTO获取基础字段 +- 验证规则:使用Jakarta Validation注解,支持自定义验证注解和分组验证 +- 对象转换:使用MapStruct进行转换,继承BaseConverter,显式声明特殊映射 ### Service层规范 -- 简单CRUD场景直接继承BaseServiceImpl即可 -- 复杂业务场景需要: - 1. 定义业务接口方法 - 2. 实现具体的业务逻辑 - 3. 处理业务异常 - 4. 添加事务控制 -- 示例: -```java +- 简单CRUD继承BaseServiceImpl,复杂业务需定义专门接口和实现,包含事务控制和异常处理 +- 使用@Transactional注解控制事务,合理设置事务传播机制和隔离级别 +- 实现乐观锁(@Version)或悲观锁(findByIdWithLock)进行并发控制 +- 示例 @Slf4j @Service @ServiceType(DATABASE) -public class ExternalSystemServiceImpl extends BaseServiceImpl +public class ExternalSystemServiceImpl extends BaseServiceImpl implements IExternalSystemService { - + @Override @Transactional public boolean testConnection(Long id) { // 业务实现 } } -``` - -### 查询规范 -- 简单查询使用BaseQuery -- 复杂查询需要: - 1. 继承BaseQuery - 2. 使用@QueryField注解标注查询字段 - 3. 指定查询类型 -- 示例: -```java -@Data -@EqualsAndHashCode(callSuper = true) -public class ExternalSystemQuery extends BaseQuery { - @QueryField(field = "name", type = QueryType.LIKE) - private String name; - - @QueryField(field = "type") - private SystemType type; -} -``` - -### 异常处理规范 -- 业务异常应承BusinessException -- 使用ResponseCode定义错误码 -- 在messages.properties中定义错误消息 -- 通过GlobalExceptionHandler统一处理异常 -- 示例: -```java -throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED); -``` ### Controller层规范 -- REST FULL接口使用框架BaseController -- 新增接口命名规范: - - 三方接口:模块名ApiController(如:ExternalSystemApiController) - - 二方接口:模块名Controller(如:ExternalSystemController) +- REST接口使用BaseController,三方接口命名为模块名ApiController,二方接口为模块名Controller +- 返回值com.qqchen.deploy.backend.framework.api.Response +- 统一使用GlobalExceptionHandler处理异常 - 示例: -```java @Slf4j @RestController @RequestMapping("/api/v1/external-system") @@ -141,19 +49,16 @@ throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED); public class ExternalSystemApiController extends BaseController { // 特定业务方法实现 } -``` ### Repository层规范 -- 继承IBaseRepository -- 定义特定的查询方法 +- 继承IBaseRepository,定义特定查询方法 +- 使用JPA命名规范定义查询方法 +- 复杂查询使用@Query注解 - 示例: -```java @Repository public interface IExternalSystemRepository extends IBaseRepository { boolean existsByNameAndDeletedFalse(String name); } -``` - ### Converter规范 - 继承BaseConverter,遵循以下规则: 1. 简单场景(字段完全匹配)示例: @@ -172,354 +77,52 @@ public interface IExternalSystemRepository extends IBaseRepository implements IUserService { - - @Resource - private IUserRepository userRepository; - - @Resource - private UserConverter userConverter; - - @Override - @Cacheable(value = "user", key = "#id") - public UserDTO findById(Long id) { - User user = userRepository.findById(id) - .orElseThrow(() -> new BusinessException(ResponseCode.USER_NOT_FOUND)); - return userConverter.toDto(user); - } - - @Override - @Transactional - @CacheEvict(value = "user", key = "#id") - public void delete(Long id) { - User user = findEntityById(id); - user.setDeleted(true); - userRepository.save(user); - } -} -``` - -### 分层设计规范 -- Controller层: - 1. REST接口设计 - 2. 参数校验 - 3. 权限控制 - 4. 响应封装 -- Service层: - 1. 业务逻辑处理 - 2. 事务控制 - 3. 缓存处理 - 4. 并发控制 -- Repository层: - 1. 数据访问 - 2. 查询优化 - 3. 持久化操作 - -### 数据对象规范 -- DTO设计: - 1. 简单CRUD场景使用统一DTO - 2. 复杂业务场景使用专门的Request/Response - 3. 继承BaseDTO获取基础字段 -- 验证规则: - 1. 使用Jakarta Validation注解 - 2. 自定义验证注解 - 3. 分组验证 -- 对象转换: - 1. 使用MapStruct进行对象转换 - 2. 继承BaseConverter - 3. 显式声明特殊映射 - -### 业务处理规范 -- 事务管理: - 1. 声明式事务(@Transactional) - 2. 事务传播机制: - - REQUIRED:默认,需要事务 - - REQUIRES_NEW:新建事务 - - SUPPORTS:支持当前事务 - - NOT_SUPPORTED:不支持事务 - - NEVER:不允许事务 - 3. 事务隔离级别: - - READ_COMMITTED:默认 - - REPEATABLE_READ:需要时使用 -- 并发控制: - 1. 乐观锁: - - 使用@Version注解 - - 实现重试机制 - 2. 悲观锁: - - findByIdWithLock方法 - - 限定锁范围和时间 - 3. 分布式锁: - - 使用Redis实现 - - 设置超时时间 - - 防止死锁 -- 缓存策略: - 1. 缓存注解使用: - - @Cacheable:查询 - - @CachePut:更新 - - @CacheEvict:删除 - 2. 缓存Key规范: - - 格式:模块:业务:标识 - - 避免特殊字符 - 3. 缓存更新策略: - - Cache Aside - - Write Through - - Write Back +- 使用@Cacheable(查询)、@CachePut(更新)、@CacheEvict(删除)注解 +- 缓存Key格式:模块:业务:标识,如user:info:1 +- 根据业务场景选择缓存策略:Cache Aside、Write Through、Write Back ### 安全规范 -- 认证授权: - 1. JWT Token结构和有效期 - 2. 权限注解使用 - 3. 自定义权限评估 -- 敏感信息: - 1. 密码加密存储 - 2. 传输数据加密 - 3. 日志脱敏处理 - 4. 接口参数脱敏 -- 接口防护: - 1. 参数校验 - 2. SQL注入防护 - 3. XSS防护 - 4. CSRF防护 - -### 异常处理规范 -- 异常分类: - 1. 业务异常(BusinessException) - 2. 系统异常(SystemException) - 3. 参数异常(ValidationException) -- 错误码: - 1. 系统级错误(1xxx) - 2. 业务级错误(2xxx) - 3. 第三方错误(3xxx) -- 异常处理: - 1. 统一异常处理器 - 2. 错误信息国际化 - 3. 异常日志记录 - -### 日志规范 -- 日志级别: - 1. ERROR:系统错误、业务异常 - 2. WARN:潜在问题警告 - 3. INFO:重要业务操作 - 4. DEBUG:调试信息 -- 日志内容: - 1. 操作人信息 - 2. 业务模块 - 3. 操作类型 - 4. 关键参数 -- 日志脱敏: - 1. 密码信息 - 2. 个人隐私 - 3. 敏感配置 - -### 文档规范 -- 代码注释: - 1. 类注释:用途、作者、版本 - 2. 方法注释:参数、返回值、异常 - 3. 业务注释:业务规则、处理逻辑 -- API文档: - 1. 使用Swagger注解 - 2. 接口说明完整 - 3. 参数说明清晰 -- README维护: - 1. 功能清单及时更新 - 2. 接口文档同步修改 - 3. 环境配置说明 +- JWT Token包含Header(算法)、Payload(用户信息)、Signature(签名) +- Access Token有效期2小时,Refresh Token有效期7天 +- 敏感数据加密存储和传输,日志和接口返回需脱敏 ### 测试规范 -- 单元测试: - 1. Service层业务测试 - 2. 重要工具类测试 - 3. 边界条件测试 -- 集成测试: - 1. Controller层接口测试 - 2. 数据库操作测试 - 3. 缓存操作测试 -- 测试原则: - 1. 测试覆盖率要求 - 2. 测试数据隔离 - 3. 测试用例完整性 +- Service层:使用@SpringBootTest和@MockBean,测试所有业务场景 +- Controller层:使用@WebMvcTest或MockMvc,测试所有接口路径 +- Repository层:使用@DataJpaTest,测试所有查询方法 + +### 文档规范 +- 类注释:说明用途、作者、版本 +- 方法注释:说明参数、返回值、异常 +- API文档:使用Swagger注解,保持文档同步更新 ### 性能优化规范 -- 数据库优化: - 1. 索引设计 - 2. SQL优化 - 3. 分页查询 -- 代码优化: - 1. 循环优化 - 2. 集合操作 - 3. 字符串处理 -- 缓存优化: - 1. 缓存粒度 - 2. 缓存更新 - 3. 缓存穿透处理 \ No newline at end of file +- 数据库优化:索引设计、SQL优化、分页查询 +- 代码优化:循环优化、集合操作、字符串处理 +- 缓存优化:合理设置缓存粒度、更新策略、防止缓存穿透 \ No newline at end of file diff --git a/backend/.cursorrules1 b/backend/.cursorrules1 new file mode 100644 index 00000000..3e61a1cb --- /dev/null +++ b/backend/.cursorrules1 @@ -0,0 +1,516 @@ +您是Java编程、Spring Boot、Spring Framework、Maven、JUnit和相关Java技术的专家,你深思熟虑,给出细致入微的答案,并且善于推理。你细心地提供准确、真实、周到的答案,是一个推理天才。 +需要实现的是企业应用级别的管理框架,需要具有高性能。 + +### 严格遵循的要求 +- 首先,一步一步地思考——详细描述你在伪代码中构建什么的计划。 +- 确认,然后写代码! +- 始终编写正确、最佳实践、DRY原则(不要重复自己)、无错误、功能齐全且可工作的代码,还应与下面代码实施指南中列出的规则保持一致。 +- 专注于简单易读的代码,而不是高性能。 +- 完全实现所有要求的功能。 +- 不要留下待办事项、占位符或缺失的部分。 +- 确保代码完整!彻底确认。 +- 包括所有必需的导入的包,并确保关键组件的正确命名。 +- 如果你认为可能没有正确答案,你就说出来。 +- 如果你不知道答案,就说出来,而不是猜测。 +- 可以提出合理化的建议,但是需要等待是否可以。 +- 对于新设计的实体类、字段、方法都要写注释,对于实际的逻辑要有逻辑注释。 +- 写了新的数据库表,应该在V1.0.0__init_schema.sql、V1.0.1__init_data.sql补充数据库表和初始化数据。 +- 编写代码前,先查看一下V1.0.0__init_schema.sql、V1.0.1__init_data.sql表结构,再进行编写。 +# Deploy Ease Platform 开发规范 + +### 包结构说明 +- 框架包路径 + com.qqchen.deploy.backend.framework + com.qqchen.deploy.backend.framework.annotation 注解实现 + com.qqchen.deploy.backend.framework.api api相关 + com.qqchen.deploy.backend.framework.audit 审计 + com.qqchen.deploy.backend.framework.controller BaseController + com.qqchen.deploy.backend.framework.converter BaseConverter + com.qqchen.deploy.backend.framework.domain 聚合(AggregateRoot、Entity所有的实体类都继承Entity) + com.qqchen.deploy.backend.framework.dto DTO(BaseDTO、BaseResponse) + com.qqchen.deploy.backend.framework.enums 枚举 框架枚举写在这个包下 + com.qqchen.deploy.backend.framework.exception 异常 + com.qqchen.deploy.backend.framework.handler 全局异常拦截 + com.qqchen.deploy.backend.framework.query 接口请求入参(BaseQuery、DateRange、Range) + com.qqchen.deploy.backend.framework.repository IBaseRepository + com.qqchen.deploy.backend.framework.security JWT + com.qqchen.deploy.backend.framework.service IBaseService + com.qqchen.deploy.backend.framework.service.impl BaseServiceImpl +- 业务包路径 + com.qqchen.deploy.backend.api 三方接口 + com.qqchen.deploy.backend.controller 二方接口 + com.qqchen.deploy.backend.converter 转换器 + com.qqchen.deploy.backend.entity 数据库实体类 + com.qqchen.deploy.backend.integration 第三方系统对接 + com.qqchen.deploy.backend.enums 实体类枚举、业务枚举统一卸载这个包下 + com.qqchen.deploy.backend.model + com.qqchen.deploy.backend.model.dto 存放所有DTO对象 + com.qqchen.deploy.backend.model.query 配套page、list接口使用 + com.qqchen.deploy.backend.model.request 接口入参(复杂业务场景使用) + com.qqchen.deploy.backend.model.response 接口出参(复杂业务场景使用) + +### DTO设计规范 +- 简单CRUD场景使用统一的DTO,无需额外的Request/Response对象 +- 以下场景需要使用专门的Request/Response: + 1. 复杂的业务场景(如用户注册、登录) + 2. 有特殊验证需求的接口 + 3. 入参和出参差异较大的接口 + 4. 需要特殊安全处理的接口 +- DTO应继承BaseDTO,获取基础字段支持 +- Request/Response对象应该放在对应的model.request和model.response包中 + +### 验证规范 +- DTO字段验证: + 1. 使用Jakarta Validation注解 + 2. 自定义验证注解 + 3. 分组验证 +- 示例: +```java +@Data +public class ExternalSystemDTO extends BaseDTO { + @NotBlank(message = "系统名称不能为空") + private String name; + + @NotNull(message = "系统类型不能为空") + private SystemType type; +} +``` +### 数据对象规范 +- DTO设计: + 1. 简单CRUD场景使用统一DTO + 2. 复杂业务场景使用专门的Request/Response + 3. 继承BaseDTO获取基础字段 +- 验证规则: + 1. 使用Jakarta Validation注解 + 2. 自定义验证注解 + 3. 分组验证 +- 对象转换: + 1. 使用MapStruct进行对象转换 + 2. 继承BaseConverter + 3. 显式声明特殊映射 + +### Service层规范 +- 简单CRUD场景直接继承BaseServiceImpl即可 +- 复杂业务场景需要: + 1. 定义业务接口方法 + 2. 实现具体的业务逻辑 + 3. 处理业务异常 + 4. 添加事务控制 +- 示例: +```java +@Slf4j +@Service +@ServiceType(DATABASE) +public class ExternalSystemServiceImpl extends BaseServiceImpl + implements IExternalSystemService { + + @Override + @Transactional + public boolean testConnection(Long id) { + // 业务实现 + } +} +``` + +### 查询规范 +- 简单查询使用BaseQuery +- 复杂查询需要: + 1. 继承BaseQuery + 2. 使用@QueryField注解标注查询字段 + 3. 指定查询类型 +- 示例: +```java +@Data +@EqualsAndHashCode(callSuper = true) +public class ExternalSystemQuery extends BaseQuery { + @QueryField(field = "name", type = QueryType.LIKE) + private String name; + + @QueryField(field = "type") + private SystemType type; +} +``` + +### 异常处理规范 +- 业务异常应承BusinessException +- 使用ResponseCode定义错误码 +- 在messages.properties中定义错误消息 +- 通过GlobalExceptionHandler统一处理异常 +- 示例: +```java +throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED); +``` + +### Controller层规范 +- REST FULL接口使用框架BaseController +- 新增接口命名规范: + - 三方接口:模块名ApiController(如:ExternalSystemApiController) + - 二方接口:模块名Controller(如:ExternalSystemController) +- 示例: +```java +@Slf4j +@RestController +@RequestMapping("/api/v1/external-system") +@Tag(name = "外部系统管理", description = "外部系统管理相关接口") +public class ExternalSystemApiController extends BaseController { + // 特定业务方法实现 +} +``` + +### Repository层规范 +- 继承IBaseRepository +- 定义特定的查询方法 +- 示例: +```java +@Repository +public interface IExternalSystemRepository extends IBaseRepository { + boolean existsByNameAndDeletedFalse(String name); +} +``` + +### Converter规范 +- 继承BaseConverter,遵循以下规则: + 1. 简单场景(字段完全匹配)示例: + ```java + @Mapper(config = BaseConverter.class) + public interface ExternalSystemConverter extends BaseConverter { + // 字段完全匹配时无需额外配置 + } + ``` + 2. 复杂场景(需要特殊映射)示例: + ```java + @Mapper(config = BaseConverter.class) + public interface UserConverter extends BaseConverter { + @Mapping(target = "departmentId", source = "department.id") + @Mapping(target = "departmentName", source = "department.name") + UserDTO toDto(User entity); + } + ``` + +### 测试规范 +- Service层测试: + 1. 使用@SpringBootTest + 2. 使用@MockBean模拟依赖 + 3. 测试所有业务场景 +- Controller层测试: + 1. 使用@WebMvcTest或@SpringBootTest + @AutoConfigureMockMvc + 2. 使用MockMvc测试接口 + 3. 测试所有接口路径 +- Repository层测试: + 1. 使用@DataJpaTest + 2. 测试所有查询方法 + +### 文档规范 +- 类注释:说明类的用途、作者、版本 +- 方法注释:说明参数、返回值、异常 +- 业务方法注释:说明业务规则 +- API文档:使用Swagger注解 + +### 命名规范 +- 使用PascalCase作为类名(例,UserController、OrderService) +- 使用camelCase作为方法和变量名(例如,findUserById、isOrderValid) +- 对常量使用ALL_CAPS(例如,MAX_RETRY_ATTEMPTS、DEFAULT_PAGE_SIZE) +- service、repository接口类需要以I开头,service实现类无需使用I开头但尾部需要Impl结尾 + +### 数据库规范 +- 使用Flyway进行数据库版本控制 +- 新增表结构写入V1.0.0__init_schema.sql +- 新增初始数据写入V1.0.1__init_data.sql +- 表名使用下划线命名法(例如:sys_user, sys_role) +- 字段名使用下划线命名法(例如:create_time, update_by) +- 必须包含基础字段:id, create_time, create_by, update_time, update_by, version, deleted + +### 国际化规范 +- 使用ResponseCode进行异常码定义 +- 在messages.properties中定义中文消息、messages_en_US.properties中定义英文消息、在messages_zh_CN.properties中定义中文消息 + +### 文档维护规范 +- README.md文件维护规则: + 1. 接口变更必须同步更新README.md + - 新增接口:在对应模块的API文档中添加接口说明、修改接口:更新对应接口的参数说明和响应格式、删除接口:移除对应接口的文档说明 + 2. 接口文档格式要求: + ```http + [HTTP方法] [接口路径] + + 请求参数: + { + "参数1": "值1", // 参数说明(是否必填) + "参数2": "值2" // 参数说明(是否必填) + } + + 响应结果: + { + "success": true, + "code": 200, + "message": "操作成功", + "data": { + // 响应数据结构 + } + } + ``` + 3. 功能清单维护: + - 新增功能:在"已实现功能"章节添加功能说明;修改功能:更新对应功能的描述;删除功能:移除对应功能说明 + 4. 文档结构规范: + - 功能描述必须清晰准确;使用markdown格式化文档;保持文档层级结构一致;使用统一的示例格式 + +### 错误码规范 +- 错误码分类: + 1. 系统级错误(1xxx) + - 1000-1099:通用系统错误 + - 1100-1199:依赖注入错误 + - 1200-1299:数据库错误 + 2. 业务级错误(2xxx) + - 2000-2099:通用业务错误 + - 2100-2199:角色相关错误 + - 2200-2299:JWT相关错误 + - 2300-2399:部门相关错误 + - 2400-2499:权限相关错误 + - 2500-2599:外部系统相关错误 +- 错误码命名规则: + 1. 使用大写字母和下划线 + 2. 采用`模块_操作_错误`的命名方式 + 3. 在ResponseCode枚举类中定义 +- 错误信息规范: + 1. 在messages.properties中定义错误信息 + 2. 错误信息要简洁明了 + 3. 包含问题描述和可能的解决方案 + 4. 支持国际化 + +### 接口安全规范 +- JWT Token规范: + 1. Token结构: + - Header:包含算法信息 + - Payload:包含用户信息和权限信息 + - Signature:使用密钥签名 + 2. Token有效期: + - Access Token:2小时 + - Refresh Token:7天 + 3. Token刷新机制: + - 使用Refresh Token获取新的Access Token + - Refresh Token一次性使用 +- 接口权限控制: + 1. 使用@PreAuthorize注解控制接口访问权限 + 2. 使用@Secured注解控制方法级别权限 + 3. 实现自定义权限评估器 +- 敏感数据处理: + 1. 密码等敏感信息必须加密存储 + 2. 使用AES加密传输敏感数据 + 3. 日志中不得打印敏感信息 + 4. 接口返回时脱敏处理 + +### 代码审查规范 +- 审查重点: + 1. 代码质量: + - 代码是否符合编码规范 + - 是否存在重复代码 + - 是否有性能问题 + 2. 业务逻辑: + - 业务流程是否正确 + - 异常处理是否完善 + - 边界条件是否考虑 + 3. 安全性: + - 是否存在安全漏洞 + - 敏感数据是否安全处理 + - 权限控制是否正确 + 4. 测试覆盖: + - 单元测试是否完整 + - 是否覆盖关键路径 + - 是否包含边界测试 +- 审查流程: + 1. 提交前自查 + 2. 提交Pull Request + 3. 代码评审 + 4. 修改完善 + 5. 评审通过 +- 审查标准: + 1. 代码实现是否满足需求 + 2. 是否符合开发规范 + 3. 是否有充分的测试覆盖 + 4. 文档是否同步更新 + +### 缓存使用规范 +- 缓存注解: + 1. @Cacheable:适用于查询操作 + 2. @CachePut:适用于更新操作 + 3. @CacheEvict:适用于删除操作 + 4. @Caching:组合多个缓存操作 +- 缓存Key: + 1. 格式:`模块:业务:标识` + 2. 示例:`user:info:1` + 3. 避免特殊字符 + 4. 长度控制在200以内 +- 缓存策略: + 1. 读多写少用Cache Aside + 2. 写多读少用Write Through + 3. 高一致性用Write Back +- 示例: +```java +@Slf4j +@Service +@ServiceType(DATABASE) +public class UserServiceImpl extends BaseServiceImpl implements IUserService { + + @Resource + private IUserRepository userRepository; + + @Resource + private UserConverter userConverter; + + @Override + @Cacheable(value = "user", key = "#id") + public UserDTO findById(Long id) { + User user = userRepository.findById(id) + .orElseThrow(() -> new BusinessException(ResponseCode.USER_NOT_FOUND)); + return userConverter.toDto(user); + } + + @Override + @Transactional + @CacheEvict(value = "user", key = "#id") + public void delete(Long id) { + User user = findEntityById(id); + user.setDeleted(true); + userRepository.save(user); + } +} +``` + +### 分层设计规范 +- Controller层: + 1. REST接口设计 + 2. 参数校验 + 3. 权限控制 + 4. 响应封装 +- Service层: + 1. 业务逻辑处理 + 2. 事务控制 + 3. 缓存处理 + 4. 并发控制 +- Repository层: + 1. 数据访问 + 2. 查询优化 + 3. 持久化操作 + +### 业务处理规范 +- 事务管理: + 1. 声明式事务(@Transactional) + 2. 事务传播机制: + - REQUIRED:默认,需要事务 + - REQUIRES_NEW:新建事务 + - SUPPORTS:支持当前事务 + - NOT_SUPPORTED:不支持事务 + - NEVER:不允许事务 + 3. 事务隔离级别: + - READ_COMMITTED:默认 + - REPEATABLE_READ:需要时使用 +- 并发控制: + 1. 乐观锁: + - 使用@Version注解 + - 实现重试机制 + 2. 悲观锁: + - findByIdWithLock方法 + - 限定锁范围和时间 + 3. 分布式锁: + - 使用Redis实现 + - 设置超时时间 + - 防止死锁 +- 缓存策略: + 1. 缓存注解使用: + - @Cacheable:查询 + - @CachePut:更新 + - @CacheEvict:删除 + 2. 缓存Key规范: + - 格式:模块:业务:标识 + - 避免特殊字符 + 3. 缓存更新策略: + - Cache Aside + - Write Through + - Write Back + +### 安全规范 +- 认证授权: + 1. JWT Token结构和有效期 + 2. 权限注解使用 + 3. 自定义权限评估 +- 敏感信息: + 1. 密码加密存储 + 2. 传输数据加密 + 3. 日志脱敏处理 + 4. 接口参数脱敏 +- 接口防护: + 1. 参数校验 + 2. SQL注入防护 + 3. XSS防护 + 4. CSRF防护 + +### 异常处理规范 +- 异常分类: + 1. 业务异常(BusinessException) + 2. 系统异常(SystemException) + 3. 参数异常(ValidationException) +- 错误码: + 1. 系统级错误(1xxx) + 2. 业务级错误(2xxx) + 3. 第三方错误(3xxx) +- 异常处理: + 1. 统一异常处理器 + 2. 错误信息国际化 + 3. 异常日志记录 + +### 日志规范 +- 日志级别: + 1. ERROR:系统错误、业务异常 + 2. WARN:潜在问题警告 + 3. INFO:重要业务操作 + 4. DEBUG:调试信息 +- 日志内容: + 1. 操作人信息 + 2. 业务模块 + 3. 操作类型 + 4. 关键参数 +- 日志脱敏: + 1. 密码信息 + 2. 个人隐私 + 3. 敏感配置 + +### 文档规范 +- 代码注释: + 1. 类注释:用途、作者、版本 + 2. 方法注释:参数、返回值、异常 + 3. 业务注释:业务规则、处理逻辑 +- API文档: + 1. 使用Swagger注解 + 2. 接口说明完整 + 3. 参数说明清晰 +- README维护: + 1. 功能清单及时更新 + 2. 接口文档同步修改 + 3. 环境配置说明 + +### 测试规范 +- 单元测试: + 1. Service层业务测试 + 2. 重要工具类测试 + 3. 边界条件测试 +- 集成测试: + 1. Controller层接口测试 + 2. 数据库操作测试 + 3. 缓存操作测试 +- 测试原则: + 1. 测试覆盖率要求 + 2. 测试数据隔离 + 3. 测试用例完整性 + +### 性能优化规范 +- 数据库优化: + 1. 索引设计 + 2. SQL优化 + 3. 分页查询 +- 代码优化: + 1. 循环优化 + 2. 集合操作 + 3. 字符串处理 +- 缓存优化: + 1. 缓存粒度 + 2. 缓存更新 + 3. 缓存穿透处理 \ No newline at end of file diff --git a/backend/README.md b/backend/README.md index 9574e1dd..23d29045 100644 --- a/backend/README.md +++ b/backend/README.md @@ -772,7 +772,7 @@ POST /api/v1/permission - 详细的接口文档 - 规范的代码注释 -详细规范请参考:[后端开发规范](.cursorrules) +详细规范请参考:[后端开发规范](.cursorrules1) ### 前端开发规范 - 统一的接口调用方式 diff --git a/backend/src/main/java/com/qqchen/deploy/backend/api/DepartmentApiController.java b/backend/src/main/java/com/qqchen/deploy/backend/api/DepartmentApiController.java index a17cca82..6db7b1c0 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/api/DepartmentApiController.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/api/DepartmentApiController.java @@ -12,10 +12,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/entity/ExternalSystem.java b/backend/src/main/java/com/qqchen/deploy/backend/entity/ExternalSystem.java index 311e2f07..fabd27d0 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/entity/ExternalSystem.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/entity/ExternalSystem.java @@ -1,5 +1,8 @@ package com.qqchen.deploy.backend.entity; +import com.qqchen.deploy.backend.enums.ExternalSystemAuthTypeEnum; +import com.qqchen.deploy.backend.enums.ExternalSystemSyncStatusEnum; +import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum; import com.qqchen.deploy.backend.framework.annotation.LogicDelete; import com.qqchen.deploy.backend.framework.domain.Entity; import jakarta.persistence.*; @@ -26,7 +29,7 @@ public class ExternalSystem extends Entity { */ @Column(nullable = false) @Enumerated(EnumType.STRING) - private SystemType type; + private ExternalSystemTypeEnum type; /** * 系统访问地址 @@ -51,11 +54,11 @@ public class ExternalSystem extends Entity { private Boolean enabled = true; /** - * 认证方式:BASIC/TOKEN/OAUTH�� + * 认证方式:BASIC/TOKEN/OAUTH */ @Column(name = "auth_type", nullable = false) @Enumerated(EnumType.STRING) - private AuthType authType; + private ExternalSystemAuthTypeEnum authType; /** * 用户名 @@ -77,7 +80,7 @@ public class ExternalSystem extends Entity { */ @Column(name = "sync_status") @Enumerated(EnumType.STRING) - private SyncStatus syncStatus; + private ExternalSystemSyncStatusEnum syncStatus; /** * 最后同步时间 @@ -96,31 +99,6 @@ public class ExternalSystem extends Entity { */ @Column(columnDefinition = "JSON") private String config; - - /** - * 系统类型枚举 - */ - public enum SystemType { - JENKINS, - GIT, - ZENTAO - } - - /** - * 认证类型枚举 - */ - public enum AuthType { - BASIC, - TOKEN, - OAUTH - } - - /** - * 同步状态枚举 - */ - public enum SyncStatus { - SUCCESS, - FAILED, - RUNNING - } -} \ No newline at end of file + + +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemAuthTypeEnum.java b/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemAuthTypeEnum.java new file mode 100644 index 00000000..094b92be --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemAuthTypeEnum.java @@ -0,0 +1,10 @@ +package com.qqchen.deploy.backend.enums; + +/** + * 认证类型枚举 + */ +public enum ExternalSystemAuthTypeEnum { + BASIC, + TOKEN, + OAUTH +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemSyncStatusEnum.java b/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemSyncStatusEnum.java new file mode 100644 index 00000000..b08e54a8 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemSyncStatusEnum.java @@ -0,0 +1,7 @@ +package com.qqchen.deploy.backend.enums; + +public enum ExternalSystemSyncStatusEnum { + SUCCESS, + FAILED, + RUNNING +} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemTypeEnum.java b/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemTypeEnum.java new file mode 100644 index 00000000..1f531645 --- /dev/null +++ b/backend/src/main/java/com/qqchen/deploy/backend/enums/ExternalSystemTypeEnum.java @@ -0,0 +1,10 @@ +package com.qqchen.deploy.backend.enums; + +/** + * 系统类型枚举 + */ +public enum ExternalSystemTypeEnum { + JENKINS, + GIT, + ZENTAO +} \ No newline at end of file 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 8636f068..546c4ccf 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 @@ -66,6 +66,7 @@ public enum ResponseCode { PERMISSION_ASSIGN_FAILED(2404, "permission.assign.failed"), // 第三方系统相关错误码 (2500-2599) + EXTERNAL_SYSTEM_NOT_FOUND(2505, "external.system.not.found"), EXTERNAL_SYSTEM_NAME_EXISTS(2500, "external.system.name.exists"), EXTERNAL_SYSTEM_TYPE_URL_EXISTS(2501, "external.system.type.url.exists"), EXTERNAL_SYSTEM_DISABLED(2502, "external.system.disabled"), diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/BusinessException.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/BusinessException.java index d9ce6940..68f3b12f 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/BusinessException.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/BusinessException.java @@ -14,4 +14,5 @@ public class BusinessException extends BaseException { public BusinessException(ResponseCode errorCode, Object[] args) { super(errorCode, args); } + } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/IExternalSystemIntegration.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/IExternalSystemIntegration.java index f9ffb5fb..a6c72aed 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/IExternalSystemIntegration.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/integration/IExternalSystemIntegration.java @@ -1,6 +1,7 @@ package com.qqchen.deploy.backend.integration; import com.qqchen.deploy.backend.entity.ExternalSystem; +import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum; /** * 第三方系统集成接口 @@ -26,5 +27,5 @@ public interface IExternalSystemIntegration { * * @return 系统类型 */ - ExternalSystem.SystemType getSystemType(); + ExternalSystemTypeEnum getSystemType(); } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/JenkinsService.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/JenkinsService.java deleted file mode 100644 index f111d64e..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/JenkinsService.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.qqchen.deploy.backend.integration; - -import com.qqchen.deploy.backend.integration.client.IJenkinsClient; -import com.qqchen.deploy.backend.integration.dto.JenkinsBuildDTO; -import com.qqchen.deploy.backend.integration.dto.JenkinsConnectionDTO; -import feign.FeignException; -import jakarta.annotation.Resource; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -@Slf4j -public class JenkinsService { - -// @Resource -// private final IJenkinsClient jenkinsClient; -// -// public boolean testConnection(JenkinsConnectionDTO config) { -// try { -// String authHeader = createAuthHeader(config.getUsername(), config.getPassword()); -// jenkinsClient.testConnection(authHeader); -// log.info("Jenkins连接测试成功"); -// return true; -// } catch (FeignException.Unauthorized e) { -// log.error("Jenkins连接测试失败: 认证失败"); -// return false; -// } catch (Exception e) { -// log.error("Jenkins连接测试失败: {}", e.getMessage()); -// return false; -// } -// } -// -// public BuildInfo getBuildInfo(JenkinsConnectionDTO config, String jobName, Integer buildNumber) { -// try { -// String authHeader = createAuthHeader(config.getUsername(), config.getPassword()); -// JenkinsBuildDTO jenkinsBuild = jenkinsClient.getBuildInfo(authHeader, jobName, buildNumber); -// return convertToBuildInfo(jenkinsBuild); -// } catch (Exception e) { -// log.error("获取Jenkins构建信息失败: {}", e.getMessage()); -// throw new RuntimeException("Failed to get build info", e); -// } -// } -// -// private String createAuthHeader(String username, String password) { -// String auth = username + ":" + password; -// return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes()); -// } -// -// private BuildInfo convertToBuildInfo(JenkinsBuildDTO dto) { -// BuildInfo buildInfo = new BuildInfo(); -// buildInfo.setJobName(dto.getJobName()); -// buildInfo.setBuildNumber(dto.getBuildNumber()); -// buildInfo.setBuildUrl(dto.getUrl()); -// buildInfo.setBuildTime(LocalDateTime.ofInstant( -// Instant.ofEpochMilli(dto.getTimestamp()), -// ZoneId.systemDefault() -// )); -// -// // 转换构建状态 -// buildInfo.setStatus(convertBuildStatus(dto.getResult(), dto.getStatus())); -// -// return buildInfo; -// } -// -// private BuildInfo.BuildStatus convertBuildStatus(String result, String status) { -// if ("IN_PROGRESS".equals(status)) { -// return BuildInfo.BuildStatus.IN_PROGRESS; -// } -// -// return switch (result) { -// case "SUCCESS" -> BuildInfo.BuildStatus.SUCCESS; -// case "FAILURE" -> BuildInfo.BuildStatus.FAILURE; -// case "ABORTED" -> BuildInfo.BuildStatus.CANCELLED; -// default -> throw new IllegalStateException("Unknown build status: " + result); -// }; -// } -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/client/IJenkinsClient.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/client/IJenkinsClient.java deleted file mode 100644 index 76a77a0a..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/client/IJenkinsClient.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.qqchen.deploy.backend.integration.client; - -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestHeader; - -import java.util.Map; - -@FeignClient(name = "jenkins-service", url = "#{null}") // 动态URL -public interface IJenkinsClient { - - @GetMapping("/api/json") - Map testConnection( - @RequestHeader("Authorization") String authorization - ); - - @PostMapping("/job/{jobName}/build") - void triggerBuild( - @RequestHeader("Authorization") String authorization, - @PathVariable("jobName") String jobName - ); -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/config/JenkinsProperties.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/config/JenkinsProperties.java deleted file mode 100644 index e676ee16..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/config/JenkinsProperties.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.qqchen.deploy.backend.integration.config; - -import lombok.Data; -import org.springframework.stereotype.Component; - -import java.util.Base64; - -@Data -@Component -public class JenkinsProperties { - - private String url; - - private String username; - - private String password; - - public String getBasicAuth() { - String auth = username + ":" + password; - return "Basic " + Base64.getEncoder().encodeToString(auth.getBytes()); - } -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/BuildInfo.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/BuildInfo.java deleted file mode 100644 index 9aa795ee..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/BuildInfo.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.qqchen.deploy.backend.integration.dto; - -import lombok.Data; - -import java.time.LocalDateTime; - -@Data -public class BuildInfo { - private String jobName; - - private Integer buildNumber; - - private BuildStatus status; - - private LocalDateTime buildTime; - - private String buildUrl; - - public enum BuildStatus { - SUCCESS, FAILURE, IN_PROGRESS, CANCELLED - } -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/JenkinsBuildDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/JenkinsBuildDTO.java deleted file mode 100644 index 3306c52e..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/JenkinsBuildDTO.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.qqchen.deploy.backend.integration.dto; - -import com.qqchen.deploy.backend.framework.integration.dto.ThirdPartyDTO; -import lombok.Data; - -@Data -public class JenkinsBuildDTO implements ThirdPartyDTO { - - private String jobName; - - private Integer buildNumber; - - private String status; - - private String result; - - private Long timestamp; - - private String url; -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/JenkinsConnectionDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/JenkinsConnectionDTO.java deleted file mode 100644 index 246d3e08..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/dto/JenkinsConnectionDTO.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.qqchen.deploy.backend.integration.dto; - -import com.qqchen.deploy.backend.framework.integration.dto.ThirdPartyDTO; -import lombok.Data; - -@Data -public class JenkinsConnectionDTO implements ThirdPartyDTO { - - private String url; - - private String username; - - private String password; -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/enums/JenkinsBuildStatus.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/enums/JenkinsBuildStatus.java deleted file mode 100644 index 2b25be18..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/enums/JenkinsBuildStatus.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.qqchen.deploy.backend.integration.enums; - -public enum JenkinsBuildStatus { - SUCCESS, FAILURE, IN_PROGRESS, CANCELLED -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/impl/GitIntegration.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/impl/GitIntegration.java index 8943870c..7dc0402f 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/impl/GitIntegration.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/integration/impl/GitIntegration.java @@ -1,6 +1,7 @@ package com.qqchen.deploy.backend.integration.impl; import com.qqchen.deploy.backend.entity.ExternalSystem; +import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum; import com.qqchen.deploy.backend.integration.IExternalSystemIntegration; import lombok.extern.slf4j.Slf4j; import org.springframework.http.*; @@ -43,7 +44,7 @@ public class GitIntegration implements IExternalSystemIntegration { } @Override - public ExternalSystem.SystemType getSystemType() { - return ExternalSystem.SystemType.GIT; + public ExternalSystemTypeEnum getSystemType() { + return ExternalSystemTypeEnum.GIT; } } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/integration/impl/JenkinsIntegration.java b/backend/src/main/java/com/qqchen/deploy/backend/integration/impl/JenkinsIntegration.java index c89474df..7c94ffaa 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/integration/impl/JenkinsIntegration.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/integration/impl/JenkinsIntegration.java @@ -1,6 +1,8 @@ package com.qqchen.deploy.backend.integration.impl; import com.qqchen.deploy.backend.entity.ExternalSystem; +import com.qqchen.deploy.backend.enums.ExternalSystemAuthTypeEnum; +import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum; import com.qqchen.deploy.backend.integration.IExternalSystemIntegration; import lombok.extern.slf4j.Slf4j; import org.springframework.http.*; @@ -9,6 +11,10 @@ import org.springframework.web.client.RestTemplate; import java.util.Base64; +import static com.qqchen.deploy.backend.enums.ExternalSystemAuthTypeEnum.BASIC; +import static com.qqchen.deploy.backend.enums.ExternalSystemAuthTypeEnum.OAUTH; +import static com.qqchen.deploy.backend.enums.ExternalSystemAuthTypeEnum.TOKEN; + @Slf4j @Service public class JenkinsIntegration implements IExternalSystemIntegration { @@ -51,7 +57,7 @@ public class JenkinsIntegration implements IExternalSystemIntegration { } @Override - public ExternalSystem.SystemType getSystemType() { - return ExternalSystem.SystemType.JENKINS; + public ExternalSystemTypeEnum getSystemType() { + return ExternalSystemTypeEnum.JENKINS; } } \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/model/ExternalSystemDTO.java b/backend/src/main/java/com/qqchen/deploy/backend/model/ExternalSystemDTO.java index ec0912d9..ce487e8a 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/model/ExternalSystemDTO.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/model/ExternalSystemDTO.java @@ -1,8 +1,8 @@ package com.qqchen.deploy.backend.model; -import com.qqchen.deploy.backend.entity.ExternalSystem.SystemType; -import com.qqchen.deploy.backend.entity.ExternalSystem.AuthType; -import com.qqchen.deploy.backend.entity.ExternalSystem.SyncStatus; +import com.qqchen.deploy.backend.enums.ExternalSystemAuthTypeEnum; +import com.qqchen.deploy.backend.enums.ExternalSystemSyncStatusEnum; +import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum; import com.qqchen.deploy.backend.framework.dto.BaseDTO; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; @@ -19,7 +19,7 @@ public class ExternalSystemDTO extends BaseDTO { private String name; @NotNull(message = "系统类型不能为空") - private SystemType type; + private ExternalSystemTypeEnum type; @NotBlank(message = "系统访问地址不能为空") private String url; @@ -31,7 +31,7 @@ public class ExternalSystemDTO extends BaseDTO { private Boolean enabled = true; @NotNull(message = "认证方式不能为空") - private AuthType authType; + private ExternalSystemAuthTypeEnum authType; private String username; @@ -39,7 +39,7 @@ public class ExternalSystemDTO extends BaseDTO { private String token; - private SyncStatus syncStatus; + private ExternalSystemSyncStatusEnum syncStatus; private LocalDateTime lastSyncTime; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/model/query/ExternalSystemQuery.java b/backend/src/main/java/com/qqchen/deploy/backend/model/query/ExternalSystemQuery.java index 3c64b2d8..2a806d7b 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/model/query/ExternalSystemQuery.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/model/query/ExternalSystemQuery.java @@ -1,6 +1,6 @@ package com.qqchen.deploy.backend.model.query; -import com.qqchen.deploy.backend.entity.ExternalSystem.SystemType; +import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum; import com.qqchen.deploy.backend.framework.annotation.QueryField; import com.qqchen.deploy.backend.framework.enums.QueryType; import com.qqchen.deploy.backend.framework.query.BaseQuery; @@ -25,7 +25,7 @@ public class ExternalSystemQuery extends BaseQuery { private String name; @QueryField(field = "type") - private SystemType type; + private ExternalSystemTypeEnum type; @QueryField(field = "enabled") private Boolean enabled; diff --git a/backend/src/main/java/com/qqchen/deploy/backend/repository/IExternalSystemRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/repository/IExternalSystemRepository.java index e1161650..b586a2b4 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/repository/IExternalSystemRepository.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/repository/IExternalSystemRepository.java @@ -1,6 +1,7 @@ package com.qqchen.deploy.backend.repository; import com.qqchen.deploy.backend.entity.ExternalSystem; +import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum; import com.qqchen.deploy.backend.framework.repository.IBaseRepository; import org.springframework.stereotype.Repository; @@ -17,7 +18,7 @@ public interface IExternalSystemRepository extends IBaseRepository listGroups(Long externalSystemId); - - /** - * 获取指定的仓库组信息 - * - * @param externalSystemId 外部系统ID - * @param groupId 仓库组ID - * @return 仓库组信息 - * @throws com.qqchen.deploy.backend.framework.exception.BusinessException 当仓库组不存在时抛出异常 - */ - RepositoryGroupDTO getGroup(Long externalSystemId, Long groupId); - - /** - * 同步指定外部系统的仓库组数据 - * - * @param externalSystemId 外部系统ID - */ - void syncGroups(Long externalSystemId); - - /** - * 获取指定仓库组下的项目列表 - * - * @param externalSystemId 外部系统ID - * @param groupId 仓库组ID - * @return 项目列表,按排序号升序排列 - */ - List listProjects(Long externalSystemId, Long groupId); - - /** - * 获取指定的项目信息 - * - * @param externalSystemId 外部系统ID - * @param projectId 项目ID - * @return 项目信息 - * @throws com.qqchen.deploy.backend.framework.exception.BusinessException 当项目不存在时抛出异常 - */ - RepositoryProjectDTO getProject(Long externalSystemId, Long projectId); - - /** - * 同步指定仓库组下的项目数据 - * - * @param externalSystemId 外部系统ID - * @param groupId 仓库组ID - */ - void syncProjects(Long externalSystemId, Long groupId); - - /** - * 获取指定项目的分支列表 - * - * @param externalSystemId 外部系统ID - * @param projectId 项目ID - * @return 分支列表 - */ - List listBranches(Long externalSystemId, Long projectId); - - /** - * 获取指定的分支信息 - * - * @param externalSystemId 外部系统ID - * @param projectId 项目ID - * @param branchName 分支名称 - * @return 分支信息 - * @throws com.qqchen.deploy.backend.framework.exception.BusinessException 当分支不存在时抛出异常 - */ - RepositoryBranchDTO getBranch(Long externalSystemId, Long projectId, String branchName); - - /** - * 同步指定项目的分支数据 - * - * @param externalSystemId 外部系统ID - * @param projectId 项目ID - */ - void syncBranches(Long externalSystemId, Long projectId); - - /** - * 同步指定外部系统的所有数据(同步组、项目、分支) - * - * @param externalSystemId 外部系统ID - */ - void syncAll(Long externalSystemId); - - /** - * 异步同步指定外部系统的所有数据 - * - * @param externalSystemId 外部系统ID - * @return 同步任务ID - */ - Long asyncSyncAll(Long externalSystemId); - - /** - * 获取同步任务状态 - * - * @param historyId 同步历史ID - * @return 同步状态信息 - */ - RepositorySyncStatusDTO getSyncStatus(Long historyId); - - /** - * 获取正在运行的同步任务列表 - * - * @return 运行中的同步任务列表 - */ - List getRunningSyncs(); -} \ No newline at end of file diff --git a/backend/src/main/java/com/qqchen/deploy/backend/service/impl/ExternalSystemServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/service/impl/ExternalSystemServiceImpl.java index a342d505..5d8a67fb 100644 --- a/backend/src/main/java/com/qqchen/deploy/backend/service/impl/ExternalSystemServiceImpl.java +++ b/backend/src/main/java/com/qqchen/deploy/backend/service/impl/ExternalSystemServiceImpl.java @@ -1,6 +1,9 @@ package com.qqchen.deploy.backend.service.impl; import com.qqchen.deploy.backend.entity.ExternalSystem; +import com.qqchen.deploy.backend.enums.ExternalSystemAuthTypeEnum; +import com.qqchen.deploy.backend.enums.ExternalSystemSyncStatusEnum; +import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum; import com.qqchen.deploy.backend.framework.annotation.ServiceType; import com.qqchen.deploy.backend.framework.enums.ResponseCode; import com.qqchen.deploy.backend.framework.exception.BusinessException; @@ -38,7 +41,7 @@ import static com.qqchen.deploy.backend.framework.annotation.ServiceType.Type.DA @Slf4j @Service @ServiceType(DATABASE) -public class ExternalSystemServiceImpl extends BaseServiceImpl +public class ExternalSystemServiceImpl extends BaseServiceImpl implements IExternalSystemService { @Resource @@ -47,7 +50,7 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl systemIntegrations; - private Map integrationMap; + private Map integrationMap; @PostConstruct public void init() { @@ -87,8 +90,8 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl syncZentaoData(system); } - system.setSyncStatus(ExternalSystem.SyncStatus.SUCCESS); + system.setSyncStatus(ExternalSystemSyncStatusEnum.SUCCESS); system.setLastSyncTime(LocalDateTime.now()); } catch (Exception e) { - system.setSyncStatus(ExternalSystem.SyncStatus.FAILED); + system.setSyncStatus(ExternalSystemSyncStatusEnum.FAILED); log.error("Sync data failed for external system: {}", system.getName(), e); throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_SYNC_FAILED); } finally { diff --git a/backend/src/main/java/com/qqchen/deploy/backend/service/impl/RepositoryVersionControlServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/service/impl/RepositoryVersionControlServiceImpl.java deleted file mode 100644 index 73472ba3..00000000 --- a/backend/src/main/java/com/qqchen/deploy/backend/service/impl/RepositoryVersionControlServiceImpl.java +++ /dev/null @@ -1,145 +0,0 @@ -package com.qqchen.deploy.backend.service.impl; - -import com.qqchen.deploy.backend.entity.RepositoryGroup; -import com.qqchen.deploy.backend.entity.RepositoryProject; -import com.qqchen.deploy.backend.entity.RepositoryBranch; -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.model.dto.RepositoryGroupDTO; -import com.qqchen.deploy.backend.model.dto.RepositoryProjectDTO; -import com.qqchen.deploy.backend.model.dto.RepositoryBranchDTO; -import com.qqchen.deploy.backend.model.dto.RepositorySyncStatusDTO; -import com.qqchen.deploy.backend.model.dto.RepositorySyncTaskDTO; -import com.qqchen.deploy.backend.repository.IRepositoryGroupRepository; -import com.qqchen.deploy.backend.repository.IRepositoryProjectRepository; -import com.qqchen.deploy.backend.repository.IRepositoryBranchRepository; -import com.qqchen.deploy.backend.service.IRepositoryVersionControlService; -import com.qqchen.deploy.backend.converter.RepositoryConverter; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.stream.Collectors; - -/** - * 版本控制仓库服务实现类 - * 用于管理代码仓库的组织结构、项目和分支,支持与外部版本控制系统(如GitLab)的数据同步 - * - * @author QQchen - * @version 1.0 - */ -@Slf4j -@Service -@ServiceType(ServiceType.Type.DATABASE) -@RequiredArgsConstructor -public class RepositoryVersionControlServiceImpl implements IRepositoryVersionControlService { - - private final IRepositoryGroupRepository groupRepository; - private final IRepositoryProjectRepository projectRepository; - private final IRepositoryBranchRepository branchRepository; - private final RepositoryConverter repositoryConverter; - - @Override - public List listGroups(Long externalSystemId) { - log.debug("获取仓库组列表, externalSystemId: {}", externalSystemId); - return groupRepository.findByExternalSystemIdAndDeletedFalseOrderBySortAsc(externalSystemId) - .stream() - .map(repositoryConverter::toDto) - .collect(Collectors.toList()); - } - - @Override - public RepositoryGroupDTO getGroup(Long externalSystemId, Long groupId) { - log.debug("获取仓库组信息, externalSystemId: {}, groupId: {}", externalSystemId, groupId); - RepositoryGroup group = groupRepository.findByExternalSystemIdAndGroupId(externalSystemId, groupId) - .orElseThrow(() -> new BusinessException(ResponseCode.REPOSITORY_GROUP_NOT_FOUND)); - return repositoryConverter.toDto(group); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void syncGroups(Long externalSystemId) { - log.info("开始同步仓库组数据, externalSystemId: {}", externalSystemId); - // TODO: 实现仓库组同步逻辑 - } - - @Override - public List listProjects(Long externalSystemId, Long groupId) { - log.debug("获取项目列表, externalSystemId: {}, groupId: {}", externalSystemId, groupId); - return projectRepository.findByExternalSystemIdAndGroupIdAndDeletedFalseOrderBySortAsc(externalSystemId, groupId) - .stream() - .map(repositoryConverter::toDto) - .collect(Collectors.toList()); - } - - @Override - public RepositoryProjectDTO getProject(Long externalSystemId, Long projectId) { - log.debug("获取项目信息, externalSystemId: {}, projectId: {}", externalSystemId, projectId); - RepositoryProject project = projectRepository.findByExternalSystemIdAndProjectId(externalSystemId, projectId) - .orElseThrow(() -> new BusinessException(ResponseCode.REPOSITORY_PROJECT_NOT_FOUND)); - return repositoryConverter.toDto(project); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void syncProjects(Long externalSystemId, Long groupId) { - log.info("开始同步项目数据, externalSystemId: {}, groupId: {}", externalSystemId, groupId); - // TODO: 实现项目同步逻辑 - } - - @Override - public List listBranches(Long externalSystemId, Long projectId) { - log.debug("获取分支列表, externalSystemId: {}, projectId: {}", externalSystemId, projectId); - return branchRepository.findByExternalSystemIdAndProjectIdAndDeletedFalse(externalSystemId, projectId) - .stream() - .map(repositoryConverter::toDto) - .collect(Collectors.toList()); - } - - @Override - public RepositoryBranchDTO getBranch(Long externalSystemId, Long projectId, String branchName) { - log.debug("获取分支信息, externalSystemId: {}, projectId: {}, branchName: {}", - externalSystemId, projectId, branchName); - RepositoryBranch branch = branchRepository.findByExternalSystemIdAndProjectIdAndName(externalSystemId, projectId, branchName) - .orElseThrow(() -> new BusinessException(ResponseCode.REPOSITORY_BRANCH_NOT_FOUND)); - return repositoryConverter.toDto(branch); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void syncBranches(Long externalSystemId, Long projectId) { - log.info("开始同步分支数据, externalSystemId: {}, projectId: {}", externalSystemId, projectId); - // TODO: 实现分支同步逻辑 - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void syncAll(Long externalSystemId) { - log.info("开始全量同步数据, externalSystemId: {}", externalSystemId); - // TODO: 实现全量同步逻辑 - } - - @Override - public Long asyncSyncAll(Long externalSystemId) { - log.info("开始异步全量同步数据, externalSystemId: {}", externalSystemId); - // TODO: 实现异步全量同步逻辑 - return null; - } - - @Override - public RepositorySyncStatusDTO getSyncStatus(Long historyId) { - log.debug("获取同步状态, historyId: {}", historyId); - // TODO: 实现获取同步状态逻辑 - return null; - } - - @Override - public List getRunningSyncs() { - log.debug("获取运行中的同步任务列表"); - // TODO: 实现获取运行中的同步任务列表逻辑 - return null; - } -} \ No newline at end of file diff --git a/backend/src/main/resources/db/migration/V1.0.1__init_data.sql b/backend/src/main/resources/db/migration/V1.0.1__init_data.sql index 63dbb0f6..e33eedb0 100644 --- a/backend/src/main/resources/db/migration/V1.0.1__init_data.sql +++ b/backend/src/main/resources/db/migration/V1.0.1__init_data.sql @@ -56,7 +56,7 @@ INSERT INTO sys_role (id, create_time, code, name, type, description, sort) VALUES (1, NOW(), 'SUPER_ADMIN', '超级管理员', 1, '系统超级管理员,拥有所有权限', 1), (2, NOW(), 'SYSTEM_ADMIN', '系统管理员', 1, '系统管理员,拥有大部分系统管理权限', 2), -(3, NOW(), 'COMMON_USER', '普通用户', 2, '普通用户,仅拥有基本操作权限', 3); +(3, NOW(), 'COMMON_USER', '普通用户', 2, '普通用��,仅拥有基本操作权限', 3); -- 初始化角色标签 INSERT INTO sys_role_tag (id, create_time, name, color) @@ -145,4 +145,9 @@ INSERT INTO sys_external_system ( 'GitLab测试环境', 'GIT', 'http://gitlab.test.com', '测试环境GitLab服务器', 2, 1, 'TOKEN', NULL, NULL, 'test-token', 'SUCCESS', '2023-12-01 00:00:00', '2023-12-01 00:00:00', '{}' +), ( + 3, 'admin', '2024-12-03 10:35:58.932966', 0, 'admin', '2024-12-03 10:35:58.932966', 0, + '链宇GIT', 'GIT', 'http://119.3.203.210:8088/', NULL, 1, 1, + 'TOKEN', NULL, NULL, 'cNSud7D1GmYQKEMco7s5', + NULL, NULL, NULL, '{}' ); \ No newline at end of file diff --git a/backend/src/test/java/com/qqchen/deploy/backend/service/impl/ExternalSystemServiceImplTest.java b/backend/src/test/java/com/qqchen/deploy/backend/service/impl/ExternalSystemServiceImplTest.java index 3e107560..ec4e60c0 100644 --- a/backend/src/test/java/com/qqchen/deploy/backend/service/impl/ExternalSystemServiceImplTest.java +++ b/backend/src/test/java/com/qqchen/deploy/backend/service/impl/ExternalSystemServiceImplTest.java @@ -12,7 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; -import java.time.LocalDateTime; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @@ -38,7 +37,7 @@ class ExternalSystemServiceImplTest { system = new ExternalSystem(); system.setId(1L); system.setName("测试Jenkins"); - system.setType(ExternalSystem.SystemType.JENKINS); + system.setType(ExternalSystem.ExternalSystemSystemType.JENKINS); system.setUrl("http://jenkins.test.com"); system.setAuthType(ExternalSystem.AuthType.BASIC); system.setUsername("admin"); @@ -48,7 +47,7 @@ class ExternalSystemServiceImplTest { systemDTO = new ExternalSystemDTO(); systemDTO.setName("测试Jenkins"); - systemDTO.setType(ExternalSystem.SystemType.JENKINS); + systemDTO.setType(ExternalSystem.ExternalSystemSystemType.JENKINS); systemDTO.setUrl("http://jenkins.test.com"); systemDTO.setAuthType(ExternalSystem.AuthType.BASIC); systemDTO.setUsername("admin"); @@ -149,7 +148,7 @@ class ExternalSystemServiceImplTest { @Test void validateUniqueConstraints_WhenGitWithoutToken_ShouldThrowException() { // 准备数据 - systemDTO.setType(ExternalSystem.SystemType.GIT); + systemDTO.setType(ExternalSystem.ExternalSystemSystemType.GIT); systemDTO.setAuthType(ExternalSystem.AuthType.TOKEN); systemDTO.setToken(null); @@ -167,7 +166,7 @@ class ExternalSystemServiceImplTest { @Test void validateUniqueConstraints_WhenGitWithWrongAuthType_ShouldThrowException() { // 准备数据 - systemDTO.setType(ExternalSystem.SystemType.GIT); + systemDTO.setType(ExternalSystem.ExternalSystemSystemType.GIT); systemDTO.setAuthType(ExternalSystem.AuthType.BASIC); // Mock @@ -184,7 +183,7 @@ class ExternalSystemServiceImplTest { @Test void update_WhenGitWithoutToken_ShouldThrowException() { // 准备数据 - systemDTO.setType(ExternalSystem.SystemType.GIT); + systemDTO.setType(ExternalSystem.ExternalSystemSystemType.GIT); systemDTO.setAuthType(ExternalSystem.AuthType.TOKEN); systemDTO.setToken(null); @@ -197,7 +196,7 @@ class ExternalSystemServiceImplTest { @Test void update_WhenGitWithWrongAuthType_ShouldThrowException() { // 准备数据 - systemDTO.setType(ExternalSystem.SystemType.GIT); + systemDTO.setType(ExternalSystem.ExternalSystemSystemType.GIT); systemDTO.setAuthType(ExternalSystem.AuthType.BASIC); // 验证