# 工具类任务详解 > 本文档说明飞球 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 调度元数据的数据库结构,并准备运行时目录 | ### 执行流程 ``` extract() ├── 读取 DDL 文件路径(schema_ODS_doc.sql、schema_etl_admin.sql) ├── 收集需创建的目录列表 └── 返回 SQL 文本 + 目录列表 load() ├── 创建必要目录(log_root、export_root、fetch_root、ingest_dir) ├── 执行 etl_admin DDL(schema_etl_admin.sql) └── 执行 ODS DDL(schema_ODS_doc.sql,清洗后) ``` ### 执行的 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()` 阶段会做轻量清洗: 1. 定位第一个 `DROP SCHEMA` 语句,丢弃之前的非 SQL 文本 2. 逐行过滤掉以 `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 示例 ```bash python -m cli.main --tasks INIT_ODS_SCHEMA --pg-dsn "$PG_DSN" ``` --- ## 2. INIT_DWD_SCHEMA — DWD Schema 初始化 | 属性 | 值 | |------|-----| | 任务代码 | `INIT_DWD_SCHEMA` | | Python 类 | `tasks.utility.init_dwd_schema_task.InitDwdSchemaTask` | | 继承 | `BaseTask` | | 用途 | 创建 DWD 明细数据层的数据库结构 | ### 执行流程 ``` extract() ├── 读取 DDL 文件路径(schema_dwd_doc.sql) └── 读取 drop_first 配置 load() ├── [可选] DROP SCHEMA billiards_dwd CASCADE └── 执行 DWD DDL(schema_dwd_doc.sql) ``` ### 执行的 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 示例 ```bash # 常规初始化 python -m cli.main --tasks INIT_DWD_SCHEMA --pg-dsn "$PG_DSN" # 重建(先删后建,慎用) python -m cli.main --tasks INIT_DWD_SCHEMA --pg-dsn "$PG_DSN" --extra dwd.drop_schema_first=true ``` --- ## 3. INIT_DWS_SCHEMA — DWS Schema 初始化 | 属性 | 值 | |------|-----| | 任务代码 | `INIT_DWS_SCHEMA` | | Python 类 | `tasks.utility.init_dws_schema_task.InitDwsSchemaTask` | | 继承 | `BaseTask` | | 用途 | 创建 DWS 数据服务层的数据库结构 | ### 执行流程 ``` extract() ├── 读取 DDL 文件路径(schema_dws.sql) └── 读取 drop_first 配置 load() ├── [可选] DROP SCHEMA billiards_dws CASCADE └── 执行 DWS DDL(schema_dws.sql) ``` ### 执行的 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 示例 ```bash python -m cli.main --tasks INIT_DWS_SCHEMA --pg-dsn "$PG_DSN" ``` --- ## 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_*`)已存在 ### 执行流程 ``` extract() └── 读取 seed_dws_config.sql 文件内容 load() └── 执行 SQL(TRUNCATE + INSERT 配置数据) ``` ### 执行的种子文件 | 文件 | 目标表 | 说明 | |------|--------|------| | `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 示例 ```bash # 通常与 INIT_DWS_SCHEMA 一起执行 python -m cli.main --tasks INIT_DWS_SCHEMA,SEED_DWS_CONFIG --pg-dsn "$PG_DSN" ``` --- ## 5. MANUAL_INGEST — 手动 JSON 入库 | 属性 | 值 | |------|-----| | 任务代码 | `MANUAL_INGEST` | | Python 类 | `tasks.utility.manual_ingest_task.ManualIngestTask` | | 继承 | `BaseTask` | | 用途 | 从本地 JSON 文件批量灌入 ODS 表,用于离线回放或示例数据导入 | ### 执行流程概览 ``` execute() ├── 确定数据目录(manual.data_dir / pipeline.ingest_source_dir / tests/testdata_json) ├── 遍历目录下所有 .json 文件(按文件名排序) │ ├── [可选] 按 include_files 过滤 │ ├── 读取并解析 JSON │ ├── 提取记录列表(兼容多层 data/list 包装) │ ├── 按文件名关键字匹配目标 ODS 表 │ └── 批量入库(INSERT ON CONFLICT) └── 返回统计计数(fetched/inserted/updated/skipped/errors) ``` ### 文件匹配规则 `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 包装格式: 1. **顶层数组**:`[{...}, {...}]` → 直接作为记录列表 2. **data 包装**:`{"data": [...]}` 或 `{"code": 0, "data": [...]}` → 展开 `data` 字段 3. **嵌套 list**:`{"data": {"someKey": [{...}]}}` → 自动查找第一个 list 类型的值 4. **settleList 特殊处理**:充值/结算记录的 `data.settleList` 结构会被展开,内层 `settleList` 提取为独立记录,并保留外层 `siteProfile` 供字段补充 ### 入库流程 对每张目标表,入库过程如下: 1. **查询表结构**:通过 `information_schema.columns` 获取目标表的列名、数据类型 2. **构建 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 覆盖) 3. **值映射**:逐列匹配 JSON 字段(忽略大小写),特殊列处理: - `payload`:存储原始 JSON 记录 - `source_file`:填入文件名 - `fetched_at`:取记录中的值或当前时间 - `content_hash`:基于记录内容计算 SHA-256(排除 `fetched_at`、`payload` 等 ETL 元数据字段) - JSON 类型列:自动包装为 `psycopg2.extras.Json` - 整数/浮点/时间戳列:自动类型转换 4. **批量执行**:使用 `psycopg2.extras.execute_values` 分批提交(默认 chunk_size=50,最大 500) 5. **降级处理**:批量执行失败时,降级为逐行 + `SAVEPOINT` 模式,跳过异常行继续处理 6. **事务粒度**:每个文件一次 `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 示例 ```bash # 从默认目录灌入所有 JSON python -m cli.main --tasks MANUAL_INGEST --pg-dsn "$PG_DSN" # 指定数据目录 python -m cli.main --tasks MANUAL_INGEST --pg-dsn "$PG_DSN" \ --extra manual.data_dir=/path/to/json_files # 只灌入指定文件 python -m cli.main --tasks MANUAL_INGEST --pg-dsn "$PG_DSN" \ --extra manual.include_files=member_profiles,settlement_records ``` --- ## 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` 逐单抓取。 ### 执行流程 ``` extract() ├── 验证 API 客户端类型(必须为 APIClient,即在线模式) ├── 确定输出目录(api.output_dir / pipeline.fetch_root) ├── 遍历 ENDPOINTS,逐端点分页抓取 │ ├── 构建请求参数(按 window_style 选择参数格式) │ ├── 调用 iter_paginated() 分页获取 │ ├── 每页落盘为独立 JSON 文件 │ └── 从支付日志中收集 orderSettleId(用于小票抓取) ├── 按 orderSettleId 逐单抓取小票详情 └── 生成 manifest.json 清单文件 ``` ### 配置参数 | 参数 | 默认值 | 说明 | |------|--------|------| | `pipeline.fetch_root` | — | JSON 文件输出目录 | | `api.page_size` | `200` | API 分页大小 | | `io.write_pretty_json` | `False` | 是否格式化输出 JSON | ### CLI 示例 ```bash # 在线抓取并归档 python -m cli.main --tasks ODS_JSON_ARCHIVE --pg-dsn "$PG_DSN" \ --store-id "$STORE_ID" --api-token "$API_TOKEN" ``` --- ## 7. CHECK_CUTOFF — 数据截止时间检查 | 属性 | 值 | |------|-----| | 任务代码 | `CHECK_CUTOFF` | | Python 类 | `tasks.utility.check_cutoff_task.CheckCutoffTask` | | 继承 | `BaseTask` | | 用途 | 报告各任务的游标截止时间和各层数据表的最新时间戳,用于运维监控 | ### 执行流程 该任务不走标准的 extract → transform → load 流程,而是直接在 `execute()` 中完成所有逻辑: ``` execute() ├── 1. 查询 etl_admin 游标截止时间 │ ├── 关联 etl_task + etl_cursor 表 │ ├── 筛选当前门店已启用的任务 │ ├── [可选] 按 task_codes 过滤 │ └── 计算总体截止时间(排除 INIT_* 任务的最小 last_end) ├── 2. 探测 ODS 表抓取时间 │ ├── 遍历 DwdLoadTask.TABLE_MAP 中的 ODS 表 │ ├── 查询每张表的 MAX(fetched_at) 和 COUNT(*) │ └── 计算 ODS 截止时间(最小 max_fetched_at) └── 3. 探测 DWD/DWS 关键时间列 ├── DWD: max(pay_time) from dwd_settlement_head / dwd_payment / dwd_refund └── DWS: max(order_date) / max(updated_at) from dws_order_summary ``` ### 校验逻辑 - **游标截止时间**:从 `etl_admin.etl_cursor.last_end` 获取每个任务的最后成功窗口结束时间,排除 `INIT_*` 任务后取最小值作为总体截止时间 - **ODS 抓取时间**:查询每张 ODS 表的 `MAX(fetched_at)`,取最小值作为 ODS 层截止时间 - **DWD/DWS 业务时间**:探测关键业务时间列(`pay_time`、`order_date`、`updated_at`),反映数据实际覆盖范围 ### 输出 任务通过日志输出检查结果,同时在返回值的 `report` 字段中包含结构化数据: ```python { "rows": [...], # 每个任务的游标信息 "overall_cutoff": datetime, # 总体截止时间 "ods_fetched_at": {...}, # 每张 ODS 表的 max_fetched_at "dw_max_times": {...}, # DWD/DWS 关键时间列 } ``` ### 配置参数 | 参数 | 默认值 | 说明 | |------|--------|------| | `app.store_id` | — | 门店 ID(必填) | | `run.cutoff_task_codes` | `None`(全部) | 逗号分隔的任务代码列表,限定检查范围 | ### CLI 示例 ```bash # 检查所有任务的截止时间 python -m cli.main --tasks CHECK_CUTOFF --pg-dsn "$PG_DSN" --store-id "$STORE_ID" # 只检查指定任务 python -m cli.main --tasks CHECK_CUTOFF --pg-dsn "$PG_DSN" --store-id "$STORE_ID" \ --extra run.cutoff_task_codes=ORDERS,PAYMENTS,MEMBERS ``` --- ## 8. DATA_INTEGRITY_CHECK — 数据完整性校验 | 属性 | 值 | |------|-----| | 任务代码 | `DATA_INTEGRITY_CHECK` | | Python 类 | `tasks.utility.data_integrity_task.DataIntegrityTask` | | 继承 | `BaseTask` | | 注册 task_type | `verification`(非 utility,但在本文档中一并说明) | | 用途 | 检查 API → ODS → DWD 全链路数据完整性,支持自动回填缺失数据 | ### 两种运行模式 #### 1. 历史模式(`history`,默认) 从指定起始日期到结束日期,按月分段检查全量历史数据的完整性。 ``` execute() [mode=history] ├── 解析 history_start / history_end 时间范围 ├── 调用 run_history_flow() │ ├── 按月分段执行完整性检查 │ ├── 对比 API 记录数 vs ODS 记录数 │ ├── [可选] 对比内容一致性(content_hash) │ └── [可选] 自动回填缺失数据 └── 生成 JSON 报表 ``` #### 2. 窗口模式(`window`) 检查指定时间窗口内的数据完整性,当提供 CLI 窗口覆盖参数时自动切换到此模式。 ``` execute() [mode=window] ├── 获取时间窗口(支持 CLI 覆盖) ├── 构建窗口分段(build_window_segments) ├── 调用 run_window_flow() │ ├── 逐段执行完整性检查 │ ├── 汇总缺失/不一致/错误计数 │ └── [可选] 自动回填 + 复查 └── 生成 JSON 报表 ``` ### 校验逻辑 核心校验由 `quality/integrity_service.py` 和 `quality/integrity_checker.py` 实现: 1. **记录数对比**:API 返回的记录数 vs ODS 表中的记录数 2. **内容一致性**(可选):抽样对比 API 记录与 ODS 记录的 `content_hash` 3. **缺失检测**:识别 API 中存在但 ODS 中缺失的记录 4. **不一致检测**:识别 API 与 ODS 中内容不匹配的记录 ### 自动回填 当 `auto_backfill=True` 时,检测到缺失或不一致数据后会自动触发回填: 1. 调用 `scripts/repair/backfill_missing_data.run_backfill()` 重新抓取缺失数据 2. 回填完成后可选复查(`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 示例 ```bash # 历史全量检查(默认从 2025-07-01 至今) python -m cli.main --tasks DATA_INTEGRITY_CHECK --pg-dsn "$PG_DSN" \ --store-id "$STORE_ID" --api-token "$API_TOKEN" # 指定时间范围 python -m cli.main --tasks DATA_INTEGRITY_CHECK --pg-dsn "$PG_DSN" \ --store-id "$STORE_ID" --api-token "$API_TOKEN" \ --extra integrity.history_start=2026-01-01 --extra integrity.history_end=2026-02-01 # 窗口模式 + 自动回填 python -m cli.main --tasks DATA_INTEGRITY_CHECK --pg-dsn "$PG_DSN" \ --store-id "$STORE_ID" --api-token "$API_TOKEN" \ --window-start "2026-02-01" --window-end "2026-02-15" \ --extra integrity.auto_backfill=true ```