""" app/api/v1/benchmarks.py — 标杆笔记 + 违禁词路由(管理员) 主通道:截图+手填亮点(不做原始抓取)。 """ import logging from typing import Annotated from fastapi import APIRouter, Depends from pydantic import BaseModel from sqlalchemy.orm import Session from app.core.database import get_db from app.core.response import ok, paginate, raise_not_found from app.middleware.workspace_guard import CurrentUser, require_admin, require_write_permission from app.models.product import BannedWord, BenchmarkNote logger = logging.getLogger(__name__) router = APIRouter(tags=["products"]) class BenchmarkCreate(BaseModel): screenshot_url: str | None = None highlights: str | None = None # 手填亮点(主通道) link_url: str | None = None # 可选,不做自动抓取 class BannedWordCreate(BaseModel): word: str level: str # auto_fix | soft_warn | hard_block replacement: str | None = None updatable: bool = True def _fmt_benchmark(b: BenchmarkNote) -> dict: return { "id": b.id, "product_id": b.product_id, "screenshot_url": b.screenshot_url, "highlights": b.highlights, "link_url": b.link_url, "created_at": b.created_at.isoformat(), } def _fmt_banned(bw: BannedWord) -> dict: return { "id": bw.id, "word": bw.word, "level": bw.level, "replacement": bw.replacement, "updatable": bw.updatable, "workspace_id": bw.workspace_id, } # ── 标杆笔记 ──────────────────────────────────────────────── @router.get("/products/{product_id}/benchmarks") def list_benchmarks( product_id: int, current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None, db: Session = Depends(get_db), ): items = ( db.query(BenchmarkNote) .filter( BenchmarkNote.product_id == product_id, BenchmarkNote.workspace_id == current_user.workspace_id, ) .all() ) return ok({"items": [_fmt_benchmark(b) for b in items]}) @router.post("/products/{product_id}/benchmarks") def create_benchmark( product_id: int, body: BenchmarkCreate, current_user: Annotated[CurrentUser, Depends(require_admin)] = None, db: Session = Depends(get_db), ): b = BenchmarkNote( workspace_id=current_user.workspace_id, product_id=product_id, screenshot_url=body.screenshot_url, highlights=body.highlights, link_url=body.link_url, ) db.add(b); db.commit(); db.refresh(b) return ok(_fmt_benchmark(b)) # ── 违禁词库 ──────────────────────────────────────────────── @router.get("/banned-words") def list_banned_words( page: int = 1, page_size: int = 50, current_user: Annotated[CurrentUser, Depends(require_write_permission)] = None, db: Session = Depends(get_db), ): q = db.query(BannedWord).filter(BannedWord.workspace_id == current_user.workspace_id) total = q.count() items = q.offset((page - 1) * page_size).limit(page_size).all() return ok(paginate([_fmt_banned(bw) for bw in items], total, page, page_size)) @router.post("/banned-words") def create_banned_word( body: BannedWordCreate, current_user: Annotated[CurrentUser, Depends(require_admin)] = None, db: Session = Depends(get_db), ): bw = BannedWord( workspace_id=current_user.workspace_id, word=body.word, level=body.level, replacement=body.replacement, updatable=body.updatable, ) db.add(bw); db.commit(); db.refresh(bw) return ok(_fmt_banned(bw)) @router.put("/banned-words/{word_id}") def update_banned_word( word_id: int, body: BannedWordCreate, current_user: Annotated[CurrentUser, Depends(require_admin)] = None, db: Session = Depends(get_db), ): bw = db.query(BannedWord).filter(BannedWord.id == word_id, BannedWord.workspace_id == current_user.workspace_id).first() if not bw: raise_not_found("违禁词不存在") if not bw.updatable: from app.core.response import raise_forbidden raise_forbidden("该违禁词不可修改") bw.word = body.word; bw.level = body.level bw.replacement = body.replacement; bw.updatable = body.updatable db.commit(); db.refresh(bw) return ok(_fmt_banned(bw)) @router.delete("/banned-words/{word_id}") def delete_banned_word( word_id: int, current_user: Annotated[CurrentUser, Depends(require_admin)] = None, db: Session = Depends(get_db), ): bw = db.query(BannedWord).filter(BannedWord.id == word_id, BannedWord.workspace_id == current_user.workspace_id).first() if not bw: raise_not_found("违禁词不存在") db.delete(bw); db.commit() return ok({"deleted": word_id})