from datetime import UTC, date, datetime from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import desc, select from sqlalchemy.ext.asyncio import AsyncSession from app.database import get_db from app.models.task import Task, TaskStatus from app.models.user import User from app.routers.auth import get_current_user from app.schemas.task import TaskCreate, TaskUpdate, TaskOut router = APIRouter(prefix="/api/tasks", tags=["看板"]) @router.get("", response_model=list[TaskOut]) async def list_tasks( status: TaskStatus | None = None, due_date: date | None = Query(default=None), date_from: date | None = Query(default=None), date_to: date | None = Query(default=None), current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): query = select(Task).where(Task.user_id == current_user.id) if status: query = query.where(Task.status == status) if due_date: start = datetime.combine(due_date, datetime.min.time()) end = datetime.combine(due_date, datetime.max.time()) query = query.where(Task.due_date.is_not(None), Task.due_date >= start, Task.due_date <= end) else: start = datetime.combine(date_from, datetime.min.time()) if date_from else None end = datetime.combine(date_to, datetime.max.time()) if date_to else None if start and end and start > end: raise HTTPException(status_code=400, detail="开始日期不能晚于结束日期") if start is not None: query = query.where(Task.due_date.is_not(None), Task.due_date >= start) if end is not None: query = query.where(Task.due_date.is_not(None), Task.due_date <= end) query = query.order_by(desc(Task.created_at)) result = await db.execute(query) return result.scalars().all() @router.post("", response_model=TaskOut, status_code=201) async def create_task( data: TaskCreate, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): import json task = Task( user_id=current_user.id, title=data.title, description=data.description, priority=data.priority, due_date=data.due_date, tags=json.dumps(data.tags) if data.tags else None, ) db.add(task) await db.commit() await db.refresh(task) return task @router.patch("/{task_id}", response_model=TaskOut) async def update_task( task_id: str, data: TaskUpdate, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): import json result = await db.execute( select(Task).where(Task.id == task_id, Task.user_id == current_user.id) ) task = result.scalar_one_or_none() if not task: raise HTTPException(status_code=404, detail="任务不存在") for field, value in data.model_dump(exclude_none=True).items(): if field == "tags": setattr(task, field, json.dumps(value)) elif field == "status" and value == TaskStatus.DONE: task.completed_at = datetime.now(UTC) setattr(task, field, value) elif field == "status": task.completed_at = None setattr(task, field, value) await db.commit() await db.refresh(task) return task @router.delete("/{task_id}", status_code=204) async def delete_task( task_id: str, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): result = await db.execute( select(Task).where(Task.id == task_id, Task.user_id == current_user.id) ) task = result.scalar_one_or_none() if not task: raise HTTPException(status_code=404, detail="任务不存在") await db.delete(task) await db.commit()