可正常启动

This commit is contained in:
dengqichen 2024-12-03 13:26:43 +08:00
parent 7df3b9b36b
commit a17f1971bb
28 changed files with 663 additions and 987 deletions

View File

@ -1,89 +1,29 @@
您是Java编程、Spring Boot、Spring Framework、Maven、JUnit和相关Java技术的专家你深思熟虑给出细致入微的答案并且善于推理。你细心地提供准确、真实、周到的答案是一个推理天才。 - 作为Java编程、Spring Boot、Spring Framework、Maven、JUnit和相关Java技术专家需要实现企业应用级别的高性能管理框架
需要实现的是企业应用级别的管理框架,需要具有高性能、
### 严格遵循的要求 ### 严格遵循要求
- 首先,一步一步地思考——详细描述你在伪代码中构建什么的计划。 - 首先一步一步思考,详细描述伪代码构建计划,确认后再写代码
- 确认,然后写代码! - 遵循正确、最佳实践、DRY原则、无错误、功能齐全的代码编写原则
- 始终编写正确、最佳实践、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补充数据库表和初始化数据。
- 编写代码前先查看一下V1.0.0__init_schema.sql、V1.0.1__init_data.sql表结构再进行编写。
# Deploy Ease Platform 开发规范
### 包结构说明 ### 包结构规范
- 框架包路径 - 框架包路径(com.qqchen.deploy.backend.framework)包含annotation、api、audit、controller等多个子包
com.qqchen.deploy.backend.framework - 业务包路径(com.qqchen.deploy.backend)包含api、controller、converter、entity等多个子包
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 DTOBaseDTO、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 接口出参(复杂业务场景使用)
### DTO设计规范 ### 数据对象规范
- 简单CRUD场景使用统一的DTO无需额外的Request/Response对象 - DTO设计简单CRUD使用统一DTO复杂场景使用专门Request/Response继承BaseDTO获取基础字段
- 以下场景需要使用专门的Request/Response - 验证规则使用Jakarta Validation注解支持自定义验证注解和分组验证
1. 复杂的业务场景(如用户注册、登录) - 对象转换使用MapStruct进行转换继承BaseConverter显式声明特殊映射
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;
}
```
### Service层规范 ### Service层规范
- 简单CRUD场景直接继承BaseServiceImpl即可 - 简单CRUD继承BaseServiceImpl复杂业务需定义专门接口和实现包含事务控制和异常处理
- 复杂业务场景需要: - 使用@Transactional注解控制事务合理设置事务传播机制和隔离级别
1. 定义业务接口方法 - 实现乐观锁(@Version)或悲观锁(findByIdWithLock)进行并发控制
2. 实现具体的业务逻辑 - 示例
3. 处理业务异常
4. 添加事务控制
- 示例:
```java
@Slf4j @Slf4j
@Service @Service
@ServiceType(DATABASE) @ServiceType(DATABASE)
@ -96,44 +36,12 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
// 业务实现 // 业务实现
} }
} }
```
### 查询规范
- 简单查询使用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层规范 ### Controller层规范
- REST FULL接口使用框架BaseController - REST接口使用BaseController三方接口命名为模块名ApiController二方接口为模块名Controller
- 新增接口命名规范: - 返回值com.qqchen.deploy.backend.framework.api.Response<T>
- 三方接口模块名ApiControllerExternalSystemApiController - 统一使用GlobalExceptionHandler处理异常
- 二方接口模块名ControllerExternalSystemController
- 示例: - 示例:
```java
@Slf4j @Slf4j
@RestController @RestController
@RequestMapping("/api/v1/external-system") @RequestMapping("/api/v1/external-system")
@ -141,19 +49,16 @@ throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED);
public class ExternalSystemApiController extends BaseController<ExternalSystem, ExternalSystemDTO, Long, ExternalSystemQuery> { public class ExternalSystemApiController extends BaseController<ExternalSystem, ExternalSystemDTO, Long, ExternalSystemQuery> {
// 特定业务方法实现 // 特定业务方法实现
} }
```
### Repository层规范 ### Repository层规范
- 继承IBaseRepository - 继承IBaseRepository定义特定查询方法
- 定义特定的查询方法 - 使用JPA命名规范定义查询方法
- 复杂查询使用@Query注解
- 示例: - 示例:
```java
@Repository @Repository
public interface IExternalSystemRepository extends IBaseRepository<ExternalSystem, Long> { public interface IExternalSystemRepository extends IBaseRepository<ExternalSystem, Long> {
boolean existsByNameAndDeletedFalse(String name); boolean existsByNameAndDeletedFalse(String name);
} }
```
### Converter规范 ### Converter规范
- 继承BaseConverter遵循以下规则 - 继承BaseConverter遵循以下规则
1. 简单场景(字段完全匹配)示例: 1. 简单场景(字段完全匹配)示例:
@ -172,354 +77,52 @@ public interface IExternalSystemRepository extends IBaseRepository<ExternalSyste
UserDTO toDto(User entity); UserDTO toDto(User entity);
} }
``` ```
### 枚举规范
- 新增加枚举应添加到com.qqchen.deploy.backend.enums后缀以Enum结尾
- 增加枚举时,不要修改现有没有,只追加即可
### 测试规范 ### 异常规范
- Service层测试 - 异常分为系统异常应继承SystemException和业务异常应承BusinessException使用ResponseCode定义错误码在messages.properties中定义错误消息
1. 使用@SpringBootTest - 示例
2. 使用@MockBean模拟依赖 throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_DISABLED);
3. 测试所有业务场景
- Controller层测试
1. 使用@WebMvcTest或@SpringBootTest + @AutoConfigureMockMvc
2. 使用MockMvc测试接口
3. 测试所有接口路径
- Repository层测试
1. 使用@DataJpaTest
2. 测试所有查询方法
### 文档规范
- 类注释:说明类的用途、作者、版本
- 方法注释:说明参数、返回值、异常
- 业务方法注释:说明业务规则
- API文档使用Swagger注解
### 命名规范 ### 命名规范
- 使用PascalCase作为类名例,UserController、OrderService - 类名使用PascalCaseUserController、OrderService
- 使用camelCase作为方法和变量名例如,findUserById、isOrderValid - 方法和变量名使用camelCasefindUserById、isOrderValid
- 常量使用ALL_CAPS例如,MAX_RETRY_ATTEMPTS、DEFAULT_PAGE_SIZE - 常量使用ALL_CAPSMAX_RETRY_ATTEMPTS、DEFAULT_PAGE_SIZE
- service、repository接口类需要以I开头service实现类无需使用I开头但尾部需要Impl结尾 - Service、Repository接口类以I开头实现类需要Impl结尾
### 数据库规范 ### 数据库规范
- 使用Flyway进行数据库版本控制 - 使用Flyway进行版本控制新表结构写入V1.0.0__init_schema.sql初始数据写入V1.0.1__init_data.sql
- 新增表结构写入V1.0.0__init_schema.sql - 表名和字段名使用下划线命名法sys_user, create_time
- 新增初始数据写入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 - 必须包含基础字段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格式化文档
- 保持文档层级结构一致
- 使用统一的示例格式
### 错误码规范 ### 错误码规范
- 错误码分类: - 系统级错误(1xxx)1000-1099通用系统错误1100-1199依赖注入错误1200-1299数据库错误
1. 系统级错误1xxx - 业务级错误(2xxx)2000-2099通用业务错误2100-2199角色相关2200-2299 JWT相关等
- 1000-1099通用系统错误 - 错误码命名使用大写字母和下划线,采用"模块_操作_错误"命名方式
- 1100-1199依赖注入错误
- 1200-1299数据库错误
2. 业务级错误2xxx
- 2000-2099通用业务错误
- 2100-2199角色相关错误
- 2200-2299JWT相关错误
- 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 Token2小时
- Refresh Token7天
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. 文档是否同步更新
### 缓存使用规范 ### 缓存使用规范
- 缓存注解: - 使用@Cacheable(查询)、@CachePut(更新)、@CacheEvict(删除)注解
1. @Cacheable适用于查询操作 - 缓存Key格式模块:业务:标识如user:info:1
2. @CachePut适用于更新操作 - 根据业务场景选择缓存策略Cache Aside、Write Through、Write Back
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<User, UserDTO, Long> 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
### 安全规范 ### 安全规范
- 认证授权: - JWT Token包含Header(算法)、Payload(用户信息)、Signature(签名)
1. JWT Token结构和有效期 - Access Token有效期2小时Refresh Token有效期7天
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. 环境配置说明
### 测试规范 ### 测试规范
- 单元测试: - Service层使用@SpringBootTest和@MockBean测试所有业务场景
1. Service层业务测试 - Controller层使用@WebMvcTest或MockMvc测试所有接口路径
2. 重要工具类测试 - Repository层使用@DataJpaTest测试所有查询方法
3. 边界条件测试
- 集成测试: ### 文档规范
1. Controller层接口测试 - 类注释:说明用途、作者、版本
2. 数据库操作测试 - 方法注释:说明参数、返回值、异常
3. 缓存操作测试 - API文档使用Swagger注解保持文档同步更新
- 测试原则:
1. 测试覆盖率要求
2. 测试数据隔离
3. 测试用例完整性
### 性能优化规范 ### 性能优化规范
- 数据库优化: - 数据库优化索引设计、SQL优化、分页查询
1. 索引设计 - 代码优化:循环优化、集合操作、字符串处理
2. SQL优化 - 缓存优化:合理设置缓存粒度、更新策略、防止缓存穿透
3. 分页查询
- 代码优化:
1. 循环优化
2. 集合操作
3. 字符串处理
- 缓存优化:
1. 缓存粒度
2. 缓存更新
3. 缓存穿透处理

516
backend/.cursorrules1 Normal file
View File

@ -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 DTOBaseDTO、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<ExternalSystem, ExternalSystemDTO, Long>
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
- 新增接口命名规范:
- 三方接口模块名ApiControllerExternalSystemApiController
- 二方接口模块名ControllerExternalSystemController
- 示例:
```java
@Slf4j
@RestController
@RequestMapping("/api/v1/external-system")
@Tag(name = "外部系统管理", description = "外部系统管理相关接口")
public class ExternalSystemApiController extends BaseController<ExternalSystem, ExternalSystemDTO, Long, ExternalSystemQuery> {
// 特定业务方法实现
}
```
### Repository层规范
- 继承IBaseRepository
- 定义特定的查询方法
- 示例:
```java
@Repository
public interface IExternalSystemRepository extends IBaseRepository<ExternalSystem, Long> {
boolean existsByNameAndDeletedFalse(String name);
}
```
### Converter规范
- 继承BaseConverter遵循以下规则
1. 简单场景(字段完全匹配)示例:
```java
@Mapper(config = BaseConverter.class)
public interface ExternalSystemConverter extends BaseConverter<ExternalSystem, ExternalSystemDTO> {
// 字段完全匹配时无需额外配置
}
```
2. 复杂场景(需要特殊映射)示例:
```java
@Mapper(config = BaseConverter.class)
public interface UserConverter extends BaseConverter<User, UserDTO> {
@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-2299JWT相关错误
- 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 Token2小时
- Refresh Token7天
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<User, UserDTO, Long> 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. 缓存穿透处理

View File

@ -772,7 +772,7 @@ POST /api/v1/permission
- 详细的接口文档 - 详细的接口文档
- 规范的代码注释 - 规范的代码注释
详细规范请参考:[后端开发规范](.cursorrules) 详细规范请参考:[后端开发规范](.cursorrules1)
### 前端开发规范 ### 前端开发规范
- 统一的接口调用方式 - 统一的接口调用方式

View File

@ -12,10 +12,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping; 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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.List; import java.util.List;

View File

@ -1,5 +1,8 @@
package com.qqchen.deploy.backend.entity; 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.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity; import com.qqchen.deploy.backend.framework.domain.Entity;
import jakarta.persistence.*; import jakarta.persistence.*;
@ -26,7 +29,7 @@ public class ExternalSystem extends Entity<Long> {
*/ */
@Column(nullable = false) @Column(nullable = false)
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private SystemType type; private ExternalSystemTypeEnum type;
/** /**
* 系统访问地址 * 系统访问地址
@ -51,11 +54,11 @@ public class ExternalSystem extends Entity<Long> {
private Boolean enabled = true; private Boolean enabled = true;
/** /**
* 认证方式BASIC/TOKEN/OAUTH<EFBFBD><EFBFBD> * 认证方式BASIC/TOKEN/OAUTH
*/ */
@Column(name = "auth_type", nullable = false) @Column(name = "auth_type", nullable = false)
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private AuthType authType; private ExternalSystemAuthTypeEnum authType;
/** /**
* 用户名 * 用户名
@ -77,7 +80,7 @@ public class ExternalSystem extends Entity<Long> {
*/ */
@Column(name = "sync_status") @Column(name = "sync_status")
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
private SyncStatus syncStatus; private ExternalSystemSyncStatusEnum syncStatus;
/** /**
* 最后同步时间 * 最后同步时间
@ -97,30 +100,5 @@ public class ExternalSystem extends Entity<Long> {
@Column(columnDefinition = "JSON") @Column(columnDefinition = "JSON")
private String config; private String config;
/**
* 系统类型枚举
*/
public enum SystemType {
JENKINS,
GIT,
ZENTAO
}
/**
* 认证类型枚举
*/
public enum AuthType {
BASIC,
TOKEN,
OAUTH
}
/**
* 同步状态枚举
*/
public enum SyncStatus {
SUCCESS,
FAILED,
RUNNING
}
} }

