mirror of
https://git.oceanpay.cc/danial/kami_apple_exchage.git
synced 2025-12-18 22:29:09 +00:00
- 新增 CODEBUDDY.md、GEMINI.md、GEMINI_CN.md 等项目文档 - 更新 Dockerfile 和其他配置文件 - 优化部分代码结构,如 orders.py、tasks.py 等 - 新增 .dockerignore 文件
297 lines
9.3 KiB
Python
297 lines
9.3 KiB
Python
"""
|
||
Apple Gift Card Exchange Backend
|
||
FastAPI应用程序入口点
|
||
"""
|
||
|
||
import asyncio
|
||
import os
|
||
import sys
|
||
from contextlib import asynccontextmanager
|
||
from pathlib import Path
|
||
import uvicorn
|
||
|
||
from fastapi import FastAPI
|
||
from fastapi.middleware.trustedhost import TrustedHostMiddleware
|
||
|
||
from app.api import api_router
|
||
from app.core.redis_manager import redis_manager
|
||
from app.core.config import get_settings
|
||
from app.core.database import close_database, init_database
|
||
from app.core.exceptions import setup_exception_handlers
|
||
from app.core.graceful_shutdown import graceful_shutdown_manager
|
||
from app.core.log import get_logger
|
||
from app.core.middleware import (
|
||
setup_cors_middleware,
|
||
setup_custom_middleware,
|
||
add_api_logging_middleware,
|
||
add_metrics_middleware,
|
||
)
|
||
from app.core.telemetry import (
|
||
initialize_telemetry,
|
||
shutdown_telemetry,
|
||
)
|
||
|
||
# 设置控制台编码为UTF-8,确保中文字符正常显示
|
||
if sys.platform.startswith("win"):
|
||
os.system("chcp 65001 > nul") # Windows设置控制台为UTF-8
|
||
|
||
settings = get_settings()
|
||
logger = get_logger(__name__)
|
||
|
||
# 请求跟踪
|
||
active_requests = set()
|
||
shutdown_requested = False
|
||
|
||
|
||
@asynccontextmanager
|
||
async def lifespan(app: FastAPI):
|
||
"""应用生命周期管理"""
|
||
logger.info("🚀 启动Apple Gift Card Exchange Backend")
|
||
|
||
try:
|
||
# 设置优雅关闭信号处理器
|
||
graceful_shutdown_manager.setup_signal_handlers()
|
||
|
||
# 按照优先级注册关闭回调(相反顺序关闭)
|
||
# 1. 先关闭 OpenTelemetry(需要首先停止新的追踪和指标收集)
|
||
graceful_shutdown_manager.register_shutdown_callback(shutdown_telemetry)
|
||
|
||
# 2. 然后关闭 Redis(可能被 OpenTelemetry 使用)
|
||
graceful_shutdown_manager.register_shutdown_callback(redis_manager.close_redis)
|
||
|
||
# 3. 最后关闭数据库(基础资源)
|
||
graceful_shutdown_manager.register_shutdown_callback(close_database)
|
||
|
||
logger.info("✅ 优雅关闭机制已设置")
|
||
|
||
# 初始化 OpenTelemetry
|
||
await initialize_telemetry()
|
||
logger.info("✅ OpenTelemetry 初始化完成")
|
||
|
||
# 初始化数据库
|
||
await init_database()
|
||
logger.info("✅ 数据库初始化完成")
|
||
|
||
# 创建数据库表
|
||
from app.models import create_tables
|
||
|
||
await create_tables()
|
||
logger.info("✅ 数据库表创建完成")
|
||
|
||
# 创建必要的目录
|
||
Path(settings.UPLOAD_DIR).mkdir(parents=True, exist_ok=True)
|
||
Path(settings.SNAPSHOT_DIR).mkdir(parents=True, exist_ok=True)
|
||
Path(settings.HTML_DIR).mkdir(parents=True, exist_ok=True)
|
||
logger.info("✅ 文件目录创建完成")
|
||
|
||
# 应用启动完成
|
||
logger.info("🎉 应用启动完成")
|
||
|
||
yield
|
||
|
||
# 应用关闭清理 - 优雅关闭已经处理了大部分清理工作
|
||
logger.info("🔄 开始应用关闭清理...")
|
||
|
||
global shutdown_requested
|
||
shutdown_requested = True
|
||
|
||
# 等待活跃请求完成
|
||
max_wait_time = 30
|
||
wait_interval = 0.5
|
||
waited_time = 0
|
||
|
||
while active_requests and waited_time < max_wait_time:
|
||
logger.info(
|
||
f"⏳ 等待 {len(active_requests)} 个活跃请求完成... ({waited_time:.1f}s/{max_wait_time}s)"
|
||
)
|
||
await asyncio.sleep(wait_interval)
|
||
waited_time += wait_interval
|
||
|
||
if active_requests:
|
||
logger.warning(f"⚠️ 仍有 {len(active_requests)} 个请求未完成,强制关闭")
|
||
else:
|
||
logger.info("✅ 所有活跃请求已完成")
|
||
|
||
logger.info("👋 应用关闭完成")
|
||
|
||
except Exception as e:
|
||
logger.error(f"❌ 应用生命周期管理异常: {e}")
|
||
raise
|
||
|
||
|
||
def create_app() -> FastAPI:
|
||
"""创建FastAPI应用实例"""
|
||
|
||
app = FastAPI(
|
||
title=settings.APP_NAME,
|
||
version=settings.APP_VERSION,
|
||
description="Apple礼品卡兑换服务后端API - 基于FastAPI的现代异步微服务架构",
|
||
summary="Apple Gift Card Exchange Backend API",
|
||
docs_url="/docs" if settings.DEBUG else None,
|
||
redoc_url="/redoc" if settings.DEBUG else None,
|
||
openapi_url="/openapi.json" if settings.DEBUG else None,
|
||
lifespan=lifespan,
|
||
servers=[
|
||
{
|
||
"url": f"http://{settings.HOST}:{settings.PORT}",
|
||
"description": f"{settings.ENVIRONMENT.value.title()} server",
|
||
}
|
||
],
|
||
openapi_tags=[
|
||
{
|
||
"name": "Health Check",
|
||
"description": "System health status check related interfaces",
|
||
},
|
||
{
|
||
"name": "Thread Pool Management",
|
||
"description": "Thread pool configuration and management related interfaces",
|
||
},
|
||
{
|
||
"name": "Timeout Configuration",
|
||
"description": "System timeout parameter configuration related interfaces",
|
||
},
|
||
{
|
||
"name": "Crawler Management",
|
||
"description": "Crawler service control and status management related interfaces",
|
||
},
|
||
{
|
||
"name": "System Configuration",
|
||
"description": "System global configuration management related interfaces",
|
||
},
|
||
{
|
||
"name": "User Management",
|
||
"description": "User account and permission management related interfaces",
|
||
},
|
||
{
|
||
"name": "Order Management",
|
||
"description": "Order processing and status management related interfaces",
|
||
},
|
||
{
|
||
"name": "Playwright Service",
|
||
"description": "Browser automation service related interfaces",
|
||
},
|
||
{
|
||
"name": "Link Management",
|
||
"description": "Link management related interfaces",
|
||
},
|
||
{
|
||
"name": "Gift Card Management",
|
||
"description": "Gift card management related interfaces",
|
||
},
|
||
{
|
||
"name": "User Data Management",
|
||
"description": "User data management related interfaces",
|
||
},
|
||
],
|
||
)
|
||
|
||
# 添加中间件
|
||
setup_middleware(app)
|
||
|
||
# 添加路由
|
||
setup_routes(app)
|
||
|
||
# 添加异常处理器
|
||
setup_exception_handlers(app)
|
||
|
||
return app
|
||
|
||
|
||
def setup_middleware(app: FastAPI):
|
||
"""设置中间件"""
|
||
|
||
# 请求跟踪中间件
|
||
@app.middleware("http")
|
||
async def request_tracking_middleware(request, call_next):
|
||
"""请求跟踪中间件 - 跟踪活跃请求"""
|
||
if shutdown_requested:
|
||
from fastapi import HTTPException
|
||
|
||
raise HTTPException(status_code=503, detail="服务正在关闭中,请稍后重试")
|
||
|
||
# 生成请求ID并添加到活跃请求集合
|
||
request_id = f"{id(request)}_{asyncio.current_task()}"
|
||
active_requests.add(request_id)
|
||
|
||
try:
|
||
response = await call_next(request)
|
||
return response
|
||
finally:
|
||
# 请求完成后从活跃请求集合中移除
|
||
active_requests.discard(request_id)
|
||
|
||
# 信任主机中间件
|
||
if settings.ALLOWED_HOSTS != ["*"]:
|
||
app.add_middleware(TrustedHostMiddleware, allowed_hosts=settings.ALLOWED_HOSTS)
|
||
|
||
# 设置CORS中间件
|
||
setup_cors_middleware(app)
|
||
|
||
# 设置自定义中间件(日志、安全头等)
|
||
setup_custom_middleware(app)
|
||
|
||
# # 设置 OpenTelemetry 中间件
|
||
# add_metrics_middleware(app)
|
||
|
||
# 添加API日志记录中间件
|
||
add_api_logging_middleware(app)
|
||
|
||
|
||
def setup_routes(app: FastAPI):
|
||
"""设置路由"""
|
||
|
||
# API路由
|
||
app.include_router(api_router, prefix="/api")
|
||
|
||
# 健康检查端点
|
||
@app.get("/", tags=["root"])
|
||
async def root():
|
||
"""根路径健康检查"""
|
||
base_url = f"http://{settings.HOST}:{settings.PORT}"
|
||
return {
|
||
"message": "Apple Gift Card Exchange Backend",
|
||
"version": settings.APP_VERSION,
|
||
"status": "healthy",
|
||
"environment": settings.ENVIRONMENT.value,
|
||
"api_docs": {
|
||
"swagger_ui": (
|
||
f"{base_url}/docs" if settings.DEBUG else "仅在开发环境可用"
|
||
),
|
||
"redoc": f"{base_url}/redoc" if settings.DEBUG else "仅在开发环境可用",
|
||
"openapi_json": (
|
||
f"{base_url}/openapi.json" if settings.DEBUG else "仅在开发环境可用"
|
||
),
|
||
"openapi_version": "3.0.0",
|
||
"specification": "OpenAPI 3.0.0 Specification",
|
||
},
|
||
"api_endpoints": {
|
||
"base_api": f"{base_url}/api",
|
||
"health_check": f"{base_url}/",
|
||
"api_v1": f"{base_url}/api/v1",
|
||
},
|
||
"server_info": {
|
||
"host": settings.HOST,
|
||
"port": settings.PORT,
|
||
"debug_mode": settings.DEBUG,
|
||
},
|
||
}
|
||
|
||
|
||
# 创建应用实例
|
||
app = create_app()
|
||
|
||
# 导出常用函数供其他模块使用
|
||
__all__ = ["app", "active_requests", "shutdown_requested"]
|
||
|
||
|
||
if __name__ == "__main__":
|
||
uvicorn.run(
|
||
"app.main:app",
|
||
host=settings.HOST,
|
||
port=settings.PORT,
|
||
reload=settings.DEBUG,
|
||
workers=1 if settings.DEBUG else settings.WORKERS,
|
||
log_level=settings.LOG_LEVEL.lower(),
|
||
access_log=True,
|
||
)
|