# 审计记录：DWS 基类 bugfix — 绩效档位兜底 + safe_decimal 异常捕获

- 日期：2026-02-14（Asia/Shanghai）
- Prompt：「继续。完成后检查所有任务是否全面，所有数据能否覆盖 api - ods - dwd - dws - index 的全流程？」

## 直接原因

运行 `pytest tests/unit -x` 验证清理结果时，发现 3 个已有测试 bug + 2 个业务代码 bug（均非本次清理引入）：

1. `get_performance_tier()` 在 `max_tier_level` 过滤后，若小时数超过所有剩余档位的 `max_hours`，返回 `None` 而非最高可用档位（新入职封顶场景失效）
2. `safe_decimal()` 未捕获 `decimal.InvalidOperation`，传入非数值字符串（如 `"invalid"`）时抛异常
3. 测试中 `mock_config.get.return_value = None` 导致 timezone 为 None
4. 测试中 `_build_daily_record` 调用缺少 `gift_card` 参数
5. 测试中 `loaded_at=datetime.now()` 为 naive，与业务代码 aware datetime 不兼容

## 修改文件清单

| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `tasks/dws/base_dws_task.py` | bugfix | `get_performance_tier` 增加 best_fallback 兜底；`safe_decimal` 增加 `InvalidOperation` 捕获；导入增加 `InvalidOperation` |
| `tests/unit/test_dws_tasks.py` | bugfix | 修复 mock_config、gift_card 参数、aware datetime 三处测试 bug |

## 风险点

- `get_performance_tier` 兜底逻辑仅在 `max_tier_level is not None` 时生效，正常匹配路径（无封顶）不受影响
- `safe_decimal` 变更为纯防御性，仅扩大异常捕获范围，不改变正常路径行为
- 不涉及数据库 schema、API 契约、资金精度变更

## 回滚要点

- 回滚 `base_dws_task.py` 的两处改动即可恢复原行为
- 回滚后 `test_max_tier_level_cap` 和 `test_safe_decimal` 将重新失败

## 验证步骤

```bash
pytest tests/unit -x -q
# 预期：449 passed, 1 skipped
```