View File

@ -0,0 +1,10 @@
package com.qqchen.deploy.backend.enums;
/**
* 认证类型枚举
*/
public enum ExternalSystemAuthTypeEnum {
BASIC,
TOKEN,
OAUTH
}

View File

@ -0,0 +1,7 @@
package com.qqchen.deploy.backend.enums;
public enum ExternalSystemSyncStatusEnum {
SUCCESS,
FAILED,
RUNNING
}

View File

@ -0,0 +1,10 @@
package com.qqchen.deploy.backend.enums;
/**
* 系统类型枚举
*/
public enum ExternalSystemTypeEnum {
JENKINS,
GIT,
ZENTAO
}

View File

@ -66,6 +66,7 @@ public enum ResponseCode {
PERMISSION_ASSIGN_FAILED(2404, "permission.assign.failed"), PERMISSION_ASSIGN_FAILED(2404, "permission.assign.failed"),
// 第三方系统相关错误码 (2500-2599) // 第三方系统相关错误码 (2500-2599)
EXTERNAL_SYSTEM_NOT_FOUND(2505, "external.system.not.found"),
EXTERNAL_SYSTEM_NAME_EXISTS(2500, "external.system.name.exists"), EXTERNAL_SYSTEM_NAME_EXISTS(2500, "external.system.name.exists"),
EXTERNAL_SYSTEM_TYPE_URL_EXISTS(2501, "external.system.type.url.exists"), EXTERNAL_SYSTEM_TYPE_URL_EXISTS(2501, "external.system.type.url.exists"),
EXTERNAL_SYSTEM_DISABLED(2502, "external.system.disabled"), EXTERNAL_SYSTEM_DISABLED(2502, "external.system.disabled"),

View File

@ -14,4 +14,5 @@ public class BusinessException extends BaseException {
public BusinessException(ResponseCode errorCode, Object[] args) { public BusinessException(ResponseCode errorCode, Object[] args) {
super(errorCode, args); super(errorCode, args);
} }
} }

