Files
Neo-ZQYY/apps/etl/pipelines/feiqiu/docs/etl_tasks/ods_tasks.md

14 KiB
Raw Blame History

ODS 层任务详解

本文档说明飞球 ETL 系统中 ODS操作数据存储层的所有任务。 ODS 层负责从上游 SaaS API 抽取原始业务数据并落地到 PostgreSQLbilliards_ods schema保留源 payload 便于回溯。


概述

ODS 层采用声明式配置驱动的通用任务模式:由 BaseOdsTask + OdsTaskSpec 配置驱动,通过 ODS_TASK_CLASSES 字典动态注册,共 23 个任务。

所有 ODS 任务写入 billiards_ods.* 表,原始 API 响应以 JSON 格式存入 payload 列,元数据列(fetched_atsource_filecontent_hash 等)自动填充。

历史说明:早期版本曾有 14 个独立 ODS 任务ORDERS、PAYMENTS、MEMBERS 等),写入不存在的 billiards.* schema。 这些任务已于 2026-02-14 废弃删除,全部由下述通用 ODS 任务替代。

任务总览

任务代码 动态类名 API 端点 目标 ODS 表 说明
ODS_ASSISTANT_ACCOUNT OdsAssistantAccountsTask /PersonnelManagement/SearchAssistantInfo assistant_accounts_master 助教账号档案
ODS_SETTLEMENT_RECORDS OdsOrderSettleTask /Site/GetAllOrderSettleList settlement_records 结账记录
ODS_TABLE_USE OdsTableUseTask /Site/GetSiteTableOrderDetails table_fee_transactions 台费计费流水
ODS_ASSISTANT_LEDGER OdsAssistantLedgerTask /AssistantPerformance/GetOrderAssistantDetails assistant_service_records 助教服务流水
ODS_ASSISTANT_ABOLISH OdsAssistantAbolishTask /AssistantPerformance/GetAbolitionAssistant assistant_cancellation_records 助教废除记录
ODS_STORE_GOODS_SALES OdsGoodsLedgerTask /TenantGoods/GetGoodsSalesList store_goods_sales_records 门店商品销售流水
ODS_PAYMENT OdsPaymentTask /PayLog/GetPayLogListPage payment_transactions 支付流水
ODS_REFUND OdsRefundTask /Order/GetRefundPayLogList refund_transactions 退款流水
ODS_PLATFORM_COUPON OdsCouponVerifyTask /Promotion/GetOfflineCouponConsumePageList platform_coupon_redemption_records 平台/团购券核销
ODS_MEMBER OdsMemberTask /MemberProfile/GetTenantMemberList member_profiles 会员档案
ODS_MEMBER_CARD OdsMemberCardTask /MemberProfile/GetTenantMemberCardList member_stored_value_cards 会员储值卡
ODS_MEMBER_BALANCE OdsMemberBalanceTask /MemberProfile/GetMemberCardBalanceChange member_balance_changes 会员余额变动
ODS_RECHARGE_SETTLE OdsRechargeSettleTask /Site/GetRechargeSettleList recharge_settlements 充值结算
ODS_GROUP_PACKAGE OdsPackageTask /PackageCoupon/QueryPackageCouponList group_buy_packages 团购套餐定义
ODS_GROUP_BUY_REDEMPTION OdsGroupBuyRedemptionTask /Site/GetSiteTableUseDetails group_buy_redemption_records 团购套餐核销
ODS_INVENTORY_STOCK OdsInventoryStockTask /TenantGoods/GetGoodsStockReport goods_stock_summary 库存汇总
ODS_INVENTORY_CHANGE OdsInventoryChangeTask /GoodsStockManage/QueryGoodsOutboundReceipt goods_stock_movements 库存变化记录
ODS_TABLES OdsTablesTask /Table/GetSiteTables site_tables_master 台桌维表
ODS_GOODS_CATEGORY OdsGoodsCategoryTask /TenantGoodsCategory/QueryPrimarySecondaryCategory stock_goods_category_tree 库存商品分类树
ODS_STORE_GOODS OdsStoreGoodsTask /TenantGoods/GetGoodsInventoryList store_goods_master 门店商品档案
ODS_TABLE_FEE_DISCOUNT OdsTableDiscountTask /Site/GetTaiFeeAdjustList table_fee_discount_records 台费折扣/调账
ODS_TENANT_GOODS OdsTenantGoodsTask /TenantGoods/QueryTenantGoods tenant_goods_master 租户商品档案
ODS_SETTLEMENT_TICKET OdsSettlementTicketTask /Order/GetOrderSettleTicketNew settlement_ticket_details 结账小票详情

所有目标表均位于 billiards_ods schema 下。


通用 ODS 任务架构BaseOdsTask + OdsTaskSpec 模式)

