datai/datai-scenes/datai-scene-salesforce/docs/design/2026-02-03-002-04-代码覆盖率-设计.md

30 KiB
Raw Permalink Blame History

设计文档 - 代码覆盖率

元数据

  • 需求编号002-04
  • 创建时间2026-02-03
  • 创建人AI Assistant
  • 状态:已完成

设计概述

本文档描述了 Salesforce Apex 代码覆盖率功能的详细设计方案。代码覆盖率功能用于查询、统计和分析 Apex 类与触发器的测试覆盖率数据,支持按类、触发器、命名空间等多维度查询,并提供覆盖率统计和聚合分析能力。

本设计复用 002-03测试执行的数据库表和基础设施重点扩展代码覆盖率查询和统计分析功能。

架构设计

系统架构

┌─────────────────────────────────────────────────────────────┐
│                     前端应用层                               │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ 覆盖率查询    │  │ 覆盖率统计    │  │ 覆盖率详情    │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                     Controller 层                          │
│              ApexCodeCoverageController                     │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ /coverage    │  │ /coverage/   │  │ /coverage/   │      │
│  │              │  │ total        │  │ summary      │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                     Service 层                             │
│           IApexCodeCoverageService                          │
│                    (接口)                                   │
│                         │                                   │
│                         ▼                                   │
│           ApexCodeCoverageServiceImpl                       │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐      │
│  │ 查询覆盖率    │  │ 统计覆盖率    │  │ 计算总覆盖率  │      │
│  └──────────────┘  └──────────────┘  └──────────────┘      │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                     数据访问层                              │
│  ┌──────────────────┐  ┌──────────────────┐                │
│  │ CodeCoverage     │  │ TestResult       │                │
│  │ Mapper           │  │ Mapper           │                │
│  └──────────────────┘  └──────────────────┘                │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                     数据库层                               │
│  ┌──────────────────┐  ┌──────────────────┐                │
│  │ datai_apex_code_ │  │ datai_apex_test_ │                │
│  │ coverage         │  │ result           │                │
│  └──────────────────┘  └──────────────────┘                │
└─────────────────────────────────────────────────────────────┘

模块架构

模块datai-salesforce-apex

包结构

com.datai.apex
├── controller
│   └── ApexCodeCoverageController.java    # 代码覆盖率控制器
├── service
│   ├── IApexCodeCoverageService.java      # 代码覆盖率服务接口
│   └── impl
│       └── ApexCodeCoverageServiceImpl.java  # 代码覆盖率服务实现
├── mapper
│   └── DataiApexCodeCoverageMapper.java   # 代码覆盖率数据访问
├── model
│   ├── dto
│   │   ├── QueryCodeCoverageDto.java      # 查询覆盖率请求DTO
│   │   └── QueryCoverageSummaryDto.java   # 查询覆盖率统计请求DTO
│   └── vo
│       ├── CodeCoverageResultVo.java      # 代码覆盖率结果VO
│       ├── CodeCoverageSummaryVo.java     # 代码覆盖率统计VO
│       └── CoverageDetailVo.java          # 覆盖率详情VO

数据流

1. 用户请求 -> Controller
   - 接收查询参数testResultId、name、type、minCoverage等
   - 参数校验
   - 调用 Service 层

2. Controller -> Service
   - 构建查询条件
   - 调用 Mapper 查询数据
   - 计算覆盖率统计
   - 返回 VO 对象

3. Service -> Mapper
   - 执行数据库查询
   - 支持分页查询
   - 支持多条件筛选

4. Mapper -> Database
   - 查询 datai_apex_code_coverage 表
   - 关联 datai_apex_test_result 表
   - 返回实体对象列表

5. Service -> Controller
   - 将实体对象转换为 VO
   - 计算统计指标
   - 封装返回结果

6. Controller -> 用户
   - 返回 AjaxResult 统一响应格式
   - 包含分页信息和数据列表

技术方案

技术选型

技术组件 版本/类型 选择理由
Spring Boot 2.7.x 项目标准框架,提供 IOC、AOP、事务管理等
MyBatis 3.5.x 项目标准 ORM 框架,灵活高效
PageHelper 5.3.x 分页插件,支持物理分页
ReentrantLock JDK 标准 保证多线程安全,性能优于 synchronized
@Transactional Spring 标准 保证数据一致性,支持声明式事务

核心算法

