Files
shaguabijia-app-server/app/main.py
T
marco 4d392f44f5 feat(invite): 好友邀请后端(注册即生效,邀请人+被邀请人各发1万金币) (#24)
- user.invite_code 列 + invite_relation 表(invitee_user_id 唯一 = 幂等防重复发奖)
- GET /api/v1/invite/me(我的码+分享链接+战绩)、POST /api/v1/invite/bind
- 复用 wallet.grant_coins 同事务发币;自邀屏蔽 / 无效码 / 新用户闸(注册72h内才发)
- alembic 迁移(11a1d08c6f55 -> invite_code_and_relation)+ 8 个测试(钱路/幂等/并发兜底全覆盖)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Reviewed-on: #24
2026-06-08 04:57:55 +08:00

109 lines
3.6 KiB
Python

"""FastAPI 入口。
通过 `uvicorn app.main:app --reload` 启动。
"""
from __future__ import annotations
import logging
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from app.api.v1.ad import router as ad_router
from app.api.v1.auth import router as auth_router
from app.api.v1.compare import router as compare_router
from app.api.v1.compare_milestone import router as compare_milestone_router
from app.api.v1.compare_record import router as compare_record_router
from app.api.v1.coupon import router as coupon_router
from app.api.internal.price import router as internal_price_router
from app.api.v1.feedback import router as feedback_router
from app.api.v1.invite import router as invite_router
from app.api.v1.meituan import router as meituan_router
from app.api.v1.order import router as order_router
from app.api.v1.platform import router as platform_router
from app.api.v1.report import router as report_router
from app.api.v1.savings import router as savings_router
from app.api.v1.signin import router as signin_router
from app.api.v1.tasks import router as tasks_router
from app.api.v1.user import router as user_router
from app.api.v1.wallet import router as wallet_router
from app.api.v1.wxpay import router as wxpay_router
from app.core.config import settings
from app.core.logging import setup_logging
setup_logging(debug=settings.APP_DEBUG)
logger = logging.getLogger("shagua.main")
@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
# 提示而非强制建表:生产用 alembic upgrade head,本地 dev 也建议先跑一次 migration。
# 这里只 log,不自动 create_all,避免生产环境意外建出过时 schema。
logger.info(
"started env=%s debug=%s db=%s",
settings.APP_ENV,
settings.APP_DEBUG,
settings.DATABASE_URL.split("://", 1)[0],
)
yield
logger.info("shutting down")
app = FastAPI(
title=settings.APP_NAME,
version="0.1.0",
docs_url="/docs" if not settings.is_prod else None,
redoc_url="/redoc" if not settings.is_prod else None,
lifespan=lifespan,
)
if settings.cors_origins_list:
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins_list,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/health", tags=["meta"])
def health() -> dict[str, str]:
return {"status": "ok"}
app.include_router(auth_router)
app.include_router(user_router)
app.include_router(feedback_router)
app.include_router(invite_router)
app.include_router(coupon_router)
app.include_router(compare_router)
app.include_router(compare_record_router)
app.include_router(compare_milestone_router)
app.include_router(meituan_router)
app.include_router(wallet_router)
app.include_router(wxpay_router)
app.include_router(signin_router)
app.include_router(tasks_router)
app.include_router(savings_router)
app.include_router(ad_router)
app.include_router(order_router)
app.include_router(report_router)
# 内部(server→server)端点:pricebot 上报价格观测,靠共享密钥头校验,不对客户端开放。
app.include_router(internal_price_router)
app.include_router(platform_router)
# 用户上传文件(头像)静态服务。生产可改由 nginx 直接 serve MEDIA_ROOT。
_media_root = Path(settings.MEDIA_ROOT)
_media_root.mkdir(parents=True, exist_ok=True)
app.mount(
settings.MEDIA_URL_PREFIX,
StaticFiles(directory=str(_media_root)),
name="media",
)