通用 ODS 任务采用声明式配置驱动:开发者只需定义一个 OdsTaskSpec 数据类实例,由 BaseOdsTask 提供统一的 execute() 流程,再通过 _build_task_class() 工厂函数动态生成 Python 类,最终在 ODS_TASK_CLASSES 字典中注册。

核心优势:

  • 零代码新增任务:只需添加一条 OdsTaskSpec 配置即可接入新的 API 端点
  • Schema-aware 写入:运行时从 information_schema 读取目标表结构,自动匹配列名和类型,无需手写字段映射
  • 统一去重与冲突处理:通过 content_hashON CONFLICT 策略保证幂等性

OdsTaskSpec 配置结构

OdsTaskSpec 是一个不可变数据类(@dataclass(frozen=True)),定义了单个 ODS 任务的全部配置。

核心字段

字段 类型 说明
code str 任务代码,如 ODS_PAYMENT,用于注册和调度
class_name str 动态生成的 Python 类名,如 OdsPaymentTask
table_name str 目标 ODS 表全限定名,如 billiards_ods.payment_transactions
endpoint str 上游 API 端点路径,如 /PayLog/GetPayLogListPage
description str 任务描述(中文),用于日志和文档

分页与数据提取字段

字段 类型 默认值 说明
data_path Tuple[str, ...] ("data",) API 响应中数据的 JSON 路径,逐层深入
list_key str | None None 数据列表在 data_path 下的键名(如 "settleList"),为 None 时直接取 data_path 下的列表

主键与列定义字段