1. 代码覆盖率计算

/**
 * 计算单个类的代码覆盖率
 * 
 * @param numLocations 总位置数
 * @param numLocationsNotCovered 未覆盖位置数
 * @return 覆盖率百分比
 */
public Double calculateCoveragePercent(Integer numLocations, Integer numLocationsNotCovered) {
    if (numLocations == null || numLocations == 0) {
        return 0.0;
    }
    double coverage = (1.0 - (double) numLocationsNotCovered / numLocations) * 100;
    return Math.round(coverage * 100.0) / 100.0; // 保留两位小数
}

2. 总体覆盖率计算

/**
 * 计算总体代码覆盖率
 * 
 * @param coverageList 覆盖率结果列表
 * @return 总体覆盖率百分比
 */
public Double calculateTotalCoverage(List<DataiApexCodeCoverage> coverageList) {
    if (CollectionUtils.isEmpty(coverageList)) {
        return 0.0;
    }
    
    int totalLocations = 0;
    int totalNotCovered = 0;
    
    for (DataiApexCodeCoverage coverage : coverageList) {
        totalLocations += coverage.getNumLocations();
        totalNotCovered += coverage.getNumLocationsNotCovered();
    }
    
    if (totalLocations == 0) {
        return 0.0;
    }
    
    double totalCoverage = (1.0 - (double) totalNotCovered / totalLocations) * 100;
    return Math.round(totalCoverage * 100.0) / 100.0;
}

3. 按类型统计覆盖率

/**
 * 按类型Class/Trigger统计覆盖率
 * 
 * @param coverageList 覆盖率结果列表
 * @return 类型统计映射
 */
public Map<String, CoverageTypeSummary> calculateCoverageByType(List<DataiApexCodeCoverage> coverageList) {
    Map<String, CoverageTypeSummary> summaryMap = new HashMap<>();
    
    // 按类型分组统计
    Map<String, List<DataiApexCodeCoverage>> groupedByType = coverageList.stream()
        .collect(Collectors.groupingBy(DataiApexCodeCoverage::getType));
    
    for (Map.Entry<String, List<DataiApexCodeCoverage>> entry : groupedByType.entrySet()) {
        String type = entry.getKey();
        List<DataiApexCodeCoverage> typeCoverages = entry.getValue();
        
        CoverageTypeSummary summary = new CoverageTypeSummary();
        summary.setTotal(typeCoverages.size());
        summary.setCoveragePercent(calculateTotalCoverage(typeCoverages));
        
        // 计算总位置数和未覆盖位置数
        int totalLocations = typeCoverages.stream()
            .mapToInt(DataiApexCodeCoverage::getNumLocations)
            .sum();
        int totalNotCovered = typeCoverages.stream()
            .mapToInt(DataiApexCodeCoverage::getNumLocationsNotCovered)
            .sum();
        
        summary.setTotalLocations(totalLocations);
        summary.setTotalNotCovered(totalNotCovered);
        
        summaryMap.put(type, summary);
    }
    
    return summaryMap;
}

集成方案

与测试执行功能的集成

代码覆盖率功能与测试执行功能紧密集成:

  1. 数据复用:复用 002-03 创建的 datai_apex_code_coverage
  2. 查询复用:复用测试执行功能中保存的覆盖率数据
  3. 关联查询:通过 test_result_id 关联测试结果表

与连接管理功能的集成

  1. 连接复用:复用 002-01 的 ApexConnectionFactory 获取连接
  2. 异常处理:使用统一的异常处理机制

数据模型

数据库表设计

1. datai_apex_code_coverage代码覆盖率表

说明:该表已在 002-03 中创建,本功能复用此表

字段名 类型 长度 是否必填 默认值 说明
id BIGINT - 自增 主键
test_result_id BIGINT - - 测试结果 ID外键
name VARCHAR 255 - 类/触发器名称
type VARCHAR 20 - 类型Class/Trigger
namespace VARCHAR 255 NULL 命名空间
num_locations INT - 0 总位置数
num_locations_not_covered INT - 0 未覆盖位置数
coverage_percent DOUBLE - 0.0 覆盖率百分比
dept_id BIGINT - NULL 部门 ID
create_by VARCHAR 64 NULL 创建者
create_time DATETIME - NULL 创建时间
update_by VARCHAR 64 NULL 更新者
update_time DATETIME - NULL 更新时间
del_flag TINYINT - 0 删除标志0正常 1删除

