Files
JARVIS/backend/app/routers/todo.py

150 lines
5.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import UTC, date, datetime
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.models.todo import DailyTodo, TodoSource
from app.models.user import User
from app.routers.auth import get_current_user
from app.schemas.todo import (
TodoCreate, TodoUpdate, TodoOut, TodoListOut, TodoSummaryOut
)
from app.services.todo_service import generate_daily_todos
router = APIRouter(prefix="/api/todos", tags=["待办"])
@router.get("", response_model=TodoListOut)
async def list_todos(
date_str: str = Query(default=None), # YYYY-MM-DD默认当天
page: int = Query(default=1, ge=1),
page_size: int = Query(default=50, ge=1, le=100),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
target_date = date_str or date.today().isoformat()
offset = (page - 1) * page_size
# 查询总数
count_q = select(func.count()).select_from(DailyTodo).where(
DailyTodo.user_id == current_user.id,
DailyTodo.todo_date == target_date,
)
total = (await db.execute(count_q)).scalar()
# 查询列表
q = select(DailyTodo).where(
DailyTodo.user_id == current_user.id,
DailyTodo.todo_date == target_date,
).order_by(DailyTodo.created_at.desc()).offset(offset).limit(page_size)
items = (await db.execute(q)).scalars().all()
return TodoListOut(items=items, total=total, page=page, page_size=page_size)
@router.post("", response_model=TodoOut, status_code=201)
async def create_todo(
data: TodoCreate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
todo = DailyTodo(
user_id=current_user.id,
title=data.title,
source=TodoSource.MANUAL,
todo_date=(data.todo_date or date.today()).isoformat(),
)
db.add(todo)
await db.commit()
await db.refresh(todo)
return todo
@router.patch("/{todo_id}", response_model=TodoOut)
async def update_todo(
todo_id: str,
data: TodoUpdate,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(DailyTodo).where(DailyTodo.id == todo_id, DailyTodo.user_id == current_user.id)
)
todo = result.scalar_one_or_none()
if not todo:
raise HTTPException(status_code=404, detail="待办不存在")
if data.title is not None:
todo.title = data.title
if data.todo_date is not None:
todo.todo_date = data.todo_date.isoformat()
if data.is_completed is not None:
todo.is_completed = data.is_completed
todo.completed_at = datetime.now(UTC) if data.is_completed else None
await db.commit()
await db.refresh(todo)
return todo
@router.delete("/{todo_id}", status_code=204)
async def delete_todo(
todo_id: str,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(
select(DailyTodo).where(DailyTodo.id == todo_id, DailyTodo.user_id == current_user.id)
)
todo = result.scalar_one_or_none()
if not todo:
raise HTTPException(status_code=404, detail="待办不存在")
await db.delete(todo)
await db.commit()
@router.post("/ai-generate", response_model=TodoListOut)
async def ai_generate_todos(
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
target_date = date.today().isoformat()
# 幂等检查是否已有AI生成记录
check_q = select(func.count()).select_from(DailyTodo).where(
DailyTodo.user_id == current_user.id,
DailyTodo.todo_date == target_date,
DailyTodo.source.in_([TodoSource.AI_KANBAN, TodoSource.AI_CHAT]),
)
count = (await db.execute(check_q)).scalar()
if count > 0:
# 已生成,返回现有记录
q = select(DailyTodo).where(
DailyTodo.user_id == current_user.id,
DailyTodo.todo_date == target_date,
).order_by(DailyTodo.created_at.desc())
items = (await db.execute(q)).scalars().all()
return TodoListOut(items=items, total=len(items), page=1, page_size=50)
# 执行AI生成
todos = await generate_daily_todos(current_user.id, db)
return TodoListOut(items=todos, total=len(todos), page=1, page_size=50)
@router.get("/summary", response_model=TodoSummaryOut)
async def get_summary(
date_str: str = Query(default=None),
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
target_date = date_str or date.today().isoformat()
q = select(DailyTodo).where(
DailyTodo.user_id == current_user.id,
DailyTodo.todo_date == target_date,
)
todos = (await db.execute(q)).scalars().all()
completed = sum(1 for t in todos if t.is_completed)
return TodoSummaryOut(date=target_date, total=len(todos), completed=completed, pending=len(todos) - completed)