Files
dreamweaver/backend/app/api/push_configs.py
zhangtuo e9d7f8832a Initial commit: clean project structure
- Backend: FastAPI + SQLAlchemy + Celery (Python 3.11+)
- Frontend: Vue 3 + TypeScript + Pinia + Tailwind
- Admin Frontend: separate Vue 3 app for management
- Docker Compose: 9 services orchestration
- Specs: design prototypes, memory system PRD, product roadmap

Cleanup performed:
- Removed temporary debug scripts from backend root
- Removed deprecated admin_app.py (embedded UI)
- Removed duplicate docs from admin-frontend
- Updated .gitignore for Vite cache and egg-info
2026-01-20 18:20:03 +08:00

121 lines
3.6 KiB
Python

"""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