字段 类型 默认值 说明
pk_columns Tuple[ColumnSpec, ...] () 业务主键列定义,用于冲突检测(通常为 id
extra_columns Tuple[ColumnSpec, ...] () 额外列定义,用于从嵌套 JSON 中提取特定字段
conflict_columns_override Tuple[str, ...] | None None 覆盖默认冲突列(默认使用表的 PRIMARY KEY

时间窗口字段

字段 类型 默认值 说明
requires_window bool True 是否需要时间窗口参数(事实表为 True,维度快照表为 False
time_fields Tuple[str, str] | None ("startTime", "endTime") API 请求中时间窗口参数的键名对
include_site_id bool True 是否在请求中传 siteId 参数
extra_params Dict[str, Any] {} 额外的固定请求参数

快照与软删除字段

字段 类型 默认值 说明
snapshot_full_table bool False 全表快照模式API 返回全量数据,不在返回集中的记录标记为已删除
snapshot_window_columns Tuple[str, ...] | None None 窗口快照模式:指定用于限定软删除范围的时间列

快照模式说明:当 snapshot_full_table=Truesnapshot_window_columns 非空时,任务会在每个分段结束后调用 _mark_missing_as_deleted(),将 API 未返回但数据库中存在的记录的 is_delete 标记为 1。此行为还需配合运行时配置 run.snapshot_missing_delete=True 才会生效。

元数据控制字段

字段 类型 默认值 说明
include_source_file bool True 是否写入 source_file
include_source_endpoint bool True 是否写入 source_endpoint
include_fetched_at bool True 是否写入 fetched_at
include_record_index bool False 是否写入 record_index
include_site_column bool True 是否写入 site_id / store_id

ColumnSpec 列映射定义

ColumnSpec 是不可变数据类,定义单个列的映射规则:

字段 类型 说明
column str 目标数据库列名
sources Tuple[str, ...] 源 JSON 字段名列表,按优先级回退(支持点号路径)
required bool 是否为必填字段(缺失时跳过整条记录)
default Any 默认值(源字段全部为空时使用)
transform Callable | None 类型转换函数

代码中提供了三个快捷构造函数:

函数 用途 transform
_int_col(name, *sources) 整数列 TypeParser.parse_int
_decimal_col(name, *sources) 金额列(保留 2 位小数) TypeParser.parse_decimal(v, 2)
_bool_col(name, *sources) 布尔列 自定义 _to_bool

BaseOdsTask 通用 execute 流程

所有通用 ODS 任务共享 BaseOdsTask.execute() 方法,流程如下:

execute(cursor_data)
│
├── 1. 解析时间窗口_resolve_window
│   ├── 优先级:用户手动覆盖 > 游标 + MAX(fetched_at) 兜底 > 默认窗口
│   └── 若游标推进但表未实际入库,回退到 MAX(fetched_at) 作为起点
│
├── 2. 窗口分段build_window_segments
│   └── 按闲忙时段或配置拆分为多个子窗口
│
├── 3. 准备运行参数
│   ├── 读取 store_id、page_size
│   ├── 解析快照模式配置
│   ├── 获取表主键列_get_table_pk_columns
│   └── 检查表是否有 is_delete 列
│
├── 4. 逐段执行for seg_start, seg_end in segments
│   ├── 4a. 构建 API 请求参数_build_params
│   ├── 4b. 分页抓取api.iter_paginated→ _insert_records_schema_aware 写入
│   ├── 4c. 软删除标记(若快照模式启用)
│   └── 4d. 提交事务db.commit
│
├── 5. 汇总结果
│   └── 返回 {status, counts, window, segments, request_params}
│
└── 异常处理
    └── db.rollback + 记录错误日志 + 重新抛出

Schema-aware 写入(_insert_records_schema_aware

核心写入方法,运行时动态适配表结构:

  1. 读取表结构:从 information_schema.columns 获取目标表的所有列名、数据类型

  2. 读取主键:从 information_schema.table_constraints 获取 PRIMARY KEY 列

  3. 记录合并_merge_record_layers() 将嵌套 JSON 展平为单层字典

  4. is_delete 标准化:统一为 0/1

  5. content_hash 计算:对记录内容计算 SHA-256 哈希

  6. content_hash 去重:与数据库中同一业务主键的最新 content_hash 比对,相同则跳过

  7. 值映射:逐列匹配,特殊列(payloadsource_filefetched_atcontent_hash)自动填充

  8. 冲突处理:根据 run.ods_conflict_mode 配置:

    模式 SQL 行为 说明
    update ON CONFLICT ... DO UPDATE SET ... WHERE IS DISTINCT FROM 全字段对比,仅在有变化时更新
    backfill ON CONFLICT ... DO UPDATE SET COALESCE(existing, new) WHERE ... IS NULL 仅回填 NULL 列
    nothing ON CONFLICT ... DO NOTHING 跳过已存在记录
  9. 批量写入:使用 psycopg2.extras.execute_values 分块写入,通过 RETURNING (xmax = 0) 区分插入和更新

软删除标记(_mark_missing_as_deleted

当快照模式启用时,任务在每个分段结束后执行软删除:

  • 全表快照snapshot_full_table=True):将数据库中所有 is_delete != 1 且不在本次 API 返回集中的记录标记为 is_delete=1
  • 窗口快照snapshot_window_columns 非空):仅在指定时间列的窗口范围内执行软删除

content_hash 去重机制

content_hash 是通用 ODS 任务的核心去重手段:

  1. 计算:排除元数据字段后,对剩余字段按 key 排序后 JSON 序列化,计算 SHA-256 哈希
  2. 比对:从数据库中按业务主键取最新一条记录的 content_hash
  3. 跳过:若新记录的 content_hash 与数据库中最新记录相同,则跳过写入

仅在目标表包含 content_hash 列且有 fetched_at 列时生效。


各任务详细配置

任务代码 需要窗口 快照模式 特殊说明
ODS_ASSISTANT_ACCOUNT 全表快照 助教账号档案,全量抓取后标记离职/删除
ODS_SETTLEMENT_RECORDS 结账记录,按时间窗口增量抓取
ODS_TABLE_USE 窗口(create_time 台费计费流水
ODS_ASSISTANT_LEDGER 窗口(create_time 助教服务流水
ODS_ASSISTANT_ABOLISH 助教废除记录
ODS_STORE_GOODS_SALES 窗口(create_time 门店商品销售流水
ODS_PAYMENT 支付流水
ODS_REFUND 窗口(pay_time 退款流水
ODS_PLATFORM_COUPON 窗口(consume_time 平台/团购券核销
ODS_MEMBER 会员档案
ODS_MEMBER_CARD 全表快照 会员储值卡
ODS_MEMBER_BALANCE 窗口(create_time 会员余额变动
ODS_RECHARGE_SETTLE 充值结算
ODS_GROUP_PACKAGE 全表快照 团购套餐定义
ODS_GROUP_BUY_REDEMPTION 窗口(create_time 团购套餐核销
ODS_INVENTORY_STOCK 库存汇总
ODS_INVENTORY_CHANGE 库存变化记录
ODS_TABLES 台桌维表
ODS_GOODS_CATEGORY 库存商品分类树
ODS_STORE_GOODS 全表快照 门店商品档案
ODS_TABLE_FEE_DISCOUNT 窗口(create_time 台费折扣/调账
ODS_TENANT_GOODS 全表快照 租户商品档案
ODS_SETTLEMENT_TICKET 结账小票详情(专用实现,见下文)

特殊任务ODS_SETTLEMENT_TICKET 虽然在 ODS_TASK_SPECS 中声明,但其 ODS_TASK_CLASSES 条目被 OdsSettlementTicketTask 专用实现覆盖。该任务不走标准分页抓取流程,而是先从 payment_transactions 表或支付 API 收集 orderSettleId,再逐个调用小票接口获取详情。