索引设计

  • 主键索引id
  • 外键索引test_result_id
  • 查询索引name、type、namespace
  • 复合索引:(test_result_id, type)

2. datai_apex_test_result测试结果表

说明:该表已在 002-03 中创建,用于关联查询

字段名 类型 说明
id BIGINT 主键
test_run_id VARCHAR(50) 测试运行 ID
num_tests_run INT 运行的测试数量
num_failures INT 失败的测试数量
total_time DOUBLE 总执行时间
apex_log_id VARCHAR(50) Apex 日志 ID
test_time DATETIME 测试时间

实体类设计

DataiApexCodeCoverage代码覆盖率实体

@Data
@EqualsAndHashCode(callSuper = true)
public class DataiApexCodeCoverage extends BaseEntity {
    private static final long serialVersionUID = 1L;

    /** 记录 ID */
    private Long id;

    /** 测试结果 ID */
    private Long testResultId;

    /** 类/触发器名称 */
    private String name;

    /** 类型Class/Trigger */
    private String type;

    /** 命名空间 */
    private String namespace;

    /** 总位置数 */
    private Integer numLocations;

    /** 未覆盖位置数 */
    private Integer numLocationsNotCovered;

    /** 覆盖率百分比 */
    private Double coveragePercent;
}

DTO 设计

QueryCodeCoverageDto查询代码覆盖率请求 DTO

@Data
public class QueryCodeCoverageDto extends PageDomain {
    private static final long serialVersionUID = 1L;

    /** 测试结果 ID */
    private Long testResultId;

    /** 类/触发器名称 */
    private String name;

    /** 类型Class/Trigger */
    private String type;

    /** 命名空间 */
    private String namespace;

    /** 最小覆盖率 */
    private Double minCoverage;

    /** 最大覆盖率 */
    private Double maxCoverage;
}

QueryCoverageSummaryDto查询覆盖率统计请求 DTO

@Data
public class QueryCoverageSummaryDto {
    private static final long serialVersionUID = 1L;

    /** 测试结果 ID */
    private Long testResultId;
}

VO 设计

CodeCoverageResultVo代码覆盖率结果 VO

@Data
public class CodeCoverageResultVo implements Serializable {
    private static final long serialVersionUID = 1L;

    /** 记录 ID */
    private Long id;

    /** 测试结果 ID */
    private Long testResultId;

    /** 类/触发器名称 */
    private String name;

    /** 类型Class/Trigger */
    private String type;

    /** 命名空间 */
    private String namespace;

    /** 总位置数 */
    private Integer numLocations;

    /** 未覆盖位置数 */
    private Integer numLocationsNotCovered;

    /** 覆盖率百分比 */
    private Double coveragePercent;

    /** 创建时间 */
    private Date createTime;
}

CodeCoverageSummaryVo代码覆盖率统计 VO

@Data
public class CodeCoverageSummaryVo implements Serializable {
    private static final long serialVersionUID = 1L;

    /** 测试结果 ID */
    private Long testResultId;

    /** 总体覆盖率 */
    private Double totalCoverage;

    /** 总类数 */
    private Integer totalClasses;

    /** 总触发器数 */
    private Integer totalTriggers;

    /** 总位置数 */
    private Integer totalLocations;

    /** 未覆盖位置数 */
    private Integer totalNotCovered;

    /** 已覆盖位置数 */
    private Integer coveredLocations;

    /** 类覆盖率 */
    private Double classCoverage;

    /** 触发器覆盖率 */
    private Double triggerCoverage;

    /** 测试时间 */
    private Date testTime;
}

CoverageTypeSummaryVo按类型统计 VO

@Data
public class CoverageTypeSummaryVo implements Serializable {
    private static final long serialVersionUID = 1L;

    /** 类型Class/Trigger */
    private String type;

    /** 总数 */
    private Integer total;

    /** 总位置数 */
    private Integer totalLocations;

    /** 未覆盖位置数 */
    private Integer totalNotCovered;

    /** 覆盖率百分比 */
    private Double coveragePercent;
}

CoverageSummaryResponseVo覆盖率统计响应 VO

@Data
public class CoverageSummaryResponseVo implements Serializable {
    private static final long serialVersionUID = 1L;

    /** 测试结果 ID */
    private Long testResultId;

