这是《FastAPI+Vue 搭建运维平台》系列的第二篇。
第一篇我们讲了项目架构和目录结构,这篇我们来写代码——从 0 到 1 实现后端核心功能。
GitHub 仓库: github.com/WGsummer/op…
Gitee 仓库(国内访问更快): gitee.com/wgsummer/op…
本文内容
- ✅ FastAPI 项目初始化
- ✅ 数据库模型设计(SQLAlchemy)
- ✅ API 接口开发(增删改查)
- ✅ 用户认证(JWT)
- ✅ 异步任务(Celery + Redis)
前置知识:
- Python 基础
- 了解 RESTful API
- 看过第一篇(项目架构)
代码量: 约 500 行(核心功能)
1. 项目初始化
1.1 创建虚拟环境
# 创建项目目录
mkdir ops-platform && cd ops-platform
# 创建虚拟环境
python -m venv venv
source venv/bin/activate # Linux/Mac
# 或
venv\Scripts\activate # Windows
# 安装依赖
pip install fastapi uvicorn sqlalchemy python-jose[cryptography] passlib[bcrypt] python-multipart
pip install celery redis # 异步任务
1.2 项目结构
ops-platform/
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI 应用入口
│ ├── config.py # 配置文件
│ ├── database.py # 数据库连接
│ ├── models/ # 数据模型
│ │ ├── __init__.py
│ │ └── user.py
│ ├── schemas/ # Pydantic 模型
│ │ ├── __init__.py
│ │ └── user.py
│ ├── api/ # API 路由
│ │ ├── __init__.py
│ │ └── users.py
│ ├── core/ # 核心功能
│ │ ├── __init__.py
│ │ ├── security.py # 密码加密/JWT
│ │ └── celery_app.py
│ └── tasks/ # 异步任务
│ ├── __init__.py
│ └── example.py
├── requirements.txt
└── .env
2. 配置文件
2.1 环境变量(.env)
# 数据库
DATABASE_URL=sqlite:///./ops_platform.db
# 或用 MySQL/PostgreSQL
# DATABASE_URL=mysql+pymysql://user:password@localhost:3306/ops_platform
# JWT 配置
SECRET_KEY=your-secret-key-here-change-in-production
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=30
# Redis(Celery 用)
REDIS_URL=redis://localhost:6379/0
2.2 配置类(app/config.py)
from pydantic_settings import BaseSettings
from functools import lru_cache
class Settings(BaseSettings):
DATABASE_URL: str = "sqlite:///./ops_platform.db"
SECRET_KEY: str = "your-secret-key-here-change-in-production"
ALGORITHM: str = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES: int = 30
REDIS_URL: str = "redis://localhost:6379/0"
class Config:
env_file = ".env"
@lru_cache()
def get_settings() -> Settings:
return Settings()
settings = get_settings()
3. 数据库连接
3.1 数据库配置(app/database.py)
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from .config import settings
# 创建数据库引擎
engine = create_engine(
settings.DATABASE_URL,
connect_args={"check_same_thread": False} # SQLite 需要
)
# 创建会话工厂
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# 创建基类
Base = declarative_base()
# 依赖注入:获取数据库会话
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
4. 数据模型
4.1 用户模型(app/models/user.py)
from sqlalchemy import Column, Integer, String, Boolean, DateTime
from sqlalchemy.sql import func
from ..database import Base
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, index=True, nullable=False)
email = Column(String(100), unique=True, index=True, nullable=False)
hashed_password = Column(String(255), nullable=False)
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
4.2 初始化数据库(在 main.py 中)
from .database import engine, Base
# 创建所有表
Base.metadata.create_all(bind=engine)
5. Pydantic 模型(数据验证)
5.1 用户 Schema(app/schemas/user.py)
from pydantic import BaseModel, EmailStr
from datetime import datetime
from typing import Optional
# 创建用户
class UserCreate(BaseModel):
username: str
email: EmailStr
password: str
# 更新用户
class UserUpdate(BaseModel):
username: Optional[str] = None
email: Optional[EmailStr] = None
is_active: Optional[bool] = None
# 用户响应(不包含密码)
class UserResponse(BaseModel):
id: int
username: str
email: str
is_active: bool
created_at: datetime
class Config:
from_attributes = True
# Token 响应
class Token(BaseModel):
access_token: str
token_type: str
6. 核心功能
6.1 密码加密(app/core/security.py)
from passlib.context import CryptContext
from datetime import datetime, timedelta
from jose import JWTError, jwt
from ..config import settings
# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""验证密码"""
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str:
"""生成密码哈希"""
return pwd_context.hash(password)
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
"""创建 JWT Token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt
7. API 路由
7.1 用户 API(app/api/users.py)
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import List
from ..database import get_db
from ..models import user as models
from ..schemas import user as schemas
from ..core import security
router = APIRouter(prefix="/api/v1/users", tags=["用户管理"])
@router.post("/register", response_model=schemas.UserResponse, status_code=status.HTTP_201_CREATED)
def register(user: schemas.UserCreate, db: Session = Depends(get_db)):
"""用户注册"""
# 检查用户名是否已存在
db_user = db.query(models.User).filter(models.User.username == user.username).first()
if db_user:
raise HTTPException(status_code=400, detail="用户名已存在")
# 检查邮箱是否已存在
db_email = db.query(models.User).filter(models.User.email == user.email).first()
if db_email:
raise HTTPException(status_code=400, detail="邮箱已被注册")
# 创建新用户
hashed_password = security.get_password_hash(user.password)
new_user = models.User(
username=user.username,
email=user.email,
hashed_password=hashed_password
)
db.add(new_user)
db.commit()
db.refresh(new_user)
return new_user
@router.get("/", response_model=List[schemas.UserResponse])
def get_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
"""获取用户列表"""
users = db.query(models.User).offset(skip).limit(limit).all()
return users
@router.get("/{user_id}", response_model=schemas.UserResponse)
def get_user(user_id: int, db: Session = Depends(get_db)):
"""获取单个用户"""
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
return user
@router.put("/{user_id}", response_model=schemas.UserResponse)
def update_user(user_id: int, user_update: schemas.UserUpdate, db: Session = Depends(get_db)):
"""更新用户"""
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
# 更新字段
update_data = user_update.model_dump(exclude_unset=True)
for field, value in update_data.items():
setattr(user, field, value)
db.commit()
db.refresh(user)
return user
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_user(user_id: int, db: Session = Depends(get_db)):
"""删除用户"""
user = db.query(models.User).filter(models.User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="用户不存在")
db.delete(user)
db.commit()
return None
8. 用户认证
8.1 认证路由(app/api/auth.py)
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from jose import JWTError, jwt
from datetime import timedelta
from ..database import get_db
from ..models import user as models
from ..schemas import user as schemas
from ..core import security
from ..config import settings
router = APIRouter(prefix="/api/v1/auth", tags=["认证"])
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/v1/auth/login")
async def get_current_user(
token: str = Depends(oauth2_scheme),
db: Session = Depends(get_db)
) -> models.User:
"""获取当前登录用户"""
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无法验证凭据",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = db.query(models.User).filter(models.User.username == username).first()
if user is None:
raise credentials_exception
return user
@router.post("/login", response_model=schemas.Token)
def login(
form_data: OAuth2PasswordRequestForm = Depends(),
db: Session = Depends(get_db)
):
"""用户登录"""
# 验证用户
user = db.query(models.User).filter(models.User.username == form_data.username).first()
if not user or not security.verify_password(form_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
)
# 创建 Token
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = security.create_access_token(
data={"sub": user.username},
expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
@router.get("/me", response_model=schemas.UserResponse)
def get_current_user_info(current_user: models.User = Depends(get_current_user)):
"""获取当前用户信息"""
return current_user
9. 异步任务(Celery)
9.1 Celery 配置(app/core/celery_app.py)
from celery import Celery
from ..config import settings
celery_app = Celery(
"ops_platform",
broker=settings.REDIS_URL,
backend=settings.REDIS_URL
)
celery_app.conf.task_routes = {
"app.tasks.*": "main-queue"
}
9.2 示例任务(app/tasks/example.py)
from ..core.celery_app import celery_app
import time
@celery_app.task
def send_email_task(email: str, subject: str, body: str):
"""发送邮箱任务(示例)"""
print(f"发送邮件到 {email}")
print(f"主题:{subject}")
print(f"内容:{body}")
time.sleep(2) # 模拟发送延迟
return {"status": "success", "email": email}
@celery_app.task
def backup_database_task(db_name: str):
"""数据库备份任务(示例)"""
print(f"开始备份数据库:{db_name}")
time.sleep(5) # 模拟备份时间
print(f"数据库备份完成:{db_name}")
return {"status": "success", "db_name": db_name}
9.3 在 API 中调用异步任务
from fastapi import APIRouter, BackgroundTasks
from ..tasks.example import send_email_task, backup_database_task
router = APIRouter()
@router.post("/send-email")
async def send_email(email: str, subject: str, body: str):
"""发送邮箱(异步)"""
task = send_email_task.delay(email, subject, body)
return {"task_id": task.id, "status": "任务已提交"}
@router.post("/backup-db")
async def backup_database(db_name: str):
"""数据库备份(异步)"""
task = backup_database_task.delay(db_name)
return {"task_id": task.id, "status": "备份任务已提交"}
10. FastAPI 应用入口
10.1 主应用(app/main.py)
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from .database import engine, Base
from .api import users, auth
from .config import settings
# 创建数据库表
Base.metadata.create_all(bind=engine)
# 创建 FastAPI 应用
app = FastAPI(
title="运维管理平台",
description="基于 FastAPI + Vue 的运维管理平台",
version="1.0.0"
)
# CORS 配置(允许前端访问)
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173"], # Vue 开发服务器
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 注册路由
app.include_router(users.router)
app.include_router(auth.router)
@app.get("/")
def root():
return {"message": "运维管理平台 API", "version": "1.0.0"}
@app.get("/health")
def health_check():
return {"status": "healthy"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
11. 运行项目
11.1 启动 FastAPI
# 开发模式(热重载)
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 生产模式
uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
11.2 启动 Celery Worker
# 启动 Celery Worker
celery -A app.core.celery_app worker --loglevel=info
# 启动 Celery Beat(定时任务,可选)
celery -A app.core.celery_app beat --loglevel=info
11.3 启动 Redis
# Docker 启动 Redis
docker run -d -p 6379:6379 --name redis redis:latest
# 或用系统包管理器
# macOS: brew install redis
# Ubuntu: sudo apt-get install redis-server
12. 测试 API
12.1 使用 Swagger UI
- 先注册一个用户(POST /api/v1/users/register)
- 然后登录获取 Token(POST /api/v1/auth/login)
- 点击"Authorize",填入 Token
- 测试其他接口
12.2 使用 curl 测试
# 注册用户
curl -X POST "http://localhost:8000/api/v1/users/register" \
-H "Content-Type: application/json" \
-d '{"username": "admin", "email": "admin@example.com", "password": "123456"}'
# 登录
curl -X POST "http://localhost:8000/api/v1/auth/login" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin&password=123456"
# 获取当前用户信息(替换 YOUR_TOKEN)
curl -X GET "http://localhost:8000/api/v1/auth/me" \
-H "Authorization: Bearer YOUR_TOKEN"
13. 常见问题
Q1: SQLite vs MySQL/PostgreSQL?
开发阶段: SQLite 够用,方便快速原型。
生产环境: 建议 MySQL 或 PostgreSQL。
修改 .env 中的 DATABASE_URL 即可切换。
Q2: 密码忘了怎么办?
这篇先不写密码重置,第三篇前端篇再加。
临时方案:直接改数据库。
Q3: Celery 任务不执行?
检查:
- Redis 是否启动
- Celery Worker 是否运行
- 任务路由配置是否正确
14. 总结
这篇我们完成了:
✅ FastAPI 项目初始化
✅ 数据库模型设计
✅ 用户 CRUD 接口
✅ JWT 认证
✅ Celery 异步任务
代码量: 约 500 行
用时: 2-3 小时(跟着做)
代码仓库:
- GitHub: github.com/WGsummer/op…
- Gitee(国内访问更快): gitee.com/wgsummer/op…
下一篇预告
第三篇:前端开发(Vue 3 + Element Plus)
- Vue 3 项目初始化
- Element Plus UI 组件库
- 登录/注册页面
- 用户管理界面
- API 调用封装
预计发布时间: 本周内
我是运维老王,10 年运维老鸟。
有问题评论区聊,看到就回。
觉得有用,点个赞/收藏,让我知道没白写😄
系列文章:
- 第一篇:避坑指南(已发布)
- 第二篇:后端开发(本文)
- 第三篇:前端开发(待发布)
- 第四篇:部署上线(待发布)