Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

完整功能实现

核心理念: 我们不是在学“写代码“,而是在学“用 AI 搭建系统“!

这一节会教你如何像设计师一样思考项目结构,然后让 AI 帮你填充代码细节


📖 本节目标

学完本节,你将能够:

  • 理解为什么要用三层架构(而不是死记硬背)
  • ✅ 知道 JWT Token 和密码加密是什么解决什么问题(AI 会帮你写具体代码)
  • ✅ 学会用 AI 生成 Router/Service/DAO 的代码
  • ✅ 看懂报错日志,知道该问 AI 什么问题
  • ✅ 实现完整的用户注册+登录+验证流程(能跑起来就是成功!)

预计用时: 50 分钟(其中 40 分钟是让 AI 写代码,10 分钟是你理解架构)


💡 Vibe Coding 提醒

重要!开始前请记住

  • 这一节的代码很多都不需要你手写,看到复杂语法不要慌
  • 我们会明确标注哪些部分“让 AI 写“,哪些部分“你需要理解“
  • 如果遇到报错,先别急着放弃,报错信息就是给 AI 的“线索卡片“

1. 为什么要分层?(🎯 第一性原理:设计思维)

1.1 先别急着否定“简单代码“

之前你可能见过这样的代码(所有东西挤在一起):

@app.post("/api/users/register")
def register(data: UserRegisterRequest):
    # 看起来很直观对吧?一眼就能看完整个流程
    existing_user = supabase.table('users').select("*").eq('username', data.username).execute()
    if existing_user.data:
        return {"code": 400, "msg": "用户名已存在"}

    password_hash = hashlib.sha256(data.password.encode()).hexdigest()

    supabase.table('users').insert({
        "username": data.username,
        "password_hash": password_hash
    }).execute()

    return {"code": 200, "msg": "注册成功"}

✅ 对于初学者,这样写完全OK! 如果你只需要做一个简单的小网站(比如只有 3-5 个功能),这样写没问题。

1.2 但如果你想做“正经项目“,就需要“整理柜子“

现实问题

  • 就像把牙刷、衣服、锅碗瓢盆都塞进一个抽屉,刚开始还行,东西多了就乱套了
  • 当你的网站有 20+ 个功能时,所有代码挤在一起会变成“意大利面条代码“,自己都找不到哪里出问题

三层架构 = 给柜子分格子

用户请求 → Router(服务员) → Service(厨师) → DAO(仓库管理员) → 数据库
         ↓                       ↓                ↓
      返回响应 ← Router ← Service(做好菜) ← DAO(取到食材)

餐厅类比 (参考资料):

层次职责餐厅类比真实工作
Router接收请求、返回响应服务员(点菜、上菜)接收用户的注册请求,返回“注册成功“
Service业务逻辑处理厨师(决定怎么做菜)检查用户名是否重复、密码加密
DAO数据库操作仓库管理员(取食材)从数据库查数据、往数据库存数据

关键理解:服务员不用懂厨师怎么炒菜,厨师不用懂仓库管理员怎么进货。他们各干各的,互不干扰。

1.3 为什么选三层而不是两层或四层?

设计思维

  • 两层不够:如果只有 Router 和数据库,那 Router 既要管接待客人,又要管做菜,又要管进货,太累了
  • 四层太多:增加复杂度,小项目没必要
  • 三层刚刚好:职责清晰,又不会过度设计

记住:这个架构不是“死规矩“,而是前人总结的“最佳实践“。你理解了原理,以后可以根据项目大小灵活调整。


2. 项目结构(🎯 第一性原理:框架理解)

2.1 先建立“脚手架“

下面是完整的项目结构。不要被吓到! 这些文件夹就像是搭积木前先摆好的空盒子。

my-backend/
├── main.py                 # 应用入口(房子的大门)
├── db_config.py            # 数据库配置(水电开关)
├── routers/                # 路由层(接待处)
│   └── user_router.py
├── services/               # 服务层(工作室)
│   └── user_service.py
├── dao/                    # 数据访问层(仓库)
│   └── user_dao.py
├── models/                 # 数据模型(图纸)
│   └── user_model.py
├── utils/                  # 工具类(工具箱)
│   ├── response.py         # 统一响应(对讲机)
│   └── auth.py             # JWT 认证(门禁卡)
└── requirements.txt        # 依赖包(材料清单)

