Files
2026-05-07 10:49:58 +08:00

128 lines
3.3 KiB
Python

import asyncio
import time
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI, Query, Request
from typing import Optional
import aiomysql
from aiomysql import DictCursor
# ========== 日志配置 ==========
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger(__name__)
# ========== 数据库配置 ==========
DB_CONFIG = {
"host": "localhost",
"user": "cmk",
"password": "Admin@123",
"db": "tast_api",
"charset": "utf8mb4",
"autocommit": True,
"connect_timeout": 30,
}
pool = None
# ========== 最新 lifespan 写法(无废弃警告)==========
@asynccontextmanager
async def lifespan(app: FastAPI):
global pool
pool = await aiomysql.create_pool(
**DB_CONFIG,
cursorclass=DictCursor,
maxsize=100, # 加大连接池,支持高并发
minsize=20
)
logger.info("✅ 数据库连接池创建成功")
yield
pool.close()
await pool.wait_closed()
logger.info("✅ 数据库连接池已关闭")
app = FastAPI(
title="高并发异步API",
lifespan=lifespan
)
# ========== 请求日志中间件 ==========
@app.middleware("http")
async def log_middleware(request: Request, call_next):
start_time = time.time()
logger.info(f"[请求] {request.method} {request.url.path}")
try:
response = await call_next(request)
cost = round((time.time() - start_time) * 1000, 2)
logger.info(f"[完成] {response.status_code} | 耗时 {cost}ms")
return response
except Exception as e:
logger.error(f"[异常] {str(e)}")
raise
# ========== 核心接口 ==========
@app.get("/api/user_info")
async def get_user_info(
id: Optional[int] = Query(None),
name: Optional[str] = Query(None),
age: Optional[int] = Query(None),
email: Optional[str] = Query(None),
fields: Optional[str] = Query(None),
page: int = Query(1, ge=1),
size: int = Query(500, ge=1, le=5000) # 降低默认条数,更稳定
):
sql = "SELECT * FROM user_info WHERE 1=1"
params = []
if id:
sql += " AND id = %s"
params.append(id)
if name:
sql += " AND name LIKE %s"
params.append(f"%{name}%")
if age:
sql += " AND age = %s"
params.append(age)
if email:
sql += " AND email = %s"
params.append(email)
offset = (page - 1) * size
sql += " LIMIT %s, %s"
params.extend((offset, size))
try:
async with pool.acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(sql, params)
data = await cur.fetchall()
except Exception as e:
logger.error(f"[SQL执行错误] {e}")
return {"code": 500, "msg": "数据库错误", "data": []}
if fields:
fields_list = [f.strip() for f in fields.split(",")]
data = [{k: v for k, v in item.items() if k in fields_list} for item in data]
logger.info(f"[返回数据] {len(data)}")
return {
"code": 200,
"msg": "success",
"data": data
}
# ========== 单文件启动 ==========
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=True,
log_level="info"
)