128 lines
3.3 KiB
Python
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"
|
|
) |