feat: Phase 0 — monorepo 骨架 + 全表迁移 + FastAPI/Next 骨架 + CI

- uv(workspace) + pnpm monorepo;docker-compose(pg+api+web)
- SQLAlchemy 16 MVP 表 + Alembic 初版迁移(无漂移,users stub)
- FastAPI 骨架:统一错误信封(带 request_id) + structlog + /jobs/:id + OpenAPI
- Next.js 骨架:纸感主题 token + OpenAPI→TS 客户端代码生成(gen:api)
- CI(ruff/mypy/pytest + pg service + alembic 漂移校验)
- 四份设计规格(PRODUCT/UX/ARCHITECTURE/DEV_PLAN) + CLAUDE.md
This commit is contained in:
Yaojia Wang
2026-06-18 11:38:28 +02:00
commit d3dc620a71
74 changed files with 12960 additions and 0 deletions

View File

View File

@@ -0,0 +1,17 @@
"""根路由 + 健康检查。"""
from __future__ import annotations
from fastapi import APIRouter
router = APIRouter()
@router.get("/")
async def root() -> dict[str, str]:
return {"service": "ww-api", "status": "ok"}
@router.get("/health")
async def health() -> dict[str, str]:
return {"status": "ok"}

View File

@@ -0,0 +1,33 @@
"""长任务轮询端点 GET /jobs/:idARCH §7.4)。"""
from __future__ import annotations
import uuid
from typing import Annotated
from fastapi import APIRouter, Depends
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from ww_db import get_session
from ww_db.models import Job
from ww_shared import AppError, ErrorCode
router = APIRouter(prefix="/jobs", tags=["jobs"])
@router.get("/{job_id}")
async def get_job(
job_id: uuid.UUID,
session: Annotated[AsyncSession, Depends(get_session)],
) -> dict[str, object]:
job = (await session.execute(select(Job).where(Job.id == job_id))).scalar_one_or_none()
if job is None:
raise AppError(ErrorCode.NOT_FOUND, f"job {job_id} not found")
return {
"id": str(job.id),
"kind": job.kind,
"status": job.status,
"progress": job.progress,
"result": job.result,
"error": job.error,
}