View File

@ -1,6 +1,7 @@
package com.qqchen.deploy.backend.integration; package com.qqchen.deploy.backend.integration;
import com.qqchen.deploy.backend.entity.ExternalSystem; import com.qqchen.deploy.backend.entity.ExternalSystem;
import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum;
/** /**
* 第三方系统集成接口 * 第三方系统集成接口
@ -26,5 +27,5 @@ public interface IExternalSystemIntegration {
* *
* @return 系统类型 * @return 系统类型
*/ */
ExternalSystem.SystemType getSystemType(); ExternalSystemTypeEnum getSystemType();
} }

View File

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

View File

@ -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<String, Object> testConnection(
@RequestHeader("Authorization") String authorization
);
@PostMapping("/job/{jobName}/build")
void triggerBuild(
@RequestHeader("Authorization") String authorization,
@PathVariable("jobName") String jobName
);
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +0,0 @@
package com.qqchen.deploy.backend.integration.enums;
public enum JenkinsBuildStatus {
SUCCESS, FAILURE, IN_PROGRESS, CANCELLED
}

View File

@ -1,6 +1,7 @@
package com.qqchen.deploy.backend.integration.impl; package com.qqchen.deploy.backend.integration.impl;
import com.qqchen.deploy.backend.entity.ExternalSystem; import com.qqchen.deploy.backend.entity.ExternalSystem;
import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum;
import com.qqchen.deploy.backend.integration.IExternalSystemIntegration; import com.qqchen.deploy.backend.integration.IExternalSystemIntegration;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*; import org.springframework.http.*;
@ -43,7 +44,7 @@ public class GitIntegration implements IExternalSystemIntegration {
} }
@Override @Override
public ExternalSystem.SystemType getSystemType() { public ExternalSystemTypeEnum getSystemType() {
return ExternalSystem.SystemType.GIT; return ExternalSystemTypeEnum.GIT;
} }
} }

