# 实现计划：SPI 消费力指数（Spending Power Index）

## 概述

基于设计文档，将 SPI 指数建设拆分为 DDL 建表 → 核心算法实现 → 任务注册与执行流程 → 配置种子数据 → 属性测试 → 文档更新 六个阶段，每个阶段增量构建并可验证。

## 任务

- [x] 1. 创建 DDL 与数据库表
  - [x] 1.1 编写迁移脚本 `db/etl_feiqiu/migrations/<日期>_create_dws_member_spending_power_index.sql`
    - 创建 `dws.dws_member_spending_power_index` 表（含序列、唯一索引、查询索引）
    - 字段定义参照设计文档数据模型章节
    - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5_
  - [x] 1.2 在测试库 test_etl_feiqiu 执行迁移脚本建表
    - 通过 TEST_DB_DSN 连接测试库执行 SQL
    - _Requirements: 1.6_
  - [x] 1.3 运行 `gen_consolidated_ddl.py` 从测试库导出 DDL 合并到主 DDL
    - 执行 `python scripts/ops/gen_consolidated_ddl.py`，验证 `docs/database/ddl/etl_feiqiu__dws.sql` 已包含新表
    - _Requirements: 1.7_

- [x] 2. 实现 SPI 核心算法（纯函数）
  - [x] 2.1 创建 `SPIMemberFeatures` 数据类和 `SpendingPowerIndexTask` 骨架
    - 新建 `apps/etl/connectors/feiqiu/tasks/dws/index/spending_power_index_task.py`
    - 定义 `SPIMemberFeatures` dataclass
    - 定义 `SpendingPowerIndexTask` 类继承 `BaseIndexTask`，包含 `INDEX_TYPE`、`DEFAULT_PARAMS`、抽象方法实现
    - _Requirements: 7.2, 7.3, 9.1_
  - [x] 2.2 实现 `compute_level` 静态方法
    - Level 子分公式：`L = w_s30 × ln(1 + spend_30/M30) + w_s90 × ln(1 + spend_90/M90) + w_ticket × ln(1 + avg_ticket_90/T0) + w_r90 × ln(1 + recharge_90/R90)`
    - 全零输入返回 0.0
    - _Requirements: 3.1, 3.2, 3.3_
  - [x] 2.3 实现 `compute_speed` 静态方法
    - 绝对速度 V_abs、相对速度 V_rel（仅加速加分）、EWMA 速度 V_ewma
    - Speed 子分公式：`S = w_abs × V_abs + w_rel × max(0, V_rel) + w_ewma × V_ewma`
    - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5_
  - [x] 2.4 实现 `compute_stability` 静态方法
    - 周覆盖率：`P = active_weeks_90 / 13`
    - 支持 `use_stability=0` 时返回 0.0
    - _Requirements: 5.1, 5.2, 5.3, 5.4_
  - [x] 2.5 实现 `compute_spi_raw` 静态方法
    - 总分公式：`SPI_raw = w_L × L + w_S × S + w_P × P`
    - _Requirements: 6.1_
  - [x] 2.6 编写属性测试：SPI 总分非负性
    - **Property 1: SPI 总分非负性**
    - **Validates: Requirements 6.1, 10.1**
  - [x] 2.7 编写属性测试：Level 子分单调性
    - **Property 2: Level 子分关于消费金额单调非递减**
    - **Validates: Requirements 3.1, 10.2**
  - [x] 2.8 编写属性测试：Speed 子分单调性
    - **Property 3: Speed 子分关于 spend_30 单调非递减**
    - **Validates: Requirements 4.1, 4.4, 10.3**
  - [x] 2.9 编写属性测试：Stability 子分范围
    - **Property 4: Stability 子分取值范围 [0, 1]**
    - **Validates: Requirements 5.2, 5.4, 10.4**
  - [x] 2.10 编写属性测试：Display Score 范围
    - **Property 5: Display Score 取值范围 [0, 10]**
    - **Validates: Requirements 6.6, 10.5**

