feat: add search and user data routes, services, and tests
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"""用户数据 JSON API — 收藏、阅读状态、笔记。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.database import get_db
|
||||
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)
|
||||
|
||||
if "error" in result:
|
||||
raise HTTPException(status_code=404, detail=result["error"])
|
||||
|
||||
# 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),
|
||||
):
|
||||
"""更新阅读状态。"""
|
||||
result = set_reading_status(db, arxiv_id, body.status)
|
||||
|
||||
if "error" in result:
|
||||
if result["error"] == "not_found":
|
||||
raise HTTPException(status_code=404, detail="Paper not found")
|
||||
elif result["error"] == "invalid_status":
|
||||
raise HTTPException(
|
||||
status_code=422,
|
||||
detail=f"Invalid status. Valid: {result['valid']}",
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
# ── 笔记 ──────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@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 HTTPException(status_code=404, detail="Paper not found")
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/note/{arxiv_id}")
|
||||
def note_save(arxiv_id: str, body: NoteRequest, db: Session = Depends(get_db)):
|
||||
"""保存笔记。"""
|
||||
result = save_note(db, arxiv_id, body.content)
|
||||
|
||||
if "error" in result:
|
||||
raise HTTPException(status_code=404, detail=result["error"])
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user