"""Push configuration APIs.""" from datetime import time from fastapi import APIRouter, Depends, HTTPException, Response, status from pydantic import BaseModel from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import require_user from app.db.database import get_db from app.db.models import ChildProfile, PushConfig, User router = APIRouter() class PushConfigUpsert(BaseModel): """Upsert push config payload.""" child_profile_id: str push_time: time | None = None push_days: list[int] | None = None enabled: bool | None = None class PushConfigResponse(BaseModel): """Push config response.""" id: str child_profile_id: str push_time: time | None push_days: list[int] enabled: bool class Config: from_attributes = True class PushConfigListResponse(BaseModel): """Push config list response.""" configs: list[PushConfigResponse] total: int def _validate_push_days(push_days: list[int]) -> list[int]: invalid = [day for day in push_days if day < 0 or day > 6] if invalid: raise HTTPException(status_code=400, detail="推送日期必须在 0-6 之间") return list(dict.fromkeys(push_days)) @router.get("/push-configs", response_model=PushConfigListResponse) async def list_push_configs( user: User = Depends(require_user), db: AsyncSession = Depends(get_db), ): """List push configs for current user.""" result = await db.execute( select(PushConfig).where(PushConfig.user_id == user.id) ) configs = result.scalars().all() return PushConfigListResponse(configs=configs, total=len(configs)) @router.put("/push-configs", response_model=PushConfigResponse) async def upsert_push_config( payload: PushConfigUpsert, response: Response, user: User = Depends(require_user), db: AsyncSession = Depends(get_db), ): """Create or update push config for a child profile.""" result = await db.execute( select(ChildProfile).where( ChildProfile.id == payload.child_profile_id, ChildProfile.user_id == user.id, ) ) profile = result.scalar_one_or_none() if not profile: raise HTTPException(status_code=404, detail="孩子档案不存在") result = await db.execute( select(PushConfig).where(PushConfig.child_profile_id == payload.child_profile_id) ) config = result.scalar_one_or_none() if config is None: if payload.push_time is None or payload.push_days is None: raise HTTPException(status_code=400, detail="创建配置需要提供推送时间和日期") push_days = _validate_push_days(payload.push_days) config = PushConfig( user_id=user.id, child_profile_id=payload.child_profile_id, push_time=payload.push_time, push_days=push_days, enabled=True if payload.enabled is None else payload.enabled, ) db.add(config) await db.commit() await db.refresh(config) response.status_code = status.HTTP_201_CREATED return config updates = payload.model_dump(exclude_unset=True) if "push_days" in updates and updates["push_days"] is not None: updates["push_days"] = _validate_push_days(updates["push_days"]) if "push_time" in updates and updates["push_time"] is None: raise HTTPException(status_code=400, detail="推送时间不能为空") for key, value in updates.items(): if key == "child_profile_id": continue if value is not None: setattr(config, key, value) await db.commit() await db.refresh(config) return config