    /** 类统计 */
    private CoverageTypeSummaryVo classSummary;

    /** 触发器统计 */
    private CoverageTypeSummaryVo triggerSummary;

    /** 总体统计 */
    private CoverageTypeSummaryVo overallSummary;
}

接口设计

RESTful API 设计

1. 查询代码覆盖率列表

  • 接口名称:查询代码覆盖率列表
  • 请求方式GET
  • 请求路径/api/apex/coverage
  • 权限要求apex:coverage:query

请求参数

参数名 类型 必填 说明 示例
testResultId Long 测试结果 ID 1
name String 类/触发器名称 "AccountTest"
type String 类型Class/Trigger "Class"
namespace String 命名空间 "datai"
minCoverage Double 最小覆盖率 75.0
maxCoverage Double 最大覆盖率 100.0
pageNum Integer 页码,默认 1 1
pageSize Integer 每页大小,默认 10 20

响应参数

参数名 类型 说明
code Integer 状态码
msg String 提示信息
data Object 分页数据
data.total Integer 总记录数
data.rows Array 覆盖率列表

成功响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "total": 30,
    "rows": [
      {
        "id": 1,
        "testResultId": 1,
        "name": "AccountTest",
        "type": "Class",
        "namespace": null,
        "numLocations": 100,
        "numLocationsNotCovered": 20,
        "coveragePercent": 80.0,
        "createTime": "2026-02-03 10:00:00"
      }
    ]
  }
}

2. 查询代码覆盖率详情

  • 接口名称:查询代码覆盖率详情
  • 请求方式GET
  • 请求路径/api/apex/coverage/{id}
  • 权限要求apex:coverage:query

请求参数

参数名 类型 必填 说明 示例
id Long 覆盖率记录 ID 1

响应参数:同「查询代码覆盖率列表」接口的单个对象

3. 查询总体代码覆盖率

  • 接口名称:查询总体代码覆盖率
  • 请求方式GET
  • 请求路径/api/apex/coverage/total
  • 权限要求apex:coverage:query

请求参数

参数名 类型 必填 说明 示例
testResultId Long 测试结果 ID 1

响应参数

参数名 类型 说明
code Integer 状态码
msg String 提示信息
data Object 统计结果
data.testResultId Long 测试结果 ID
data.totalCoverage Double 总体覆盖率
data.totalClasses Integer 总类数
data.totalTriggers Integer 总触发器数
data.totalLocations Integer 总位置数
data.totalNotCovered Integer 未覆盖位置数
data.coveredLocations Integer 已覆盖位置数
data.classCoverage Double 类覆盖率
data.triggerCoverage Double 触发器覆盖率
data.testTime Date 测试时间

成功响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "testResultId": 1,
    "totalCoverage": 82.5,
    "totalClasses": 30,
    "totalTriggers": 5,
    "totalLocations": 5000,
    "totalNotCovered": 875,
    "coveredLocations": 4125,
    "classCoverage": 85.0,
    "triggerCoverage": 75.0,
    "testTime": "2026-02-03 10:00:00"
  }
}

4. 按类型统计代码覆盖率

  • 接口名称:按类型统计代码覆盖率
  • 请求方式GET
  • 请求路径/api/apex/coverage/summary
  • 权限要求apex:coverage:query

请求参数

参数名 类型 必填 说明 示例
testResultId Long 测试结果 ID 1

响应参数

参数名 类型 说明
code Integer 状态码
msg String 提示信息
data Object 统计结果
data.testResultId Long 测试结果 ID
data.classSummary Object 类统计
data.classSummary.total Integer 类总数
data.classSummary.totalLocations Integer 类总位置数
data.classSummary.totalNotCovered Integer 类未覆盖位置数
data.classSummary.coveragePercent Double 类覆盖率
data.triggerSummary Object 触发器统计
data.triggerSummary.total Integer 触发器总数
data.triggerSummary.totalLocations Integer 触发器总位置数
data.triggerSummary.totalNotCovered Integer 触发器未覆盖位置数
data.triggerSummary.coveragePercent Double 触发器覆盖率
data.overallSummary Object 总体统计
data.overallSummary.total Integer 总数
data.overallSummary.totalLocations Integer 总位置数
data.overallSummary.totalNotCovered Integer 未覆盖位置数
data.overallSummary.coveragePercent Double 总体覆盖率