数据流向(框架理解核心):

1. 用户发请求 → main.py 收到
2. main.py 找到 user_router.py(服务员)
3. user_router 调用 user_service.py(厨师)
4. user_service 调用 user_dao.py(仓库)
5. user_dao 从 Supabase 数据库取数据
6. 数据原路返回 → 最终用户收到响应

2.2 🤖 让 AI 帮你创建文件结构

Vibe Coding 核心技巧:不要手动一个个建文件,用 AI!

复制这段话给 ChatGPT/Claude

我要创建一个 FastAPI 后端项目,使用三层架构(Router/Service/DAO)。
请帮我生成创建以下文件夹结构的 shell 命令(适用于 Windows/Mac/Linux):

my-backend/
├── main.py
├── db_config.py
├── routers/
│   └── user_router.py
├── services/
│   └── user_service.py
├── dao/
│   └── user_dao.py
├── models/
│   └── user_model.py
├── utils/
│   ├── response.py
│   └── auth.py
└── requirements.txt

同时,请为每个 Python 文件生成一个最基本的空模板(只包含必要的 import 和注释),让我知道每个文件是干什么的。

AI 会返回

  • 创建文件夹的命令(如 mkdir -p routers services dao models utils
  • 每个文件的空模板代码
  • 你只需要复制粘贴运行即可!

2.3 理解术语(工具认知)

看到这些文件夹名可能会蒙,我们来用人话解释

术语人话解释通俗比喻
Router路由器 = 网址分配器就像邮局,看地址(URL)把信送到对应部门
Service服务 = 干活的部门公司里的各个部门(人事部、财务部)
DAOData Access Object = 数据库搬运工仓库管理员,只负责拿东西和放东西
Model模型 = 数据的格式说明书快递单上的格式(姓名、电话、地址)
utils工具 = 通用小工具瑞士军刀,到处都能用

DAO 这个词特别迷惑:它的全称是 “Data Access Object”(数据访问对象),但你可以直接理解为**“数据库操作员”** —— 只有它有数据库的钥匙。


3. 核心概念速通(🎯 第一性原理:工具认知)

在看代码之前,先理解这些工具是干嘛的。记住:你只需要理解概念,具体语法让 AI 写!

3.1 JWT Token = 游乐园手环

问题:用户登录后,怎么证明“他还是他“?

传统方案(Session)

  • 像银行给你一张存折,每次来都要查你的账户(服务器要记住每个人)
  • 缺点:服务器累,用户多了就卡

现代方案(JWT Token) (详细解释):

  • 就像游乐园的手环 (参考)
  • 你买完票,工作人员给你手腕上戴个手环(这就是 Token)
  • 之后你玩每个项目,只需要出示手环,工作人员验证后就让你通过
  • 关键:手环上印着“有效期到今天晚上8点“,过期就得重新买票

JWT 的三部分(看不懂这些字母没关系,让 AI 写):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsImV4cCI6MTcwMDAwMDAwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
───────────────────────────────── ────────────────────────────────────── ──────────────────────────────────────
    Header(头部)                    Payload(内容)                        Signature(签名,防伪码)
    说明加密算法                    存用户ID、过期时间等                    防止有人伪造手环

你需要理解的

  • ✅ JWT 是一串加密字符串,像身份凭证
  • ✅ 登录成功后,服务器给用户发 Token
  • ✅ 用户后续请求都要带上这个 Token(放在请求头 Header 里)
  • ❌ 你不需要记住怎么写 JWT 的代码(让 AI 写!)

3.2 bcrypt 密码加密 = 把鸡蛋打碎做蛋饼

问题:用户密码怎么存?直接存 “123456” 会被黑客看光!

加密对比 (安全性解释):

方式安全性比喻问题
明文❌ 危险密码写在纸上贴墙上数据库泄露 = 所有密码泄露
MD5/SHA256⚠️ 一般把密码打碎成粉末相同密码打出来的粉末一样,可以用“彩虹表“破解
bcrypt✅ 安全每次打碎都加不同调料同一密码每次加密结果都不同,黑客无法暴力破解

bcrypt 神奇的地方 (原理):

# 同一个密码 "123456",每次加密结果都不同!
hash1 = bcrypt.hash("123456")
# 结果:$2b$12$abc123...xyz789

hash2 = bcrypt.hash("123456")
# 结果:$2b$12$def456...uvw012  (完全不一样!)

# 但验证时都能认出来是 "123456"
bcrypt.verify("123456", hash1)  # True
bcrypt.verify("123456", hash2)  # True

就像做蛋饼

  • 你无法从蛋饼还原成生鸡蛋(单向不可逆)
  • 每次做蛋饼加的调料不一样,看起来不同
  • 但你尝一口就知道“这是用鸡蛋做的“(能验证)

你需要理解的

  • ✅ 密码必须加密存储,绝不能明文
  • ✅ bcrypt 很安全,因为“每次结果都不同“
  • ✅ bcrypt 故意设计得“慢一点“(0.1秒),让黑客破解时要花几百年
  • ❌ 你不需要记 bcrypt 的 API(让 AI 写!)

3.3 🤖 AI 协作策略

接下来我们要写很多代码文件,但你不需要手写!

分工原则

  • 你负责:理解每个文件是干嘛的(Router/Service/DAO 的职责)
  • AI 负责:写具体的代码语法

给 AI 的 Prompt 模板(复制修改使用):

我正在用 FastAPI + Supabase 开发后端,使用三层架构。

请帮我生成 [文件名] 的代码,需求如下:
1. [具体功能描述,比如"实现用户注册,检查用户名是否重复"]
2. 使用 bcrypt 加密密码
3. [其他特殊要求]

请包含完整的类型注解和中文注释。

示例

我正在用 FastAPI + Supabase 开发后端,使用三层架构。

请帮我生成 services/user_service.py 的代码,需求如下:
1. 实现用户注册功能:检查用户名是否重复,使用 bcrypt 加密密码,调用 UserDAO 存储
2. 实现用户登录功能:验证密码,生成 JWT Token
3. 使用 python-jose 生成 JWT,密钥设为 "your-secret-key",有效期 7 天

请包含完整的类型注解和中文注释。

4. 完整代码实现(跟着 AI 一起搭建)

说明:下面的每个代码块会标注:

  • 🤖 让 AI 写:复杂语法,直接复制或让 AI 生成
  • 🧠 你需要理解:核心逻辑,必须看懂这部分在干嘛
  • ⚙️ 改这里:你需要修改的配置(如数据库地址)

4.1 配置文件

db_config.py (数据库配置):

⚙️ 改这里 - 把 Supabase URL 和 Key 换成你的:

from supabase import create_client, Client

SUPABASE_URL = "https://xxx.supabase.co"  # 替换
SUPABASE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI..."  # 替换

supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)

