import uuid from pathlib import Path from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status from fastapi.responses import FileResponse from sqlalchemy.orm import Session from app.core.config import settings from app.core.database import get_db from app.core.deps import get_current_user from app.models.user import Student, User from app.schemas import StudentCreate, StudentOut, StudentUpdate from app.services.student_access import get_student_for_user from app.services.student_avatar import delete_avatar_file, save_avatar router = APIRouter(prefix="/students", tags=["students"]) def _to_out(student: Student) -> StudentOut: return StudentOut.from_student(student) @router.get("", response_model=list[StudentOut]) def list_students( db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): rows = ( db.query(Student) .filter(Student.user_id == current_user.id) .order_by(Student.created_at.desc()) .all() ) return [_to_out(row) for row in rows] @router.post("", response_model=StudentOut, status_code=status.HTTP_201_CREATED) def create_student( data: StudentCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): student = Student(user_id=current_user.id, **data.model_dump()) db.add(student) db.commit() db.refresh(student) return _to_out(student) @router.get("/{student_id}", response_model=StudentOut) def get_student( student_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): student = get_student_for_user(db, student_id, current_user.id) return _to_out(student) @router.patch("/{student_id}", response_model=StudentOut) def update_student( student_id: uuid.UUID, data: StudentUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): student = get_student_for_user(db, student_id, current_user.id) for key, value in data.model_dump(exclude_unset=True).items(): setattr(student, key, value) db.commit() db.refresh(student) return _to_out(student) @router.delete("/{student_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_student( student_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): student = get_student_for_user(db, student_id, current_user.id) delete_avatar_file(student.avatar_path) db.delete(student) db.commit() @router.post("/{student_id}/avatar", response_model=StudentOut) async def upload_avatar( student_id: uuid.UUID, file: UploadFile = File(...), db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): student = get_student_for_user(db, student_id, current_user.id) content = await file.read() if len(content) > settings.MAX_UPLOAD_SIZE: raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="文件超过10MB限制") if not (file.content_type or "").startswith("image/"): raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="请上传图片文件") delete_avatar_file(student.avatar_path) student.avatar_path = save_avatar(str(current_user.id), str(student.id), content) db.commit() db.refresh(student) return _to_out(student) @router.delete("/{student_id}/avatar", response_model=StudentOut) def remove_avatar( student_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): student = get_student_for_user(db, student_id, current_user.id) delete_avatar_file(student.avatar_path) student.avatar_path = None db.commit() db.refresh(student) return _to_out(student) @router.get("/{student_id}/avatar") def get_avatar( student_id: uuid.UUID, db: Session = Depends(get_db), current_user: User = Depends(get_current_user), ): student = get_student_for_user(db, student_id, current_user.id) if not student.avatar_path: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="未设置头像") path = Path(settings.UPLOAD_DIR) / student.avatar_path if not path.is_file(): raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="头像文件不存在") return FileResponse(path, media_type="image/jpeg")