# 设计文档:ETL 任务说明文档 ## 概述 本设计描述如何为飞球 ETL 系统生成一套完整的任务说明文档,放置于 `docs/etl_tasks/` 目录下。文档以 Markdown 格式编写,按数据层(ODS / DWD / DWS / INDEX / Utility)分文件组织,并提供一个总览 README 作为入口。 文档的目标读者是开发者和运维人员,需要覆盖: - 每个任务的代码标识、Python 类、数据来源与目标 - Extract / Transform / Load 各阶段的处理逻辑 - CLI 参数与管道执行方式 - BaseTask 公共机制 ## 架构 文档为纯静态 Markdown 文件,不涉及运行时代码变更。整体结构如下: ``` docs/etl_tasks/ ├── README.md # 总览:任务清单 + 跳转链接 + 执行方式 ├── ods_tasks.md # ODS 层任务详解 ├── dwd_tasks.md # DWD 层任务详解 ├── dws_tasks.md # DWS 层任务详解 ├── index_tasks.md # INDEX 层任务详解 ├── utility_tasks.md # 工具类任务详解 └── base_task_mechanism.md # BaseTask 公共机制与执行参数 ``` ```mermaid graph TD README["README.md
总览入口"] --> ODS["ods_tasks.md"] README --> DWD["dwd_tasks.md"] README --> DWS["dws_tasks.md"] README --> IDX["index_tasks.md"] README --> UTL["utility_tasks.md"] README --> BASE["base_task_mechanism.md"] ``` ## 组件与接口 本 spec 不涉及代码组件。产出物为 7 个 Markdown 文件,各文件的内容范围如下: ### README.md(总览) | 章节 | 内容 | |------|------| | 系统简介 | 飞球 ETL 系统概述、数据流向(API → ODS → DWD → DWS) | | 任务清单 | 按层分组的表格:任务代码、Python 类、简要说明、跳转链接 | | 管道类型 | 7 种管道(api_ods / api_ods_dwd / api_full / ods_dwd / dwd_dws / dwd_dws_index / dwd_index)的层组合 | | 处理模式 | increment_only / verify_only / increment_verify 的区别 | | 数据源模式 | online / offline / hybrid 的区别 | | CLI 参数速查 | 所有 CLI 参数的表格(参数名、类型、默认值、说明) | | 常见命令示例 | 典型使用场景的命令行示例 | ### ods_tasks.md(ODS 层) 每个 ODS 任务一个小节,包含: - 任务代码与 Python 类 - API 端点与请求参数 - 字段解析逻辑(transform 阶段) - 目标 ODS 表与写入策略 - 特殊说明(如分页、去重、content_hash 等) 需区分两种 ODS 任务模式: 1. 独立任务类(如 OrdersTask、MembersTask):继承 BaseTask,有独立的 E/T/L 实现 2. 通用 ODS 任务(由 `ods_tasks.py` 中 OdsTaskSpec + BaseOdsTask 动态生成):通过声明式配置定义端点、列映射等 已注册的 ODS 任务(14 个独立 + N 个通用): | 任务代码 | Python 类 | API 端点 | 目标表 | |----------|-----------|----------|--------| | ORDERS | OrdersTask | /Site/GetAllOrderSettleList | billiards_ods.fact_order | | PAYMENTS | PaymentsTask | 支付相关端点 | billiards_ods.fact_payment | | MEMBERS | MembersTask | /MemberProfile/GetTenantMemberList | billiards_ods.dim_member | | PRODUCTS | ProductsTask | 商品相关端点 | billiards_ods 商品表 | | TABLES | TablesTask | 台桌相关端点 | billiards_ods 台桌表 | | ASSISTANTS | AssistantsTask | 助教相关端点 | billiards_ods 助教表 | | PACKAGES_DEF | PackagesDefTask | 套餐相关端点 | billiards_ods 套餐表 | | REFUNDS | RefundsTask | 退款相关端点 | billiards_ods 退款表 | | COUPON_USAGE | CouponUsageTask | 优惠券相关端点 | billiards_ods 优惠券表 | | INVENTORY_CHANGE | InventoryChangeTask | 库存变动端点 | billiards_ods 库存表 | | TOPUPS | TopupsTask | 充值相关端点 | billiards_ods 充值表 | | TABLE_DISCOUNT | TableDiscountTask | 台费折扣端点 | billiards_ods 折扣表 | | ASSISTANT_ABOLISH | AssistantAbolishTask | 助教取消端点 | billiards_ods 取消表 | | LEDGER | LedgerTask | 台账端点 | billiards_ods 台账表 | 通用 ODS 任务由 `ODS_TASK_CLASSES` 字典动态注册,每个任务通过 `OdsTaskSpec` 声明: - `endpoint`:API 端点路径 - `table`:目标 ODS 表名 - `columns`:列定义列表(ColumnSpec) - `page_size`、`data_path`、`list_key`:分页参数 - `pk_columns`:主键列 - `snapshot_mode`:快照模式(content_hash 去重) ### dwd_tasks.md(DWD 层) DWD 层有 5 个已注册任务: | 任务代码 | Python 类 | 说明 | |----------|-----------|------| | DWD_LOAD_FROM_ODS | DwdLoadTask | 核心装载任务:遍历 TABLE_MAP,维度走 SCD2,事实走增量 | | TICKET_DWD | TicketDwdTask | 结账小票明细 → fact_order / fact_order_goods / fact_table_usage / fact_assistant_service | | PAYMENTS_DWD | PaymentsDwdTask | ODS 支付记录 → fact_payment | | MEMBERS_DWD | MembersDwdTask | ODS 会员记录 → dim_member | | DWD_QUALITY_CHECK | DwdQualityTask | ODS 与 DWD 行数/金额核对,输出 JSON 报表 | 核心任务 DWD_LOAD_FROM_ODS 的处理逻辑: - TABLE_MAP 定义了 40+ 对 DWD→ODS 表映射 - 维度表(dim_*):检测 SCD2 列是否存在,有则执行 SCD2 合并(关闭旧版+插入新版),无则执行 Type1 Upsert - 事实表(dwd_*、fact_*):按 fetched_at 水位线增量插入,支持 upsert 或 insert-only - FACT_MAPPINGS 定义了列名映射(ODS 驼峰命名 → DWD 下划线命名) - 每张表独立事务,单表失败不影响后续表 SCD2 处理流程: 1. 从 ODS 取最新快照(DISTINCT ON 按业务主键 + fetched_at DESC) 2. 与 DWD 当前版本(scd2_is_current=1)逐列对比 3. 有变更:关闭旧版(scd2_end_time=now, scd2_is_current=0)+ 插入新版(version+1) 4. 无变更:跳过 ### dws_tasks.md(DWS 层) DWS 层有 15 个已注册任务,按业务域分组: **助教业绩域:** | 任务代码 | Python 类 | 目标表 | 粒度 | |----------|-----------|--------|------| | DWS_ASSISTANT_DAILY | AssistantDailyTask | dws_assistant_daily_detail | 日期+助教 | | DWS_ASSISTANT_MONTHLY | AssistantMonthlyTask | dws_assistant_monthly_summary | 月份+助教 | | DWS_ASSISTANT_CUSTOMER | AssistantCustomerTask | dws_assistant_customer_stats | 日期+助教+会员 | | DWS_ASSISTANT_SALARY | AssistantSalaryTask | dws_assistant_salary_calc | 月份+助教 | | DWS_ASSISTANT_FINANCE | AssistantFinanceTask | dws_assistant_finance_analysis | 日期+助教 | **会员分析域:** | 任务代码 | Python 类 | 目标表 | 粒度 | |----------|-----------|--------|------| | DWS_MEMBER_CONSUMPTION | MemberConsumptionTask | dws_member_consumption_summary | 日期+会员 | | DWS_MEMBER_VISIT | MemberVisitTask | dws_member_visit_detail | 日期+会员+结账单 | **财务统计域:** | 任务代码 | Python 类 | 目标表 | 粒度 | |----------|-----------|--------|------| | DWS_FINANCE_DAILY | FinanceDailyTask | dws_finance_daily_summary | 日期 | | DWS_FINANCE_RECHARGE | FinanceRechargeTask | dws_finance_recharge_summary | 日期 | | DWS_FINANCE_INCOME_STRUCTURE | FinanceIncomeStructureTask | dws_finance_income_structure | 日期+收入类型 | | DWS_FINANCE_DISCOUNT_DETAIL | FinanceDiscountDetailTask | dws_finance_discount_detail | 日期+折扣类型 | **运维任务:** | 任务代码 | Python 类 | 说明 | |----------|-----------|------| | DWS_BUILD_ORDER_SUMMARY | DwsBuildOrderSummaryTask | 构建订单汇总中间表 | | DWS_RETENTION_CLEANUP | DwsRetentionCleanupTask | 按时间分层清理历史数据 | | DWS_MV_REFRESH_FINANCE_DAILY | DwsMvRefreshFinanceDailyTask | 刷新财务日报物化视图 | | DWS_MV_REFRESH_ASSISTANT_DAILY | DwsMvRefreshAssistantDailyTask | 刷新助教日报物化视图 | 所有 DWS 任务继承 BaseDwsTask,共享以下机制: - 时间分层范围计算(TimeLayer: LAST_2_DAYS / LAST_1_MONTH / LAST_3_MONTHS / LAST_6_MONTHS / ALL) - 配置缓存(ConfigCache):业绩档位、等级价格、奖金规则、区域分类、技能类型 - delete-before-insert 更新策略(按日期范围先删后插,保证幂等) - bulk_insert / upsert 写入方法 ### index_tasks.md(INDEX 层) INDEX 层有 4 个已注册任务: | 任务代码 | Python 类 | 目标表 | 指数类型 | |----------|-----------|--------|----------| | DWS_WINBACK_INDEX | WinbackIndexTask | dws_member_winback_index | WBI(回流指数) | | DWS_NEWCONV_INDEX | NewconvIndexTask | dws_member_newconv_index | NCI(新客转化指数) | | DWS_RELATION_INDEX | RelationIndexTask | dws_relation_index | RS(关系指数) | | DWS_ML_MANUAL_IMPORT | MlManualImportTask | dws_ml_manual_ledger | ML(手动台账导入) | 所有指数任务继承 BaseIndexTask,共享: - 参数从 `billiards_dws.cfg_index_parameters` 表加载 - 百分位历史记录(PercentileHistory) - 标准化的指数计算流程 ### utility_tasks.md(工具类) | 任务代码 | Python 类 | 用途 | |----------|-----------|------| | INIT_ODS_SCHEMA | InitOdsSchemaTask | 执行 ODS + etl_admin DDL,创建必要目录 | | INIT_DWD_SCHEMA | InitDwdSchemaTask | 执行 DWD DDL | | INIT_DWS_SCHEMA | InitDwsSchemaTask | 执行 DWS DDL | | MANUAL_INGEST | ManualIngestTask | 从本地 JSON 文件手动入库到 ODS | | ODS_JSON_ARCHIVE | OdsJsonArchiveTask | 归档 ODS JSON 文件 | | CHECK_CUTOFF | CheckCutoffTask | 检查数据截止时间 | | SEED_DWS_CONFIG | SeedDwsConfigTask | 初始化 DWS 配置种子数据 | | DATA_INTEGRITY_CHECK | DataIntegrityTask | 数据完整性校验 | ### base_task_mechanism.md(公共机制) 覆盖内容: - BaseTask 模板方法流程(execute → build_context → [分段] → extract → transform → load → commit) - TaskContext 字段说明 - 时间窗口计算逻辑(优先级:手动覆盖 > 游标 > 闲忙时段默认值) - 窗口分段(build_window_segments) - TaskRegistry 注册方式与元数据(TaskMeta: task_class, requires_db_config, layer, task_type) - PipelineRunner 管道执行流程 - 校验框架(Verifier)概述 ## 数据模型 本 spec 不涉及数据模型变更。文档中引用的数据模型均为现有系统中的表结构,包括: **ODS 层表**(billiards_ods schema): - settlement_records, table_fee_transactions, assistant_service_records, member_profiles, payment_transactions, refund_transactions 等 20+ 表 **DWD 层表**(billiards_dwd schema): - 维度表:dim_site, dim_table, dim_assistant, dim_member, dim_member_card_account, dim_tenant_goods, dim_store_goods, dim_goods_category, dim_groupbuy_package(各含 _ex 扩展表) - 事实表:dwd_settlement_head, dwd_table_fee_log, dwd_table_fee_adjust, dwd_store_goods_sale, dwd_assistant_service_log, dwd_assistant_trash_event, dwd_member_balance_change, dwd_groupbuy_redemption, dwd_platform_coupon_redemption, dwd_recharge_order, dwd_payment, dwd_refund(各含 _ex 扩展表) **DWS 层表**(billiards_dws schema): - 助教域:dws_assistant_daily_detail, dws_assistant_monthly_summary, dws_assistant_customer_stats, dws_assistant_salary_calc, dws_assistant_finance_analysis - 会员域:dws_member_consumption_summary, dws_member_visit_detail - 财务域:dws_finance_daily_summary, dws_finance_recharge_summary, dws_finance_income_structure, dws_finance_discount_detail - 指数域:dws_member_winback_index, dws_member_newconv_index, dws_relation_index, dws_ml_manual_ledger - 配置表:cfg_index_parameters, cfg_skill_type, cfg_performance_tier, cfg_level_price, cfg_bonus_rule, cfg_area_category ## 正确性属性 *正确性属性是一种在系统所有有效执行中都应成立的特征或行为——本质上是关于系统应该做什么的形式化陈述。属性是人类可读规范与机器可验证正确性保证之间的桥梁。* 由于本 spec 的产出物是文档(Markdown 文件),而非运行时代码,正确性属性主要关注文档的完整性和一致性——即文档是否覆盖了所有已注册任务。 Property 1: ODS 任务文档覆盖完整性 *对于所有*在 `task_registry.py` 中注册且 layer="ODS" 的任务代码,`ods_tasks.md` 中应包含该任务代码的说明章节,并列出其目标表。 **Validates: Requirements 2.1, 2.4** Property 2: DWD 任务文档覆盖完整性 *对于所有*在 `task_registry.py` 中注册且 layer="DWD" 的任务代码,`dwd_tasks.md` 中应包含该任务代码的说明章节,并列出其源表和目标表。 **Validates: Requirements 3.1** Property 3: DWS 任务文档覆盖完整性 *对于所有*在 `task_registry.py` 中注册且 layer="DWS" 的任务代码,`dws_tasks.md` 中应包含该任务代码的说明章节,并标注其更新策略。 **Validates: Requirements 4.1, 4.4** Property 4: INDEX 任务文档覆盖完整性 *对于所有*在 `task_registry.py` 中注册且 layer="INDEX" 的任务代码,`index_tasks.md` 中应包含该任务代码的说明章节。 **Validates: Requirements 5.1** Property 5: Utility 任务文档覆盖完整性 *对于所有*在 `task_registry.py` 中注册且 task_type="utility" 的任务代码,`utility_tasks.md` 中应包含该任务代码的说明章节。 **Validates: Requirements 6.1** Property 6: CLI 参数文档覆盖完整性 *对于所有*在 `cli/main.py` 的 `parse_args()` 中定义的 CLI 参数,`README.md` 或 `base_task_mechanism.md` 中应包含该参数的说明。 **Validates: Requirements 7.1** Property 7: 管道类型文档覆盖完整性 *对于所有*在 `PipelineRunner.PIPELINE_LAYERS` 中定义的管道类型,`README.md` 中应包含该管道类型的层组合说明。 **Validates: Requirements 7.2** ## 错误处理 本 spec 为文档生成任务,不涉及运行时错误处理。文档编写过程中需注意: 1. 若源代码中的任务类或注册信息发生变更,文档可能过时——应在 README.md 中注明"最后更新日期"和"基于代码版本" 2. 若某个任务的 API 端点或参数无法从代码中直接读取(如动态配置),应在文档中标注"参见配置文件" ## 测试策略 **单元测试(示例验证):** - 验证所有 7 个 Markdown 文件存在于 `docs/etl_tasks/` 目录下 - 验证 README.md 包含指向其他 6 个文件的链接 - 验证每个分层文件中包含对应层的所有已注册任务代码 **属性测试(覆盖完整性验证):** - 使用 pytest 编写脚本,从 `task_registry.py` 动态读取已注册任务列表 - 解析对应的 Markdown 文件,检查每个任务代码是否出现在文档中 - 从 `cli/main.py` 解析 CLI 参数列表,检查文档中是否覆盖 - 属性测试库:pytest(本项目已使用),配合 parametrize 实现参数化验证 - 每个属性测试标注对应的设计属性编号 **测试标注格式:** ```python # Feature: etl-task-documentation, Property 1: ODS 任务文档覆盖完整性 def test_ods_task_coverage(): ... ``` 由于本 spec 的产出物是静态文档,属性测试的核心价值在于确保文档与代码的一致性,防止文档遗漏任务。测试应在文档生成后运行一次即可,无需持续集成。