# 实现计划：ETL DWS 层扩展 — 小程序数据支撑

## 概述

基于设计文档，将实现拆分为：DDL 建表/扩展 → 助教订单流水统计任务 → 会员消费汇总扩展 → 定档折算惩罚 → RLS 视图 + FDW 映射 → 影子跑数验证 六个阶段。所有数据库操作在测试库（`test_etl_feiqiu` / `test_zqyy_app`）中进行。

## 任务

- [x] 1. DDL 建表与字段扩展
  - [x] 1.1 编写迁移脚本创建 `dws.dws_assistant_order_contribution` 表
    - 新建 `db/etl_feiqiu/migrations/<日期>__create_dws_assistant_order_contribution.sql`
    - 包含表定义、唯一索引 `idx_aoc_site_assistant_date`、查询索引 `idx_aoc_stat_date`
    - 字段参照设计文档数据模型章节
    - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5_
  - [x] 1.2 编写迁移脚本扩展 `dws_member_consumption_summary` 字段
    - 新建 `db/etl_feiqiu/migrations/<日期>__alter_member_consumption_add_recharge_fields.sql`
    - ALTER TABLE 添加 `recharge_count_30d/60d/90d`、`recharge_amount_30d/60d/90d`、`avg_ticket_amount`
    - _Requirements: 3.1, 3.2_
  - [x] 1.3 编写迁移脚本扩展 `dws_assistant_daily_detail` 字段
    - 新建 `db/etl_feiqiu/migrations/<日期>__alter_assistant_daily_add_penalty_fields.sql`
    - ALTER TABLE 添加 `penalty_minutes`、`penalty_reason`、`is_exempt`、`per_hour_contribution`
    - _Requirements: 5.1, 5.2_
  - [x] 1.4 在测试库 `test_etl_feiqiu` 执行全部迁移脚本
    - 通过 `PG_DSN`（指向测试库）连接执行 SQL
    - _Requirements: 1.5, 3.2, 5.2_
  - [x] 1.5 运行 `gen_consolidated_ddl.py` 导出最新 DDL
    - 执行 `python scripts/ops/gen_consolidated_ddl.py`
    - 验证 `docs/database/ddl/etl_feiqiu__dws.sql` 已包含新表和扩展字段
    - _Requirements: 1.6_

- [x] 2. 实现助教订单流水统计任务
  - [x] 2.1 创建数据结构和 `AssistantOrderContributionTask` 骨架
    - 新建 `apps/etl/connectors/feiqiu/tasks/dws/assistant_order_contribution_task.py`
    - 定义 `TableUsage`、`AssistantService`、`OrderData` dataclass
    - 定义 `AssistantOrderContributionTask` 类继承 `BaseDwsTask`
    - 实现 `get_task_code`、`get_target_table`、`get_primary_keys`
    - _Requirements: 1.1, 2.7_
  - [x] 2.2 实现四项统计核心计算（纯函数）
    - 实现 `compute_order_gross_revenue` 静态方法
    - 实现 `compute_order_net_revenue` 静态方法
    - 实现 `compute_time_weighted_revenue` 静态方法（含台费分摊、酒水均分逻辑）
    - 实现 `compute_time_weighted_net_revenue` 静态方法
    - 处理超休/打赏课特殊情况
    - _Requirements: 2.2, 2.3, 2.4, 2.5, 2.6_
  - [x] 2.3 编写属性测试：订单级统计不变量
    - **Property 1: 订单级统计不变量 — gross/net 各助教相等**
    - **Validates: Requirements 2.2, 2.3, 10.1, 10.2**
  - [x] 2.4 编写属性测试：时效贡献流水之和约束
    - **Property 2: 时效贡献流水之和 ≤ order_gross_revenue**
    - **Validates: Requirements 2.4, 10.3**
  - [x] 2.5 编写属性测试：时效净贡献减法关系
    - **Property 3: time_weighted_net_revenue = time_weighted_revenue - commission**
    - **Validates: Requirements 2.5, 10.4**
  - [x] 2.6 实现 `extract` 方法
    - 从 `dwd_settlement_head`、`dwd_table_fee_log`、`dwd_assistant_service_log` 提取数据
    - 按 `order_settle_id` 聚合为 `OrderData` 结构
    - _Requirements: 2.1_
  - [x] 2.7 实现 `transform` 方法
    - 遍历订单，调用四项统计计算函数
    - 按 `(assistant_id, stat_date)` 聚合日度统计
    - _Requirements: 2.2, 2.3, 2.4, 2.5, 2.6_
  - [x] 2.8 注册任务到 task_registry 并导出模块
    - 在 `tasks/dws/__init__.py` 中导出 `AssistantOrderContributionTask`
    - 在 `orchestration/task_registry.py` 中注册 `DWS_ASSISTANT_ORDER_CONTRIBUTION`，`layer="DWS"`，`depends_on=["DWD_LOAD_FROM_ODS"]`
    - _Requirements: 2.7, 2.8_

- [x] 3. 检查点 — 确保助教订单流水统计测试通过
  - 运行属性测试：`cd C:\Project\NeoZQYY && pytest tests/test_dws_contribution_properties.py -v`
  - 确保所有属性测试通过，如有问题请询问用户。