4.2 统一响应格式

utils/response.py:

🧠 你需要理解:这个文件定义了“对讲机的统一格式“,所有接口返回的数据都用这个格式。

from typing import Optional, Any
from pydantic import BaseModel

class Response(BaseModel):
    """统一响应格式"""
    code: int          # 业务状态码
    msg: str           # 提示信息
    data: Optional[Any] = None  # 返回数据

class R:
    """响应工具类"""

    @staticmethod
    def success(data: Any = None, msg: str = "操作成功"):
        return Response(code=200, msg=msg, data=data)

    @staticmethod
    def error(msg: str = "操作失败", code: int = 500):
        return Response(code=code, msg=msg, data=None)

3.3 JWT 认证工具

安装依赖:

pip install python-jose[cryptography] passlib[bcrypt]

utils/auth.py:

from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext
from typing import Optional

# JWT 配置
SECRET_KEY = "your-secret-key-here"  # 生产环境要用复杂的密钥
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_DAYS = 7

# 密码加密
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def hash_password(password: str) -> str:
    """密码加密"""
    return pwd_context.hash(password)

def verify_password(plain_password: str, hashed_password: str) -> bool:
    """验证密码"""
    return pwd_context.verify(plain_password, hashed_password)

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    """生成 JWT Token"""
    to_encode = data.copy()

    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(days=ACCESS_TOKEN_EXPIRE_DAYS)

    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

def decode_access_token(token: str) -> Optional[dict]:
    """解析 JWT Token"""
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError:
        return None

3.4 数据模型

models/user_model.py:

from pydantic import BaseModel, Field, EmailStr
from typing import Optional

class UserRegisterRequest(BaseModel):
    """用户注册请求"""
    username: str = Field(..., min_length=3, max_length=50)
    password: str = Field(..., min_length=6)
    email: Optional[EmailStr] = None

class UserLoginRequest(BaseModel):
    """用户登录请求"""
    username: str = Field(..., min_length=3)
    password: str = Field(..., min_length=6)

class UserResponse(BaseModel):
    """用户响应(不返回密码)"""
    id: int
    username: str
    email: Optional[str]
    created_at: str

3.5 DAO 层(数据访问层)

dao/user_dao.py:

from typing import Optional, Dict, Any
from db_config import supabase

class UserDAO:
    """
    用户数据访问对象

    职责:只负责数据库 CRUD 操作
    """

    @staticmethod
    def create(data: Dict[str, Any]) -> int:
        """创建用户"""
        response = supabase.table('users').insert(data).execute()
        return response.data[0]['id']

    @staticmethod
    def get_by_username(username: str) -> Optional[Dict[str, Any]]:
        """根据用户名查询用户"""
        response = supabase.table('users').select("*").eq('username', username).execute()
        return response.data[0] if response.data else None

    @staticmethod
    def get_by_id(user_id: int) -> Optional[Dict[str, Any]]:
        """根据 ID 查询用户"""
        response = supabase.table('users').select("*").eq('id', user_id).execute()
        return response.data[0] if response.data else None

    @staticmethod
    def list_all() -> list:
        """查询所有用户"""
        response = supabase.table('users').select("id, username, email, created_at").execute()
        return response.data

3.6 Service 层(服务层)

services/user_service.py:

from dao.user_dao import UserDAO
from utils.auth import hash_password, verify_password, create_access_token

class UserService:
    """
    用户业务逻辑层

    职责:业务逻辑处理、数据验证、密码加密等
    """

    @staticmethod
    def register(username: str, password: str, email: str = None) -> int:
        """
        用户注册

        Returns:
            新创建的用户 ID

        Raises:
            Exception: 用户名已存在
        """
        dao = UserDAO()

        # 1. 检查用户名是否存在
        if dao.get_by_username(username):
            raise Exception("用户名已存在")

        # 2. 密码加密
        password_hash = hash_password(password)

        # 3. 调用 DAO 创建用户
        user_id = dao.create({
            "username": username,
            "password_hash": password_hash,
            "email": email
        })

        return user_id

    @staticmethod
    def login(username: str, password: str) -> dict:
        """
        用户登录

        Returns:
            包含 token 和用户信息的字典

        Raises:
            Exception: 用户名或密码错误
        """
        dao = UserDAO()

        # 1. 查询用户
        user = dao.get_by_username(username)
        if not user:
            raise Exception("用户名或密码错误")

        # 2. 验证密码
        if not verify_password(password, user['password_hash']):
            raise Exception("用户名或密码错误")

        # 3. 生成 Token
        token = create_access_token(data={"user_id": user['id'], "username": user['username']})

        return {
            "token": token,
            "user": {
                "id": user['id'],
                "username": user['username'],
                "email": user['email']
            }
        }

    @staticmethod
    def get_user_info(user_id: int) -> dict:
        """
        获取用户信息

        Raises:
            Exception: 用户不存在
        """
        dao = UserDAO()
        user = dao.get_by_id(user_id)

        if not user:
            raise Exception("用户不存在")

        return {
            "id": user['id'],
            "username": user['username'],
            "email": user['email'],
            "created_at": user['created_at']
        }

3.7 Router 层(路由层)

routers/user_router.py:

from fastapi import APIRouter, Depends, HTTPException, Header
from models.user_model import UserRegisterRequest, UserLoginRequest
from services.user_service import UserService
from utils.response import R, Response
from utils.auth import decode_access_token
from typing import Optional

router = APIRouter(prefix="/api/users", tags=["用户管理"])

@router.post("/register", response_model=Response)
def register(data: UserRegisterRequest):
    """
    用户注册接口

    职责:
    1. 接收请求参数
    2. 调用服务层
    3. 返回统一响应
    """
    try:
        user_id = UserService.register(
            username=data.username,
            password=data.password,
            email=data.email
        )
        return R.success(msg="注册成功", data={"user_id": user_id})
    except Exception as e:
        return R.error(msg=str(e), code=400)

