工具类任务详解
本文档说明飞球 ETL 系统中所有工具类(Utility)和校验类(Verification)任务。
这些任务不属于 ODS/DWD/DWS/INDEX 四层业务管线,而是为系统初始化、
数据灌入、归档、截止时间检查和完整性校验等运维场景服务。
概述
工具类任务共 8 个(含 1 个校验类任务),注册于 orchestration/task_registry.py:
| 任务代码 |
Python 类 |
用途 |
task_type |
requires_db_config |
INIT_ODS_SCHEMA |
InitOdsSchemaTask |
执行 ODS + etl_admin DDL,创建必要目录 |
utility |
False |
INIT_DWD_SCHEMA |
InitDwdSchemaTask |
执行 DWD DDL |
utility |
False |
INIT_DWS_SCHEMA |
InitDwsSchemaTask |
执行 DWS DDL |
utility |
False |
MANUAL_INGEST |
ManualIngestTask |
从本地 JSON 文件手动入库到 ODS |
utility |
False |
ODS_JSON_ARCHIVE |
OdsJsonArchiveTask |
在线抓取 ODS 接口数据并落盘 JSON |
utility |
False |
CHECK_CUTOFF |
CheckCutoffTask |
检查各任务/表的数据截止时间 |
utility |
False |
SEED_DWS_CONFIG |
SeedDwsConfigTask |
初始化 DWS 配置种子数据 |
utility |
True(默认) |
DATA_INTEGRITY_CHECK |
DataIntegrityTask |
API → ODS → DWD 数据完整性校验 |
verification |
False |
典型执行顺序(首次部署):INIT_ODS_SCHEMA → INIT_DWD_SCHEMA → INIT_DWS_SCHEMA → SEED_DWS_CONFIG
1. INIT_ODS_SCHEMA — ODS + etl_admin Schema 初始化
| 属性 |
值 |
| 任务代码 |
INIT_ODS_SCHEMA |
| Python 类 |
tasks.utility.init_schema_task.InitOdsSchemaTask |
| 继承 |
BaseTask |
| 用途 |
创建 ODS 层和 etl_admin 调度元数据的数据库结构,并准备运行时目录 |
执行流程
执行的 DDL 文件
| DDL 文件 |
创建的 Schema |
主要内容 |
database/schema_etl_admin.sql |
etl_admin |
etl_task(任务注册表)、etl_cursor(游标表)、etl_run(运行记录表) |
database/schema_ODS_doc.sql |
billiards_ods |
20+ 张 ODS 原始表(member_profiles、settlement_records、payment_transactions 等) |
ODS DDL 清洗逻辑
ODS DDL 文件可能包含头部说明文本和 COMMENT ON 语句(CamelCase 未加引号会导致执行失败),因此 load() 阶段会做轻量清洗:
- 定位第一个
DROP SCHEMA 语句,丢弃之前的非 SQL 文本
- 逐行过滤掉以
COMMENT ON 开头的行
创建的目录
| 配置路径 |
说明 |
io.log_root |
日志输出根目录 |
io.export_root |
数据导出根目录 |
pipeline.fetch_root |
API 抓取数据落盘目录 |
pipeline.ingest_source_dir |
手动入库数据源目录(默认同 fetch_root) |
配置参数
| 参数 |
默认值 |
说明 |
schema.ods_file |
database/schema_ODS_doc.sql |
ODS DDL 文件路径 |
schema.etl_admin_file |
database/schema_etl_admin.sql |
etl_admin DDL 文件路径 |
io.log_root |
— |
日志目录 |
io.export_root |
— |
导出目录 |
pipeline.fetch_root |
— |
抓取数据目录 |
pipeline.ingest_source_dir |
同 fetch_root |
入库数据源目录 |
CLI 示例
2. INIT_DWD_SCHEMA — DWD Schema 初始化
| 属性 |
值 |
| 任务代码 |
INIT_DWD_SCHEMA |
| Python 类 |
tasks.utility.init_dwd_schema_task.InitDwdSchemaTask |
| 继承 |
BaseTask |
| 用途 |
创建 DWD 明细数据层的数据库结构 |
执行流程
执行的 DDL 文件
| DDL 文件 |
创建的 Schema |
主要内容 |
database/schema_dwd_doc.sql |
billiards_dwd |
维度表(dim_,含 SCD2 约束)、事实表(dwd_、fact_)、扩展表(_ex) |
DWD DDL 的特殊处理:
- 自动为含
scd2_start_time 列的表设置 SCD2 默认值(scd2_start_time=now()、scd2_end_time='9999-12-31'、scd2_is_current=1、scd2_version=1)
- 自动创建 SCD2 排他约束(
EXCLUDE USING gist,防止同一业务主键的生效区间重叠)
- 自动创建当前版本唯一索引(
WHERE scd2_is_current = 1)
配置参数
| 参数 |
默认值 |
说明 |
schema.dwd_file |
database/schema_dwd_doc.sql |
DWD DDL 文件路径 |
dwd.drop_schema_first |
False |
是否先 DROP 再重建(危险操作,会丢失所有 DWD 数据) |
CLI 示例
3. INIT_DWS_SCHEMA — DWS Schema 初始化
| 属性 |
值 |
| 任务代码 |
INIT_DWS_SCHEMA |
| Python 类 |
tasks.utility.init_dws_schema_task.InitDwsSchemaTask |
| 继承 |
BaseTask |
| 用途 |
创建 DWS 数据服务层的数据库结构 |
执行流程
执行的 DDL 文件
| DDL 文件 |
创建的 Schema |
主要内容 |
database/schema_dws.sql |
billiards_dws |
配置表(5 张 cfg_*)、助教域(5 张)、会员域(2 张)、财务域(7 张)、订单汇总(1 张) |
DWS Schema 包含的配置表:
| 配置表 |
说明 |
cfg_performance_tier |
绩效档位配置(阈值、抽成比例、假期天数) |
cfg_assistant_level_price |
助教等级定价 |
cfg_bonus_rules |
奖金规则配置 |
cfg_area_category |
台区分类映射 |
cfg_skill_type |
技能课程类型映射 |
配置参数
| 参数 |
默认值 |
说明 |
schema.dws_file |
database/schema_dws.sql |
DWS DDL 文件路径 |
dws.drop_schema_first |
False |
是否先 DROP 再重建(危险操作) |
CLI 示例
4. SEED_DWS_CONFIG — DWS 配置种子数据初始化
| 属性 |
值 |
| 任务代码 |
SEED_DWS_CONFIG |
| Python 类 |
tasks.utility.seed_dws_config_task.SeedDwsConfigTask |
| 继承 |
BaseTask |
| 用途 |
向 DWS 配置表插入初始数据(绩效档位、等级定价、奖金规则等) |
前置条件
billiards_dws schema 已创建(需先执行 INIT_DWS_SCHEMA)
- 配置表(
cfg_*)已存在
执行流程
执行的种子文件
| 文件 |
目标表 |
说明 |
database/seed_dws_config.sql |
cfg_performance_tier |
绩效档位(含历史口径:旧方案至 2026-02-28,新方案 2026-03-01 起) |
|
cfg_assistant_level_price |
助教等级定价 |
|
cfg_bonus_rules |
奖金规则 |
|
cfg_area_category |
台区分类映射 |
|
cfg_skill_type |
技能课程类型映射 |
配置参数
| 参数 |
默认值 |
说明 |
schema.seed_dws_file |
database/seed_dws_config.sql |
种子数据 SQL 文件路径 |
CLI 示例
5. MANUAL_INGEST — 手动 JSON 入库
| 属性 |
值 |
| 任务代码 |
MANUAL_INGEST |
| Python 类 |
tasks.utility.manual_ingest_task.ManualIngestTask |
| 继承 |
BaseTask |
| 用途 |
从本地 JSON 文件批量灌入 ODS 表,用于离线回放或示例数据导入 |
执行流程概览
文件匹配规则
MANUAL_INGEST 通过 FILE_MAPPING 将文件名关键字映射到目标 ODS 表。匹配逻辑:文件名中包含关键字即匹配(大小写敏感)。
| 文件名关键字 |
目标 ODS 表 |
member_profiles |
billiards_ods.member_profiles |
member_balance_changes |
billiards_ods.member_balance_changes |
member_stored_value_cards |
billiards_ods.member_stored_value_cards |
recharge_settlements |
billiards_ods.recharge_settlements |
settlement_records |
billiards_ods.settlement_records |
assistant_cancellation_records |
billiards_ods.assistant_cancellation_records |
assistant_accounts_master |
billiards_ods.assistant_accounts_master |
assistant_service_records |
billiards_ods.assistant_service_records |
site_tables_master |
billiards_ods.site_tables_master |
table_fee_discount_records |
billiards_ods.table_fee_discount_records |
table_fee_transactions |
billiards_ods.table_fee_transactions |
goods_stock_movements |
billiards_ods.goods_stock_movements |
stock_goods_category_tree |
billiards_ods.stock_goods_category_tree |
goods_stock_summary |
billiards_ods.goods_stock_summary |
payment_transactions |
billiards_ods.payment_transactions |
refund_transactions |
billiards_ods.refund_transactions |
platform_coupon_redemption_records |
billiards_ods.platform_coupon_redemption_records |
group_buy_redemption_records |
billiards_ods.group_buy_redemption_records |
group_buy_packages |
billiards_ods.group_buy_packages |
settlement_ticket_details |
billiards_ods.settlement_ticket_details |
store_goods_master |
billiards_ods.store_goods_master |
tenant_goods_master |
billiards_ods.tenant_goods_master |
store_goods_sales_records |
billiards_ods.store_goods_sales_records |
JSON 解析逻辑
_extract_records() 方法兼容多种 JSON 包装格式:
- 顶层数组:
[{...}, {...}] → 直接作为记录列表
- data 包装:
{"data": [...]} 或 {"code": 0, "data": [...]} → 展开 data 字段
- 嵌套 list:
{"data": {"someKey": [{...}]}} → 自动查找第一个 list 类型的值
- settleList 特殊处理:充值/结算记录的
data.settleList 结构会被展开,内层 settleList 提取为独立记录,并保留外层 siteProfile 供字段补充
入库流程
对每张目标表,入库过程如下:
- 查询表结构:通过
information_schema.columns 获取目标表的列名、数据类型
- 构建 SQL:生成
INSERT INTO ... VALUES %s ON CONFLICT ... 语句
- 有
content_hash 列:ON CONFLICT (pk, content_hash) DO NOTHING(内容去重)
- 无
content_hash 列:ON CONFLICT (pk) DO UPDATE SET ...(upsert 覆盖)
- 值映射:逐列匹配 JSON 字段(忽略大小写),特殊列处理:
payload:存储原始 JSON 记录
source_file:填入文件名
fetched_at:取记录中的值或当前时间
content_hash:基于记录内容计算 SHA-256(排除 fetched_at、payload 等 ETL 元数据字段)
- JSON 类型列:自动包装为
psycopg2.extras.Json
- 整数/浮点/时间戳列:自动类型转换
- 批量执行:使用
psycopg2.extras.execute_values 分批提交(默认 chunk_size=50,最大 500)
- 降级处理:批量执行失败时,降级为逐行 +
SAVEPOINT 模式,跳过异常行继续处理
- 事务粒度:每个文件一次
commit,避免长事务
特殊处理
- 充值/结算记录(
recharge_settlements、settlement_records):自动从 siteProfile 补齐 tenantid、siteid、sitename
- 空值规范化:空字符串
""、空 JSON "{}" / "[]" 统一转为 None
- 主键校验:主键值为
None 或空字符串的记录直接跳过
配置参数
| 参数 |
默认值 |
说明 |
manual.data_dir |
— |
JSON 数据文件目录(优先级最高) |
pipeline.ingest_source_dir |
— |
入库数据源目录(次优先) |
| — |
tests/testdata_json |
兜底默认目录 |
manual.include_files |
[](全部) |
限定处理的文件名列表(不含扩展名,小写匹配) |
manual.execute_values_page_size |
50 |
批量插入每批行数(1-500) |
CLI 示例
6. ODS_JSON_ARCHIVE — ODS 接口数据归档
| 属性 |
值 |
| 任务代码 |
ODS_JSON_ARCHIVE |
| Python 类 |
tasks.ods.ods_json_archive_task.OdsJsonArchiveTask |
| 继承 |
BaseTask |
| 用途 |
在线抓取所有 ODS 相关 API 接口数据,落盘为简化 JSON 文件,供后续离线回放/入库 |
注意:虽然注册为 task_type="utility",但该任务的源文件位于 tasks/ods/ 目录下,因为它本质上是 ODS 数据的抓取归档。
归档策略
- 输出格式:每页一个 JSON 文件,格式为
{"code": 0, "data": [...records...]},与 MANUAL_INGEST 的解析逻辑兼容
- 文件命名:
{endpoint_stem}__p{page_no:04d}.json(如 GetAllOrderSettleList__p0001.json)
- 小票文件:按
orderSettleId 分文件写入(GetOrderSettleTicketNew__{orderSettleId}.json)
- 清单文件:抓取完成后生成
manifest.json,记录窗口、端点、记录数等元信息
抓取的 API 端点
任务内置 22 个端点配置(ENDPOINTS),按窗口参数风格分类:
| 窗口风格 |
参数 |
端点示例 |
site |
siteId |
/MemberProfile/GetTenantMemberList、/Table/GetSiteTables 等 |
start_end |
siteId + startTime / endTime |
/MemberProfile/GetMemberCardBalanceChange、/TenantGoods/GetGoodsSalesList 等 |
range |
siteId + rangeStartTime / rangeEndTime |
/Site/GetAllOrderSettleList、/Site/GetRechargeSettleList |
pay |
siteId + StartPayTime / EndPayTime |
/PayLog/GetPayLogListPage |
此外,还有一个特殊端点 /Order/GetOrderSettleTicketNew(小票详情),按支付日志中提取的 orderSettleId 逐单抓取。
执行流程
配置参数
| 参数 |
默认值 |
说明 |
pipeline.fetch_root |
— |
JSON 文件输出目录 |
api.page_size |
200 |
API 分页大小 |
io.write_pretty_json |
False |
是否格式化输出 JSON |
CLI 示例
7. CHECK_CUTOFF — 数据截止时间检查
| 属性 |
值 |
| 任务代码 |
CHECK_CUTOFF |
| Python 类 |
tasks.utility.check_cutoff_task.CheckCutoffTask |
| 继承 |
BaseTask |
| 用途 |
报告各任务的游标截止时间和各层数据表的最新时间戳,用于运维监控 |
执行流程
该任务不走标准的 extract → transform → load 流程,而是直接在 execute() 中完成所有逻辑:
校验逻辑
- 游标截止时间:从
etl_admin.etl_cursor.last_end 获取每个任务的最后成功窗口结束时间,排除 INIT_* 任务后取最小值作为总体截止时间
- ODS 抓取时间:查询每张 ODS 表的
MAX(fetched_at),取最小值作为 ODS 层截止时间
- DWD/DWS 业务时间:探测关键业务时间列(
pay_time、order_date、updated_at),反映数据实际覆盖范围
输出
任务通过日志输出检查结果,同时在返回值的 report 字段中包含结构化数据:
配置参数
| 参数 |
默认值 |
说明 |
app.store_id |
— |
门店 ID(必填) |
run.cutoff_task_codes |
None(全部) |
逗号分隔的任务代码列表,限定检查范围 |
CLI 示例
8. DATA_INTEGRITY_CHECK — 数据完整性校验
| 属性 |
值 |
| 任务代码 |
DATA_INTEGRITY_CHECK |
| Python 类 |
tasks.utility.data_integrity_task.DataIntegrityTask |
| 继承 |
BaseTask |
| 注册 task_type |
verification(非 utility,但在本文档中一并说明) |
| 用途 |
检查 API → ODS → DWD 全链路数据完整性,支持自动回填缺失数据 |
两种运行模式
1. 历史模式(history,默认)
从指定起始日期到结束日期,按月分段检查全量历史数据的完整性。
2. 窗口模式(window)
检查指定时间窗口内的数据完整性,当提供 CLI 窗口覆盖参数时自动切换到此模式。
校验逻辑
核心校验由 quality/integrity_service.py 和 quality/integrity_checker.py 实现:
- 记录数对比:API 返回的记录数 vs ODS 表中的记录数
- 内容一致性(可选):抽样对比 API 记录与 ODS 记录的
content_hash
- 缺失检测:识别 API 中存在但 ODS 中缺失的记录
- 不一致检测:识别 API 与 ODS 中内容不匹配的记录
自动回填
当 auto_backfill=True 时,检测到缺失或不一致数据后会自动触发回填:
- 调用
scripts/repair/backfill_missing_data.run_backfill() 重新抓取缺失数据
- 回填完成后可选复查(
recheck_after_backfill),验证回填效果
配置参数
| 参数 |
默认值 |
说明 |
integrity.mode |
history |
运行模式:history(历史全量)/ window(时间窗口) |
integrity.history_start |
2025-07-01 |
历史模式起始日期 |
integrity.history_end |
—(当前时间) |
历史模式结束日期 |
integrity.include_dimensions |
False |
是否包含维度表检查 |
integrity.ods_task_codes |
—(全部) |
限定检查的 ODS 任务代码 |
integrity.auto_backfill |
False |
是否自动回填缺失数据 |
integrity.compare_content |
True |
是否对比内容一致性 |
integrity.content_sample_limit |
— |
内容对比抽样上限 |
integrity.backfill_mismatch |
True |
是否回填不一致数据(仅 auto_backfill 时生效) |
integrity.recheck_after_backfill |
True |
回填后是否复查 |
integrity.force_monthly_split |
True |
是否强制按月分段 |
run.window_override.start |
— |
CLI 窗口覆盖起始时间(触发 window 模式) |
run.window_override.end |
— |
CLI 窗口覆盖结束时间 |
输出报表
检查结果以 JSON 格式写入 reports/ 目录:
- 历史模式:
reports/data_integrity_history_{timestamp}.json
- 窗口模式:
reports/data_integrity_window_{timestamp}.json
CLI 示例