Initial commit: snapAna 截图智能整理工具
包含 FastAPI 后端、React 前端、队列/OCR/标签/待办等完整功能。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
"""待办清单接口。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from sqlalchemy import and_, func, or_, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.api.deps import db_session
|
||||
from app.models.todo import Todo, TodoStatus
|
||||
from app.schemas.common import TodoUpdate
|
||||
from app.schemas.screenshot import TodoBrief, TodoListResp
|
||||
|
||||
|
||||
router = APIRouter(prefix="/api/todos", tags=["todos"])
|
||||
|
||||
|
||||
def _todo_filters(
|
||||
status: Optional[str],
|
||||
kind: Optional[str],
|
||||
q: Optional[str],
|
||||
) -> list:
|
||||
"""构建待办筛选条件。"""
|
||||
filters = []
|
||||
if status:
|
||||
filters.append(Todo.status == status)
|
||||
if kind:
|
||||
filters.append(Todo.kind == kind)
|
||||
if q:
|
||||
like = f"%{q.strip()}%"
|
||||
filters.append(or_(Todo.title.ilike(like), Todo.note.ilike(like)))
|
||||
return filters
|
||||
|
||||
|
||||
@router.get("", response_model=TodoListResp)
|
||||
def list_todos(
|
||||
session: Session = Depends(db_session),
|
||||
status: Optional[str] = Query(None),
|
||||
kind: Optional[str] = Query(None),
|
||||
q: Optional[str] = Query(None, description="标题/备注关键词"),
|
||||
page: int = Query(1, ge=1),
|
||||
size: int = Query(50, ge=1, le=200),
|
||||
) -> TodoListResp:
|
||||
"""按状态/类型/关键词分页查询。"""
|
||||
filters = _todo_filters(status, kind, q)
|
||||
base = select(Todo)
|
||||
if filters:
|
||||
base = base.where(and_(*filters))
|
||||
|
||||
total = session.scalar(select(func.count()).select_from(base.subquery())) or 0
|
||||
rows = session.scalars(
|
||||
base.order_by(Todo.created_at.desc()).offset((page - 1) * size).limit(size)
|
||||
).all()
|
||||
return TodoListResp(
|
||||
items=[TodoBrief.model_validate(r) for r in rows],
|
||||
total=int(total),
|
||||
page=page,
|
||||
size=size,
|
||||
)
|
||||
|
||||
|
||||
@router.get("/summary")
|
||||
def summary(session: Session = Depends(db_session)) -> dict:
|
||||
"""各状态待办数量。"""
|
||||
return {
|
||||
st.value: session.scalar(select(func.count(Todo.id)).where(Todo.status == st.value)) or 0
|
||||
for st in TodoStatus
|
||||
}
|
||||
|
||||
|
||||
@router.patch("/{todo_id}", response_model=TodoBrief)
|
||||
def update_todo(
|
||||
todo_id: int,
|
||||
payload: TodoUpdate,
|
||||
session: Session = Depends(db_session),
|
||||
) -> TodoBrief:
|
||||
"""更新状态/标题/备注。"""
|
||||
todo = session.get(Todo, todo_id)
|
||||
if todo is None:
|
||||
raise HTTPException(404, "Todo not found")
|
||||
if payload.status is not None:
|
||||
todo.status = payload.status
|
||||
if payload.status == TodoStatus.DONE.value:
|
||||
todo.completed_at = datetime.utcnow()
|
||||
if payload.title is not None:
|
||||
todo.title = payload.title
|
||||
if payload.note is not None:
|
||||
todo.note = payload.note
|
||||
session.commit()
|
||||
session.refresh(todo)
|
||||
return TodoBrief.model_validate(todo)
|
||||
|
||||
|
||||
@router.delete("/{todo_id}")
|
||||
def delete_todo(
|
||||
todo_id: int,
|
||||
session: Session = Depends(db_session),
|
||||
) -> dict:
|
||||
todo = session.get(Todo, todo_id)
|
||||
if todo is None:
|
||||
raise HTTPException(404, "Todo not found")
|
||||
session.delete(todo)
|
||||
session.commit()
|
||||
return {"ok": True}
|
||||
Reference in New Issue
Block a user