@router.post("/login", response_model=Response)
def login(data: UserLoginRequest):
    """用户登录接口"""
    try:
        result = UserService.login(
            username=data.username,
            password=data.password
        )
        return R.success(msg="登录成功", data=result)
    except Exception as e:
        return R.error(msg=str(e), code=400)

def get_current_user(authorization: Optional[str] = Header(None)):
    """
    获取当前登录用户(依赖项)

    用法:
        @router.get("/profile")
        def get_profile(user: dict = Depends(get_current_user)):
            return R.success(data=user)
    """
    if not authorization:
        raise HTTPException(status_code=401, detail="未登录")

    # 提取 Token(格式:Bearer xxx)
    token = authorization.replace("Bearer ", "")

    # 验证 Token
    payload = decode_access_token(token)
    if not payload:
        raise HTTPException(status_code=401, detail="Token 无效或已过期")

    return payload  # 返回 {"user_id": 123, "username": "zhangsan"}

@router.get("/profile", response_model=Response)
def get_profile(current_user: dict = Depends(get_current_user)):
    """获取当前用户信息(需要登录)"""
    try:
        user_info = UserService.get_user_info(current_user['user_id'])
        return R.success(data=user_info)
    except Exception as e:
        return R.error(msg=str(e), code=404)

@router.get("/list", response_model=Response)
def list_users():
    """查看所有用户(公开接口)"""
    from dao.user_dao import UserDAO
    users = UserDAO.list_all()
    return R.success(data=users)

3.8 主程序入口

main.py:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from routers import user_router

app = FastAPI(
    title="我的后端 API",
    description="使用三层架构的 FastAPI 项目",
    version="1.0.0"
)

# CORS 跨域配置
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 生产环境要改成具体域名
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 注册路由
app.include_router(user_router.router)

@app.get("/")
def root():
    return {"message": "Hello World", "docs": "/docs"}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)

3.9 依赖文件

requirements.txt:

fastapi
uvicorn[standard]
supabase
pydantic[email]
python-jose[cryptography]
passlib[bcrypt]

5. 报错不要慌!(🎯 第一性原理:问题定位)

重要心态调整:报错不是“失败“,而是系统在告诉你“哪里需要修复“。把报错信息当成AI 的线索卡片

5.1 常见报错速查表

报错信息人话翻译责任归属怎么办
ModuleNotFoundError: No module named 'xxx'缺少某个库你的环境运行 pip install xxx
ImportError: cannot import name 'xxx'文件导入路径错了你的代码检查文件名和 import 语句
401 UnauthorizedToken 无效或过期前端或用户重新登录获取新 Token
500 Internal Server Error服务器内部错误后端代码看终端日志,找具体错误行
connection refused数据库连不上配置问题检查 db_config.py 的 URL 和 Key

5.2 怎么看报错日志?

示例报错

Traceback (most recent call last):
  File "/app/services/user_service.py", line 25, in register
    user_id = dao.create({...})
  File "/app/dao/user_dao.py", line 15, in create
    response = supabase.table('users').insert(data).execute()
KeyError: 'id'

解读方法(从下往上看):

  1. 最后一行KeyError: 'id'):关键错误信息 = “找不到 ‘id’ 这个键”
  2. 倒数第二行dao.py line 15):出错的具体位置
  3. 推理:Supabase 返回的数据里没有 ‘id’ 字段,可能是数据库表结构不对

🤖 把报错丢给 AI 的 Prompt

我在运行 FastAPI + Supabase 项目时遇到以下报错:

[把完整报错信息粘贴在这里]

我的代码是这样的:
[粘贴出错的那几行代码]

请帮我分析原因并提供解决方案。

5.3 三层架构的报错定位

根据报错文件名判断是哪一层的问题

报错文件               → 可能原因
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
user_router.py         → 请求参数格式不对,或者调用 Service 的方式错了
user_service.py        → 业务逻辑有问题(比如验证密码的逻辑错了)
user_dao.py            → 数据库操作有问题(表名、字段名不对)
db_config.py           → 数据库连接配置错误
auth.py                → JWT 或密码加密的配置有问题

