本文是 头条【vue+fastapi 】全栈教学项目系列之《架构说明》
📎 配套开源项目(均已开源,欢迎 Star / Fork):
- 前端仓库(Vue3 + Vite):https://gitee.com/rukei/toutiao_frontend ·
git clone git@gitee.com:rukei/toutiao_frontend.git- 后端仓库(FastAPI):https://gitee.com/rukei/toutiao ·
git clone git@gitee.com:rukei/toutiao.git
📐 项目架构与文件结构说明书
目的:全面理解项目的组织方式和设计思路
适合时机:开发过程中随时查阅,理解"为什么这样设计"
一、项目总览
1.1 技术架构图
┌─────────────────────────────────────────────────────────────────┐
│ 用户浏览器 │
│ (Chrome / Safari / 手机浏览器) │
└──────────────────────────┬──────────────────────────────────────┘
│ HTTP/HTTPS
▼
┌─────────────────────────────────────────────────────────────────┐
│ 前端 (Frontend) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Vue 3 + Vite │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │
│ │ │ Vue │ │ Vant UI │ │ Vue │ │ Pinia │ │ │
│ │ │ Router │ │ 组件库 │ │ I18n │ │ 状态管理 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────────┘ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Axios │ │ Marked │ │ DOMPure │ │ │
│ │ │ HTTP │ │ MD渲染 │ │ XSS防护 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └──────────────────────────┬──────────────────────────────┘ │
└─────────────────────────────┼──────────────────────────────────┘
│ RESTful API
▼
┌─────────────────────────────────────────────────────────────────┐
│ 后端 (Backend) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ FastAPI │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │ │
│ │ │ Routes │ │ Schemas │ │ CRUD │ │ Models │ │ │
│ │ │ 路由层 │ │ 数据验证│ │ 数据操作│ │ ORM模型 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────────┘ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Auth │ │ Security│ │ Response│ │ │
│ │ │ JWT认证 │ │ 密码加密│ │ 统一响应│ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └──────────────────────────┬──────────────────────────────┘ │
│ ┌──────────────────────────┴──────────────────────────────┐ │
│ │ 数据层 (Data Layer) │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ MySQL │ │ Redis │ │ │
│ │ │ (持久化存储) │ │ (缓存/会话) │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
1.2 前后端交互流程
用户操作 → 前端组件 → Vuex/Pinia Store → Axios HTTP请求
│
▼
FastAPI Router
│
▼
Pydantic验证
│
▼
CRUD操作
│
▼
┌───────┴───────┐
▼ ▼
MySQL Redis
│ │
└───────┬───────┘
▼
JSON响应
│
▼
前端接收→更新状态→视图更新
二、完整目录树
2.1 项目顶层结构
toutiao_heima-main/ # 项目根目录
├── README.md # 项目说明文档
├── docs_output/ # 教学文档输出目录(本套文档)
│ ├── 01_项目导学与学习大纲.md
│ ├── 02_双系统零基础环境搭建手册.md
│ ├── 03_分阶段分步实操指引.md
│ ├── 04_项目架构与文件结构说明书.md ← 你在这里
│ ├── 05_源码注释与核心知识点手册.md
│ ├── 06_高频报错排查手册.md
│ ├── 07_功能测试与学生验收清单.md
│ └── 08_项目部署与进阶拓展指南.md
│
├── toutiao_frontend/ # 【前端项目】Vue 3 + Vite
│ ├── public/ # 静态资源目录
│ │ └── favicon.ico
│ ├── src/ # 源代码目录
│ │ ├── views/ # 页面组件(12个)
│ │ ├── components/ # 公共组件
│ │ ├── router/ # 路由配置
│ │ ├── api/ # 网络请求层(request 实例 + 拦截器)
│ │ ├── store/ # 状态管理(Pinia)
│ │ ├── config/ # 配置文件(后端地址常量)
│ │ ├── i18n/ # 国际化
│ │ ├── utils/ # 工具函数
│ │ ├── App.vue # 根组件
│ │ ├── main.js # 入口文件
│ │ └── style.css # 全局样式
│ ├── index.html # HTML模板
│ ├── package.json # 项目依赖配置(已显式声明 vue)
│ ├── vite.config.js # Vite构建配置(构建期 drop console)
│ ├── .env.example # 环境变量模板(真实 .env 不入库)
│ └── .gitignore # 已排除真实 .env 与 node_modules
│
└── toutiao_backend/ # 【后端项目】FastAPI + MySQL
├── main.py # 应用入口
├── database.py # 数据库初始化
├── models.py # 数据模型(旧版)
├── auth.py # 认证模块(旧版)
├── requirements.txt # Python依赖
├── config/ # 配置模块
│ ├── db_conf.py # 数据库配置
│ ├── cache_conf.py # Redis配置
│ └── ai_conf.py # AI/OpenAI配置
├── models/ # ORM模型(新版分层)
│ ├── users.py # 用户模型
│ ├── news.py # 新闻模型
│ ├── favorite.py # 收藏模型
│ └── history.py # 历史模型
├── schemas/ # Pydantic数据验证
│ ├── base.py # 基础Schema
│ ├── users.py # 用户Schema
│ ├── favorite.py # 收藏Schema
│ ├── histroy.py # 历史Schema
│ └── aichat.py # AI Schema
├── crud/ # 数据库操作层
│ ├── users.py # 用户CRUD
│ ├── news.py # 新闻CRUD
│ ├── news_cache.py # 新闻缓存CRUD
│ ├── favorite.py # 收藏CRUD
│ └── history.py # 历史CRUD
├── routers/ # API路由层
│ ├── __init__.py
│ ├── users.py # 用户路由
│ ├── news.py # 新闻路由
│ ├── favorite.py # 收藏路由
│ ├── history.py # 历史路由
│ └── aichat.py # AI路由
└── utils/ # 工具模块
├── auth.py # JWT认证
├── security.py # 密码加密
├── response.py # 统一响应
├── exception.py # 自定义异常
└── exception_handlers.py # 异常处理器
三、前端文件详解
3.1 入口文件
main.js - 应用入口
文件路径 :toutiao_frontend/src/main.js
作用:Vue应用的起点,负责创建Vue实例并挂载到DOM
核心职责:
- 创建Vue应用实例
- 注册全局插件(Router、Pinia、I18n)
- 引入全局样式
- 挂载应用到
#appDOM元素
依赖关系:
main.js
├── App.vue (根组件)
├── router/index.js (路由)
├── stores/* (状态管理)
└── i18n (国际化)
App.vue - 根组件
文件路径 :toutiao_frontend/src/App.vue
作用:应用的最外层组件,包含全局布局
核心功能:
- 渲染
<router-view />(路由出口) - 条件显示底部TabBar导航
- 应用主题类名(亮色/暗色)
组件结构:
App.vue
├── <router-view /> ← 页面内容区(根据路由变化)
└── <TabBar /> ← 底部导航(某些页面隐藏)
vite.config.js - 构建配置
文件路径 :toutiao_frontend/vite.config.js
作用:Vite构建工具的配置文件
关键配置项:
| 配置项 | 值 | 说明 |
|---|---|---|
| plugins | [vue()] |
使用Vue插件 |
| server.port | 5173 |
开发服务器端口 |
| server.proxy./api | localhost:8000 |
API代理到后端 |
| server.proxy./docs | localhost:8000 |
Swagger文档代理 |
为什么需要代理?
- 开发环境前端运行在
:5173,后端在:8000 - 浏览器的同源策略禁止跨域请求
- Vite代理将
/api开头的请求转发到后端 - 生产环境不需要(前端打包后和后端同源部署)
3.2 路由配置
router/index.js - 路由定义
文件路径 :toutiao_frontend/src/router/index.js
作用:定义URL路径与页面组件的映射关系
路由表:
| 路径 | 组件 | 是否显示TabBar | 说明 |
|---|---|---|---|
/ |
Home.vue | ✅ | 首页(新闻列表) |
/category |
Category.vue | ❌ | 分类浏览页 |
/news/:id |
NewsDetail.vue | ❌ | 新闻详情页(动态参数) |
/favorite |
Favorite.vue | ✅ | 我的收藏 |
/history |
History.vue | ✅ | 浏览历史 |
/aichat |
AIChat.vue | ✅ | AI问答 |
/my |
My.vue | ✅ | 个人中心 |
/login |
Login.vue | ❌ | 登录页 |
/register |
Register.vue | ❌ | 注册页 |
/profile |
Profile.vue | ❌ | 编辑资料 |
/settings |
Settings.vue | ❌ | 设置页 |
路由守卫:
beforeEach钩子:根据路由meta.title设置页面标题
3.3 页面组件(views/)
视图组件总览
src/views/
├── Home.vue # 首页 - 新闻列表、分类标签、下拉刷新、上拉加载
├── Login.vue # 登录页 - 用户名密码表单
├── Register.vue # 注册页 - 用户名密码手机号表单
├── NewsDetail.vue # 新闻详情 - 内容渲染、收藏、评论
├── Category.vue # 分类浏览 - 按分类查看新闻
├── Favorite.vue # 我的收藏 - 收藏列表
├── History.vue # 浏览历史 - 历史记录列表
├── AIChat.vue # AI问答 - 对话界面
├── My.vue # 个人中心 - 用户信息、功能入口
├── Profile.vue # 编辑资料 - 修改昵称头像简介
└── Settings.vue # 设置页 - 主题切换、语言切换
各页面详细说明
Home.vue - 首页
功能清单:
- 顶部导航栏(标题 + 搜索入口)
- 分类标签栏(横向滚动,9个分类)
- 新闻列表(封面图 + 标签 + 摘要 + 元信息)
- 下拉刷新(PullRefresh)
- 上拉加载更多(List组件)
- 点击进入详情页
使用的Store :useNewsStore()
API调用:
GET /api/news/categories- 获取分类GET /api/news/list- 获取新闻列表(分页)
组件依赖 :NewsItem.vue(新闻列表项子组件)
Login.vue - 登录页
功能清单:
- Logo和标语
- 用户名输入框(必填验证)
- 密码输入框(必填验证)
- 登录按钮(带Loading状态)
- 注册入口链接
使用的Store :useUserStore()
API调用 :POST /api/user/login
交互逻辑:
- 用户填写表单
- 点击登录 → 显示Loading
- 调用登录API
- 成功 → 保存Token → 跳转首页
- 失败 → 显示错误提示
Register.vue - 注册页
功能清单:
- 用户名输入(3-50字符)
- 密码输入(6-100字符)
- 确认密码输入(必须一致)
- 手机号输入(可选,11位数字)
- 注册协议勾选
- 注册按钮
API调用 :POST /api/user/register
NewsDetail.vue - 新闻详情页
功能清单:
- 顶部导航(标题 + 收藏按钮)
- 新闻标题
- 元信息(作者、来源、时间、阅读量)
- 封面图
- Markdown正文渲染(防XSS)
- 底部互动栏(点赞、评论、分享)
- 评论区(输入框 + 评论列表)
使用的Store:
useFavoriteStore()- 收藏操作useHistoryStore()- 添加历史
API调用:
GET /api/news/detail/:id- 获取详情POST /api/favorite/add- 添加收藏DELETE /api/favorite/remove- 取消收藏GET /api/favorite/check- 检查收藏状态POST /api/history/add- 添加历史GET/POST /api/comment/*- 评论相关
第三方库:
marked- Markdown转HTMLDOMPurify- HTML消毒(防XSS攻击)
Favorite.vue - 我的收藏
功能清单:
- 收藏新闻列表(与首页类似)
- 空状态提示(无收藏时)
- 取消收藏(滑动删除或按钮)
- 清空全部收藏
- 点击进入详情
API调用:
GET /api/favorite/list- 获取收藏列表DELETE /api/favorite/remove- 取消收藏POST /api/favorite/clear- 清空收藏
History.vue - 浏览历史
功能清单:
- 历史记录列表(按时间倒序)
- 分组显示(今天、昨天、更早)
- 删除单条记录
- 清空全部历史
- 点击进入详情
API调用:
GET /api/history/list- 获取历史列表POST /api/history/clear- 清空历史
AIChat.vue - AI问答
功能清单:
- 对话消息列表(用户 + AI)
- 消息输入框
- 发送按钮
- Loading状态(AI思考中)
- Markdown回复渲染
- 清空对话
API调用 :POST /api/aichat/chat
特殊处理:
- 流式响应(可选优化)
- 对话上下文维护
My.vue - 个人中心
功能清单:
- 用户信息卡片(头像、昵称、简介)
- 未登录状态显示"点击登录"
- 功能入口列表:
- 我的收藏(带数量角标)
- 浏览历史(带数量角标)
- AI问答
- 编辑资料
- 设置
- 退出登录按钮
条件渲染:
- 根据
isLoggedIn()显示不同内容
Settings.vue - 设置页
功能清单:
- 深色模式开关(Switch组件)
- 语言选择(ActionSheet弹出)
- 关于信息
- 版本号显示
使用的Store:
useThemeStore()- 主题切换useLanguageStore()- 语言切换
Profile.vue - 编辑资料
功能清单:
- 头像上传/更换
- 昵称修改
- 手机号修改
- 个人简介修改
- 保存按钮
API调用 :PUT /api/user/update
3.4 公共组件(components/)
TabBar.vue - 底部导航栏
作用:固定在底部的标签导航
Tab项:
| 图标 | 文字 | 路径 |
|---|---|---|
| home-o | 首页 | / |
| search | 分类 | /category |
| chat-o | AI问答 | /aichat |
| user-o | 我的 | /my |
特性:
- 使用Vant的
van-tabbar组件 - 通过
routeprop 实现路由联动 - 根据当前路由自动高亮
NewsItem.vue - 新闻列表项
作用:可复用的新闻卡片组件
Props:
| Prop名 | 类型 | 说明 |
|---|---|---|
| news | Object | 新闻数据对象 |
显示内容:
- 左侧/上方:封面图(圆角)
- 右侧/下方:标题(最多2行)、摘要、元信息
Events:
| 事件名 | 触发条件 |
|---|---|
| click | 点击整张卡片 |
3.5 状态管理(store/)
Store架构图
store/
├── index.js # Store入口,导出所有Store
├── user.js # 用户状态(登录、Token、用户信息)
├── theme.js # 主题状态(亮色/暗色)
├── language.js # 语言状态(中文/英文)
└── modules/ # 子模块
├── news.js # 新闻状态(列表、分类、详情)
├── favorite.js # 收藏状态(列表、收藏状态)
└── history.js # 历史状态(列表)
各Store详细说明
user.js - 用户Store
状态(State):
javascript
{
token: '', // JWT Token
userInfo: {}, // 用户信息对象
isLoggedIn: false // 是否已登录
}
操作(Actions):
| 方法 | 说明 | 参数 |
|---|---|---|
login(formData) |
登录 | {username, password} |
register(formData) |
注册 | {username, password, phone} |
logout() |
退出登录 | 无 |
getUserInfo() |
获取用户信息 | 无 |
updateProfile(data) |
更新资料 | {nickname, avatar_url, ...} |
持久化 (采用 pinia-plugin-persistedstate v4 语法):
- 用户信息
userInfo、登录态isLogin持久化到sessionStorage(关闭标签页即清除,避免 token 明文常驻 localStorage) - Token 不再落盘 :仅存于内存与请求拦截器;刷新页面时由
main.js从 sessionStorage 回灌,降低 XSS 盗取风险 - 刷新页面后登录态恢复(会话内)
news.js - 新闻Store
状态(State):
javascript
{
categories: [], // 分类列表
newsList: [], // 当前新闻列表
currentNews: {}, // 当前查看的新闻详情
loading: false, // 加载状态
total: 0, // 总数
page: 1, // 当前页码
finished: false // 是否加载完全部
}
操作(Actions):
| 方法 | 说明 |
|---|---|
fetchCategories() |
获取分类列表 |
fetchNewsList(params) |
获取新闻列表(分页) |
fetchNewsDetail(id) |
获取新闻详情 |
resetList() |
重置列表(切换分类时调用) |
favorite.js - 收藏Store
状态(State):
javascript
{
favoriteList: [], // 收藏列表
favoriteSet: new Set(), // 已收藏的ID集合(快速查找)
loading: false
}
操作(Actions):
| 方法 | 说明 |
|---|---|
fetchFavorites() |
获取收藏列表 |
addFavorite(newsId) |
添加收藏 |
removeFavorite(newsId) |
取消收藏 |
checkFavorite(newsId) |
检查是否已收藏 |
clearFavorites() |
清空收藏 |
theme.js - 主题Store
状态(State):
javascript
{
isDark: false // 是否暗色模式
}
操作(Actions):
| 方法 | 说明 |
|---|---|
toggleTheme() |
切换主题 |
applyTheme() |
应用主题到DOM |
实现原理:
- 切换
<html>元素的darkclass - CSS变量根据class切换颜色值
- 保存偏好到
localStorage
language.js - 语言Store
状态(State):
javascript
{
locale: 'zh-CN' // 当前语言
}
支持的语言:
zh-CN- 简体中文en-US- English
3.6 配置文件
config/api.js - API 基础地址配置
作用:从环境变量读取后端基础地址,供请求层复用
配置项:
javascript
// 开发走 Vite 的 /api 代理;生产/部署通过 VITE_API_BASE 指定真实地址
export const apiConfig = {
baseURL: import.meta.env.VITE_API_BASE || '' // 默认空串,配合 Vite 代理转发到后端
}
api/request.js - 统一请求层(新增)
作用:基于 Axios 创建全局请求实例,集中处理 token 注入与错误拦截
拦截器:
- 请求拦截器 :自动从用户状态读取 token 并附加
Authorization: Bearer <token>头 - 响应拦截器 :统一错误处理;遇到
401自动清除 token 并跳转登录页 - 各 Store 不再手写
Authorization头,统一import request from '../api/request'调用
3.7 国际化(i18n/)
目录结构
i18n/
├── index.js # i18n实例创建和配置
└── locales/
├── zh-CN.js # 中文语言包
└── en-US.js # 英文语言包
翻译键命名规范
javascript
{
common: { ... }, // 通用文案
tabBar: { ... }, // 底部导航
home: { ... }, // 首页
login: { ... }, // 登录
register: { ... }, // 注册
my: { ... }, // 我的
settings: { ... }, // 设置
// ... 其他页面
}
四、后端文件详解
4.1 入口文件
main.py - FastAPI应用入口
文件路径 :toutiao_backend/main.py
作用:创建FastAPI应用、注册中间件和路由、启动服务器
核心职责:
| 职责 | 代码位置 |
|---|---|
| 创建FastAPI实例 | app = FastAPI(...) |
| CORS配置 | app.add_middleware(CORSMiddleware, ...) |
| 生命周期管理 | lifespan 异步上下文管理器 |
| 路由注册 | app.include_router(...) |
| 启动服务器 | uvicorn.run(app, ...) |
启动命令:
bash
# 开发模式(热重载)
python main.py
# 或者使用uvicorn直接启动
uvicorn main:app --reload --host 0.0.0.0 --port 8000
4.2 配置模块(config/)
db_conf.py - 数据库配置
作用:管理MySQL数据库连接
核心组件:
| 组件 | 类型 | 说明 |
|---|---|---|
engine |
AsyncEngine |
异步数据库引擎(连接池) |
AsyncSessionLocal |
sessionmaker |
会话工厂 |
Base |
DeclarativeBase |
ORM模型基类 |
get_db() |
依赖函数 | 为每个请求提供数据库会话 |
连接配置(从环境变量读取):
python
DB_HOST = localhost
DB_PORT = 3306
DB_USER = root
DB_PASSWORD = 123456
DB_NAME = toutiao_db
连接URL格式:
mysql+aiomysql://user:password@host:port/database?charset=utf8mb4
cache_conf.py - Redis缓存配置
作用:管理Redis连接
核心组件:
| 组件 | 类型 | 说明 |
|---|---|---|
redis_client |
redis.Redis |
Redis客户端实例 |
get_redis() |
依赖函数 | 提供Redis连接 |
配置项:
python
REDIS_HOST = localhost
REDIS_PORT = 6379
REDIS_PASSWORD = # 默认无密码
REDIS_DB = 0 # 使用0号数据库
典型用途:
- 缓存热点新闻数据
- 存储用户会话/Token
- 实现分布式锁
ai_conf.py - AI配置
作用:管理OpenAI API配置
配置项:
python
OPENAI_API_KEY = sk-xxx # API密钥
OPENAI_BASE_URL = https://api.openai.com/v1 # API地址
MODEL_NAME = gpt-3.5-turbo # 模型名称
安全提醒:生产环境中API Key应从环境变量读取,不要硬编码!
4.3 数据模型(models/)
模型总览
| 模型文件 | 对应表 | 主要字段 |
|---|---|---|
users.py |
user, user_token |
id, username, hashed_password, nickname, avatar... |
news.py |
category, news |
id, name/title, content, view_count... |
favorite.py |
favorite |
id, user_id, news_id |
history.py |
history |
id, user_id, news_id |
ER关系图
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ user │ │ category │ │ news │
├──────────┤ ├──────────┤ ├──────────────┤
│ PK id │──┐ │ PK id │──┐ │ PK id │
│ username │ │ │ name │ │ │ FK category_id├──┘
│ password │ │ │ sort_order│ │ │ title │
│ nickname │ │ └──────────┘ │ │ content │
│ ... │ │ │ │ FK author_id ├──┐
└──────────┘ │ │ │ view_count │ │
│ │ └──────────────┘ │
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│user_token│ │ favorite │ │ history │
├──────────┤ ├──────────────┤ ├──────────────┤
│ PK id │ │ PK id │ │ PK id │
│ FK user_id│ │ FK user_id ──┼─────┼─ FK user_id │
│ token │ │ FK news_id ──┘ │ FK news_id ──┘
│expire_time│ │ created_at │ │ created_at │
└──────────┘ └──────────────┘ └──────────────┘
4.4 数据验证(schemas/)
Schema的作用
Pydantic Schema用于:
- 请求数据验证:确保前端提交的数据符合要求
- 响应数据序列化:控制返回给前端的数据格式
- 自动生成文档:Swagger UI中的请求/响应示例
Schema命名规范
| 类型 | 命名规则 | 示例 |
|---|---|---|
| 请求模型 | xxxRequest |
UserRegisterRequest, LoginRequest |
| 响应模型 | xxxResponse |
UserResponse, LoginResponse |
| 列表响应 | xxxListResult |
NewsListResult |
验证装饰器示例
python
class UserRegisterRequest(BaseModel):
username: str = Field(
..., # 必填
min_length=3, # 最小长度
max_length=50, # 最大长度
description="用户名" # API文档描述
)
password: str = Field(..., min_length=6, max_length=100)
phone: Optional[str] = Field( # 可选
None,
pattern=r"^1[3-9]\d{9}$" # 正则验证手机号
)
4.5 数据操作层(crud/)
CRUD的职责
CRUD (Create, Read, Update, Delete) 层封装了所有数据库操作:
| 文件 | 操作的表 | 主要函数 |
|---|---|---|
users.py |
user, user_token | create_user, authenticate_user, update_user |
news.py |
news, category | get_news_list, create_news, update_news |
news_cache.py |
redis缓存 | get_cached_news, set_cached_news |
favorite.py |
favorite | add_favorite, remove_favorite, get_favorites |
history.py |
history | add_history, get_histories, clear_histories |
代码模式示例
python
async def get_xxx(db: AsyncSession, id: int) -> Optional[Xxx]:
"""查询单条记录"""
result = await db.execute(select(Xxx).where(Xxx.id == id))
return result.scalar_one_or_none()
async def create_xxx(db: AsyncSession, **kwargs) -> Xxx:
"""创建记录"""
db_obj = Xxx(**kwargs)
db.add(db_obj)
await db.commit()
await db.refresh(db_obj)
return db_obj
async def update_xxx(db: AsyncSession, id: int, **kwargs) -> Optional[Xxx]:
"""更新记录"""
obj = await get_xxx(db, id)
if obj:
for key, value in kwargs.items():
setattr(obj, key, value)
await db.commit()
await db.refresh(obj)
return obj
async def delete_xxx(db: AsyncSession, id: int) -> bool:
"""删除记录"""
obj = await get_xxx(db, id)
if obj:
await db.delete(obj)
await db.commit()
return True
return False
4.6 API路由层(routers/)
路由组织方式
每个业务模块一个路由文件,通过 APIRouter 创建:
python
router = APIRouter(
prefix="/api/xxx", # URL前缀
tags=["模块名称"], # Swagger文档分组
)
路由注册到主应用
在 main.py 中:
python
from routers.users import router as user_router
from routers.news import router as news_router
# ...
app.include_router(user_router)
app.include_router(news_router)
# ...
API接口清单
用户模块 (/api/user):
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /register |
用户注册 | 否 |
| POST | /login |
用户登录 | 否 |
| GET | /info |
获取用户信息 | 是 |
| PUT | /update |
更新用户信息 | 是 |
| POST | /password |
修改密码 | 是 |
新闻模块 (/api/news):
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| GET | /categories |
获取分类列表 | 否 |
| GET | /list |
获取新闻列表 | 否 |
| GET | /detail/{id} |
获取新闻详情 | 否 |
| POST | /publish |
发布新闻 | 是 |
| PUT | /update/{id} |
更新新闻 | 是 |
| DELETE | /delete/{id} |
删除新闻 | 是 |
收藏模块 (/api/favorite):
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /add |
添加收藏 | 是 |
| DELETE | /remove |
取消收藏 | 是 |
| GET | /check |
检查收藏状态 | 是 |
| GET | /list |
收藏列表 | 是 |
| POST | /clear |
清空收藏 | 是 |
历史模块 (/api/history):
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /add |
添加历史 | 是 |
| GET | /list |
历史列表 | 是 |
| POST | /clear |
清空历史 | 是 |
AI模块 (/api/aichat):
| 方法 | 路径 | 说明 | 认证 |
|---|---|---|---|
| POST | /chat |
AI问答 | 是 |
4.7 工具模块(utils/)
auth.py - JWT认证
核心函数:
| 函数 | 说明 |
|---|---|
create_access_token(data) |
生成JWT Token |
get_current_user() |
依赖注入,获取当前登录用户 |
使用方式:
python
@router.get("/protected")
async def protected_route(current_user = Depends(get_current_user)):
# current_user 就是当前登录的User对象
return {"user_id": current_user.id}
security.py - 密码加密
核心函数:
| 函数 | 说明 |
|---|---|
hash_password(password) |
明文→哈希 |
verify_password(plain, hashed) |
验证密码 |
算法:bcrypt(自带盐值,每次加密结果不同)
response.py - 统一响应格式
标准响应结构:
json
{
"code": 0, // 0=成功,其他=失败
"message": "success", // 提示信息
"data": {} // 业务数据
}
exception.py - 自定义异常
| 异常类 | HTTP状态码 | 说明 |
|---|---|---|
UnauthorizedException |
401 | 未认证 |
ForbiddenException |
403 | 无权限 |
NotFoundException |
404 | 资源不存在 |
BusinessException |
400 | 业务逻辑错误 |
五、数据流图
5.1 用户登录流程
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ 用户 │───▶│ Login │───▶│ user │───▶│ Axios │───▶│ FastAPI│
│ 输入 │ │ Vue │ │ Store │ │ │ │ Router │
└────────┘ └────────┘ └────────┘ └────────┘ └───┬────┘
│
▼
┌───────────────┐
│ auth.py │
│ 验证用户名密码 │
└───────┬───────┘
│
▼
┌───────────────┐
│ crud/users.py │
│ 查询user表 │
│ bcrypt验密 │
└───────┬───────┘
│
▼
┌───────────────┐
│ 生成JWT Token │
│ 返回用户+Token │
└───────┬───────┘
│
▼
原路返回→会话内保存
sessionStorage
5.2 新闻浏览流程
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Home │──▶│ news │──▶│ Axios │──▶│ /api/ │──▶│ news │
│ Vue │ │ Store │ │ │ │ news/ │ │ routes│
└────────┘ └────────┘ └────────┘ │ list │ └───┬───┘
└────┬───┘ │
│ ▼
┌────▼─────┐ ┌──────────┐
│ Vite Proxy│ │crud/news │
│ 转发请求 │ │ 分页查询 │
└────┬─────┘ └────┬─────┘
│ │
▼ ▼
┌──────────────────────┐
│ MySQL │
│ SELECT * FROM news │
│ WHERE is_published │
│ ORDER BY ... LIMIT │
└──────────┬───────────┘
│
▼
JSON响应
│
▼
更新newsList
│
▼
视图重新渲染
六、开发规范
6.1 命名规范
| 类型 | 规范 | 示例 |
|---|---|---|
| 文件名 | 小写+下划线 | user_routes.py, NewsItem.vue |
| 组件名 | 大驼峰 | NewsItem, TabBar |
| 变量/函数 | 小驼峰 | userInfo, fetchData |
| 常量 | 大蛇形 | MAX_PAGE_SIZE, API_BASE_URL |
| 数据库表 | 小写+下划线 | user_token, news_detail |
| API路径 | 小写+连字符 | /api/user-info, /news-list |
| CSS类 | 连字符 | .news-item, .login-form |
6.2 目录组织原则
1. 按功能分层(router → schema → crud → model)
2. 同类文件放同一目录
3. 公共组件放 components/
4. 页面组件放 views/
5. 配置文件放 config/
6. 工具函数放 utils/
6.3 代码风格
Python (PEP 8):
- 缩进:4空格
- 行宽:最大120字符
- 导入顺序:标准库 → 第三方 → 本地
- 字符串:使用f-string格式化
JavaScript (Standard):
- 缩进:2空格
- 使用单引号(模板字符串除外)
- 末尾不加分号(可选)
- 使用const/let,禁止var
6.4 Git提交规范
feat: 新功能
fix: 修复bug
docs: 文档更新
style: 代码格式调整
refactor: 重构
test: 测试相关
chore: 构建/工具链
示例:
feat: 实现用户收藏功能
fix: 修复下拉刷新不触发的问题
docs: 更新API接口文档
style: 调整Home页样式
七、模块依赖关系
7.1 前端依赖图
App.vue
├── router/index.js
│ └── views/*
│ ├── Home.vue ──────────┬── components/NewsItem.vue
│ ├── NewsDetail.vue ────┤
│ ├── Favorite.vue ──────┤
│ └── History.vue ───────┘
│
├── store/
│ ├── user.js ────────────── api/request.js (Axios 实例 + 拦截器)
│ ├── news.js
│ ├── favorite.js
│ ├── history.js
│ ├── theme.js
│ └── language.js
│
├── components/
│ ├── TabBar.vue
│ └── NewsItem.vue
│
├── api/request.js ──────────── axios (带拦截器)
├── config/api.js ───────────── 后端地址常量(VITE_API_BASE)
└── i18n/
└── locales/*
7.2 后端依赖图
main.py
├── config/
│ ├── db_conf.py ──────────── sqlalchemy, aiomysql
│ ├── cache_conf.py ───────── redis
│ └── ai_conf.py
│
├── routers/
│ ├── users.py ───────────── schemas/users.py
│ │ └── crud/users.py ─── models/users.py
│ │ └── utils/auth.py, security.py
│ │
│ ├── news.py ────────────── schemas/news.py
│ │ └── crud/news.py ──── models/news.py
│ │
│ ├── favorite.py ──── schemas/favorite.py
│ │ └── crud/favorite.py ── models/favorite.py
│ │
│ ├── history.py ───── schemas/histroy.py
│ │ └── crud/history.py ── models/history.py
│ │
│ └── aichat.py ──────── schemas/aichat.py
│ └── config/ai_conf.py
│
└── utils/
├── auth.py ────────────── python-jose
├── security.py ────────── passlib
├── response.py
├── exception.py
└── exception_handlers.py
八、常见问题FAQ
Q1:为什么要分这么多文件夹?
A:分层架构的好处:
- 职责清晰:每个文件只做一件事
- 易于维护:改某个功能只需找到对应文件
- 团队协作:多人开发互不冲突
- 方便测试:可以单独测试某一层
Q2:前端为什么用Pinia而不是Vuex?
A:Pinia是Vue官方推荐的新一代状态管理:
- TypeScript支持更好
- API更简洁(不再需要mutations)
- 更好的DevTools支持
- 更小的打包体积
Q3:后端为什么用async/await?
A:异步编程的优势:
- 高并发处理能力(不阻塞)
- 特别适合I/O密集型任务(数据库查询、API调用)
- FastAPI原生支持异步
Q4:为什么要用Redis?
A:Redis作为缓存的用途:
- 减少MySQL查询压力
- 热点数据快速读取(内存级速度)
- 实现会话管理、排行榜等功能
文档版本 :v1.0
更新日期 :2026年7月
适用项目:toutiao_heima 新闻头条全栈项目