111 lines
3.0 KiB
Markdown
111 lines
3.0 KiB
Markdown
---
|
||
phase: 5
|
||
plan: 1
|
||
wave: 1
|
||
title: "30 天窗口去重修复(DATA-01)"
|
||
depends_on: []
|
||
files_modified:
|
||
- app/services/ingest/dedup.py
|
||
- tests/ingest/test_dedup.py # NEW
|
||
autonomous: true
|
||
requirements:
|
||
- DATA-01
|
||
---
|
||
|
||
# Phase 5 Plan 01: 30 天窗口去重修复(DATA-01)
|
||
|
||
## Objective
|
||
|
||
`dedup.py` 的 `batch_dedup_filter()` 目前查全量历史数据去重,导致同一职位超过 30 天后无法重新入库。
|
||
本 Plan 在去重查询 SQL 中加入 30 天时间窗口。
|
||
|
||
## Must Haves
|
||
|
||
- [ ] 单字段 dedup SQL 加 `AND created_at > now() - INTERVAL 30 DAY`
|
||
- [ ] 双字段 dedup SQL 同样加 30 天窗口
|
||
- [ ] 新增 Mock 测试(用 `AsyncMock` 模拟 ClickHouse 客户端)覆盖:
|
||
- 30 天内有记录 → 视为重复,过滤
|
||
- 30 天外有记录 → 不重复,允许入库
|
||
- 无记录 → 允许入库
|
||
- [ ] `pipenv run python -m pytest tests/ingest/test_dedup.py -v` 全部通过
|
||
- [ ] 全量回归 `pytest tests/ -v` 无失败
|
||
|
||
---
|
||
|
||
## Wave 1
|
||
|
||
### Task 1.1: 修改 dedup.py
|
||
|
||
<read_first>
|
||
- `app/services/ingest/dedup.py`(当前 81 行)
|
||
</read_first>
|
||
|
||
<action>
|
||
修改 `batch_dedup_filter()` 中的两个 SQL 查询:
|
||
|
||
**单字段去重(第 51 行附近):**
|
||
```python
|
||
# 修改前
|
||
query = f"SELECT {key_col} FROM {table} WHERE {key_col} IN {{keys:Array(String)}}"
|
||
|
||
# 修改后
|
||
query = (
|
||
f"SELECT {key_col} FROM {table} "
|
||
f"WHERE {key_col} IN {{keys:Array(String)}} "
|
||
f"AND created_at > now() - INTERVAL 30 DAY"
|
||
)
|
||
```
|
||
|
||
**双字段去重(第 65 行附近):**
|
||
```python
|
||
# 修改前
|
||
query = f"SELECT {c1}, {c2} FROM {table} WHERE {c1} IN {{keys:Array(String)}}"
|
||
|
||
# 修改后
|
||
query = (
|
||
f"SELECT {c1}, {c2} FROM {table} "
|
||
f"WHERE {c1} IN {{keys:Array(String)}} "
|
||
f"AND created_at > now() - INTERVAL 30 DAY"
|
||
)
|
||
```
|
||
</action>
|
||
|
||
<acceptance_criteria>
|
||
- `grep "INTERVAL 30 DAY" app/services/ingest/dedup.py` 有两处输出
|
||
- `grep "INTERVAL 30 DAY" app/services/ingest/dedup.py | wc -l` 输出为 2
|
||
</acceptance_criteria>
|
||
|
||
---
|
||
|
||
### Task 1.2: 新增 Mock 测试
|
||
|
||
<action>
|
||
创建 `tests/ingest/__init__.py`(空文件)和 `tests/ingest/test_dedup.py`:
|
||
|
||
测试覆盖:
|
||
1. `test_single_field_dedup_within_30_days`:30 天内有相同 job_id → 过滤(duplicate=1)
|
||
2. `test_single_field_dedup_outside_30_days`:先不存在 → 允许入库(duplicate=0)(模拟 30 天外无记录)
|
||
3. `test_double_field_dedup_within_30_days`:双字段在 30 天内有记录 → 过滤
|
||
4. `test_dedup_empty_input`:空输入 → 直接返回,不查 ClickHouse
|
||
5. `test_dedup_no_dedup_columns`:无 dedup 字段 → 跳过过滤
|
||
6. `test_build_insert_row_has_channel`:验证 build_insert_row 生成的行包含 channel 列
|
||
|
||
SQL 验证:用 `mock_client.query.call_args[0][0]` 断言 SQL 中包含 `INTERVAL 30 DAY`
|
||
</action>
|
||
|
||
---
|
||
|
||
## Verification
|
||
|
||
```bash
|
||
# 1. 验证 SQL 变更
|
||
grep "INTERVAL 30 DAY" app/services/ingest/dedup.py
|
||
# 预期输出:2 行
|
||
|
||
# 2. 运行新增测试
|
||
pipenv run python -m pytest tests/ingest/test_dedup.py -v
|
||
|
||
# 3. 全量回归
|
||
pipenv run python -m pytest tests/ -v --tb=short
|
||
```
|