- [x] 4. 扩展会员消费汇总任务
  - [x] 4.1 在 `MemberConsumptionTask` 中新增充值统计提取
    - 新增 `_extract_recharge_stats` 方法，从 `dwd.dwd_recharge_order` 按 30/60/90 天窗口聚合
    - 在 `extract` 方法中调用并返回充值统计数据
    - _Requirements: 4.1, 3.3_
  - [x] 4.2 在 `MemberConsumptionTask.transform` 中填充新字段
    - 合并充值统计到输出记录
    - 计算 `avg_ticket_amount = total_consume_amount / MAX(total_visit_count, 1)`
    - 处理无充值/无消费的边界情况
    - _Requirements: 4.2, 4.3, 4.4, 3.4_
  - [x] 4.3 编写属性测试：次均消费公式
    - **Property 5: avg_ticket_amount = total_consume_amount / MAX(total_visit_count, 1)**
    - **Validates: Requirements 3.4, 10.7**

- [x] 5. 实现定档折算惩罚检测与计算
  - [x] 5.1 实现时间重叠检测逻辑
    - 在 `AssistantDailyTask` 中新增 `detect_overlap_violations` 静态方法
    - 定义惩罚区域集合（大厅 A/B/C/S/TV + 麻将房 M1–M7）
    - 按 `(table_id, service_date)` 分组，检测时间段重叠且助教数 > 2
    - _Requirements: 6.1_
  - [x] 5.2 实现惩罚分钟数计算
    - 在 `AssistantDailyTask` 中新增 `compute_penalty_minutes` 静态方法
    - 计算 `per_hour_contribution = 台费每小时单价 / 助教人数`
    - 按分段公式计算 `penalty_minutes`
    - 处理 `is_exempt = TRUE` 豁免逻辑
    - _Requirements: 6.2, 6.3, 6.4, 6.5_
  - [x] 5.3 集成惩罚逻辑到 `AssistantDailyTask.transform`
    - 在现有聚合逻辑后调用重叠检测和惩罚计算
    - 将 `penalty_minutes`、`penalty_reason`、`is_exempt`、`per_hour_contribution` 填充到输出记录
    - _Requirements: 6.6, 6.7_
  - [x] 5.4 编写属性测试：惩罚分钟数分段公式
    - **Property 4: 惩罚分钟数符合分段公式且在 [0, actual_minutes] 范围内**
    - **Validates: Requirements 6.3, 6.4, 10.5, 10.6**
  - [x] 5.5 编写属性测试：重叠检测正确性
    - **Property 6: 3+ 助教时间重叠时应检测到违规**
    - **Validates: Requirements 6.1**

- [x] 6. 检查点 — 确保惩罚计算和消费汇总测试通过
  - 运行属性测试：`cd C:\Project\NeoZQYY && pytest tests/test_dws_contribution_properties.py -v`
  - 运行单元测试：`cd apps/etl/connectors/feiqiu && pytest tests/unit/ -k "contribution or penalty or consumption" -v`
  - 确保所有测试通过，如有问题请询问用户。

- [x] 7. RLS 视图与 FDW 映射
  - [x] 7.1 创建 `dws_assistant_order_contribution` 的 RLS 视图
    - 在测试库 `test_etl_feiqiu` 的 `app` schema 中创建 `v_dws_assistant_order_contribution` 视图
    - 使用 `current_setting('app.current_site_id')::bigint` 过滤
    - 授予 `app_reader` 角色 SELECT 权限
    - _Requirements: 7.1, 7.3_
  - [x] 7.2 验证已有 RLS 视图自动包含新增字段
    - 确认 `app.v_dws_member_consumption_summary` 和 `app.v_dws_assistant_daily_detail` 使用 `SELECT *`，新增字段自动包含
    - _Requirements: 7.2_
  - [x] 7.3 创建/更新 FDW 外部表映射
    - 在测试库 `test_zqyy_app` 的 `fdw_etl` schema 中创建 `dws_assistant_order_contribution` 外部表
    - 重建 `dws_member_consumption_summary` 和 `dws_assistant_daily_detail` 的 FDW 外部表以包含新字段
    - FDW 映射通过 `app` schema RLS 视图访问
    - _Requirements: 8.1, 8.2, 8.3_

- [x] 8. 影子跑数验证
  - [x] 8.1 编写验证脚本
    - 新建 `apps/etl/connectors/feiqiu/scripts/verify_dws_extensions.py`
    - 验证四项统计：对照 PRD 示例数据验算，检查 gross/net 各助教相等
    - 验证充值窗口：检查新增字段有值，充值金额与源数据一致
    - 验证惩罚字段：检查符合条件的记录正确填充
    - _Requirements: 9.1, 9.2, 9.3, 9.4_
  - [x] 8.2 编写数据库手册文档
    - 新建 `docs/database/BD_Manual_dws_assistant_order_contribution.md`
    - 包含表结构、字段说明、索引、验证 SQL（至少 3 条）、兼容性说明、回滚策略
    - 更新 `docs/database/` 中 `dws_member_consumption_summary` 和 `dws_assistant_daily_detail` 的文档
    - _Requirements: 1.1_

- [x] 9. 最终检查点 — 确保所有测试通过
  - 运行属性测试：`cd C:\Project\NeoZQYY && pytest tests/test_dws_contribution_properties.py -v`    
  - 运行单元测试：`cd apps/etl/connectors/feiqiu && pytest tests/unit/ -k "contribution or penalty or consumption" -v`
  - 确保所有测试通过，如有问题请询问用户。

## 备注

- 标记 `*` 的子任务为可选（属性测试），可跳过以加速 MVP
- 每个任务引用具体需求编号以确保可追溯
- 所有数据库操作在测试库（`test_etl_feiqiu` / `test_zqyy_app`）中进行
- 检查点确保增量验证
- 属性测试验证全称正确性属性，单元测试验证具体示例和边界情况