调试技巧:在怀疑的地方加 print()logger.debug(),看看变量的值对不对。

5.4 验证 AI 代码的方法

问题:AI 写的代码怎么知道对不对?

验证清单

  • 能运行python main.py 不报错,能访问 /docs
  • 逻辑对:测试注册/登录流程,符合预期
  • 错误处理:故意输入错误数据(如重复用户名),看能否正确返回错误信息
  • 安全性:密码在数据库里是加密的(不是明文 “123456”)

如果 AI 代码有问题,这样问它

你之前生成的 user_service.py 代码在测试时出现了 [具体问题描述]。

预期行为:[应该怎样]
实际行为:[实际怎样]
报错信息:[如果有]

请帮我修复这个问题。

6. 完整流程测试

6.1 启动应用

python main.py

访问 http://localhost:8000/docs

预期结果:看到 FastAPI 的自动文档界面,左侧列出了所有接口。

6.2 测试注册

POST /api/users/register
{
  "username": "testuser",
  "password": "123456",
  "email": "test@example.com"
}

返回:

{
  "code": 200,
  "msg": "注册成功",
  "data": {
    "user_id": 1
  }
}

6.3 测试登录

POST /api/users/login
{
  "username": "testuser",
  "password": "123456"
}

返回:

{
  "code": 200,
  "msg": "登录成功",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
      "id": 1,
      "username": "testuser",
      "email": "test@example.com"
    }
  }
}

复制 token 备用!

6.4 测试获取用户信息(需要登录)

请求:

GET /api/users/profile
Headers:
  Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

⚠️ 注意Bearer 后面有个空格,别漏了!

在 /docs 中测试:

  1. 点击 GET /api/users/profile
  2. 点击右上角 “Authorize” 锁头图标
  3. 在弹出框输入:Bearer 你刚才复制的token(注意 Bearer 和 token 之间有空格)
  4. 点击 “Authorize” 按钮
  5. 点击 “Try it out” → “Execute”

返回:

{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "id": 1,
    "username": "testuser",
    "email": "test@example.com",
    "created_at": "2025-01-15T10:30:00Z"
  }
}

7. 理解三层架构(回顾与总结)

7.1 完整调用链路

用户注册的完整流程:

1. 用户请求
   POST /api/users/register
   {username: "zhangsan", password: "123456"}

2. Router 层(user_router.py)
   ├─ 接收请求参数
   ├─ 调用 UserService.register()
   └─ 返回统一响应 R.success()

3. Service 层(user_service.py)
   ├─ 检查用户名是否存在(调用 DAO)
   ├─ 密码加密(hash_password)
   ├─ 调用 UserDAO.create()
   └─ 返回用户 ID

4. DAO 层(user_dao.py)
   ├─ 执行数据库插入
   └─ 返回新用户 ID

5. 数据库(Supabase)
   └─ 存储用户数据

7.2 分层的好处

1. 单一职责:

  • Router 只负责接收请求和返回响应
  • Service 只负责业务逻辑
  • DAO 只负责数据库操作

2. 易于测试:

# 可以单独测试服务层
def test_register():
    user_id = UserService.register("testuser", "password123")
    assert user_id > 0

3. 易于维护:

  • 修改业务逻辑:只改 Service
  • 修改数据库操作:只改 DAO
  • 添加新接口:只加 Router

4. 代码复用:

# 多个接口可以复用同一个服务方法
@router.post("/api/users/register")
def register(data: UserRegisterRequest):
    return UserService.register(...)

@router.post("/api/admin/users/create")
def admin_create_user(data: UserCreateRequest):
    return UserService.register(...)  # 复用

8. 常见问题(FAQ)

Q1: 为什么不用 Session 而用 JWT?

A:

特性SessionJWT
存储位置服务器客户端
服务器压力小(无状态)
扩展性好(适合分布式)
适用场景传统 WebAPI 后端

Q2: SECRET_KEY 怎么生成?

A:

import secrets

# 生成安全的随机密钥
secret = secrets.token_hex(32)
print(secret)
# "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6..."

Q3: Token 过期了怎么办?

A: 前端重新调用登录接口获取新 Token。

Q4: 怎么防止 Token 泄露?

