2. 核心概念
在报表导出服务的设计中,领域模型的清晰定义是构建健壮接口契约的基础。本章将围绕四个核心概念展开:同步导出与异步导出的适用场景,以及任务(Task)、作业(Job)、工单(Order)三层领域模型的职责划分与关系。
2.1 同步导出 vs 异步导出:场景与取舍
导出方式的选择直接决定了接口的响应模型与用户体验。下表从多个维度对比两种模式:
| 对比维度 | 同步导出 | 异步导出 |
|---|---|---|
| 数据量级 | 适合小规模(< 1 万行) | 适合大规模(> 1 万行) |
| 响应时间 | 毫秒~秒级 | 秒级~分钟级 |
| 接口模型 | 请求-响应(直接返回文件流) | 请求-任务-回调/轮询 |
| 资源消耗 | 占用当前请求线程 | 后台队列处理,释放前端 |
| 失败重试 | 客户端自行重试 | 服务端支持自动重试 |
| 典型场景 | 实时预览、小报表下载 | 月报、年报、大数据量导出 |
架构建议: 推荐采用“同步优先,异步兜底”的策略。对于预估数据量小于阈值(如 5000 行)的请求,优先尝试同步导出;若超时或数据量超限,自动降级为异步模式,避免接口设计割裂。
2.2 三层领域模型:Task、Job、Order
为了支撑异步导出的全生命周期管理,我们将导出请求抽象为三个层次:工单(Order)、作业(Job)、任务(Task)。它们各自承担不同的职责,形成清晰的层级关系。
2.2.1 工单(Order)—— 用户视角的导出请求
工单是用户发起一次导出操作的完整记录,包含导出参数、筛选条件、文件格式等业务信息。它是整个导出流程的入口与对外暴露的唯一标识。
- 核心属性: orderId、userId、reportType、filters、format、status、createTime、expireTime
- 生命周期: CREATED → PROCESSING → COMPLETED / FAILED / EXPIRED
- 关键约束: 一个工单对应一个最终输出文件,不可拆分
2.2.2 作业(Job)—— 调度与执行的逻辑单元
作业是系统内部对工单的分解与调度单位。当工单涉及多数据源、多分片或复杂计算时,系统会将其拆分为多个作业,每个作业负责一个相对独立的子任务。
- 核心属性: jobId、orderId、jobType、dataSource、status、retryCount、maxRetries
- 生命周期: PENDING → RUNNING → SUCCEEDED / FAILED / CANCELLED
- 关键约束: 一个工单可包含多个作业,作业之间可能存在依赖关系
2.2.3 任务(Task)—— 最小执行单元
任务是作业内部的最小可执行单元,通常对应一次数据库查询、一次文件写入或一次数据转换操作。任务级别的粒度控制使得系统能够实现细粒度的进度追踪与失败重试。
- 核心属性: taskId、jobId、taskType、payload、status、startTime、endTime
- 生命周期: QUEUED → RUNNING → SUCCESS / FAILURE / TIMEOUT
- 关键约束: 一个作业包含多个任务,任务之间通常无依赖,可并行执行
2.3 三层模型关系图
下图展示了 Order、Job、Task 之间的层级关系与状态流转:
2.4 状态同步机制
三层模型的状态同步是异步导出服务的核心挑战。系统需要确保上层(Order)能够准确反映下层(Job/Task)的聚合状态,同时避免状态风暴。
2.4.1 状态聚合规则
| Order 状态 | 聚合条件(基于所有 Job 的状态) |
|---|---|
| CREATED | 所有 Job 均为 PENDING 或未创建 |
| PROCESSING | 至少一个 Job 处于 RUNNING,且无 FAILED |
| COMPLETED | 所有 Job 均为 SUCCEEDED |
| FAILED | 至少一个 Job 为 FAILED,且不可自动恢复 |
| EXPIRED | 超过 Order 的 expireTime,无论 Job 状态如何 |
2.4.2 状态变更通知策略
- 回调通知: Order 状态变更时,通过配置的 webhook URL 通知调用方
- 轮询接口: 提供 GET /orders/{orderId}/status 接口,返回当前 Order 及下属 Job 的聚合状态
- 事件驱动: 内部通过事件总线(Event Bus)广播 Job/Task 状态变更,由 Order 状态机监听并聚合
避坑提示: 避免在 Task 级别直接触发 Order 状态变更。Task 的失败可能是暂时的(如网络抖动),应通过 Job 的重试机制消化,只有当 Job 最终 FAILED 时才向上传播。否则会导致 Order 状态频繁抖动,增加客户端处理复杂度。
2.5 领域模型代码骨架
以下代码片段展示了三层模型的 Java 类定义,重点关注状态枚举与核心字段:
// 工单(Order)
public class ExportOrder {
private String orderId;
private String userId;
private String reportType;
private Map<String, Object> filters;
private String format; // CSV, XLSX, PDF
private OrderStatus status; // CREATED, PROCESSING, COMPLETED, FAILED, EXPIRED
private LocalDateTime createTime;
private LocalDateTime expireTime;
private String callbackUrl; // 异步回调地址
}
// 作业(Job)
public class ExportJob {
private String jobId;
private String orderId;
private String jobType; // DATA_QUERY, DATA_TRANSFORM, FILE_GENERATE
private String dataSource; // 数据源标识
private JobStatus status; // PENDING, RUNNING, SUCCEEDED, FAILED, CANCELLED
private int retryCount;
private int maxRetries; // 默认 3 次
private List<String> dependencyJobIds; // 依赖的 Job ID 列表
}
// 任务(Task)
public class ExportTask {
private String taskId;
private String jobId;
private String taskType; // SQL_QUERY, ROW_TRANSFORM, FILE_WRITE
private String payload; // 执行参数(JSON 字符串)
private TaskStatus status; // QUEUED, RUNNING, SUCCESS, FAILURE, TIMEOUT
private LocalDateTime startTime;
private LocalDateTime endTime;
private String errorMessage;
}
// 状态枚举
public enum OrderStatus { CREATED, PROCESSING, COMPLETED, FAILED, EXPIRED }
public enum JobStatus { PENDING, RUNNING, SUCCEEDED, FAILED, CANCELLED }
public enum TaskStatus { QUEUED, RUNNING, SUCCESS, FAILURE, TIMEOUT }
2.6 同步与异步的接口契约差异
基于上述领域模型,同步导出与异步导出的接口设计呈现明显差异:
| 接口维度 | 同步导出 | 异步导出 |
|---|---|---|
| 请求路径 | POST /api/v1/export/sync | POST /api/v1/export/async |
| 请求体 | 导出参数 + 文件格式 | 导出参数 + 文件格式 + callbackUrl(可选) |
| 响应体 | 文件二进制流(Content-Type: application/octet-stream) | { "orderId": "ord_xxx", "status": "CREATED", "estimatedWaitTime": 30 } |
| 状态查询 | 无(一次性返回) | GET /api/v1/orders/{orderId}/status |
| 文件下载 | 直接返回 | GET /api/v1/orders/{orderId}/download(仅 COMPLETED 状态可用) |
| 超时处理 | 服务端 30 秒超时断开 | Order 级别 expireTime 控制(默认 24 小时) |
设计要点: 异步导出的接口设计应遵循“先创建,后查询,再下载”的三步模式。创建接口返回 orderId,客户端通过轮询或回调感知状态变更,最终通过下载接口获取文件。这种模式将长时间操作与请求线程解耦,提升了系统的吞吐量与稳定性。