Under-Utils
stable 1.0.2 1.0.3-SNAPSHOT
library

Biz

under-utils-biz

可复用业务流程模板,当前覆盖 CSV 导入、异步导入、进度查询和错误导出。

依赖

under-utils-biz Maven 坐标
<dependency>
    <groupId>io.github.yexianglun-d</groupId>
    <artifactId>under-utils-biz</artifactId>
    <version>1.0.2</version>
</dependency>

适用场景

需要沉淀可复用导入任务流程的后台系统。

能力边界

文档来源 under-utils-biz/README.md
API 入口 查看 Biz 相关 API
设计约束 保留失败语义、配置 key 和 public API 兼容性说明,不以营销描述替代真实边界。

README 同步内容

来源:under-utils-biz/README.md,构建时自动读取并渲染。

可复用业务流程模板模块。

本模块用于放置跨应用反复出现、但又比 core 更接近业务流程形态的能力。它不应该包含订单、支付、会员、营销等产品域专属模型。

依赖

<dependency>
    <groupId>io.github.yexianglun-d</groupId>
    <artifactId>under-utils-biz</artifactId>
    <version>1.0.2</version>
</dependency>

导入任务模板

ImportTaskTemplate 会让每一行依次经过三个阶段:

  1. parse
  2. validate
  3. process

模板负责收集行级错误并返回导入统计。需要查询进度时,可以通过 ImportOptions.progressListener 接收快照。

ImportTaskTemplate template = ImportTaskTemplate.create(
        ImportOptions.builder()
                .skipBlankRows(true)
                .maxErrors(100)
                .progressListener(progress -> progressService.update(progress))
                .build()
);

ImportResult result = template.execute(rows, new ImportRowHandler<String, UserImportRow>() {

    @Override
    public boolean isBlankRow(String rawRow) {
        return rawRow == null || rawRow.isBlank();
    }

    @Override
    public UserImportRow parse(String rawRow, ImportRowContext context) {
        String[] parts = rawRow.split(",", -1);
        return new UserImportRow(parts[0].trim(), parts[1].trim());
    }

    @Override
    public List<ImportRowError> validate(UserImportRow row, ImportRowContext context) {
        List<ImportRowError> errors = new ArrayList<>();
        if (row.username().isBlank()) {
            errors.add(ImportRowError.of("username", "USERNAME_REQUIRED", "username is required"));
        }
        if (row.phone().isBlank()) {
            errors.add(ImportRowError.of("phone", "PHONE_REQUIRED", "phone is required"));
        }
        return errors;
    }

    @Override
    public void process(UserImportRow row, ImportRowContext context) {
        userService.importUser(row);
    }
});

CSV Reader

CsvImportRowReader 将 CSV 输入解析为 CsvRow,并在模板执行结束后关闭 reader:

try (Reader reader = Files.newBufferedReader(path)) {
    ImportResult result = ImportTaskTemplate.create()
            .execute(CsvImportRowReader.builder(reader)
                    .hasHeader(true)
                    .build(), handler);
}

CSV 解析保持小而可预测。Excel、大文件流式导入等场景,建议业务项目自行解析文件,再把行数据交给 ImportTaskTemplate

异步导入

AsyncImportTaskTemplate 使用业务传入的 Executor 执行后台导入,并在当前 JVM 内保存任务进度、完成结果或任务级失败。

AsyncImportTaskTemplate asyncTemplate = new AsyncImportTaskTemplate(executor);

String taskId = asyncTemplate.submit("user-import-20260523", rows, handler);

ImportProgress progress = asyncTemplate.findProgress(taskId)
        .orElseThrow();

ImportResult result = asyncTemplate.findResult(taskId)
        .orElse(null);

默认状态存储是内存 Map,适合单实例后台任务或示例工程。完成或失败的任务默认在当前 JVM 内保留 24 小时, 并由每实例后台清理线程主动移除;如需调整,可使用带 Duration taskRetentionDuration cleanupInterval 的构造器。AsyncImportTaskTemplate 实现了 AutoCloseable,手动创建时可在不再使用后调用 close(); 作为 Spring Bean 使用时会随应用上下文关闭。生产环境如果需要跨实例查询、重启恢复或任务审计,应通过 ImportProgressListener 将进度写入数据库、Redis 或消息系统。

错误导出

行级错误可以导出为 CSV,供接口直接返回或写入对象存储:

String csv = ImportErrorExporter.toCsv(result.getRowErrors());

CSV 字段为:

  • rowNumber
  • field
  • errorCode
  • message
  • rawRowSummary

失败语义

  • RowValidationException 会转换为行级错误。
  • parse、validate、process 阶段的其他异常会按阶段转换为行级错误。
  • ImportTaskException 视为任务级失败,直接向外传播。
  • failFast 会在首个失败行后停止。
  • maxErrors 会在收集错误数达到上限后停止。

结果字段

ImportResult 包含:

  • 总行数
  • 成功行数
  • 失败行数
  • 跳过行数
  • 行级错误列表

调用方可以根据结果决定提交、回滚、展示预览,或要求用户修正后重试。