A:

  • ✅ 使用 HTTPS
  • ✅ Token 设置过期时间
  • ✅ 前端不要把 Token 打印到控制台
  • ✅ 后端添加 IP 白名单(进阶)

💡 Vibe Coding 总结与反思

✅ 你已经掌握了什么?

恭喜!你现在已经能用 AI 搭建生产级别的后端了! 让我们回顾一下 Vibe Coding 的五大第一性原理:

1️⃣ 工具认知 ✅

  • Router/Service/DAO:你知道每层是干嘛的(服务员/厨师/仓库管理员)
  • JWT Token:游乐园手环,证明用户身份
  • bcrypt:像做蛋饼,密码加密后无法还原
  • Supabase:云端数据库,不用自己搭服务器

2️⃣ 问题定位 ✅

  • 看懂报错日志:从下往上看,找最后一行的关键信息
  • 三层架构定位:根据报错文件名(router/service/dao)判断哪一层的问题
  • 常见报错速查表:知道 500/401/404 分别代表什么
  • AI 调试助手:会把报错信息丢给 AI 求助

3️⃣ 框架理解 ✅

  • 数据流向:用户请求 → Router → Service → DAO → 数据库 → 原路返回
  • JWT 流程:登录 → 生成 Token → 客户端存储 → 后续请求带 Token → 服务器验证
  • 密码安全:明文 → 加密 → 存储 → 登录验证

4️⃣ 设计思维 ✅

  • 为什么分层:职责清晰、易于测试、易于维护、代码复用
  • 为什么用 JWT 不用 Session:无状态、适合分布式、服务器压力小
  • 为什么用 bcrypt 不用 MD5:每次加密结果不同、可以防彩虹表攻击

5️⃣ AI 协作 ✅

  • 文件结构创建:让 AI 生成 shell 命令,不手动建文件
  • 代码生成:给 AI 清晰的 Prompt,让它写具体代码
  • 代码验证:知道怎么测试 AI 代码是否正确
  • 报错修复:会把报错信息和代码一起发给 AI 求助

🎯 接下来你可以做什么?

实战任务(选一个尝试):

  1. 扩展功能:添加“修改密码“、“忘记密码“功能

    • Prompt: “我想添加修改密码功能,需要验证旧密码后才能设置新密码,请帮我生成相应的 Service 和 Router 代码”
  2. 添加文章模块:实现发布文章、查看文章列表

    • 先创建 article_dao.py, article_service.py, article_router.py
    • 让 AI 帮你生成 CRUD(增删改查)代码
  3. 前后端联调:把这个后端和前端项目连起来

    • 前端用 fetch()axios 调用接口
    • 记得处理 Token 存储和请求头

学习建议

  • 代码能跑就是成功,细节慢慢理解
  • 多用 AI,把精力放在理解架构而非记语法
  • 遇到报错别慌,报错是学习的最佳时机
  • 实战出真知,做一个真实项目胜过看十篇教程

🤔 反思问题(巩固理解)

做完这个教程,问问自己:

  1. 工具认知:如果让你给别人解释“三层架构“,你能用餐厅比喻说清楚吗?
  2. 问题定位:遇到 500 错误,你知道第一步应该看哪里吗?
  3. 框架理解:你能画出用户注册时,数据从前端到数据库的完整流程图吗?
  4. 设计思维:为什么要用 JWT 而不是 Session?(用自己的话说)
  5. AI 协作:下次要添加新功能,你会怎么问 AI?

如果这 5 个问题你都能回答,说明你真的懂了 Vibe Coding 的精髓!


📚 深入学习资源

想进一步了解这些概念,推荐阅读:


🎉 最后的话

你不是在“学编程“,你是在“用 AI 解决问题“。

传统教育告诉你要“先学会语法再动手“,但 Vibe Coding 的理念是:

  • 先理解问题(为什么需要用户登录?)
  • 再理解方案(用 JWT Token 解决)
  • 最后让 AI 写代码(具体语法交给机器)

记住:代码只是工具,思维才是核心。 你现在已经具备了用 AI 搭建后端系统的思维框架,恭喜你迈出了 Vibe Coding 的第一步!

去做点酷炫的东西吧! 🚀


📚 下一步

👉 技术术语手册

查阅常见技术术语的通俗解释,快速理解“黑话“。

返回 后端开发基础 查看完整目录。