feat(etl): cfg_* 视图入口统一 + NULL 兼容 + 3 处 _load_* 历史 Bug (W1-T2 / P0-1)
Wave 1 Day 3 沙箱配置参数切片。 DB 迁移 (20260504__cfg_views_null_compatible.sql): - 4 个 v_cfg_* 视图 WHERE 加 NULL 兼容 (effective_to IS NULL OR effective_to >= ...) - 顺手清理 v_cfg_assistant_level_price / v_cfg_performance_tier WHERE 重复一次的 bug - 测试库已执行,4 校验 PASS ETL 代码 (4 处 SQL): - base_index_task.py: FROM dws.cfg_index_parameters -> app.v_cfg_index_parameters - base_dws_task.py 3 处 _load_* (performance_tier / level_price / bonus_rules) 改 FROM app.v_cfg_*,顺手修历史 Bug:原 SQL 不带 effective_from/to WHERE 效果: - 修复视图 NULL 行被过滤问题(SPI 27 行原本读到 0) - 沙箱模式回放历史日期时,参数自动按 sandbox_date 切片 - _load_* 直接得到当前生效行(原 12/9 行历史 Python 挑) 参考: - docs/audit/changes/2026-05-04__wave1_t2_scd2_view_unify.md - docs/database/changes/2026-05-04__cfg_views_null_compatible.md
This commit is contained in:
@@ -533,48 +533,60 @@ class BaseDwsTask(BaseTask):
|
|||||||
def _load_performance_tiers(self) -> List[Dict[str, Any]]:
|
def _load_performance_tiers(self) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
加载绩效档位配置
|
加载绩效档位配置
|
||||||
|
|
||||||
字段说明(来自DWS数据库处理需求.md):
|
字段说明(来自DWS数据库处理需求.md):
|
||||||
- base_deduction: 专业课抽成(元/小时),球房从基础课每小时扣除的金额
|
- base_deduction: 专业课抽成(元/小时),球房从基础课每小时扣除的金额
|
||||||
- bonus_deduction_ratio: 打赏课抽成比例,球房从附加课收入中扣除的比例
|
- bonus_deduction_ratio: 打赏课抽成比例,球房从附加课收入中扣除的比例
|
||||||
- vacation_days: 次月可休假天数
|
- vacation_days: 次月可休假天数
|
||||||
- vacation_unlimited: 休假自由标记(最高档为TRUE)
|
- vacation_unlimited: 休假自由标记(最高档为TRUE)
|
||||||
|
|
||||||
|
Wave 1 W1-T2(2026-05-04):走 app.v_cfg_performance_tier 视图,
|
||||||
|
视图按 app.business_date_now() 切片,沙箱模式下自动取 sandbox_date 当时生效行。
|
||||||
|
修复历史 Bug:原 SQL 不带 effective_from/to WHERE,把全部历史区间拉给 Python 挑。
|
||||||
"""
|
"""
|
||||||
sql = """
|
sql = """
|
||||||
SELECT
|
SELECT
|
||||||
tier_id, tier_code, tier_name, tier_level,
|
tier_id, tier_code, tier_name, tier_level,
|
||||||
min_hours, max_hours,
|
min_hours, max_hours,
|
||||||
base_deduction, bonus_deduction_ratio,
|
base_deduction, bonus_deduction_ratio,
|
||||||
vacation_days, vacation_unlimited,
|
vacation_days, vacation_unlimited,
|
||||||
is_new_hire_tier, effective_from, effective_to
|
is_new_hire_tier, effective_from, effective_to
|
||||||
FROM dws.cfg_performance_tier
|
FROM app.v_cfg_performance_tier
|
||||||
ORDER BY tier_level ASC, effective_from ASC
|
ORDER BY tier_level ASC, effective_from ASC
|
||||||
"""
|
"""
|
||||||
rows = self.db.query(sql)
|
rows = self.db.query(sql)
|
||||||
return [dict(row) for row in rows] if rows else []
|
return [dict(row) for row in rows] if rows else []
|
||||||
|
|
||||||
def _load_level_prices(self) -> List[Dict[str, Any]]:
|
def _load_level_prices(self) -> List[Dict[str, Any]]:
|
||||||
"""加载等级定价配置"""
|
"""加载等级定价配置
|
||||||
|
|
||||||
|
Wave 1 W1-T2(2026-05-04):走 app.v_cfg_assistant_level_price 视图,
|
||||||
|
修复历史 Bug:原 SQL 不带 effective_from/to WHERE。
|
||||||
|
"""
|
||||||
sql = """
|
sql = """
|
||||||
SELECT
|
SELECT
|
||||||
price_id, level_code, level_name,
|
price_id, level_code, level_name,
|
||||||
base_course_price, bonus_course_price,
|
base_course_price, bonus_course_price,
|
||||||
effective_from, effective_to
|
effective_from, effective_to
|
||||||
FROM dws.cfg_assistant_level_price
|
FROM app.v_cfg_assistant_level_price
|
||||||
ORDER BY level_code ASC, effective_from DESC
|
ORDER BY level_code ASC, effective_from DESC
|
||||||
"""
|
"""
|
||||||
rows = self.db.query(sql)
|
rows = self.db.query(sql)
|
||||||
return [dict(row) for row in rows] if rows else []
|
return [dict(row) for row in rows] if rows else []
|
||||||
|
|
||||||
def _load_bonus_rules(self) -> List[Dict[str, Any]]:
|
def _load_bonus_rules(self) -> List[Dict[str, Any]]:
|
||||||
"""加载奖金规则配置"""
|
"""加载奖金规则配置
|
||||||
|
|
||||||
|
Wave 1 W1-T2(2026-05-04):走 app.v_cfg_bonus_rules 视图,
|
||||||
|
修复历史 Bug:原 SQL 不带 effective_from/to WHERE。
|
||||||
|
"""
|
||||||
sql = """
|
sql = """
|
||||||
SELECT
|
SELECT
|
||||||
rule_id, rule_type, rule_code, rule_name,
|
rule_id, rule_type, rule_code, rule_name,
|
||||||
threshold_hours, rank_position, bonus_amount,
|
threshold_hours, rank_position, bonus_amount,
|
||||||
is_cumulative, priority,
|
is_cumulative, priority,
|
||||||
effective_from, effective_to
|
effective_from, effective_to
|
||||||
FROM dws.cfg_bonus_rules
|
FROM app.v_cfg_bonus_rules
|
||||||
ORDER BY rule_type, priority DESC, effective_from DESC
|
ORDER BY rule_type, priority DESC, effective_from DESC
|
||||||
"""
|
"""
|
||||||
rows = self.db.query(sql)
|
rows = self.db.query(sql)
|
||||||
|
|||||||
@@ -332,12 +332,13 @@ class BaseIndexTask(BaseDwsTask):
|
|||||||
|
|
||||||
self.logger.debug("加载指数算法参数: %s", index_type)
|
self.logger.debug("加载指数算法参数: %s", index_type)
|
||||||
|
|
||||||
|
# Wave 1 W1-T2(2026-05-04):走 app.v_cfg_index_parameters 视图,
|
||||||
|
# 视图按 app.business_date_now() 切片(GUC app.current_business_date 决定),
|
||||||
|
# 沙箱模式下自动读取 sandbox_date 当时生效的参数版本。
|
||||||
sql = """
|
sql = """
|
||||||
SELECT param_name, param_value
|
SELECT param_name, param_value
|
||||||
FROM dws.cfg_index_parameters
|
FROM app.v_cfg_index_parameters
|
||||||
WHERE index_type = %s
|
WHERE index_type = %s
|
||||||
AND effective_from <= CURRENT_DATE
|
|
||||||
AND (effective_to IS NULL OR effective_to >= CURRENT_DATE)
|
|
||||||
ORDER BY effective_from DESC
|
ORDER BY effective_from DESC
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
114
db/etl_feiqiu/migrations/20260504__cfg_views_null_compatible.sql
Normal file
114
db/etl_feiqiu/migrations/20260504__cfg_views_null_compatible.sql
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
-- ===================================================================
|
||||||
|
-- 20260504 — cfg_* 视图 NULL 兼容性 + 清理重复 WHERE bug
|
||||||
|
-- ===================================================================
|
||||||
|
-- 触发:Wave 1 W1-T2(P0-1 沙箱配置参数切片)
|
||||||
|
--
|
||||||
|
-- 问题:
|
||||||
|
-- 1. 20260502 迁移产出的 4 个 v_cfg_* 视图 WHERE 写为
|
||||||
|
-- effective_to >= app.business_date_now()
|
||||||
|
-- effective_to 是 nullable(NULL 表示"至今仍生效"),NULL 行被过滤,
|
||||||
|
-- cfg_index_parameters 27 行参数中 effective_to IS NULL 的全部丢失。
|
||||||
|
-- 2. v_cfg_assistant_level_price 与 v_cfg_performance_tier 的 WHERE
|
||||||
|
-- 条件重复一次(逻辑等价,但代码瑕疵)。
|
||||||
|
--
|
||||||
|
-- 修复:
|
||||||
|
-- ALTER VIEW(实际 CREATE OR REPLACE)4 个视图,WHERE 改为
|
||||||
|
-- effective_from <= app.business_date_now()
|
||||||
|
-- AND (effective_to IS NULL OR effective_to >= app.business_date_now())
|
||||||
|
--
|
||||||
|
-- 影响:
|
||||||
|
-- - ETL 任务通过 app.v_cfg_* 读取时,正确包含 effective_to IS NULL 行。
|
||||||
|
-- - 后端通过 FDW 映射访问 fdw_etl.v_cfg_* 时同步生效(底层视图变更)。
|
||||||
|
-- - 沙箱模式下,app.business_date_now() 返回 sandbox_date,自动切片。
|
||||||
|
--
|
||||||
|
-- 回滚:
|
||||||
|
-- - 重新执行 20260502 迁移的 4 个 CREATE OR REPLACE VIEW 部分即可。
|
||||||
|
--
|
||||||
|
-- 验证 SQL:
|
||||||
|
-- 1. SELECT COUNT(*) FROM app.v_cfg_index_parameters WHERE index_type='SPI';
|
||||||
|
-- 应等于 27(测试库实际值)。
|
||||||
|
-- 2. SELECT COUNT(*) FROM dws.cfg_index_parameters WHERE index_type='SPI'
|
||||||
|
-- AND effective_from <= CURRENT_DATE
|
||||||
|
-- AND (effective_to IS NULL OR effective_to >= CURRENT_DATE);
|
||||||
|
-- 应与 1 的结果相同。
|
||||||
|
-- 3. SET LOCAL app.current_business_date = '2026-03-01';
|
||||||
|
-- SELECT COUNT(*) FROM app.v_cfg_index_parameters WHERE index_type='SPI';
|
||||||
|
-- 切沙箱时间后,行数应等于 2026-03-01 当时生效的参数数量。
|
||||||
|
-- ===================================================================
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
-- ---- v_cfg_assistant_level_price ----
|
||||||
|
CREATE OR REPLACE VIEW app.v_cfg_assistant_level_price AS
|
||||||
|
SELECT price_id,
|
||||||
|
level_code,
|
||||||
|
level_name,
|
||||||
|
base_course_price,
|
||||||
|
bonus_course_price,
|
||||||
|
effective_from,
|
||||||
|
effective_to,
|
||||||
|
description,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM dws.cfg_assistant_level_price
|
||||||
|
WHERE effective_from <= app.business_date_now()
|
||||||
|
AND (effective_to IS NULL OR effective_to >= app.business_date_now());
|
||||||
|
|
||||||
|
-- ---- v_cfg_performance_tier ----
|
||||||
|
CREATE OR REPLACE VIEW app.v_cfg_performance_tier AS
|
||||||
|
SELECT tier_id,
|
||||||
|
tier_code,
|
||||||
|
tier_name,
|
||||||
|
tier_level,
|
||||||
|
min_hours,
|
||||||
|
max_hours,
|
||||||
|
base_deduction,
|
||||||
|
bonus_deduction_ratio,
|
||||||
|
vacation_days,
|
||||||
|
vacation_unlimited,
|
||||||
|
is_new_hire_tier,
|
||||||
|
effective_from,
|
||||||
|
effective_to,
|
||||||
|
description,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM dws.cfg_performance_tier
|
||||||
|
WHERE effective_from <= app.business_date_now()
|
||||||
|
AND (effective_to IS NULL OR effective_to >= app.business_date_now());
|
||||||
|
|
||||||
|
-- ---- v_cfg_bonus_rules ----
|
||||||
|
CREATE OR REPLACE VIEW app.v_cfg_bonus_rules AS
|
||||||
|
SELECT rule_id,
|
||||||
|
rule_type,
|
||||||
|
rule_code,
|
||||||
|
rule_name,
|
||||||
|
threshold_hours,
|
||||||
|
rank_position,
|
||||||
|
bonus_amount,
|
||||||
|
is_cumulative,
|
||||||
|
priority,
|
||||||
|
effective_from,
|
||||||
|
effective_to,
|
||||||
|
description,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM dws.cfg_bonus_rules
|
||||||
|
WHERE effective_from <= app.business_date_now()
|
||||||
|
AND (effective_to IS NULL OR effective_to >= app.business_date_now());
|
||||||
|
|
||||||
|
-- ---- v_cfg_index_parameters ----
|
||||||
|
CREATE OR REPLACE VIEW app.v_cfg_index_parameters AS
|
||||||
|
SELECT param_id,
|
||||||
|
index_type,
|
||||||
|
param_name,
|
||||||
|
param_value,
|
||||||
|
description,
|
||||||
|
effective_from,
|
||||||
|
effective_to,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
FROM dws.cfg_index_parameters
|
||||||
|
WHERE effective_from <= app.business_date_now()
|
||||||
|
AND (effective_to IS NULL OR effective_to >= app.business_date_now());
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
109
docs/audit/changes/2026-05-04__wave1_t2_scd2_view_unify.md
Normal file
109
docs/audit/changes/2026-05-04__wave1_t2_scd2_view_unify.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Wave 1 W1-T2 — SCD2 视图入口统一 + 4 视图 NULL 兼容 + 3 处 _load_*
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 日期 | 2026-05-04 |
|
||||||
|
| Wave | 1 / Day 3 |
|
||||||
|
| 范围 | P0-1 沙箱配置参数切片(SCD2 视图入口统一)+ F-1 副发现 base_dws_task 3 处 _load_* 历史 Bug |
|
||||||
|
| 文件改动 | 1 迁移 + 2 ETL py + 1 db 文档 |
|
||||||
|
| 验证 | 测试库 4 视图行数与裸表等价 WHERE 全部一致 ✅ |
|
||||||
|
|
||||||
|
## 一、改动总览
|
||||||
|
|
||||||
|
### 1. DB 视图修复(迁移)
|
||||||
|
|
||||||
|
**文件**:`db/etl_feiqiu/migrations/20260504__cfg_views_null_compatible.sql`
|
||||||
|
|
||||||
|
修 4 个 `app.v_cfg_*` 视图:
|
||||||
|
- `v_cfg_index_parameters`
|
||||||
|
- `v_cfg_assistant_level_price`
|
||||||
|
- `v_cfg_performance_tier`
|
||||||
|
- `v_cfg_bonus_rules`
|
||||||
|
|
||||||
|
WHERE 从 `effective_to >= app.business_date_now()` 改为
|
||||||
|
`(effective_to IS NULL OR effective_to >= app.business_date_now())`,
|
||||||
|
顺手清理 2 个视图重复 WHERE bug。
|
||||||
|
|
||||||
|
### 2. ETL 代码改造(4 处 SQL)
|
||||||
|
|
||||||
|
| # | 文件 | 改动 |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | `tasks/dws/index/base_index_task.py:335-342` | `FROM dws.cfg_index_parameters` + 显式 effective_from/to WHERE → `FROM app.v_cfg_index_parameters` 移除 WHERE |
|
||||||
|
| 2 | `tasks/dws/base_dws_task.py:543-552` `_load_performance_tiers` | `FROM dws.cfg_performance_tier` → `FROM app.v_cfg_performance_tier`(并修复历史 Bug:原 SQL 不带 effective_from/to WHERE,把全部历史区间拉给 Python 挑) |
|
||||||
|
| 3 | `tasks/dws/base_dws_task.py:556-567` `_load_level_prices` | 同上,`v_cfg_assistant_level_price` |
|
||||||
|
| 4 | `tasks/dws/base_dws_task.py:569-581` `_load_bonus_rules` | 同上,`v_cfg_bonus_rules` |
|
||||||
|
|
||||||
|
## 二、为什么这么改(F-1 调研结论回顾)
|
||||||
|
|
||||||
|
Neo 在 P0-1 反馈提出"沙箱机制下应该有每天的参数快照吗?"。F-1 调研结论:
|
||||||
|
|
||||||
|
- **不需要每日快照**(成本高、冗余)
|
||||||
|
- **SCD2 区间已存在**(4 张 cfg 表已有 effective_from / effective_to)
|
||||||
|
- **核心裂缝**:P20 视图层已加业务日上界,但 ETL 任务**直读 dws.cfg_* 裸表绕过视图** + 视图本身有 NULL 兼容 bug
|
||||||
|
- **修复**:让所有读取入口走视图(0.5 天工作量)
|
||||||
|
|
||||||
|
## 三、行为对照
|
||||||
|
|
||||||
|
| 场景 | 修改前 | 修改后 |
|
||||||
|
|---|---|---|
|
||||||
|
| live 模式查 SPI 参数 | 27 行(原直读裸表带 WHERE)| 27 行(走视图) |
|
||||||
|
| 视图直接查 SPI 参数 | **0 行**(NULL 兼容 Bug)| **27 行** |
|
||||||
|
| sandbox 切到 2026-03-01 查 SPI | 仍是 live 27 行(代码绕过视图)| 27 行(视图按 sandbox_date 切片) |
|
||||||
|
| sandbox 切到历史日期(假设有更早版本) | 取不到历史版本 | 视图按 sandbox_date 取当时生效行 |
|
||||||
|
| `_load_performance_tiers` | 12 行全部历史(Python 挑)| 5 行(当前生效) |
|
||||||
|
| `_load_bonus_rules` | 9 行全部历史 | 3 行(当前生效) |
|
||||||
|
|
||||||
|
## 四、验证(测试库)
|
||||||
|
|
||||||
|
测试库已执行迁移,4 校验全部 PASS:
|
||||||
|
- view SPI count = 27,等价裸表 WHERE = 27 ✅
|
||||||
|
- sandbox 2026-03-01 SPI count = 27 ✅
|
||||||
|
- v_cfg_assistant_level_price: view 5 / equiv 5 / total 5 ✅
|
||||||
|
- v_cfg_performance_tier: view 5 / equiv 5 / total 12 ✅
|
||||||
|
- v_cfg_bonus_rules: view 3 / equiv 3 / total 9 ✅
|
||||||
|
|
||||||
|
详细 SQL 见 [docs/database/changes/2026-05-04__cfg_views_null_compatible.md](../../database/changes/2026-05-04__cfg_views_null_compatible.md) §五。
|
||||||
|
|
||||||
|
## 五、风险与回滚
|
||||||
|
|
||||||
|
| 项 | 风险 | 回滚 |
|
||||||
|
|---|---|---|
|
||||||
|
| 视图 ALTER | 低 — `CREATE OR REPLACE VIEW` 原子操作 | 重新执行 20260502 迁移的 4 视图 CREATE OR REPLACE 部分 |
|
||||||
|
| ETL SQL 改造 | 低 — 视图返回字段 / 类型与裸表 WHERE 等价 | git revert |
|
||||||
|
| 性能 | 视图复杂度未变(只多 1 个 NULL 判断),性能影响可忽略 | — |
|
||||||
|
| 历史 Bug 修复(3 处 _load_*)| **低** — 修复前 Python 挑历史区间是低效但正确,修复后视图直接给当前生效行 | git revert(回到全量挑) |
|
||||||
|
|
||||||
|
## 六、未覆盖 / 后续 Wave
|
||||||
|
|
||||||
|
- 单测覆盖留 Wave 2(`tests/test_cfg_views_sandbox.py`,带 sandbox_date 切片用例)
|
||||||
|
- P20 SPEC §1.4 / §3.5 / §5.6 / AC14-15 / §11.2 / T16-T17 patch 文字落地留 Wave 5(F-1 报告 §六已列出建议)
|
||||||
|
- 生产库执行迁移留 Wave 5(P11 上线门槛达标后由 Neo 手动执行)
|
||||||
|
|
||||||
|
## 七、commit 建议消息
|
||||||
|
|
||||||
|
```
|
||||||
|
feat(etl): cfg_* 视图入口统一 + NULL 兼容 + 3 处 _load_* 历史 Bug (W1-T2 / P0-1)
|
||||||
|
|
||||||
|
Wave 1 Day 3 沙箱配置参数切片。
|
||||||
|
|
||||||
|
DB 迁移 (20260504__cfg_views_null_compatible.sql):
|
||||||
|
- 4 个 v_cfg_* 视图 WHERE 加 NULL 兼容
|
||||||
|
(effective_to IS NULL OR effective_to >= ...)
|
||||||
|
- 顺手清理 v_cfg_assistant_level_price / v_cfg_performance_tier
|
||||||
|
WHERE 重复一次的 bug
|
||||||
|
- 测试库已执行,4 校验 PASS
|
||||||
|
|
||||||
|
ETL 代码 (4 处 SQL):
|
||||||
|
- base_index_task.py: FROM dws.cfg_index_parameters → app.v_cfg_index_parameters
|
||||||
|
- base_dws_task.py 3 处 _load_* (performance_tier / level_price / bonus_rules)
|
||||||
|
改 FROM app.v_cfg_*,顺手修历史 Bug:原 SQL 不带 effective_from/to WHERE
|
||||||
|
|
||||||
|
效果:
|
||||||
|
- 修复视图 NULL 行被过滤问题(SPI 27 行原本读到 0)
|
||||||
|
- 沙箱模式回放历史日期时,参数自动按 sandbox_date 切片
|
||||||
|
- _load_* 直接得到当前生效行(原 12/9 行历史 Python 挑)
|
||||||
|
|
||||||
|
参考:
|
||||||
|
- docs/audit/changes/2026-05-04__wave1_t2_scd2_view_unify.md
|
||||||
|
- docs/database/changes/2026-05-04__cfg_views_null_compatible.md
|
||||||
|
```
|
||||||
105
docs/database/changes/2026-05-04__cfg_views_null_compatible.md
Normal file
105
docs/database/changes/2026-05-04__cfg_views_null_compatible.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# cfg_* 视图 NULL 兼容性 + 重复 WHERE 清理
|
||||||
|
|
||||||
|
| 字段 | 值 |
|
||||||
|
|---|---|
|
||||||
|
| 日期 | 2026-05-04 |
|
||||||
|
| 触发 | Wave 1 W1-T2 (P0-1 沙箱配置参数切片) |
|
||||||
|
| 迁移文件 | `db/etl_feiqiu/migrations/20260504__cfg_views_null_compatible.sql` |
|
||||||
|
| 涉及视图 | `app.v_cfg_index_parameters` / `app.v_cfg_assistant_level_price` / `app.v_cfg_performance_tier` / `app.v_cfg_bonus_rules` |
|
||||||
|
|
||||||
|
## 一、变更说明
|
||||||
|
|
||||||
|
修复 20260502 迁移产出的 4 个 v_cfg_* 视图的两个问题:
|
||||||
|
|
||||||
|
### Bug 1: NULL 兼容性
|
||||||
|
|
||||||
|
原 WHERE 写为:
|
||||||
|
```sql
|
||||||
|
effective_to >= app.business_date_now()
|
||||||
|
```
|
||||||
|
|
||||||
|
`effective_to` 是 nullable(NULL 表示"至今仍生效"),NULL 行被过滤。
|
||||||
|
`cfg_index_parameters` 中 27 行 SPI 参数大量为 `effective_to IS NULL`,通过视图读时**全部被过滤,看不到任何 SPI 参数**。
|
||||||
|
|
||||||
|
### Bug 2: 重复 WHERE
|
||||||
|
|
||||||
|
`v_cfg_assistant_level_price` / `v_cfg_performance_tier` 的 WHERE 子句重复一次(逻辑等价但代码瑕疵):
|
||||||
|
```sql
|
||||||
|
WHERE effective_from <= ... AND effective_to >= ...
|
||||||
|
AND effective_from <= ... AND effective_to >= ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## 二、修复方式
|
||||||
|
|
||||||
|
ALTER VIEW 4 个视图(用 `CREATE OR REPLACE VIEW`),WHERE 改为:
|
||||||
|
```sql
|
||||||
|
WHERE effective_from <= app.business_date_now()
|
||||||
|
AND (effective_to IS NULL OR effective_to >= app.business_date_now())
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、兼容性影响
|
||||||
|
|
||||||
|
### ETL
|
||||||
|
|
||||||
|
`apps/etl/connectors/feiqiu/tasks/dws/index/base_index_task.py` 与
|
||||||
|
`apps/etl/connectors/feiqiu/tasks/dws/base_dws_task.py`(共 4 处 SQL)
|
||||||
|
本轮一并改造为通过 `app.v_cfg_*` 读取参数,享受沙箱切片语义。
|
||||||
|
|
||||||
|
### 后端
|
||||||
|
|
||||||
|
后端通过 FDW 映射访问 `fdw_etl.v_cfg_*` ↔ `app.v_cfg_*`,底层视图修复后**自动生效**。
|
||||||
|
后端 `runtime_context` 服务层会下发 `app.current_business_date` GUC,沙箱模式读取
|
||||||
|
sandbox_date 当时生效的参数版本。
|
||||||
|
|
||||||
|
### 小程序
|
||||||
|
|
||||||
|
无直接影响(小程序通过后端 API 读)。
|
||||||
|
|
||||||
|
## 四、回滚策略
|
||||||
|
|
||||||
|
如发现新视图有问题,回滚到 20260502 版本:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 重新执行 20260502 迁移文件中 L711-782 的 4 个 CREATE OR REPLACE VIEW 部分
|
||||||
|
```
|
||||||
|
|
||||||
|
注意:回滚后 NULL 兼容性问题会重现(SPI 参数读不到)。
|
||||||
|
|
||||||
|
## 五、验证 SQL(测试库已执行 PASS)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 校验 1: 视图 SPI 行数应等于 27(测试库实际值)
|
||||||
|
SELECT COUNT(*) FROM app.v_cfg_index_parameters WHERE index_type = 'SPI';
|
||||||
|
-- 期望: 27 ✅
|
||||||
|
|
||||||
|
-- 校验 2: 裸表带等价 WHERE,应与 1 相同
|
||||||
|
SELECT COUNT(*) FROM dws.cfg_index_parameters
|
||||||
|
WHERE index_type = 'SPI'
|
||||||
|
AND effective_from <= CURRENT_DATE
|
||||||
|
AND (effective_to IS NULL OR effective_to >= CURRENT_DATE);
|
||||||
|
-- 期望: 27 ✅
|
||||||
|
|
||||||
|
-- 校验 3: 切沙箱时间为 2026-03-01,视图应正确切片
|
||||||
|
BEGIN;
|
||||||
|
SET LOCAL app.current_business_date = '2026-03-01';
|
||||||
|
SELECT COUNT(*) FROM app.v_cfg_index_parameters WHERE index_type = 'SPI';
|
||||||
|
-- 期望: 27(2026-03-01 在当前 effective 范围内)✅
|
||||||
|
ROLLBACK;
|
||||||
|
|
||||||
|
-- 校验 4: 其他 3 个视图 vs 裸表等价 WHERE
|
||||||
|
SELECT 'level_price' AS v, COUNT(*) FROM app.v_cfg_assistant_level_price
|
||||||
|
UNION ALL SELECT 'perf_tier', COUNT(*) FROM app.v_cfg_performance_tier
|
||||||
|
UNION ALL SELECT 'bonus_rules', COUNT(*) FROM app.v_cfg_bonus_rules;
|
||||||
|
-- 测试库实测: 5 / 5 / 3 (与等价 WHERE 一致 ✅;裸表全量 5 / 12 / 9)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 六、生产部署
|
||||||
|
|
||||||
|
- 测试库:已应用 ✅(2026-05-04)
|
||||||
|
- 生产库:**待 P11 上线门槛达标后由 Neo 手动执行**
|
||||||
|
|
||||||
|
## 七、关联
|
||||||
|
|
||||||
|
- 调研:[`docs/_overview/04a-feedback/P0-1-sandbox-snapshot-design.md`](../_overview/04a-feedback/P0-1-sandbox-snapshot-design.md) F-1 子代理
|
||||||
|
- 审计:[`docs/audit/changes/2026-05-04__wave1_t2_scd2_view_unify.md`](../audit/changes/2026-05-04__wave1_t2_scd2_view_unify.md)
|
||||||
|
- P20 SPEC:[`docs/prd/specs/P20-runtime-context-sandbox.md`](../prd/specs/P20-runtime-context-sandbox.md) §3.5
|
||||||
Reference in New Issue
Block a user