6.0 KiB
6.0 KiB
phase, plan, wave, title, depends_on, files_modified, autonomous, requirements
| phase | plan | wave | title | depends_on | files_modified | autonomous | requirements | ||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 6 | 2 | 2 | 爬虫入库统计 API + 前端监控区域(QUAL-06/07) |
|
|
true |
|
Phase 6 Plan 02: 爬虫入库统计 API + 前端监控(QUAL-06/07)
Objective
QUAL-07 状态确认(已完成)
cleaning/monitor.vue 已包含:
- ✅ 待清洗公司列表(队列表格)
- ✅ 触发清洗
- ✅ 查看结果
QUAL-07 无需额外改动。
QUAL-06 缺口
现有监控页面仅展示公司清洗队列状态,缺少爬虫职位入库的实时统计:
- 各平台最近抓取时间(ClickHouse
created_at最大值) - 数量趋势(近 7 天每日入库量)
- 错误状态(失败/去重统计暂不通过 ClickHouse,后续可扩展)
Must Haves
- 后端新增
GET /api/v1/job/data/stats端点,接受platform(可选)和days(默认 7)参数- 返回:各平台
total、today、last_ingest_at、daily_counts(列表)
- 返回:各平台
- 前端
monitor.vue在现有 4 个 metric-card 上方新增一个"爬虫入库"统计区域:- 3 个平台卡片,各显示:总量、今日、最近抓取时间
- 一个数量趋势表格(近 7 天,按日显示 boss/qcwy/zhilian)
web/src/api/index.js新增getIngestStats函数- 前端
pnpm dev(或工具链验证)可正常加载 - 全量 pytest 回归
pipenv run python -m pytest tests/无失败
Wave 2(依赖 Plan 01)
Task 2.1: 后端新增 GET /job/data/stats 端点
<read_first>
app/api/v1/job/job.py(当前 123 行)app/core/clickhouse.py(获取 client 方式) </read_first>
@router.get("/data/stats", summary="各平台入库统计")
async def get_ingest_stats(
platform: Optional[PlatformType] = None,
days: int = 7,
service: IngestService = Depends(get_ingest_service),
) -> Dict[str, Any]:
"""
查询各平台 ClickHouse 入库统计:总量、今日、最近入库时间、近 N 天每日趋势
"""
from app.core.clickhouse import clickhouse_manager
client = await clickhouse_manager.get_client()
platforms = [platform.value] if platform else ["boss", "qcwy", "zhilian"]
table_map = {"boss": "boss_job", "qcwy": "qcwy_job", "zhilian": "zhilian_job"}
result = {}
for p in platforms:
table = f"job_data.{table_map[p]}"
try:
# 总量
r_total = await client.query(f"SELECT count() FROM {table}")
total = r_total.result_rows[0][0] if r_total.result_rows else 0
# 今日
r_today = await client.query(
f"SELECT count() FROM {table} WHERE toDate(created_at) = today()"
)
today = r_today.result_rows[0][0] if r_today.result_rows else 0
# 最近入库时间
r_last = await client.query(
f"SELECT max(created_at) FROM {table}"
)
last_at = str(r_last.result_rows[0][0]) if r_last.result_rows and r_last.result_rows[0][0] else None
# 近 N 天每日趋势
r_daily = await client.query(
f"SELECT toDate(created_at) AS day, count() AS cnt "
f"FROM {table} "
f"WHERE created_at >= today() - {days} "
f"GROUP BY day ORDER BY day DESC"
)
daily_counts = [{"date": str(row[0]), "count": row[1]} for row in r_daily.result_rows]
result[p] = {
"total": total,
"today": today,
"last_ingest_at": last_at,
"daily_counts": daily_counts,
}
except Exception as e:
result[p] = {"error": str(e), "total": 0, "today": 0, "last_ingest_at": None, "daily_counts": []}
return {"code": 200, "data": result}
Task 2.2: 前端新增 getIngestStats API
在 `web/src/api/index.js` 找到已有 API 函数,追加:getIngestStats: (params) => request.get('/job/data/stats', { params }),
Task 2.3: 前端 monitor.vue 添加爬虫统计区域
在 `monitor.vue` 的 `<!-- 爬虫入库统计 -->
<section class="ingest-grid">
<n-card
v-for="p in ingestStats"
:key="p.platform"
:bordered="false"
class="ingest-card"
>
<div class="ingest-platform-label">{{ p.label }}</div>
<div class="ingest-total">{{ p.total.toLocaleString() }}</div>
<div class="ingest-meta">
今日 +{{ p.today }} · 最近 {{ p.last_ingest_at || '--' }}
</div>
</n-card>
<n-card :bordered="false" class="ingest-trend-card">
<div class="ingest-trend-title">近 7 天入库趋势</div>
<n-data-table
size="small"
:columns="trendColumns"
:data="trendRows"
:pagination="false"
/>
</n-card>
</section>
对应 <script setup> 中加入:
const ingestStatsRaw = ref({})const fetchIngestStats = async ()→ 调用api.getIngestStats()const ingestStats = computed(...)→ 格式化三平台卡片数据const trendRows = computed(...)→ 转置为按日期行,boss/qcwy/zhilian 各列const trendColumns→ 日期 + 三平台列- 在
refreshAll()中加入fetchIngestStats()
Verification
# 后端
pipenv run python -m pytest tests/ -v --tb=short
# 前端(确认 pnpm 就绪,验证构建无报错)
cd web && pnpm install 2>&1 | tail -3
手动验证:
pnpm dev启动前端- 访问「清洗监控」页面,确认顶部出现三个平台入库卡片和近 7 天趋势表格
- 数据加载无报错,最近入库时间正确显示格式化后的时间