21f16e6756
- Split summarizer into summary_generator and summary_persister modules - Refactor pdf_image_extractor to two-phase pipeline with PicoDet layout detection - Add layout_detector service for PicoDet-S_layout_3cls integration - Add exceptions module with ConflictError and NotFoundError - Improve admin dashboard with better statistics and task management - Add design review document with system optimization suggestions - Add new tests for crawler, pdf_downloader, pipeline, and summary_utils - Update dependencies and configuration - Clean up dead code and improve error handling
86 lines
2.8 KiB
Python
86 lines
2.8 KiB
Python
"""用户数据 JSON API — 收藏、阅读状态、笔记。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from fastapi.responses import HTMLResponse
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.database import get_db
|
|
from app.exceptions import NotFoundError
|
|
from app.services.user_data import (
|
|
get_note,
|
|
save_note,
|
|
set_reading_status,
|
|
toggle_bookmark,
|
|
)
|
|
|
|
router = APIRouter(prefix="/api", tags=["user-data"])
|
|
|
|
|
|
# ── 请求模型 ──────────────────────────────────────────────────────────
|
|
|
|
|
|
class ReadingStatusRequest(BaseModel):
|
|
status: str
|
|
|
|
|
|
class NoteRequest(BaseModel):
|
|
content: str
|
|
|
|
|
|
# ── 收藏 ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
@router.post("/bookmark/{arxiv_id}")
|
|
def bookmark_toggle(arxiv_id: str, request: Request, db: Session = Depends(get_db)):
|
|
"""切换收藏状态。支持 HTMX 局部刷新和 JSON 响应。"""
|
|
result = toggle_bookmark(db, arxiv_id)
|
|
|
|
# HTMX 请求 → 返回 HTML 片段
|
|
if request.headers.get("HX-Request"):
|
|
star = "★" if result["bookmarked"] else "☆"
|
|
active_class = " active" if result["bookmarked"] else ""
|
|
html = (
|
|
f'<button class="btn-bookmark{active_class}" '
|
|
f'hx-post="/api/bookmark/{arxiv_id}" '
|
|
f'hx-target="#user-data-{arxiv_id}" '
|
|
f'hx-swap="outerHTML">'
|
|
f"{star}</button>"
|
|
)
|
|
return HTMLResponse(content=html)
|
|
|
|
return result
|
|
|
|
|
|
# ── 阅读状态 ──────────────────────────────────────────────────────────
|
|
|
|
|
|
@router.post("/reading-status/{arxiv_id}")
|
|
def reading_status_update(
|
|
arxiv_id: str,
|
|
body: ReadingStatusRequest,
|
|
db: Session = Depends(get_db),
|
|
):
|
|
"""更新阅读状态。"""
|
|
return set_reading_status(db, arxiv_id, body.status)
|
|
|
|
|
|
# ── 笔记 ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
@router.get("/note/{arxiv_id}")
|
|
def note_get(arxiv_id: str, db: Session = Depends(get_db)):
|
|
"""获取笔记。"""
|
|
result = get_note(db, arxiv_id)
|
|
if result is None:
|
|
raise NotFoundError(f"Paper not found: {arxiv_id}")
|
|
return result
|
|
|
|
|
|
@router.post("/note/{arxiv_id}")
|
|
def note_save(arxiv_id: str, body: NoteRequest, db: Session = Depends(get_db)):
|
|
"""保存笔记。"""
|
|
return save_note(db, arxiv_id, body.content)
|