成功响应示例

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "testResultId": 1,
    "classSummary": {
      "total": 30,
      "totalLocations": 4000,
      "totalNotCovered": 600,
      "coveragePercent": 85.0
    },
    "triggerSummary": {
      "total": 5,
      "totalLocations": 1000,
      "totalNotCovered": 250,
      "coveragePercent": 75.0
    },
    "overallSummary": {
      "total": 35,
      "totalLocations": 5000,
      "totalNotCovered": 850,
      "coveragePercent": 83.0
    }
  }
}

接口权限设计

接口路径 HTTP 方法 权限标识 说明
/api/apex/coverage GET apex:coverage:query 查询代码覆盖率列表
/api/apex/coverage/{id} GET apex:coverage:query 查询代码覆盖率详情
/api/apex/coverage/total GET apex:coverage:query 查询总体代码覆盖率
/api/apex/coverage/summary GET apex:coverage:query 按类型统计代码覆盖率

实现要点

关键实现逻辑

1. 查询代码覆盖率

@Override
public List<CodeCoverageResultVo> queryCodeCoverage(QueryCodeCoverageDto dto) {
    // 构建查询条件
    DataiApexCodeCoverage queryParam = new DataiApexCodeCoverage();
    queryParam.setTestResultId(dto.getTestResultId());
    queryParam.setName(dto.getName());
    queryParam.setType(dto.getType());
    queryParam.setNamespace(dto.getNamespace());
    
    // 执行查询
    List<DataiApexCodeCoverage> coverageList = codeCoverageMapper.selectDataiApexCodeCoverageList(queryParam);
    
    // 过滤覆盖率范围
    if (dto.getMinCoverage() != null || dto.getMaxCoverage() != null) {
        coverageList = coverageList.stream()
            .filter(c -> {
                double percent = c.getCoveragePercent() != null ? c.getCoveragePercent() : 0.0;
                boolean minMatch = dto.getMinCoverage() == null || percent >= dto.getMinCoverage();
                boolean maxMatch = dto.getMaxCoverage() == null || percent <= dto.getMaxCoverage();
                return minMatch && maxMatch;
            })
            .collect(Collectors.toList());
    }
    
    // 转换为 VO
    return coverageList.stream()
        .map(this::convertToVo)
        .collect(Collectors.toList());
}

2. 计算总体覆盖率

@Override
public CodeCoverageSummaryVo getTotalCoverage(Long testResultId) {
    // 查询所有覆盖率数据
    DataiApexCodeCoverage queryParam = new DataiApexCodeCoverage();
    queryParam.setTestResultId(testResultId);
    List<DataiApexCodeCoverage> coverageList = codeCoverageMapper.selectDataiApexCodeCoverageList(queryParam);
    
    if (CollectionUtils.isEmpty(coverageList)) {
        return new CodeCoverageSummaryVo();
    }
    
    // 计算统计指标
    CodeCoverageSummaryVo summary = new CodeCoverageSummaryVo();
    summary.setTestResultId(testResultId);
    
    // 分类统计
    List<DataiApexCodeCoverage> classCoverages = coverageList.stream()
        .filter(c -> "Class".equals(c.getType()))
        .collect(Collectors.toList());
    List<DataiApexCodeCoverage> triggerCoverages = coverageList.stream()
        .filter(c -> "Trigger".equals(c.getType()))
        .collect(Collectors.toList());
    
    summary.setTotalClasses(classCoverages.size());
    summary.setTotalTriggers(triggerCoverages.size());
    
    // 计算覆盖率
    summary.setClassCoverage(calculateTotalCoverage(classCoverages));
    summary.setTriggerCoverage(calculateTotalCoverage(triggerCoverages));
    summary.setTotalCoverage(calculateTotalCoverage(coverageList));
    
    // 计算位置数
    int totalLocations = coverageList.stream()
        .mapToInt(DataiApexCodeCoverage::getNumLocations)
        .sum();
    int totalNotCovered = coverageList.stream()
        .mapToInt(DataiApexCodeCoverage::getNumLocationsNotCovered)
        .sum();
    
    summary.setTotalLocations(totalLocations);
    summary.setTotalNotCovered(totalNotCovered);
    summary.setCoveredLocations(totalLocations - totalNotCovered);
    
    return summary;
}

3. 按类型统计覆盖率

