İlk commit: Stepstead backend (FastAPI)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
ea049b8ccd
45 changed files with 6751 additions and 0 deletions
90
routers/character.py
Normal file
90
routers/character.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from pydantic import BaseModel, Field
|
||||
from sqlalchemy import select, text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from config import settings
|
||||
from deps import get_current_user
|
||||
from models import Character, Skill, User, get_db
|
||||
|
||||
router = APIRouter(prefix="/character", tags=["character"])
|
||||
|
||||
|
||||
class CharacterCreateIn(BaseModel):
|
||||
name: str = Field(min_length=3, max_length=32, pattern=r"^[A-Za-z0-9_]+$")
|
||||
|
||||
|
||||
class SkillOut(BaseModel):
|
||||
skill_name: str
|
||||
xp: int
|
||||
level: int
|
||||
|
||||
|
||||
class CharacterOut(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
level: int
|
||||
total_xp: int
|
||||
gold: int
|
||||
step_balance: int
|
||||
step_lifetime: int
|
||||
location_code: Optional[str] = None
|
||||
location_name: Optional[str] = None
|
||||
has_pending_event: bool = False
|
||||
skills: list[SkillOut]
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
async def _with_location(db: AsyncSession, char: Character) -> CharacterOut:
|
||||
out = CharacterOut.model_validate(char, from_attributes=True)
|
||||
out.has_pending_event = char.pending_event is not None
|
||||
if char.location_id is not None:
|
||||
row = (await db.execute(text(
|
||||
"SELECT code, name FROM locations WHERE id = :i"
|
||||
), {"i": char.location_id})).first()
|
||||
if row:
|
||||
out.location_code, out.location_name = row[0], row[1]
|
||||
return out
|
||||
|
||||
|
||||
@router.post("", response_model=CharacterOut, status_code=status.HTTP_201_CREATED)
|
||||
async def create_character(data: CharacterCreateIn, user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
||||
exists = (await db.execute(select(Character).where(Character.user_id == user.id))).scalar_one_or_none()
|
||||
if exists:
|
||||
raise HTTPException(409, "Zaten bir karakterin var")
|
||||
|
||||
name_taken = (await db.execute(select(Character).where(Character.name == data.name))).scalar_one_or_none()
|
||||
if name_taken:
|
||||
raise HTTPException(409, "Bu isim alınmış")
|
||||
|
||||
char = Character(user_id=user.id, name=data.name)
|
||||
db.add(char)
|
||||
await db.flush()
|
||||
|
||||
for skill_name in settings.SKILL_NAMES:
|
||||
db.add(Skill(character_id=char.id, skill_name=skill_name, xp=0, level=1))
|
||||
|
||||
# Başlangıç lokasyonu: köy. Not: başlangıç tool'u YOK —
|
||||
# ilk tool'lar köyde elle toplanan taş/sopa/sarmaşıktan craft edilir.
|
||||
start = (await db.execute(text(
|
||||
"SELECT id FROM locations WHERE type = 'village' ORDER BY id LIMIT 1"
|
||||
))).first()
|
||||
if start:
|
||||
char.location_id = start[0]
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(char, ["skills"])
|
||||
return await _with_location(db, char)
|
||||
|
||||
|
||||
@router.get("/me", response_model=CharacterOut)
|
||||
async def my_character(user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db)):
|
||||
char = (await db.execute(select(Character).where(Character.user_id == user.id))).scalar_one_or_none()
|
||||
if not char:
|
||||
raise HTTPException(404, "Karakter yok, önce oluştur")
|
||||
await db.refresh(char, ["skills"])
|
||||
return await _with_location(db, char)
|
||||
Loading…
Add table
Add a link
Reference in a new issue