11 KiB
11 KiB
提示词:测试执行功能
引用真源
需求描述
根据需求文档,实现 Apex 测试执行功能,包括:
- 运行所有测试(runAllTests)
- 运行指定类的测试(runTestsByClasses)
- 运行指定包的测试(runTestsByPackages)
- 运行指定测试方法(runTestsByMethods)
- 查询测试结果(queryTestResults)
- 查询测试详情(queryTestDetails)
- 查询代码覆盖率(queryCodeCoverage)
- 查询 Flow 覆盖率(queryFlowCoverage)
设计方案
根据设计文档,采用以下技术方案:
- 连接管理:复用 ApexConnectionFactory 获取 SoapConnection
- 并发控制:使用 ReentrantLock 保证多线程安全
- 事务管理:使用 @Transactional 保证数据一致性
- 数据存储:
- 复用 002-02 的表:datai_apex_test_result、datai_apex_test_failure、datai_apex_code_coverage
- 新建表:datai_apex_test_success、datai_apex_flow_coverage
- API 集成:使用 apex.jar 中的 SoapConnection.runTests() 方法
- 权限控制:使用 @PreAuthorize("@ss.hasPermi('apex:test:execute')") 进行权限控制
输出格式要求
1. 必须包含以下文件
Controller 层
- ApexTestController.java(路径:
datai-salesforce-apex/src/main/java/com/datai/apex/controller/ApexTestController.java)- 运行所有测试接口
- 运行指定类测试接口
- 运行指定包测试接口
- 运行指定方法测试接口
- 查询测试结果接口
- 查询测试详情接口
- 查询代码覆盖率接口
- 查询 Flow 覆盖率接口
Service 层
- IApexTestService.java(路径:
datai-salesforce-apex/src/main/java/com/datai/apex/service/IApexTestService.java)- 定义测试执行相关接口方法
- ApexTestServiceImpl.java(路径:
datai-salesforce-apex/src/main/java/com/datai/apex/service/impl/ApexTestServiceImpl.java)- 实现测试执行逻辑
- 使用 ReentrantLock 保证线程安全
- 保存测试结果到数据库
DTO 层
- RunAllTestsDto.java - 运行所有测试请求参数
- RunTestsByClassesDto.java - 运行指定类测试请求参数
- RunTestsByPackagesDto.java - 运行指定包测试请求参数
- RunTestsByMethodsDto.java - 运行指定方法测试请求参数
- TestsNodeDto.java - 测试节点 DTO
- QueryTestResultsDto.java - 查询测试结果请求参数
- QueryTestDetailsDto.java - 查询测试详情请求参数
- QueryCodeCoverageDto.java - 查询代码覆盖率请求参数
- QueryFlowCoverageDto.java - 查询 Flow 覆盖率请求参数
VO 层
- RunTestsResultVo.java - 测试结果 VO
- RunTestSuccessVo.java - 测试成功详情 VO
- RunTestFailureVo.java - 测试失败详情 VO
- CodeCoverageResultVo.java - 代码覆盖率结果 VO
- FlowCoverageResultVo.java - Flow 覆盖率结果 VO
- TestResultSummaryVo.java - 测试结果摘要 VO
- TestDetailVo.java - 测试详情 VO
Entity 层(新建)
- DataiApexTestSuccess.java - 测试成功详情实体
- DataiApexFlowCoverage.java - Flow 覆盖率实体
Mapper 层(新建)
- DataiApexTestSuccessMapper.java - 测试成功详情 Mapper 接口
- DataiApexTestSuccessMapper.xml - 测试成功详情 Mapper XML
- DataiApexFlowCoverageMapper.java - Flow 覆盖率 Mapper 接口
- DataiApexFlowCoverageMapper.xml - Flow 覆盖率 Mapper XML
2. 代码结构要求
datai-salesforce-apex/src/main/java/com/datai/apex/
├── controller/
│ └── ApexTestController.java
├── service/
│ ├── IApexTestService.java
│ └── impl/
│ └── ApexTestServiceImpl.java
├── dto/
│ ├── RunAllTestsDto.java
│ ├── RunTestsByClassesDto.java
│ ├── RunTestsByPackagesDto.java
│ ├── RunTestsByMethodsDto.java
│ ├── TestsNodeDto.java
│ ├── QueryTestResultsDto.java
│ ├── QueryTestDetailsDto.java
│ ├── QueryCodeCoverageDto.java
│ └── QueryFlowCoverageDto.java
├── vo/
│ ├── RunTestsResultVo.java
│ ├── RunTestSuccessVo.java
│ ├── RunTestFailureVo.java
│ ├── CodeCoverageResultVo.java
│ ├── FlowCoverageResultVo.java
│ ├── TestResultSummaryVo.java
│ └── TestDetailVo.java
├── domain/
│ ├── DataiApexTestSuccess.java
│ └── DataiApexFlowCoverage.java
└── mapper/
├── DataiApexTestSuccessMapper.java
└── DataiApexFlowCoverageMapper.java
datai-salesforce-apex/src/main/resources/mapper/apex/
├── DataiApexTestSuccessMapper.xml
└── DataiApexFlowCoverageMapper.xml
代码规范要求
1. 命名规范
- 类命名:首字母大写,驼峰命名,如
ApexTestController - 方法命名:首字母小写,驼峰命名,如
runAllTests - 变量命名:首字母小写,驼峰命名,如
testResult - 常量命名:全大写,下划线分隔,如
DEFAULT_MAX_FAILED_TESTS
2. 注释规范
- 类注释:使用
/** */,包含类功能描述、作者、创建时间 - 方法注释:使用
/** */,包含方法功能、参数、返回值、异常描述 - 字段注释:使用
/** */,包含字段功能描述
3. 代码格式
- 使用 4 个空格缩进
- 行宽不超过 120 字符
- 导入规范:使用 import 静态导入,避免通配符导入
4. 异常处理
- 使用 try-catch-finally 块确保锁的释放
- 捕获 SalesforceAuthException 并直接抛出
- 捕获其他异常并包装为 SalesforceOperationException
- 使用 log 记录异常信息
5. 数据库操作
- 使用 MyBatis 进行数据库操作(不使用 MyBatis Plus)
- Mapper 方法命名:insertXxx、updateXxx、deleteXxx、selectXxx
- XML 中编写 SQL 语句,使用参数绑定
6. 事务管理
- 在 Service 实现类上使用 @Transactional(rollbackFor = Exception.class)
- 确保数据库操作的原子性
7. 权限控制
- Controller 方法使用 @PreAuthorize("@ss.hasPermi('apex:test:execute')") 进行权限控制
- 查询接口使用 @PreAuthorize("@ss.hasPermi('apex:test:query')") 进行权限控制
8. 日志记录
- 使用 Lombok 的 @Slf4j 注解
- 关键操作记录 info 级别日志
- 异常记录 error 级别日志
- 使用占位符 {} 而不是字符串拼接
核心实现逻辑
1. 运行测试流程
@Override
@Transactional(rollbackFor = Exception.class)
public RunTestsResultVo runTests(RunTestsRequestDto dto) throws SalesforceAuthException, SalesforceOperationException {
lock.lock();
try {
// 1. 构建 RunTestsRequest
RunTestsRequest request = buildRunTestsRequest(dto);
// 2. 获取 SoapConnection
SoapConnection connection = apexConnectionFactory.getConnection("source");
// 3. 执行测试
RunTestsResult result = connection.runTests(request);
// 4. 保存测试结果
saveTestResults(result);
// 5. 转换为 VO 并返回
return convertToVo(result);
} catch (SalesforceAuthException e) {
throw e;
} catch (Exception e) {
throw new SalesforceOperationException("运行测试失败: " + e.getMessage(), e);
} finally {
lock.unlock();
}
}
2. 保存测试结果
private void saveTestResults(RunTestsResult result) {
// 1. 保存测试结果主表
DataiApexTestResult testResult = new DataiApexTestResult();
testResult.setTestRunId(result.getApexLogId());
testResult.setNumTestsRun(result.getNumTestsRun());
testResult.setNumFailures(result.getNumFailures());
testResult.setTotalTime(result.getTotalTime());
testResult.setApexLogId(result.getApexLogId());
testResult.setTestTime(new Date());
testResult.setAllPassed(result.getNumFailures() == 0);
testResult.setTotalCoverage(calculateTotalCoverage(result.getCodeCoverage()));
testResultMapper.insertDataiApexTestResult(testResult);
// 2. 保存测试成功详情
if (result.getSuccesses() != null) {
for (RunTestSuccess success : result.getSuccesses()) {
DataiApexTestSuccess testSuccess = convertToTestSuccess(success, testResult.getId());
testSuccessMapper.insertDataiApexTestSuccess(testSuccess);
}
}
// 3. 保存测试失败详情
if (result.getFailures() != null) {
for (RunTestFailure failure : result.getFailures()) {
DataiApexTestFailure testFailure = convertToTestFailure(failure, testResult.getId());
testFailureMapper.insertDataiApexTestFailure(testFailure);
}
}
// 4. 保存代码覆盖率
if (result.getCodeCoverage() != null) {
for (CodeCoverageResult coverage : result.getCodeCoverage()) {
DataiApexCodeCoverage codeCoverage = convertToCodeCoverage(coverage, testResult.getId());
codeCoverageMapper.insertDataiApexCodeCoverage(codeCoverage);
}
}
// 5. 保存 Flow 覆盖率
if (result.getFlowCoverage() != null) {
for (FlowCoverageResult flowCoverage : result.getFlowCoverage()) {
DataiApexFlowCoverage flowCoverageEntity = convertToFlowCoverage(flowCoverage, testResult.getId());
flowCoverageMapper.insertDataiApexFlowCoverage(flowCoverageEntity);
}
}
}
3. 覆盖率计算
private double calculateTotalCoverage(CodeCoverageResult[] coverages) {
if (coverages == null || coverages.length == 0) {
return 0.0;
}
int totalLocations = 0;
int totalNotCovered = 0;
for (CodeCoverageResult coverage : coverages) {
totalLocations += coverage.getNumLocations();
totalNotCovered += coverage.getNumLocationsNotCovered();
}
if (totalLocations == 0) {
return 0.0;
}
return (1.0 - (double) totalNotCovered / totalLocations) * 100;
}
注意事项
-
复用已有组件:
- 使用 ApexConnectionFactory 获取连接(来自 002-01)
- 复用 datai_apex_test_result、datai_apex_test_failure、datai_apex_code_coverage 表(来自 002-02)
-
正确处理 Salesforce API:
- 使用 apex.jar 中的原生类:RunTestsRequest、RunTestsResult、RunTestSuccess、RunTestFailure、CodeCoverageResult、FlowCoverageResult
- 正确处理空值情况
- 注意数组可能为 null
-
数据一致性:
- 使用 @Transactional 保证事务
- 使用 ReentrantLock 保证并发安全
- 确保 finally 块中释放锁
-
性能优化:
- 批量插入数据
- 使用索引加速查询
- 避免 N+1 查询问题
-
异常处理:
- 区分 SalesforceAuthException 和 SalesforceOperationException
- 记录详细的错误日志
- 提供有意义的错误消息
-
代码生成前置条件检查:
- 确认需求文档已创建
- 确认设计文档已创建
- 确认 ADR 决策记录已创建
- 确认 SQL 脚本已创建
- 确认本提示词文档已创建