"""待办清单接口。""" 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}