@Override
public CoverageSummaryResponseVo getCoverageSummary(Long testResultId) {
    // 查询所有覆盖率数据
    DataiApexCodeCoverage queryParam = new DataiApexCodeCoverage();
    queryParam.setTestResultId(testResultId);
    List<DataiApexCodeCoverage> coverageList = codeCoverageMapper.selectDataiApexCodeCoverageList(queryParam);
    
    CoverageSummaryResponseVo response = new CoverageSummaryResponseVo();
    response.setTestResultId(testResultId);
    
    // 按类型分组
    Map<String, List<DataiApexCodeCoverage>> groupedByType = coverageList.stream()
        .collect(Collectors.groupingBy(DataiApexCodeCoverage::getType));
    
    // 类统计
    List<DataiApexCodeCoverage> classCoverages = groupedByType.getOrDefault("Class", Collections.emptyList());
    response.setClassSummary(buildTypeSummary(classCoverages));
    
    // 触发器统计
    List<DataiApexCodeCoverage> triggerCoverages = groupedByType.getOrDefault("Trigger", Collections.emptyList());
    response.setTriggerSummary(buildTypeSummary(triggerCoverages));
    
    // 总体统计
    response.setOverallSummary(buildTypeSummary(coverageList));
    
    return response;
}

private CoverageTypeSummaryVo buildTypeSummary(List<DataiApexCodeCoverage> coverages) {
    CoverageTypeSummaryVo summary = new CoverageTypeSummaryVo();
    summary.setTotal(coverages.size());
    
    int totalLocations = coverages.stream()
        .mapToInt(DataiApexCodeCoverage::getNumLocations)
        .sum();
    int totalNotCovered = coverages.stream()
        .mapToInt(DataiApexCodeCoverage::getNumLocationsNotCovered)
        .sum();
    
    summary.setTotalLocations(totalLocations);
    summary.setTotalNotCovered(totalNotCovered);
    summary.setCoveragePercent(calculateCoveragePercent(totalLocations, totalNotCovered));
    
    return summary;
}

异常处理设计

异常捕获和转换

try {
    // 执行业务逻辑
    return queryCodeCoverage(dto);
} catch (DataAccessException e) {
    log.error("查询代码覆盖率失败 - 数据访问错误: {}", e.getMessage());
    throw new SalesforceOperationException("查询代码覆盖率失败: 数据库访问错误");
} catch (Exception e) {
    log.error("查询代码覆盖率失败: {}", e.getMessage(), e);
    throw new SalesforceOperationException("查询代码覆盖率失败: " + e.getMessage());
}

错误码定义

错误码 错误消息 说明 HTTP 状态码
APEX_COVERAGE_001 测试结果 ID 为空 测试结果 ID 不能为空 400
APEX_COVERAGE_002 覆盖率记录不存在 指定的覆盖率记录不存在 404
APEX_COVERAGE_003 查询参数无效 查询参数格式不正确 400
APEX_COVERAGE_004 数据库查询失败 数据库查询发生错误 500
APEX_COVERAGE_005 覆盖率计算失败 覆盖率计算发生错误 500

性能优化设计

1. 数据库索引优化

  • 主键索引id
  • 外键索引test_result_id加速关联查询
  • 查询索引name、type、namespace加速条件查询
  • 复合索引:(test_result_id, type)(加速按类型筛选)

2. 分页查询优化

使用 PageHelper 进行物理分页,避免全表扫描:

PageHelper.startPage(dto.getPageNum(), dto.getPageSize());
List<DataiApexCodeCoverage> list = codeCoverageMapper.selectDataiApexCodeCoverageList(queryParam);
return getDataTable(list);

3. 缓存策略

考虑到代码覆盖率数据相对稳定,可以考虑:

  • 对统计结果进行缓存(如 Redis
  • 缓存有效期设置为 1 小时
  • 数据更新时清除缓存

安全设计

1. 权限控制

  • 所有接口使用 @PreAuthorize 进行权限控制
  • 权限标识:apex:coverage:query
  • 与测试执行权限分离,支持细粒度控制

2. 数据隔离

  • 通过 dept_id 实现数据权限隔离
  • 使用若依的 @DataScope 注解
  • 用户只能查看本部门的数据

3. 输入验证

  • 使用 @Validated 进行参数校验
  • 对覆盖率范围进行校验0-100
  • 防止 SQL 注入(使用 MyBatis 参数绑定)

相关文档