fix(analytics): 命中处置ids上限+disposition枚举校验+排序断言
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ from typing import Annotated, Any, Literal
|
|||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel, Field
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.analytics import health as health_svc
|
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):
|
class DisposeIn(BaseModel):
|
||||||
ids: list[int]
|
# 巨 IN 列表会撞 MySQL max_allowed_packet 裸 500 → pydantic 层拦成干净 422
|
||||||
|
ids: list[int] = Field(max_length=1000)
|
||||||
disposition: str
|
disposition: str
|
||||||
|
|
||||||
|
|
||||||
|
DispositionParam = Literal["pending", "confirmed", "false_positive", "ignored"]
|
||||||
|
|
||||||
|
|
||||||
@router.get("/sensitive/hits")
|
@router.get("/sensitive/hits")
|
||||||
async def list_hits(
|
async def list_hits(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
disposition: str | None = None,
|
disposition: DispositionParam | None = None,
|
||||||
word_id: int | None = None,
|
word_id: int | None = None,
|
||||||
page: int = Query(1, ge=1),
|
page: int = Query(1, ge=1),
|
||||||
page_size: int = Query(20, ge=1, le=100),
|
page_size: int = Query(20, ge=1, le=100),
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""C1 命中列表——不传 disposition 默认只回 pending+confirmed(治理闭环:
|
"""C1 命中列表——不传 disposition 默认只回 pending+confirmed(治理闭环:
|
||||||
处置完即出队列);word_id 供词详情下钻。"""
|
处置完即出队列);word_id 供词详情下钻。disposition 拼错 422 不静默空列表。"""
|
||||||
return await hits_svc.list_hits(
|
return await hits_svc.list_hits(
|
||||||
session, disposition=disposition, word_id=word_id,
|
session, disposition=disposition, word_id=word_id,
|
||||||
page=page, page_size=page_size,
|
page=page, page_size=page_size,
|
||||||
|
|||||||
@@ -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",
|
session.add(SensitiveHit(word_id=w.id, msg_id="m2", channel="wechat",
|
||||||
ts=1756900001000, snippet="…私单…",
|
ts=1756900001000, snippet="…私单…",
|
||||||
disposition="ignored"))
|
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()
|
await session.commit()
|
||||||
|
|
||||||
client = await make_client(seed)
|
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",
|
r4 = await client.get("/api/analytics/sensitive/hits",
|
||||||
params={"disposition": "confirmed"})
|
params={"disposition": "confirmed"})
|
||||||
assert r4.json()["total"] == 1
|
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,不静默空列表
|
||||||
|
|||||||
Reference in New Issue
Block a user