from datetime import datetime, timezone from fastapi import APIRouter, Depends, HTTPException from sqlmodel import Session, select, func from ..database import get_session from ..models import Novel, Character from ..schemas import ( CharacterCreate, CharacterUpdate, CharacterRead, CharacterList, ) router = APIRouter(prefix="/api/novels/{novel_id}/characters", tags=["characters"]) def _get_novel_or_404(novel_id: int, session: Session) -> Novel: novel = session.get(Novel, novel_id) if not novel: raise HTTPException(status_code=404, detail="小说不存在") return novel def _get_character_or_404( character_id: int, novel_id: int, session: Session ) -> Character: character = session.get(Character, character_id) if not character or character.novel_id != novel_id: raise HTTPException(status_code=404, detail="角色不存在") return character @router.get("", response_model=CharacterList) def list_characters( novel_id: int, session: Session = Depends(get_session), ): _get_novel_or_404(novel_id, session) total = session.exec( select(func.count(Character.id)).where(Character.novel_id == novel_id) ).one() items = session.exec( select(Character) .where(Character.novel_id == novel_id) .order_by(Character.created_at) ).all() return CharacterList( items=[CharacterRead.model_validate(c, from_attributes=True) for c in items], total=total, ) @router.post("", response_model=CharacterRead, status_code=201) def create_character( novel_id: int, data: CharacterCreate, session: Session = Depends(get_session), ): _get_novel_or_404(novel_id, session) character = Character(novel_id=novel_id, **data.model_dump()) session.add(character) session.commit() session.refresh(character) return character @router.get("/{character_id}", response_model=CharacterRead) def get_character( novel_id: int, character_id: int, session: Session = Depends(get_session), ): return _get_character_or_404(character_id, novel_id, session) @router.patch("/{character_id}", response_model=CharacterRead) def update_character( novel_id: int, character_id: int, data: CharacterUpdate, session: Session = Depends(get_session), ): character = _get_character_or_404(character_id, novel_id, session) update_data = data.model_dump(exclude_unset=True) for key, value in update_data.items(): setattr(character, key, value) character.updated_at = datetime.now(timezone.utc) session.add(character) session.commit() session.refresh(character) return character @router.delete("/{character_id}", status_code=204) def delete_character( novel_id: int, character_id: int, session: Session = Depends(get_session), ): character = _get_character_or_404(character_id, novel_id, session) session.delete(character) session.commit()