在前后端开发联调前 的提交20260223

This commit is contained in:
Neo
2026-02-23 23:02:20 +08:00
parent 254ccb1e77
commit fafc95e64c
1142 changed files with 10366960 additions and 36957 deletions

View File

@@ -0,0 +1,156 @@
# 需求文档SPI 消费力指数Spending Power Index
## 简介
SPISpending Power Index消费力指数是 NeoZQYY 指数体系的新增客户级指数用于评估会员在门店场景内的综合消费力层级。SPI 基于消费水平Level、消费速度Speed、消费稳定性Stability三个子分加权合成与现有 NCI/WBI/RS/OS/MS/ML 指数协同使用,为运营人员提供客户分层和资源分配依据。
## 术语表
- **SPI_Task**`SpendingPowerIndexTask`,负责计算 SPI 指数的 ETL 任务
- **BaseIndexTask**指数算法任务基类提供展示分映射Winsorize → 压缩 → MinMax 0-10 → EWMA 平滑)
- **cfg_index_parameters**`dws.cfg_index_parameters` 表,按 `index_type` + `param_name` 存储指数算法参数
- **dws_member_spending_power_index**SPI 指数结果表,存储会员级消费力评分
- **Raw_Score**:原始评分,由算法直接计算得出的未归一化分数
- **Display_Score**展示分Raw_Score 经 P5/P95 Winsorize → 可选压缩 → MinMax 映射到 [0, 10] 的归一化分数
- **Level_Sub_Score**:消费水平子分,衡量客户消费金额层级与客单水平
- **Speed_Sub_Score**:消费速度子分,衡量近期消费推进速度与节奏变化
- **Stability_Sub_Score**:消费稳定性子分,衡量消费行为的时间覆盖稳定性
- **Winsorize**:分位截断,将值限制在 [P5, P95] 范围内以消除极端值影响
- **EWMA**指数加权移动平均Exponential Weighted Moving Average用于平滑分位点避免批次间波动
- **log1p**`ln(1 + x)` 压缩变换,用于处理长尾分布
- **settle_type**结算类型1=台桌结账3=商城订单5=充值订单
- **task_registry**`orchestration/task_registry.py`ETL 任务注册表
- **delete-before-insert**:按门店全量刷新策略,先删除该门店所有旧记录再插入新记录
## 需求
### 需求 1SPI 结果表创建
**用户故事:** 作为 ETL 开发者,我需要创建 SPI 指数结果表,以便存储会员级消费力评分及中间特征。
#### 验收标准
1. THE SPI_Task SHALL 将结果写入 `dws.dws_member_spending_power_index` 表,主键为 `(site_id, member_id)`
2. THE dws_member_spending_power_index 表 SHALL 包含基础特征字段:`spend_30``spend_90``recharge_90``orders_30``orders_90``visit_days_30``visit_days_90``avg_ticket_90``active_weeks_90``daily_spend_ewma_90`
3. THE dws_member_spending_power_index 表 SHALL 包含子分字段:`score_level_raw``score_level_display``score_speed_raw``score_speed_display``score_stability_raw``score_stability_display`
4. THE dws_member_spending_power_index 表 SHALL 包含总分字段:`raw_score`SPI 原始分)和 `display_score`SPI 展示分numeric(5,2)
5. THE dws_member_spending_power_index 表 SHALL 包含元数据字段:`calc_time``created_at``updated_at`
6. THE 开发者 SHALL 编写迁移脚本 `db/etl_feiqiu/migrations/<日期>_create_dws_member_spending_power_index.sql`,在测试库 test_etl_feiqiu 中执行建表
7. WHEN DDL 在测试库执行成功后THE 开发者 SHALL 运行 `python scripts/ops/gen_consolidated_ddl.py` 从测试库导出最新 DDL自动合并到 `docs/database/ddl/etl_feiqiu__dws.sql`
### 需求 2SPI 基础特征提取
**用户故事:** 作为 ETL 开发者,我需要从 DWD 层提取会员消费和充值数据,以便计算 SPI 所需的基础特征。
#### 验收标准
1. THE SPI_Task SHALL 从 `dwd.dwd_settlement_head` 提取近 90 天消费订单settle_type IN (1, 3)),聚合为客户级特征
2. THE SPI_Task SHALL 从 `dwd.dwd_recharge_order` 提取近 90 天充值订单settle_type = 5聚合为客户级充值特征
3. THE SPI_Task SHALL 计算以下基础特征:`spend_30`近30天消费总额`spend_90`近90天消费总额`recharge_90`近90天充值总额`orders_30`近30天消费笔数`orders_90`近90天消费笔数`visit_days_30`近30天消费日数按天去重`visit_days_90`近90天消费日数按天去重
4. THE SPI_Task SHALL 计算 `avg_ticket_90 = spend_90 / max(orders_90, 1)`
5. THE SPI_Task SHALL 计算 `active_weeks_90`近90天有消费的自然周数最多13周
6. THE SPI_Task SHALL 对近90天日消费序列计算 EWMA 得到 `daily_spend_ewma_90`,平滑系数从 cfg_index_parameters 读取
### 需求 3Level 子分计算
**用户故事:** 作为 ETL 开发者我需要实现消费水平Level子分算法以便衡量客户的消费金额层级。
#### 验收标准
1. THE SPI_Task SHALL 按以下公式计算 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)`
2. THE SPI_Task SHALL 从 cfg_index_parameters 读取 Level 子分的权重参数(`w_level_spend_30``w_level_spend_90``w_level_ticket_90``w_level_recharge_90`)和金额压缩基数(`amount_base_spend_30``amount_base_spend_90``amount_base_ticket_90``amount_base_recharge_90`
3. WHEN 所有消费和充值金额均为 0 时THE SPI_Task SHALL 将 Level 子分 Raw 设为 0.0
### 需求 4Speed 子分计算
**用户故事:** 作为 ETL 开发者我需要实现消费速度Speed子分算法以便衡量客户近期消费推进速度。
#### 验收标准
1. THE SPI_Task SHALL 计算绝对速度:`V_abs = ln(1 + spend_30 / (max(visit_days_30, 1) × V0))`
2. THE SPI_Task SHALL 计算相对速度:`V_rel = ln((v_30 + ε) / (v_90 + ε))`,其中 `v_30 = spend_30 / 30``v_90 = spend_90 / 90``ε` 为防除零小量
3. THE SPI_Task SHALL 计算 EWMA 速度:`V_ewma = ln(1 + daily_spend_ewma_90 / E0)`
4. THE SPI_Task SHALL 按以下公式合成 Speed 子分:`S = w_abs × V_abs + w_rel × max(0, V_rel) + w_ewma × V_ewma`
5. THE SPI_Task SHALL 仅对加速(`V_rel > 0`)加分,不对减速直接扣分(通过 `max(0, V_rel)` 实现)
### 需求 5Stability 子分计算
**用户故事:** 作为 ETL 开发者我需要实现消费稳定性Stability子分算法以便识别稳定高消费与偶发冲高。
#### 验收标准
1. THE SPI_Task SHALL 使用近 90 天数据计算稳定性,窗口固定为 90 天
2. THE SPI_Task SHALL 按周覆盖率计算稳定性:`P = active_weeks_90 / 13`近90天共约13个自然周
3. WHEN cfg_index_parameters 中 `use_stability = 0`THE SPI_Task SHALL 将 Stability 子分权重视为 0跳过稳定性计算
4. THE Stability_Sub_Score SHALL 的取值范围为 [0, 1]
### 需求 6SPI 总分合成与展示分映射
**用户故事:** 作为 ETL 开发者,我需要将三个子分加权合成 SPI 总分并映射为展示分,以便业务人员直观理解客户消费力层级。
#### 验收标准
1. THE SPI_Task SHALL 按以下公式计算 SPI 总分:`SPI_raw = w_L × L + w_S × S + w_P × P`,默认权重 `w_L=0.60``w_S=0.30``w_P=0.10`
2. THE SPI_Task SHALL 复用 BaseIndexTask 的 `batch_normalize_to_display` 方法将 Raw_Score 映射为 Display_Score0-10 分)
3. THE SPI_Task SHALL 对 Level、Speed、Stability 三个子分分别独立映射为展示分0-10 分)
4. THE SPI_Task SHALL 支持通过 cfg_index_parameters 配置压缩模式(`compression_mode`0=无压缩1=log1p2=asinh
5. THE SPI_Task SHALL 支持通过 cfg_index_parameters 配置 EWMA 分位平滑(`use_smoothing``ewma_alpha`
6. THE Display_Score SHALL 保留 2 位小数,取值范围为 [0.00, 10.00]
### 需求 7SPI 配置参数管理
**用户故事:** 作为 ETL 开发者,我需要在 cfg_index_parameters 中注册 SPI 的全部默认参数,以便算法参数可配置、可追溯。
#### 验收标准
1. THE 种子数据脚本 SHALL 在 cfg_index_parameters 中插入 `index_type='SPI'` 的全部参数,包括窗口参数、金额压缩基数、子分权重、总分权重、映射与平滑参数
2. THE SPI_Task SHALL 通过 BaseIndexTask 的 `load_index_parameters(index_type='SPI')` 加载参数
3. IF cfg_index_parameters 中缺少某个 SPI 参数THEN THE SPI_Task SHALL 使用 DEFAULT_PARAMS 字典中定义的默认值
4. THE 种子数据脚本 SHALL 追加到 `db/etl_feiqiu/seeds/seed_index_parameters.sql`
### 需求 8金额压缩基数校准
**用户故事:** 作为 ETL 开发者,我需要提供金额压缩基数的校准机制,以便各门店的 SPI 评分能适配不同的消费水平分布。
#### 验收标准
1. THE SPI_Task SHALL 在首次执行或参数缺失时,支持从门店历史数据自动计算金额压缩基数的建议值
2. THE SPI_Task SHALL 以近 90 天消费数据的中位数作为各金额压缩基数的默认校准值:`amount_base_spend_30` 取近30天消费中位数、`amount_base_spend_90` 取近90天消费中位数、`amount_base_ticket_90` 取90天客单中位数、`amount_base_recharge_90` 取90天充值中位数、`amount_base_speed_abs` 取每消费日平均消费中位数、`amount_base_ewma_90` 取日消费 EWMA 中位数
3. IF cfg_index_parameters 中已存在对应的金额压缩基数参数THEN THE SPI_Task SHALL 优先使用配置表中的值而非自动校准值
4. THE SPI_Task SHALL 在日志中输出实际使用的金额压缩基数值,便于运营人员审查和手动调优
5. THE 种子数据脚本 SHALL 为金额压缩基数提供合理的初始默认值(基于典型台球门店消费水平)
### 需求 9SPI 任务注册与执行
**用户故事:** 作为 ETL 开发者,我需要将 SPI 任务注册到 task_registry 并实现完整的执行流程,以便通过调度器触发计算。
#### 验收标准
1. THE SPI_Task SHALL 以任务代码 `DWS_SPENDING_POWER_INDEX` 注册到 task_registry`layer="INDEX"``requires_db_config=False`
2. THE SPI_Task SHALL 声明依赖 `depends_on=["DWS_MEMBER_CONSUMPTION"]`
3. THE SPI_Task SHALL 采用 delete-before-insert 策略:先按 `site_id` 删除旧记录,再批量插入新记录
4. WHEN 门店无任何消费或充值数据时THE SPI_Task SHALL 返回 `{'status': 'skipped', 'reason': 'no_data'}` 并跳过计算
5. THE SPI_Task SHALL 在执行完成后保存分位点历史到 `dws_index_percentile_history`index_type='SPI'
### 需求 10SPI 算法正确性测试
**用户故事:** 作为 ETL 开发者我需要通过属性测试hypothesis验证 SPI 算法的正确性,以便确保计算逻辑符合 PRD 定义。
#### 验收标准
1. THE 属性测试 SHALL 验证:对于任意非负消费/充值金额SPI_raw 为非负值
2. THE 属性测试 SHALL 验证:在其他条件不变时,增加 spend_30 或 spend_90 不会导致 Level 子分下降(单调性)
3. THE 属性测试 SHALL 验证:在其他条件不变时,增加 spend_30 不会导致 Speed 子分下降(单调性)
4. THE 属性测试 SHALL 验证Stability 子分取值范围为 [0, 1]
5. THE 属性测试 SHALL 验证Display_Score 取值范围为 [0.00, 10.00]
6. THE 属性测试 SHALL 验证SPI 总分权重 `w_L + w_S + w_P` 之和为 1.0(权重归一化)
### 需求 11文档更新
**用户故事:** 作为 ETL 开发者,我需要更新相关文档,以便团队成员了解 SPI 的表结构、算法逻辑和使用方式。
#### 验收标准
1. THE 开发者 SHALL 编写数据库手册文档 `docs/database/BD_Manual_dws_member_spending_power_index.md`,包含表结构、字段说明、索引、验证 SQL
2. THE 开发者 SHALL 更新 ETL 任务文档 `apps/etl/connectors/feiqiu/docs/etl_tasks/index_tasks.md`,新增 SPI 任务章节
3. THE 文档 SHALL 包含 SPI 算法公式、参数清单、数据来源、计算流程说明