View File

@ -1,6 +1,8 @@
package com.qqchen.deploy.backend.integration.impl; package com.qqchen.deploy.backend.integration.impl;
import com.qqchen.deploy.backend.entity.ExternalSystem; 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 com.qqchen.deploy.backend.integration.IExternalSystemIntegration;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*; import org.springframework.http.*;
@ -9,6 +11,10 @@ import org.springframework.web.client.RestTemplate;
import java.util.Base64; 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 @Slf4j
@Service @Service
public class JenkinsIntegration implements IExternalSystemIntegration { public class JenkinsIntegration implements IExternalSystemIntegration {
@ -51,7 +57,7 @@ public class JenkinsIntegration implements IExternalSystemIntegration {
} }
@Override @Override
public ExternalSystem.SystemType getSystemType() { public ExternalSystemTypeEnum getSystemType() {
return ExternalSystem.SystemType.JENKINS; return ExternalSystemTypeEnum.JENKINS;
} }
} }

View File

@ -1,8 +1,8 @@
package com.qqchen.deploy.backend.model; package com.qqchen.deploy.backend.model;
import com.qqchen.deploy.backend.entity.ExternalSystem.SystemType; import com.qqchen.deploy.backend.enums.ExternalSystemAuthTypeEnum;
import com.qqchen.deploy.backend.entity.ExternalSystem.AuthType; import com.qqchen.deploy.backend.enums.ExternalSystemSyncStatusEnum;
import com.qqchen.deploy.backend.entity.ExternalSystem.SyncStatus; import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum;
import com.qqchen.deploy.backend.framework.dto.BaseDTO; import com.qqchen.deploy.backend.framework.dto.BaseDTO;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
@ -19,7 +19,7 @@ public class ExternalSystemDTO extends BaseDTO {
private String name; private String name;
@NotNull(message = "系统类型不能为空") @NotNull(message = "系统类型不能为空")
private SystemType type; private ExternalSystemTypeEnum type;
@NotBlank(message = "系统访问地址不能为空") @NotBlank(message = "系统访问地址不能为空")
private String url; private String url;
@ -31,7 +31,7 @@ public class ExternalSystemDTO extends BaseDTO {
private Boolean enabled = true; private Boolean enabled = true;
@NotNull(message = "认证方式不能为空") @NotNull(message = "认证方式不能为空")
private AuthType authType; private ExternalSystemAuthTypeEnum authType;
private String username; private String username;
@ -39,7 +39,7 @@ public class ExternalSystemDTO extends BaseDTO {
private String token; private String token;
private SyncStatus syncStatus; private ExternalSystemSyncStatusEnum syncStatus;
private LocalDateTime lastSyncTime; private LocalDateTime lastSyncTime;

View File

@ -1,6 +1,6 @@
package com.qqchen.deploy.backend.model.query; 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.annotation.QueryField;
import com.qqchen.deploy.backend.framework.enums.QueryType; import com.qqchen.deploy.backend.framework.enums.QueryType;
import com.qqchen.deploy.backend.framework.query.BaseQuery; import com.qqchen.deploy.backend.framework.query.BaseQuery;
@ -25,7 +25,7 @@ public class ExternalSystemQuery extends BaseQuery {
private String name; private String name;
@QueryField(field = "type") @QueryField(field = "type")
private SystemType type; private ExternalSystemTypeEnum type;
@QueryField(field = "enabled") @QueryField(field = "enabled")
private Boolean enabled; private Boolean enabled;

View File

@ -1,6 +1,7 @@
package com.qqchen.deploy.backend.repository; package com.qqchen.deploy.backend.repository;
import com.qqchen.deploy.backend.entity.ExternalSystem; import com.qqchen.deploy.backend.entity.ExternalSystem;
import com.qqchen.deploy.backend.enums.ExternalSystemTypeEnum;
import com.qqchen.deploy.backend.framework.repository.IBaseRepository; import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -17,7 +18,7 @@ public interface IExternalSystemRepository extends IBaseRepository<ExternalSyste
/** /**
* 检查系统类型和URL组合是否存在 * 检查系统类型和URL组合是否存在
*/ */
boolean existsByTypeAndUrlAndDeletedFalse(ExternalSystem.SystemType type, String url); boolean existsByTypeAndUrlAndDeletedFalse(ExternalSystemTypeEnum type, String url);
/** /**
* 查询所有未删除的系统按排序字段排序 * 查询所有未删除的系统按排序字段排序

View File

@ -1,132 +0,0 @@
package com.qqchen.deploy.backend.service;
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.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 java.util.List;
/**
* 版本控制仓库服务接口
* 提供代码仓库的组织结构项目和分支的管理功能以及与外部版本控制系统的数据同步功能
*
* @author QQchen
* @version 1.0
*/
public interface IRepositoryVersionControlService {
/**
* 获取指定外部系统的仓库组列表
*
* @param externalSystemId 外部系统ID
* @return 仓库组列表按排序号升序排列
*/
List<RepositoryGroupDTO> 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<RepositoryProjectDTO> 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<RepositoryBranchDTO> 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<RepositorySyncTaskDTO> getRunningSyncs();
}

View File

@ -1,6 +1,9 @@
package com.qqchen.deploy.backend.service.impl; package com.qqchen.deploy.backend.service.impl;
import com.qqchen.deploy.backend.entity.ExternalSystem; 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.annotation.ServiceType;
import com.qqchen.deploy.backend.framework.enums.ResponseCode; import com.qqchen.deploy.backend.framework.enums.ResponseCode;
import com.qqchen.deploy.backend.framework.exception.BusinessException; import com.qqchen.deploy.backend.framework.exception.BusinessException;
@ -47,7 +50,7 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
@Resource @Resource
private List<IExternalSystemIntegration> systemIntegrations; private List<IExternalSystemIntegration> systemIntegrations;
private Map<ExternalSystem.SystemType, IExternalSystemIntegration> integrationMap; private Map<ExternalSystemTypeEnum, IExternalSystemIntegration> integrationMap;
@PostConstruct @PostConstruct
public void init() { public void init() {
@ -87,8 +90,8 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
* @param dto 外部系统DTO * @param dto 外部系统DTO
*/ */
private void validateGitAuth(ExternalSystemDTO dto) { private void validateGitAuth(ExternalSystemDTO dto) {
if (dto.getType() == ExternalSystem.SystemType.GIT) { if (dto.getType() == ExternalSystemTypeEnum.GIT) {
if (dto.getAuthType() != ExternalSystem.AuthType.TOKEN) { if (dto.getAuthType() != ExternalSystemAuthTypeEnum.TOKEN) {
throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_GIT_AUTH_TYPE_ERROR); throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_GIT_AUTH_TYPE_ERROR);
} }
if (StringUtils.isBlank(dto.getToken())) { if (StringUtils.isBlank(dto.getToken())) {
@ -127,7 +130,7 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
} }
try { try {
system.setSyncStatus(ExternalSystem.SyncStatus.RUNNING); system.setSyncStatus(ExternalSystemSyncStatusEnum.RUNNING);
externalSystemRepository.save(system); externalSystemRepository.save(system);
// TODO: 根据不同的系统类型调用不同的同步方法 // TODO: 根据不同的系统类型调用不同的同步方法
@ -137,10 +140,10 @@ public class ExternalSystemServiceImpl extends BaseServiceImpl<ExternalSystem, E
case ZENTAO -> syncZentaoData(system); case ZENTAO -> syncZentaoData(system);
} }
system.setSyncStatus(ExternalSystem.SyncStatus.SUCCESS); system.setSyncStatus(ExternalSystemSyncStatusEnum.SUCCESS);
system.setLastSyncTime(LocalDateTime.now()); system.setLastSyncTime(LocalDateTime.now());
} catch (Exception e) { } catch (Exception e) {
system.setSyncStatus(ExternalSystem.SyncStatus.FAILED); system.setSyncStatus(ExternalSystemSyncStatusEnum.FAILED);
log.error("Sync data failed for external system: {}", system.getName(), e); log.error("Sync data failed for external system: {}", system.getName(), e);
throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_SYNC_FAILED); throw new BusinessException(ResponseCode.EXTERNAL_SYSTEM_SYNC_FAILED);
} finally { } finally {

View File

@ -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<RepositoryGroupDTO> 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<RepositoryProjectDTO> 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<RepositoryBranchDTO> 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<RepositorySyncTaskDTO> getRunningSyncs() {
log.debug("获取运行中的同步任务列表");
// TODO: 实现获取运行中的同步任务列表逻辑
return null;
}
}

View File

@ -56,7 +56,7 @@ INSERT INTO sys_role (id, create_time, code, name, type, description, sort)
VALUES VALUES
(1, NOW(), 'SUPER_ADMIN', '超级管理员', 1, '系统超级管理员,拥有所有权限', 1), (1, NOW(), 'SUPER_ADMIN', '超级管理员', 1, '系统超级管理员,拥有所有权限', 1),
(2, NOW(), 'SYSTEM_ADMIN', '系统管理员', 1, '系统管理员,拥有大部分系统管理权限', 2), (2, NOW(), 'SYSTEM_ADMIN', '系统管理员', 1, '系统管理员,拥有大部分系统管理权限', 2),
(3, NOW(), 'COMMON_USER', '普通用户', 2, '普通用,仅拥有基本操作权限', 3); (3, NOW(), 'COMMON_USER', '普通用户', 2, '普通用<EFBFBD><EFBFBD>,仅拥有基本操作权限', 3);
-- 初始化角色标签 -- 初始化角色标签
INSERT INTO sys_role_tag (id, create_time, name, color) 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, 'GitLab测试环境', 'GIT', 'http://gitlab.test.com', '测试环境GitLab服务器', 2, 1,
'TOKEN', NULL, NULL, 'test-token', 'TOKEN', NULL, NULL, 'test-token',
'SUCCESS', '2023-12-01 00:00:00', '2023-12-01 00:00:00', '{}' '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, '{}'
); );

View File

@ -12,7 +12,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.MockBean;
import java.time.LocalDateTime;
import java.util.Optional; import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -38,7 +37,7 @@ class ExternalSystemServiceImplTest {
system = new ExternalSystem(); system = new ExternalSystem();
system.setId(1L); system.setId(1L);
system.setName("测试Jenkins"); system.setName("测试Jenkins");
system.setType(ExternalSystem.SystemType.JENKINS); system.setType(ExternalSystem.ExternalSystemSystemType.JENKINS);
system.setUrl("http://jenkins.test.com"); system.setUrl("http://jenkins.test.com");
system.setAuthType(ExternalSystem.AuthType.BASIC); system.setAuthType(ExternalSystem.AuthType.BASIC);
system.setUsername("admin"); system.setUsername("admin");
@ -48,7 +47,7 @@ class ExternalSystemServiceImplTest {
systemDTO = new ExternalSystemDTO(); systemDTO = new ExternalSystemDTO();
systemDTO.setName("测试Jenkins"); systemDTO.setName("测试Jenkins");
systemDTO.setType(ExternalSystem.SystemType.JENKINS); systemDTO.setType(ExternalSystem.ExternalSystemSystemType.JENKINS);
systemDTO.setUrl("http://jenkins.test.com"); systemDTO.setUrl("http://jenkins.test.com");
systemDTO.setAuthType(ExternalSystem.AuthType.BASIC); systemDTO.setAuthType(ExternalSystem.AuthType.BASIC);
systemDTO.setUsername("admin"); systemDTO.setUsername("admin");
@ -149,7 +148,7 @@ class ExternalSystemServiceImplTest {
@Test @Test
void validateUniqueConstraints_WhenGitWithoutToken_ShouldThrowException() { void validateUniqueConstraints_WhenGitWithoutToken_ShouldThrowException() {
// 准备数据 // 准备数据
systemDTO.setType(ExternalSystem.SystemType.GIT); systemDTO.setType(ExternalSystem.ExternalSystemSystemType.GIT);
systemDTO.setAuthType(ExternalSystem.AuthType.TOKEN); systemDTO.setAuthType(ExternalSystem.AuthType.TOKEN);
systemDTO.setToken(null); systemDTO.setToken(null);
@ -167,7 +166,7 @@ class ExternalSystemServiceImplTest {
@Test @Test
void validateUniqueConstraints_WhenGitWithWrongAuthType_ShouldThrowException() { void validateUniqueConstraints_WhenGitWithWrongAuthType_ShouldThrowException() {
// 准备数据 // 准备数据
systemDTO.setType(ExternalSystem.SystemType.GIT); systemDTO.setType(ExternalSystem.ExternalSystemSystemType.GIT);
systemDTO.setAuthType(ExternalSystem.AuthType.BASIC); systemDTO.setAuthType(ExternalSystem.AuthType.BASIC);
// Mock // Mock
@ -184,7 +183,7 @@ class ExternalSystemServiceImplTest {
@Test @Test
void update_WhenGitWithoutToken_ShouldThrowException() { void update_WhenGitWithoutToken_ShouldThrowException() {
// 准备数据 // 准备数据
systemDTO.setType(ExternalSystem.SystemType.GIT); systemDTO.setType(ExternalSystem.ExternalSystemSystemType.GIT);
systemDTO.setAuthType(ExternalSystem.AuthType.TOKEN); systemDTO.setAuthType(ExternalSystem.AuthType.TOKEN);
systemDTO.setToken(null); systemDTO.setToken(null);
@ -197,7 +196,7 @@ class ExternalSystemServiceImplTest {
@Test @Test
void update_WhenGitWithWrongAuthType_ShouldThrowException() { void update_WhenGitWithWrongAuthType_ShouldThrowException() {
// 准备数据 // 准备数据
systemDTO.setType(ExternalSystem.SystemType.GIT); systemDTO.setType(ExternalSystem.ExternalSystemSystemType.GIT);
systemDTO.setAuthType(ExternalSystem.AuthType.BASIC); systemDTO.setAuthType(ExternalSystem.AuthType.BASIC);
// 验证 // 验证