fix(analytics): 命中处置ids上限+disposition枚举校验+排序断言

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-04 20:51:32 +08:00
parent 6e67e9e631
commit 6a1c904018
2 changed files with 29 additions and 4 deletions
+8 -4
View File
@@ -5,7 +5,7 @@ from typing import Annotated, Any, Literal
import sqlalchemy as sa
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession
from app.analytics import health as health_svc
@@ -150,20 +150,24 @@ async def delete_word(session: SessionDep, word_id: int) -> dict:
class DisposeIn(BaseModel):
ids: list[int]
# 巨 IN 列表会撞 MySQL max_allowed_packet 裸 500 → pydantic 层拦成干净 422
ids: list[int] = Field(max_length=1000)
disposition: str
DispositionParam = Literal["pending", "confirmed", "false_positive", "ignored"]
@router.get("/sensitive/hits")
async def list_hits(
session: SessionDep,
disposition: str | None = None,
disposition: DispositionParam | None = None,
word_id: int | None = None,
page: int = Query(1, ge=1),
page_size: int = Query(20, ge=1, le=100),
) -> dict:
"""C1 命中列表——不传 disposition 默认只回 pending+confirmed(治理闭环:
处置完即出队列);word_id 供词详情下钻。"""
处置完即出队列);word_id 供词详情下钻。disposition 拼错 422 不静默空列表。"""
return await hits_svc.list_hits(
session, disposition=disposition, word_id=word_id,
page=page, page_size=page_size,
+21
View File
@@ -201,6 +201,14 @@ async def test_hits_list_default_filter_and_dispose(make_client):
session.add(SensitiveHit(word_id=w.id, msg_id="m2", channel="wechat",
ts=1756900001000, snippet="…私单…",
disposition="ignored"))
# 排序/word_id 过滤双断言用:同 disposition 两行、不同词、ts 拉开
w2 = await words_svc.create_word(session, word="飞单")
session.add(SensitiveHit(word_id=w.id, msg_id="m3", channel="wechat",
ts=1756900005000, snippet="…",
disposition="false_positive"))
session.add(SensitiveHit(word_id=w2.id, msg_id="m4", channel="wechat",
ts=1756900002000, snippet="…",
disposition="false_positive"))
await session.commit()
client = await make_client(seed)
@@ -217,3 +225,16 @@ async def test_hits_list_default_filter_and_dispose(make_client):
r4 = await client.get("/api/analytics/sensitive/hits",
params={"disposition": "confirmed"})
assert r4.json()["total"] == 1
# ts 降序:m3(ts 更大)在前;m4 属他词被 word_id 过滤掉
r5 = await client.get("/api/analytics/sensitive/hits",
params={"disposition": "false_positive"})
fp = r5.json()
assert fp["total"] == 2
assert [h["msg_id"] for h in fp["items"]] == ["m3", "m4"]
r6 = await client.get("/api/analytics/sensitive/hits",
params={"disposition": "false_positive",
"word_id": fp["items"][0]["word_id"]})
assert r6.json()["total"] == 1
r7 = await client.get("/api/analytics/sensitive/hits",
params={"disposition": "bogus"})
assert r7.status_code == 422 # 拼错值响亮 422,不静默空列表