【导入导出】功能设计方案(Java版)

1,160 阅读5分钟

通用导入导出功能设计方案

一、数据库设计

1、数据库ER图

erDiagram
    excel_config ||--o{ sheet_config : "1:n"
    sheet_config ||--o{ column_config : "1:n"
    excel_config ||--o{ export_column_config : "1:n"
    excel_config ||--o{ export_condition : "1:n"
    sheet_config ||--o{ import_validation : "1:n"
    
    excel_config {
        BIGINT config_id PK
        VARCHAR(50) business_key
        BOOLEAN template_mode
        BOOLEAN import_mode
        BOOLEAN export_mode
        VARCHAR(20) sheet_strategy
    }
    
    sheet_config {
        BIGINT sheet_id PK
        BIGINT config_id FK
        VARCHAR(100) sheet_pattern
        INTEGER start_row
        VARCHAR(100) target_table
    }
    
    column_config {
        BIGINT column_id PK
        BIGINT sheet_id FK
        VARCHAR(5) excel_col
        VARCHAR(100) db_column
        VARCHAR(20) data_type
        VARCHAR(100) format_pattern
    }
    
    export_column_config {
        BIGINT export_id PK
        BIGINT config_id FK
        VARCHAR(100) db_column
        VARCHAR(100) excel_header
        INTEGER display_order
        VARCHAR(100) format_pattern
    }
    
    import_validation {
        BIGINT validation_id PK
        BIGINT sheet_id FK
        VARCHAR(20) rule_type
        TEXT rule_expression
        VARCHAR(255) error_message
    }
    
    export_condition {
        BIGINT condition_id PK
        BIGINT config_id FK
        VARCHAR(50) param_name
        VARCHAR(20) data_type
        BOOLEAN required
    }

2、完整表结构DDL

-- 主配置表
CREATE TABLE excel_config (
  config_id BIGSERIAL PRIMARY KEY,
  business_key VARCHAR(50) UNIQUE NOT NULL,
  template_mode BOOLEAN DEFAULT FALSE,
  import_mode BOOLEAN DEFAULT FALSE,
  export_mode BOOLEAN DEFAULT FALSE,
  sheet_strategy VARCHAR(20) CHECK (sheet_strategy IN ('first','all','by_name')),
  file_types VARCHAR(50)[] CHECK (file_types <@ ARRAY['xls','xlsx','csv']),
  version INTEGER DEFAULT 1 NOT NULL
);
​
COMMENT ON TABLE excel_config IS '核心配置表';
COMMENT ON COLUMN excel_config.sheet_strategy IS '多Sheet处理策略';
​
-- Sheet配置表
CREATE TABLE sheet_config (
  sheet_id BIGSERIAL PRIMARY KEY,
  config_id BIGINT NOT NULL REFERENCES excel_config(config_id),
  sheet_pattern VARCHAR(100),
  start_row INTEGER DEFAULT 1 NOT NULL,
  end_row_detect BOOLEAN DEFAULT TRUE,
  target_table VARCHAR(100) NOT NULL,
  UNIQUE(config_id, sheet_pattern)
);
​
COMMENT ON COLUMN sheet_config.end_row_detect IS '自动检测结束行';
​
-- 列配置表
CREATE TABLE column_config (
  column_id BIGSERIAL PRIMARY KEY,
  sheet_id BIGINT NOT NULL REFERENCES sheet_config(sheet_id),
  excel_col VARCHAR(5) NOT NULL CHECK (excel_col ~ '^[A-Z]+$'),
  db_column VARCHAR(100) NOT NULL,
  data_type VARCHAR(20) CHECK (data_type IN ('string','number','date','boolean')),
  required BOOLEAN DEFAULT FALSE,
  format_pattern VARCHAR(100),
  header_style VARCHAR(50),
  data_style VARCHAR(50),
  UNIQUE(sheet_id, excel_col)
);
​
-- 导出列配置表
CREATE TABLE export_column_config (
  export_id BIGSERIAL PRIMARY KEY,
  config_id BIGINT NOT NULL REFERENCES excel_config(config_id),
  db_column VARCHAR(100) NOT NULL,
  excel_header VARCHAR(100) NOT NULL,
  display_order INTEGER NOT NULL CHECK (display_order > 0),
  format_pattern VARCHAR(100),
  security_level INTEGER CHECK (security_level BETWEEN 1 AND 3),
  mask_pattern VARCHAR(100),
  permission_code VARCHAR(50),
  UNIQUE(config_id, db_column)
);
​
-- 导入验证规则表
CREATE TABLE import_validation (
  validation_id BIGSERIAL PRIMARY KEY,
  sheet_id BIGINT REFERENCES sheet_config(sheet_id),
  rule_type VARCHAR(50) NOT NULL CHECK (rule_type IN ('file','sheet','cell','business')),
  rule_expression TEXT NOT NULL,
  error_message VARCHAR(255) NOT NULL,
  CONSTRAINT valid_sheet CHECK (
    (rule_type = 'file' AND sheet_id IS NULL) OR 
    (rule_type != 'file' AND sheet_id IS NOT NULL)
  )
);
​
-- 导出条件表
CREATE TABLE export_condition (
  condition_id BIGSERIAL PRIMARY KEY,
  config_id BIGINT NOT NULL REFERENCES excel_config(config_id),
  param_name VARCHAR(50) NOT NULL,
  data_type VARCHAR(20) CHECK (data_type IN ('date','number','string')),
  required BOOLEAN DEFAULT FALSE,
  default_value VARCHAR(100),
  UNIQUE(config_id, param_name)
);

3、索引建议

-- 主表索引
CREATE INDEX idx_business_key ON excel_config(business_key);
CREATE INDEX idx_sheet_mapping ON sheet_config(config_id, target_table);
​
-- 导出配置索引
CREATE INDEX idx_export_order ON export_column_config(config_id, display_order);
CREATE INDEX idx_export_security ON export_column_config(security_level);
​
-- 导入验证索引
CREATE INDEX idx_validation_type ON import_validation(rule_type);
CREATE INDEX idx_sheet_validations ON import_validation(sheet_id);
​
-- 导出条件索引
CREATE INDEX idx_export_params ON export_condition(config_id, param_name);

4、使用示例

场景1:合同模板配置(模板导出)
-- 启用模板模式
INSERT INTO excel_config (business_key, template_mode, sheet_strategy)
VALUES ('contract', TRUE, 'by_name');
​
-- 配置Sheet
INSERT INTO sheet_config (config_id, sheet_pattern, target_table)
VALUES (1, '^合同主表$', 'contract_main');
​
-- 配置列
INSERT INTO column_config (sheet_id, excel_col, db_column, data_type, format_pattern)
VALUES 
(1, 'A', 'contract_no', 'string', 'text'),
(1, 'B', 'sign_date', 'date', 'yyyy-MM-dd');
场景2:数据导入验证规则
-- 文件级验证
INSERT INTO import_validation (rule_type, rule_expression, error_message)
VALUES 
('file', 'size < 10485760', '文件大小不能超过10MB'),
('file', 'fileType IN (xlsx)', '仅支持xlsx格式');
​
-- 列级验证
INSERT INTO import_validation (sheet_id, rule_type, rule_expression, error_message)
VALUES 
(1, 'cell', 'regex:^HT\d{8}$', '合同编号格式错误'),
(1, 'cell', 'range:amount > 0', '金额必须大于0');
场景3:数据导出配置
-- 导出列配置
INSERT INTO export_column_config 
(config_id, db_column, excel_header, display_order, security_level)
VALUES
(1, 'contract_no', '合同编号', 1, 1),
(1, 'amount', '合同金额', 2, 2);
​
-- 导出条件
INSERT INTO export_condition 
(config_id, param_name, data_type, required)
VALUES
(1, 'start_date', 'date', TRUE),
(1, 'end_date', 'date', TRUE);

5、典型查询示例

获取完整导出配置
SELECT 
  ec.business_key,
  ecc.excel_header,
  ecc.db_column,
  ecc.format_pattern,
  excon.param_name
FROM excel_config ec
JOIN export_column_config ecc USING (config_id)
LEFT JOIN export_condition excon USING (config_id)
WHERE ec.business_key = 'contract'
ORDER BY ecc.display_order;
获取导入验证规则
SELECT 
  iv.rule_type,
  iv.rule_expression,
  iv.error_message,
  s.sheet_pattern
FROM import_validation iv
LEFT JOIN sheet_config s USING (sheet_id)
WHERE s.config_id = 1
ORDER BY 
  CASE iv.rule_type
    WHEN 'file' THEN 1
    WHEN 'sheet' THEN 2
    WHEN 'cell' THEN 3
    ELSE 4
  END;

二、核心设计

1、类设计

classDiagram
    direction TB
    
    class BaseExcelController {
        <<abstract>>
        #excelService: ExcelService
        +exportTemplate(HttpServletResponse, String): void
        +importData(MultipartFile, String): Result<?>
        +exportData(HttpServletResponse, ExportRequest): void
        #getDictService()*: DictService
        #beforeImport(ImportContext): void
        #afterExport(ExportContext): void
    }
    
    class ContractController {
        +ContractController()
        +customExport(HttpServletResponse, ContractQuery): void
    }
    
    class ExcelService {
        <<interface>>
        +generateTemplate(HttpServletResponse, String): void
        +processImport(MultipartFile, String): ImportResult
        +processExport(HttpServletResponse, ExportRequest): void
    }
    
    class ExcelServiceImpl {
        -configRepository: ConfigRepository
        -dictService: DictService
        -sqlBuilder: DynamicSQLBuilder
        +validateFile(ValidationContext): boolean
        +parseDataBySheets(ValidationContext): Map~String, List~Map~~
    }
    
    class ValidationHandler {
        <<interface>>
        +validate(ValidationContext): void
        +next(): ValidationHandler
        +setNext(ValidationHandler): void
    }
    
    class FileValidation {
        -next: ValidationHandler
        +doValidate(ValidationContext): void
    }
    
    class SheetValidation {
        -next: ValidationHandler
        +doValidate(ValidationContext): void
    }
    
    class DynamicSQLBuilder {
        +buildInsertSQL(String, Set~String~): String
        +createParameterSource(Map~String,Object~): SqlParameterSource
    }
    
    class ImportContext {
        -file: MultipartFile
        -errors: List~CellError~
        +getParsedData(): Map~String, List~Map~~
        +addError(CellError): void
    }
    
    class ExportContext {
        -queryParams: Map~String, Object~
        -resultSet: Stream~Map~String, Object~~
        +getResultStream(): Stream~Map~String, Object~~
    }
    
    BaseExcelController <|-- ContractController : 继承
    BaseExcelController o--> ExcelService : 依赖
    ExcelServiceImpl ..|> ExcelService : 实现
    ExcelServiceImpl --> DynamicSQLBuilder : 组合
    ExcelServiceImpl --> ConfigRepository : 组合
    ValidationHandler <|.. FileValidation : 实现
    ValidationHandler <|.. SheetValidation : 实现
    FileValidation --> SheetValidation : 责任链
    ExcelServiceImpl --> ValidationHandler : 使用
    ImportContext <.. ExcelServiceImpl : 创建
    ExportContext <.. ExcelServiceImpl : 创建
关键设计说明:
  1. 继承结构

    BaseExcelController
    
    ContractController
    
    • 具体业务控制器(如 ContractController)继承自基类 BaseExcelController

    • 必须实现抽象方法:

      protected abstract DictService getDictService();
      
  2. 策略模式集成

    
    BaseExcelController
    
    ExcelService
    
    ExcelServiceImpl
    
    • 通过依赖注入的 ExcelService 实现三大场景核心逻辑

    • 具体实现类 ExcelServiceImpl 包含:

      public class ExcelServiceImpl implements ExcelService {
          // 主要组件
          private final ConfigRepository configRepository;
          private final DynamicSQLBuilder sqlBuilder;
          
          // 批处理方法
          private void batchInsertMultiTable(...);
      }
      
  3. 验证责任链

    ValidationHandler
    
    FileValidation
    
    SheetValidation
    
    • 验证流程按 File → Sheet → Cell → Business 顺序执行

    • 验证器类的主要方法:

      public class FileValidation extends ValidationChain {
          @Override
          protected void doValidate(ValidationContext ctx) {
              // 1. 校验文件类型/大小
              // 2. 校验文件数字签名
          }
      }
      
  4. 上下文对象

    ImportContext
    
    ExcelServiceImpl
    
    ExportContext
    
    • ImportContext 封装导入过程数据:

      public class ImportContext {
          private final MultipartFile file;
          private List<CellError> errors;
          private Map<String, Object> parsedData;
      }
      
    • ExportContext 封装导出查询参数:

      public class ExportContext {
          private Map<String, Object> queryParams;
          private Stream<Map<String, Object>> resultStream;
      }
      
典型调用流程示例
JdbcTemplateDynamicSQLBuilderValidationHandlerExcelServiceContractControllerClientJdbcTemplateDynamicSQLBuilderValidationHandlerExcelServiceContractControllerClientalt[验证失败][验证成功]POST /contract/import + Excel文件processImport(file, "contract")执行验证链返回错误列表错误结果buildInsertSQL()生成动态SQL批量插入数据插入结果成功结果返回响应

2、BaseController核心设计

public abstract class BaseExcelController {
    
    @Autowired
    protected ExcelService excelService;
    
    // 导出模板
    @GetMapping("/template")
    public void exportTemplate(HttpServletResponse response, 
                              @RequestParam String businessKey) {
        excelService.generateTemplate(response, businessKey);
    }
    
    // 导入数据
    @PostMapping("/import")
    public Result<?> importData(@RequestParam MultipartFile file,
                               @RequestParam String businessKey) {
        ImportResult result = excelService.processImport(file, businessKey);
        return Result.success(result);
    }
    
    // 导出数据
    @PostMapping("/export")
    public void exportData(HttpServletResponse response,
                          @RequestBody ExportRequest request) {
        excelService.processExport(response, request);
    }
    
    // 需要子类实现的钩子方法
    protected abstract DictService getDictService();
    protected void beforeImport(ImportContext context) {}
    protected void afterExport(ExportContext context) {}
}

3、验证流程设计(责任链模式)

3.1. 验证处理器接口
public interface ValidationHandler {
    void validate(ValidationContext context);
    ValidationHandler next();
    ValidationHandler setNext(ValidationHandler next);
}

// 基础验证链
public class ValidationChain implements ValidationHandler {
    private ValidationHandler next;
    
    public void validate(ValidationContext context) {
        doValidate(context);
        if (next != null) {
            next.validate(context);
        }
    }
    
    protected abstract void doValidate(ValidationContext context);
}
3.2. 多级验证实现
// 文件验证
public class FileValidation extends ValidationChain {
    protected void doValidate(ValidationContext ctx) {
        // 验证文件类型、大小、病毒扫描等
    }
}

// Sheet验证
public class SheetValidation extends ValidationChain {
    protected void doValidate(ValidationContext ctx) {
        // Sheet数量、名称匹配等
    }
}

// 单元格验证
public class CellValidation extends ValidationChain {
    private final DictService dictService;
    
    protected void doValidate(ValidationContext ctx) {
        ctx.getRowData().forEach((col, value) -> {
            validateDataType(colConfig, value);
            validateDict(colConfig, value);
        });
    }
    
    private void validateDict(ColumnConfig config, Object value) {
        if (config.hasDict()) {
            if (!dictService.isValid(config.getDictType(), value)) {
                ctx.addError("无效字典值: " + value);
            }
        }
    }
}

4、动态SQL构建器

4.1. 动态元数据处理
public class DynamicSQLBuilder {
    
    public String buildInsertSQL(String table, Set<String> columns) {
        String fields = String.join(",", columns);
        String placeholders = columns.stream()
            .map(c -> ":" + c)
            .collect(Collectors.joining(","));
        
        return String.format(
            "INSERT INTO %s (%s) VALUES (%s) ON CONFLICT DO NOTHING",
            table, fields, placeholders
        );
    }
    
    public SqlParameterSource createParameterSource(Map<String, Object> row) {
        return new MapSqlParameterSource(row);
    }
}
4.2. 批量插入实现
public class BatchInserter {
    
    @Transactional
    public void batchInsertMultiTable(
        Map<String, List<Map<String, Object>>> dataByTable) {
        
        dataByTable.forEach((table, rows) -> {
            Set<String> columns = rows.get(0).keySet();
            String sql = sqlBuilder.buildInsertSQL(table, columns);
            
            jdbcTemplate.batchUpdate(sql, 
                rows.stream()
                    .map(sqlBuilder::createParameterSource)
                    .toArray(SqlParameterSource[]::new)
            );
        });
    }
}

5、模板生成引擎

5.1. 模板构建实现
public class TemplateBuilder {
    
    public Workbook buildTemplate(ExcelConfig config) {
        Workbook workbook = createWorkbook(config);
        
        config.getSheets().forEach(sheet -> {
            Sheet sheet = workbook.createSheet(sheet.getName());
            buildHeaders(sheet, sheet.getColumns());
            addValidations(sheet, sheet.getColumns());
        });
        
        return workbook;
    }
    
    private void addValidations(Sheet sheet, List<ColumnConfig> columns) {
        columns.forEach(col -> {
            if (col.hasDict()) {
                addDropdownValidation(sheet, col);
            }
            if (col.hasRegex()) {
                addRegexValidation(sheet, col);
            }
        });
    }
    
    private void addDropdownValidation(Sheet sheet, ColumnConfig col) {
        DataValidationHelper helper = sheet.getDataValidationHelper();
        String[] dictValues = dictService.getValues(col.getDictType());
        
        DataValidationConstraint constraint = 
            helper.createExplicitListConstraint(dictValues);
        
        CellRangeAddressList addressList = new CellRangeAddressList(
            1, 100000, col.getIndex(), col.getIndex()
        );
        
        sheet.addValidationData(helper.createValidation(constraint, addressList));
    }
}

6、异常处理增强

6.1. 自定义异常
public class ExcelException extends RuntimeException {
    private ErrorCode code;
    private List<CellError> errors;
    
    public ExcelException(ErrorCode code, String message) {
        super(message);
        this.code = code;
    }
}

public enum ErrorCode {
    FILE_TYPE_INVALID,
    DATA_VALIDATION_FAILED,
    TEMPLATE_MISMATCH
}
6.2. 全局异常处理
@ControllerAdvice
public class ExcelExceptionHandler {
    
    @ExceptionHandler(ExcelException.class)
    public ResponseEntity<ErrorResponse> handleExcelException(
        ExcelException ex) {
        
        ErrorResponse response = new ErrorResponse(
            ex.getCode(),
            ex.getMessage(),
            ex.getErrors()
        );
        
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
            .body(response);
    }
}

7、企业级扩展实现

7.1. 数据加密处理
public class DataMasker {
    
    public Map<String, Object> processRow(Map<String, Object> row, 
                                         List<ColumnConfig> columns) {
        return columns.stream()
            .collect(Collectors.toMap(
                ColumnConfig::getDbColumn,
                col -> maskIfRequired(col, row.get(col.getDbColumn()))
            ));
    }
    
    private Object maskIfRequired(ColumnConfig col, Object value) {
        if (col.needMask()) {
            return MaskUtils.mask(value, col.getMaskPattern());
        }
        return value;
    }
}
6.2. 分布式锁控制
public class ImportLockManager {
    
    public void processWithLock(String businessKey, Runnable task) {
        String lockKey = "import_lock:" + businessKey;
        try {
            if (redisLock.tryLock(lockKey, 30, TimeUnit.SECONDS)) {
                task.run();
            } else {
                throw new BusinessException("存在正在处理的导入任务");
            }
        } finally {
            redisLock.unlock(lockKey);
        }
    }
}

8、性能优化方案

8.1. 流式导出实现
public class StreamingExporter {
    
    public void exportLargeData(ExportRequest request, OutputStream output) {
        try (Workbook workbook = new SXSSFWorkbook(100)) {
            Sheet sheet = workbook.createSheet("Data");
            
            jdbcTemplate.queryForStream(
                buildQuerySQL(request), 
                rs -> {
                    Map<String, Object> row = parseRow(rs);
                    writeRow(sheet, row);
                    flushIfNeeded(sheet);
                }
            );
            
            workbook.write(output);
        }
    }
    
    private void flushIfNeeded(SXSSFSheet sheet) {
        if (sheet.getLastRowNum() % 100 == 0) {
            sheet.flushRows(100);
        }
    }
}
8.2. 缓存优化
@Cacheable(value = "excelConfig", key = "#businessKey")
public ExcelConfig loadConfig(String businessKey) {
    return configRepository.findByBusinessKey(businessKey)
           .orElseThrow(() -> new ConfigNotFoundException(businessKey));
}

@Cacheable(value = "dictValues", key = "#dictType")
public String[] getDictValues(String dictType) {
    return dictRepository.findCodesByType(dictType);
}

9、使用示例

@RestController
@RequestMapping("/contract")
public class ContractController extends BaseExcelController {
    
    @Autowired
    private ContractService contractService;
    
    @Override
    protected DictService getDictService() {
        return contractService.getContractDictService();
    }
    
    @Override
    protected void beforeImport(ImportContext context) {
        context.addParam("currentUser", getCurrentUser());
    }
    
    @PostMapping("/custom-export")
    public void customExport(HttpServletResponse response,
                            @RequestBody ContractQuery query) {
        ExportRequest request = new ExportRequest("contract_export", query);
        super.exportData(response, request);
    }
}

三、验证方案

1、三种场景实现示例

场景1:导出模板

1. 数据库配置

-- 主配置
INSERT INTO excel_config (business_key, template_mode, file_types) 
VALUES ('contract_template', true, '{xlsx}');

-- Sheet配置
INSERT INTO sheet_config (config_id, sheet_pattern, target_table) 
VALUES (1, '^合同主表$', 'contract_main');

-- 列配置
INSERT INTO column_config (sheet_id, excel_col, db_column, data_type, format_pattern)
VALUES 
(1, 'A', 'contract_no', 'string', 'text'),
(1, 'B', 'sign_date', 'date', 'yyyy-MM-dd'),
(1, 'C', 'amount', 'number', '#,##0.00');

-- 字典配置示例(假设存在字典表)
INSERT INTO dict_data (dict_type, dict_code, dict_name) 
VALUES 
('contract_type', '01', '采购合同'),
('contract_type', '02', '销售合同');

2. 调用代码

@RestController
@RequestMapping("/contract")
public class ContractController extends BaseExcelController {
    
    // 导出模板
    @GetMapping("/template")
    public void downloadTemplate(HttpServletResponse response) {
        exportTemplate(response, "contract_template");
    }
}

3. 输出结果

  • 生成包含以下内容的Excel文件:
| 合同编号 | 签订日期  | 合同金额 |
|----------|-----------|----------|
(带数据验证的下拉列表和格式)
场景2:数据导入

1. 数据库配置

-- 主配置
UPDATE excel_config SET import_mode = true 
WHERE business_key = 'customer'; 

-- 验证规则
INSERT INTO import_validation (sheet_id, rule_type, rule_expression, error_message) 
VALUES 
(2, 'cell', 'regex:^[A-Za-z0-9]+$', '客户编码格式错误'),
(2, 'cell', 'dict:customer_level', '无效客户等级'),
(2, 'file', 'fileSize<5000', '文件不得超过5MB');

2. 调用代码

@PostMapping("/import")
public Result<?> importCustomer(@RequestParam MultipartFile file) {
    return importData(file, "customer");
}

3. 输入文件示例(CSV)

客户编码,客户名称,客户等级
CUST001,测试客户,钻石级

4. 处理流程

DBServiceControllerClientDBServiceControllerClientalt[文件校验失败][Sheet校验失败][单元格校验失败][全部校验通过]上传文件开始验证返回错误返回错误返回详细错误批量插入返回成功
场景3:数据导出

1. 数据库配置

-- 导出列配置
INSERT INTO export_column_config (config_id, db_column, excel_header, display_order)
VALUES 
(3, 'order_no', '订单编号', 1),
(3, 'create_time', '下单时间', 2),
(3, 'total_amount', '订单金额', 3);

-- 导出条件
INSERT INTO export_condition (config_id, param_name, data_type)
VALUES 
(3, 'start_time', 'date'),
(3, 'end_time', 'date');

2. 调用代码

@PostMapping("/export")
public void exportOrders(@RequestBody ExportRequest request, 
                        HttpServletResponse response) {
    request.setBusinessKey("order_export");
    exportData(response, request);
}

3. 请求示例

{
  "businessKey": "order_export",
  "params": {
    "start_time": "2023-01-01",
    "end_time": "2023-12-31"
  }
}

4. 输出结果

  • 生成包含时间段内订单的Excel文件:
| 订单编号 | 下单时间                | 订单金额 |
|----------|-------------------------|----------|
| O2023001 | 2023-05-01 14:30:00     | 12,500.00|

2、测试方案

2.1. 功能测试
测试场景测试步骤预期结果
模板导出1. 调用/template接口返回带数据验证的规范模板
数据导入1. 上传符合要求的CSV
2. 上传包含错误数据的文件
1. 返回成功
2. 返回具体错误位置
数据导出1. 使用有效参数调用export接口
2. 使用无效时间范围
1. 返回正确Excel
2. 返回空文件提示
字典校验导入包含未定义字典值的数据返回"无效字典值"错误
2.2. 边界情况测试
测试类别测试场景预期结果
文件大小上传10MB的CSV文件返回"文件超过限制"错误
行数限制导入100001行数据返回"超出最大行数限制"
特殊字符客户名称含emoji符号编码转换后正常存储
时区验证导出时使用UTC时间参数数据按时区转换正确
空文件上传0KB的Excel文件返回"空文件"错误
2.3. 压力测试方案

测试工具:JMeter + Prometheus监控

测试类型测试参数通过标准
单接口压测100并发导出模板平均RT < 500ms
错误率 < 0.1%
组合场景50并发导入+50并发导出系统资源利用率 < 80%
大数据量导出1,000,000行数据内存峰值 < 2GB
导出时间 < 60s
长时间运行持续运行24小时混合负载无内存泄漏
TPS波动 < 10%

监控指标

  1. JVM内存使用(堆内存、GC次数)
  2. 数据库连接池使用率
  3. Excel文件生成速率(行/秒)
  4. 导入数据吞吐量(行/秒)

3、测试数据生成脚本

3.1. 大数据量导入测试文件生成(Python)
import csv
from faker import Faker

fake = Faker('zh_CN')

with open('large_data.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['客户编码', '客户名称', '客户等级'])
    
    for _ in range(100000):
        writer.writerow([
            f"CUST{fake.unique.random_number(digits=6)}",
            fake.company(),
            fake.random_element(['钻石级','金牌级','银牌级'])
        ])
3.2. 压力测试JMeter配置
<TestPlan>
  <ThreadGroup>
    <numThreads>100</numThreads>
    <rampUp>60</rampUp>
    
    <HTTPSampler>
      <method>POST</method>
      <path>/api/export</path>
      <Body>
        {"businessKey":"order_export","params":{"start_time":"2023-01-01"}}
      </Body>
    </HTTPSampler>
    
    <ResultCollector>
      <name>Aggregate Report</name>
    </ResultCollector>
  </ThreadGroup>
</TestPlan>

仓库地址:gitee.com/paladin024/…