feat(analytics): C1 命中列表默认过滤+批量处置
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""命中查询与处置——默认只显示 pending+confirmed(误报治理闭环)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.models import SensitiveHit, SensitiveWord
|
||||
|
||||
DISPOSITIONS = ("pending", "confirmed", "false_positive", "ignored")
|
||||
DEFAULT_SHOW = ("pending", "confirmed")
|
||||
|
||||
|
||||
async def list_hits(
|
||||
session: AsyncSession, *, disposition: str | None = None,
|
||||
word_id: int | None = None, page: int, page_size: int,
|
||||
) -> dict:
|
||||
conds = []
|
||||
if disposition:
|
||||
conds.append(SensitiveHit.disposition == disposition)
|
||||
else:
|
||||
conds.append(SensitiveHit.disposition.in_(DEFAULT_SHOW))
|
||||
if word_id:
|
||||
conds.append(SensitiveHit.word_id == word_id)
|
||||
|
||||
total = (
|
||||
await session.execute(
|
||||
sa.select(sa.func.count()).select_from(SensitiveHit).where(*conds)
|
||||
)
|
||||
).scalar_one()
|
||||
rows = (
|
||||
await session.execute(
|
||||
sa.select(SensitiveHit, SensitiveWord.word, SensitiveWord.category)
|
||||
.join(SensitiveWord, SensitiveHit.word_id == SensitiveWord.id)
|
||||
.where(*conds)
|
||||
# ts 可 NULL/并列,id 决胜列保证翻页稳定
|
||||
.order_by(SensitiveHit.ts.desc(), SensitiveHit.id.desc())
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
)
|
||||
).all()
|
||||
items = [
|
||||
{
|
||||
"id": h.id, "word_id": h.word_id, "word": word, "category": category,
|
||||
"msg_id": h.msg_id, "channel": h.channel,
|
||||
"owner_account_id": h.owner_account_id, "talker_id": h.talker_id,
|
||||
"room_id": h.room_id, "ts": h.ts, "snippet": h.snippet,
|
||||
"disposition": h.disposition,
|
||||
}
|
||||
for h, word, category in rows
|
||||
]
|
||||
return {"items": items, "total": int(total), "page": page, "page_size": page_size}
|
||||
|
||||
|
||||
async def dispose(session: AsyncSession, ids: list[int], disposition: str) -> int:
|
||||
"""批量改 disposition(pending 合法——用于「重开」误报);不写日志(词/snippet 红线)。"""
|
||||
if disposition not in DISPOSITIONS:
|
||||
raise ValueError(f"bad disposition: {disposition!r}")
|
||||
if not ids:
|
||||
return 0
|
||||
res = await session.execute(
|
||||
sa.update(SensitiveHit)
|
||||
.where(SensitiveHit.id.in_(ids))
|
||||
.values(disposition=disposition)
|
||||
)
|
||||
return int(res.rowcount or 0)
|
||||
Reference in New Issue
Block a user