baseline: Clover 独立仓库首次基线提交

将 Clover 从上层产品包旧仓库中独立出来,建立专属版本控制。
当前状态=纵切片端到端已打通(登录→选品→出文出图→审核→下载包),
M1文案质量去套路化已验收。此提交作为后续按核销清单逐条修复的基线。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
yangqianqian
2026-06-16 11:30:22 +08:00
commit 6a2632da70
253 changed files with 27467 additions and 0 deletions

95
backend/main.py Normal file
View File

@@ -0,0 +1,95 @@
"""
main.py — FastAPI 应用入口
统一响应包络 / 全局异常处理 / 健康检查 / 路由注册
"""
import logging
import os
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from app.core.config import get_settings
from app.core.response import CloverHTTPException, ErrorCode
logger = logging.getLogger(__name__)
settings = get_settings()
def create_app() -> FastAPI:
app = FastAPI(
title="Clover API",
version="0.1.0",
docs_url="/docs" if settings.APP_ENV != "production" else None,
redoc_url=None,
)
# ── CORS ──────────────────────────────────────────
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 生产环境收窄到前端域名
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ── 全局异常处理(统一包络)──────────────────────
@app.exception_handler(CloverHTTPException)
async def clover_http_exception_handler(request: Request, exc: CloverHTTPException):
return JSONResponse(
status_code=exc.status_code,
content={"code": exc.biz_code, "message": exc.biz_message},
)
@app.exception_handler(Exception)
async def generic_exception_handler(request: Request, exc: Exception):
logger.error("Unhandled exception: %s %s%s", request.method, request.url, exc)
return JSONResponse(
status_code=500,
content={"code": ErrorCode.SERVER_ERROR, "message": "服务器内部错误"},
)
# ── 健康检查Docker 健康检查用)──────────────────
@app.get("/health")
async def health():
return {"status": "ok"}
# ── 路由注册(每个模块路由独立文件,这里汇总注册)─
_register_routers(app)
# ── 静态文件G4坑修复图片 /uploads 路由)──────
uploads_dir = os.path.join(os.path.dirname(__file__), "uploads")
os.makedirs(uploads_dir, exist_ok=True)
app.mount("/uploads", StaticFiles(directory=uploads_dir), name="uploads")
return app
def _register_routers(app: FastAPI) -> None:
from app.api.v1.auth import router as auth_router
from app.api.v1.api_keys import router as api_keys_router
from app.api.v1.products import router as products_router
from app.api.v1.benchmarks import router as benchmarks_router
from app.api.v1.tasks import router as tasks_router
from app.api.v1.task_actions import router as task_actions_router
from app.api.v1.review import router as review_router
from app.api.v1.delivery import router as delivery_router
from app.api.v1.stream import router as stream_router
from app.api.v1.workspaces import router as workspaces_router
prefix = "/api/v1"
app.include_router(auth_router, prefix=prefix)
app.include_router(api_keys_router, prefix=prefix)
app.include_router(products_router, prefix=prefix)
app.include_router(benchmarks_router, prefix=prefix)
app.include_router(tasks_router, prefix=prefix)
app.include_router(task_actions_router, prefix=prefix)
app.include_router(review_router, prefix=prefix)
app.include_router(delivery_router, prefix=prefix)
app.include_router(stream_router, prefix=prefix)
app.include_router(workspaces_router, prefix=prefix)
app = create_app()