- [x] 3. 检查点 - 确保核心算法测试通过
  - 运行 `cd C:\Project\NeoZQYY && pytest tests/test_spi_properties.py -v`
  - 确保所有属性测试通过，如有问题请询问用户。

- [x] 4. 实现数据提取与执行流程
  - [x] 4.1 实现 `_extract_spending_features` 方法
    - 从 `dwd.dwd_settlement_head` 提取近 90 天消费订单，聚合为会员级特征
    - 计算 spend_30/90、orders_30/90、visit_days_30/90、avg_ticket_90、active_weeks_90
    - _Requirements: 2.1, 2.3, 2.4, 2.5_
  - [x] 4.2 实现 `_extract_recharge_features` 方法
    - 从 `dwd.dwd_recharge_order` 提取近 90 天充值订单
    - _Requirements: 2.2_
  - [x] 4.3 实现 `_compute_daily_spend_ewma` 方法
    - 对近 90 天日消费序列计算 EWMA
    - _Requirements: 2.6_
  - [x] 4.4 实现 `_calibrate_amount_bases` 方法
    - 从门店数据计算中位数作为金额压缩基数校准值
    - 配置表优先级高于自动校准值
    - 日志输出实际使用的基数值
    - _Requirements: 8.1, 8.2, 8.3, 8.4_
  - [x] 4.5 实现 `execute` 方法（完整执行流程）
    - 获取 site_id → 加载参数 → 提取特征 → 校准基数 → 计算子分 → 合成总分 → 归一化展示分 → 持久化
    - delete-before-insert 策略
    - 无数据时返回 skipped
    - 保存分位点历史
    - _Requirements: 6.2, 6.3, 6.4, 6.5, 6.6, 9.3, 9.4, 9.5_
  - [x] 4.6 实现 `_save_spi_data` 方法
    - 批量 INSERT 到 dws_member_spending_power_index
    - _Requirements: 1.1_

- [x] 5. 任务注册与模块导出
  - [x] 5.1 在 `tasks/dws/index/__init__.py` 中导出 `SpendingPowerIndexTask`
    - 添加 import 和 __all__ 条目
    - _Requirements: 9.1_
  - [x] 5.2 在 `tasks/dws/__init__.py` 中导出 `SpendingPowerIndexTask`
    - 添加 import 和 __all__ 条目
    - _Requirements: 9.1_
  - [x] 5.3 在 `orchestration/task_registry.py` 中注册 SPI 任务
    - `default_registry.register("DWS_SPENDING_POWER_INDEX", SpendingPowerIndexTask, requires_db_config=False, layer="INDEX", depends_on=["DWS_MEMBER_CONSUMPTION"])`
    - _Requirements: 9.1, 9.2_

- [x] 6. 配置种子数据
  - [x] 6.1 在 `db/etl_feiqiu/seeds/seed_index_parameters.sql` 中追加 SPI 参数
    - 插入 `index_type='SPI'` 的全部参数行（窗口、基数、权重、映射、稳定性）
    - 金额压缩基数使用合理初始默认值
    - _Requirements: 7.1, 7.4, 8.5_
  - [x] 6.2 在测试库执行种子数据脚本
    - 通过 TEST_DB_DSN 连接测试库执行 INSERT
    - _Requirements: 7.1_

- [x] 7. 检查点 - 确保单元测试和集成验证通过
  - 运行 `cd apps/etl/connectors/feiqiu && pytest tests/unit/test_spi_task.py -v`
  - 确保所有测试通过，如有问题请询问用户。

- [x] 8. 文档更新
  - [x] 8.1 编写数据库手册 `docs/database/BD_Manual_dws_member_spending_power_index.md`
    - 包含表结构、字段说明、索引、验证 SQL（至少 3 条）、兼容性说明、回滚策略
    - _Requirements: 11.1_
  - [x] 8.2 更新 ETL 任务文档 `apps/etl/connectors/feiqiu/docs/etl_tasks/index_tasks.md`
    - 新增 DWS_SPENDING_POWER_INDEX 章节，包含算法公式、参数清单、数据来源、计算流程
    - 更新概述表格和继承体系图
    - _Requirements: 11.2, 11.3_

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

## 备注

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