02. 环境配置和项目脚手架导入运行
1. 本节目标
这一节把项目的「地基」跑起来:
- 准备开发环境
- 把脚手架的前端、后端、数据库跑通,能用演示账号登录
- 看懂脚手架的整体结构:一次请求从浏览器到数据库是怎么走的,登录和权限是怎么实现的
脚手架已经包含登录、注册、用户管理、个人资料、修改密码和菜单这些通用功能。后续课程里的课表、知识库、AI 助手,都是在这个地基上一块块加上去的。
技术栈:Vue 3 + Element Plus + Axios · FastAPI + SQLAlchemy + JWT · MySQL。
相关带敲教程在 https://www.bilibili.com/video/BV13hbP6zEUd 第 01-14 集
2. 学习准备
2.1. 环境要求
- Node.js 24+:https://nodejs.org/zh-cn/download
- Python 3.12+:https://www.python.org/downloads/
- MySQL 8.4+:https://dev.mysql.com/downloads/mysql/
- 数据库可视化 Navicat 18 Lite:https://www.navicat.com.cn/download/navicat-premium-lite
NodeJS 版本检测:

Python 版本检测:

MySQL 服务:

2.2. 编辑器
推荐下载 VSCode:https://code.visualstudio.com/
安装插件:
- Vue (Official):Vue 语法高亮
- Prettier - Code formatter:Vue 代码格式化
- Python:Python 语法支持、高亮
- Black Formatter:Python 代码格式化
- Pylance:python 语法自动补全、自动导入
- Material Icon Theme:代码目录主题色
User Settings 设置
Vue:
{
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
}Python:
{
"python.analysis.typeCheckingMode": "off",
"python.analysis.autoImportCompletions": true,
"python.languageServer": "Pylance",
"python.analysis.extraPaths": ["./backend"],
"editor.quickSuggestions": {
"other": true,
"comments": false,
"strings": false
},
"[python]": {
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit",
"source.fixAll": "explicit"
},
"editor.defaultFormatter": "ms-python.black-formatter"
},
}3. 脚手架目录结构
下载脚手架:https://ai.codenice.cn/courses/ecom-analysis-agent
ecom-analysis-agent/
├── sql/
│ └── backend.sql # 用户表结构 + 演示账号
├── backend/ # FastAPI
│ ├── app/
│ │ ├── api/ # 路由
│ │ ├── services/ # 业务逻辑
│ │ ├── models/ # ORM
│ │ ├── schemas/ # 请求/响应模型
│ │ ├── common/ # 统一返回、异常
│ │ ├── dependencies/ # JWT 依赖
│ │ └── utils/
│ ├── uploads/ # 上传文件目录
│ ├── requirements.txt
│ ├── .env.example
│ └── .env # 本地配置(勿提交真实密钥)
├── frontend/ # Vue3
│ ├── src/
│ │ ├── api/
│ │ ├── views/
│ │ ├── layouts/
│ │ └── utils/
│ └── .env.development
└── README.md4. 运行脚手架
运行顺序:数据库 → 后端 → 前端。
flowchart LR
A["1 创建数据库<br/>导入 backend.sql"] --> B["2 启动后端<br/>127.0.0.1:8000"]
B --> C["3 启动前端<br/>localhost:5173"]
C --> D["4 浏览器登录<br/>验证成功"]先解压脚手架放到 D:\Course\ 目录 改名 ecom-analysis-agent
4.1 创建数据库
用 Navicat 或命令行连接 MySQL,先创建数据库:ecom-analysis-agent

然后在这个库里执行 sql/backend.sql,导入 sql。
4.2 启动后端
第一步:进入后端目录,创建虚拟环境
cd backend
python -m venv .venv虚拟环境相当于给这个项目单独准备一套 Python 依赖,不会和电脑上其他项目互相影响。
第二步:激活虚拟环境
# Windows(PowerShell)
.\.venv\Scripts\Activate.ps1激活成功后,终端提示符前面会出现 (.venv)。

第三步:安装依赖
先配置国内镜像加速下载:在 %AppData%目录下新建pip 目录,然后进入pip目录新建文件pip.ini,设置阿里云镜像
[global]
index-url=https://mirrors.aliyun.com/pypi/simple然后回到项目的 backend 目录下,执行安装指令:
pip install -r requirements.txt第四步:配置环境变量
把 .env.example 复制一份,重命名为 .env,修改数据库连接和 JWT 密钥:
DATABASE_URL=mysql+pymysql://root:你的数据库密码@127.0.0.1:3306/campus_course_agent
JWT_SECRET_KEY=改成一串足够长的随机字符串
JWT_EXPIRE_HOURS=24
JWT_ALGORITHM=HS256DATABASE_URL 的格式是:mysql+pymysql://用户名:密码@地址:端口/数据库名。
密钥生成器:https://jwtsecretkeygenerator.com/zh/
第五步:启动后端
uvicorn app.main:app --reload --port 8000app.main:app表示运行app/main.py文件里名为app的 FastAPI 对象--reload表示修改代码后自动重启,开发时很方便
第六步:验证
- 浏览器打开
http://127.0.0.1:8000,看到{"message":"FastAPI工程正在运行中"}说明启动成功 - 打开
http://127.0.0.1:8000/docs,可以看到 FastAPI 自动生成的接口文档,能直接在页面上调试接口
4.3 启动前端
新开一个终端(后端那个终端不要关),执行:
cd frontend
npm install
npm run devnpm install 下载慢的话可以切换国内镜像:
npm config set registry https://registry.npmmirror.com启动成功后,浏览器打开 http://localhost:5173。
4.4 登录验证
| 角色 | 账号 | 密码 |
|---|---|---|
| 管理员 | admin | admin |
| 学生 | aaa | 123 |
| 学生 | bbb | 123 |
分别用管理员和学生账号登录,观察左侧菜单:管理员能看到「用户管理」,学生看不到。 这个区别后面会专门讲。
4.5 常见问题
| 现象 | 原因与解决 |
|---|---|
| 后端启动报错,提示连不上数据库 | 检查 MySQL 是否启动;检查 .env<br/> 中的密码和数据库名是否正确;确认数据库已经创建 |
| PowerShell 激活虚拟环境时报错「禁止运行脚本」 | 以管理员身份打开 PowerShell,执行 Set-ExecutionPolicy RemoteSigned<br/>,或改用 CMD 激活 |
提示 8000或 5173 端口被占用 |
关闭占用端口的程序,或换一个端口启动 |
npm install<br/> 报错,提示 Node 版本不符合 |
升级 Node.js 到 22.18 以上 |
| 页面提示「网络异常,请检查后端服务」 | 后端没有启动,或者启动失败,回到后端终端查看报错 |
| 登录后头像不显示 | 演示账号的头像图片不在你的电脑上,到「个人资料」里重新上传即可 |
5. 一次请求是怎么走的
项目跑起来之后,先建立整体印象:用户在页面上点「登录」,到底发生了什么?
flowchart LR
B["浏览器<br/>Vue 页面"] -->|/api/auth/login| V["Vite 开发服务器<br/>代理转发"]
V --> R["FastAPI<br/>api 接口层"]
R --> S["services<br/>业务层"]
S --> M["models<br/>SQLAlchemy"]
M --> DB["MySQL"]为什么前端请求/api/... 能到达后端? 前端运行在 5173 端口,后端在 8000 端口。vite.config.js 里配置了代理,开发时凡是以 /api 和 /uploads 开头的请求,都会被转发到后端:
server: {
proxy: {
'/api': { target: 'http://127.0.0.1:8000', changeOrigin: true },
'/uploads': { target: 'http://127.0.0.1:8000', changeOrigin: true }
}
}6. 后端讲解
6.1 分层结构
后端按职责分层,每一层只做自己的事:
| 层 | 目录 | 职责 | 以登录为例 |
|---|---|---|---|
| 接口层 | api/ |
定义路由,接收请求,返回结果 | auth.py<br/> 里的 login<br/> 接口 |
| 数据校验 | schemas/ |
定义请求参数和返回数据的格式 | LoginRequest<br/> 要求必须有 username 和 password |
| 业务层 | services/ |
实现业务逻辑 | auth_service.login<br/> 校验账号密码、生成 token |
| 模型层 | models/ |
对应数据库表 | User<br/> 对应 users<br/> 表 |
这样分层的好处是:接口层很薄,业务逻辑集中在 services。后续课程开发 MCP 工具时,工具直接调用 services 里的函数,业务代码不用重写。
6.2 配置读取:config.py
使用 pydantic-settings 从 .env 文件读取配置,并自动做类型转换:
class Settings(BaseSettings):
DATABASE_URL: str
JWT_SECRET_KEY: str
JWT_EXPIRE_HOURS: int = 24
JWT_ALGORITHM: str = "HS256"没有默认值的配置项(如 DATABASE_URL)是必填的,.env 里缺少时程序启动就会报错,能尽早发现配置问题。
6.3 数据库:database.py
engine负责数据库连接,SessionLocal用来创建会话get_db在每次请求时创建会话,请求结束后自动关闭Base是所有模型的父类,统一带上id、create_time、update_time三个字段,模型里不用重复定义main.py启动时执行了这一行:
Base.metadata.create_all(bind=engine)
它会根据模型自动创建还不存在的表。后续课程新增课表、学习计划等模型时,重启后端就能自动建表。
create_all 只负责建新表,不会修改已存在的表结构。
6.4 统一返回格式
所有接口都返回同样的结构,前端处理起来很统一:
{
"code": 200,
"message": "登录成功",
"data": { }
}代码里用 Response.success(...) 和 Response.error(...) 生成。分页查询的 data 里是 list 和 total。
6.5 统一异常处理
main.py 注册了四个异常处理器,把各种异常都转换成上面的统一格式:
| 异常 | 场景 | 返回 |
|---|---|---|
BusinessException |
业务规则不满足,如「账号或密码错误」 | HTTP 200,code<br/> 为业务错误码 |
HTTPException |
如未登录返回 401 | 对应的 HTTP 状态码 |
RequestValidationError |
请求参数格式不对 | 422,「请求参数校验错误」 |
Exception |
其他未预料的错误 | 500,「服务器内部错误」 |
业务代码里遇到不符合规则的情况,直接 raise BusinessException("提示信息") 即可,不需要到处写返回逻辑。
兜底的 Exception 处理器必须最后注册。
6.6 登录与 JWT
sequenceDiagram
participant 前端
participant FastAPI
participant MySQL
前端->>FastAPI: 提交账号、密码
FastAPI->>MySQL: 按账号查询用户
MySQL-->>FastAPI: 用户信息(含加密后的密码)
FastAPI->>FastAPI: bcrypt 校验密码、检查账号状态
FastAPI-->>前端: 返回 token 和用户信息
Note over 前端: token 存入 localStorage
前端->>FastAPI: 之后每次请求都带上 Authorization: Bearer token
FastAPI->>FastAPI: 解析 token 得到 user_id
FastAPI-->>前端: 返回业务数据几个关键点:
- 密码加密:数据库里存的是 bcrypt 加密后的字符串,不是明文。登录时用
verify_password比对 - 生成 token:
create_access_token把user_id和过期时间写进 JWT,用.env里的密钥签名 - 注册的用户默认是普通用户:
role固定为 user,管理员只能在后台创建
6.7 获取当前用户与权限校验
需要登录的接口,通过依赖注入获取当前用户:
@router.get("/me")
def get_user_info(current_user: User = Depends(get_current_user)):
...get_current_user 做了三件事:
- 从请求头取出 token 并解析,失败返回 401
- 从 token 里拿到
user_id - 查数据库确认用户存在且未被禁用只允许管理员访问的接口,换成
get_current_admin,它在get_current_user的基础上再判断role是否为admin,不是则返回 403。
这个机制在后续课程里非常重要。 第一节讲过「用户身份由系统传递,不由模型填写」,MCP 工具获取当前用户的思路和这里完全一样:从 token 里解析出用户,而不是相信请求参数里传来的用户 ID。
6.8 文件上传
POST /api/files/upload 用于上传头像等文件:
- 校验文件后缀和大小(默认上限 100MB)
- 用「时间戳 + 随机串」重新命名,避免重名覆盖
- 保存到
backend/uploads/目录,返回访问地址/uploads/文件名``main.py把uploads目录挂载成了静态资源,所以通过http://127.0.0.1:8000/uploads/文件名就能直接访问。
6.9 跨域配置
main.py 里配置了 CORS,只允许 localhost:5173 和 127.0.0.1:5173 访问。开发时前端已经通过 Vite 代理访问后端,这里的配置用于前端直接请求后端地址的情况。
不要为了省事把允许的来源写成 ["*"]。
7. 前端讲解
7.1 入口:main.js
注册路由、Element Plus 组件库(设置为中文)和全部图标组件,并引入全局样式。
7.2 路由与路由守卫:router/index.js
/login、/register是独立页面- 登录后的页面都放在
/manager下,共用Layout.vue的布局 - 路由守卫:除登录和注册页外,没有 token 就跳转到登录页
router.beforeEach((to) => {
const token = getToken()
if (to.path === '/login' || to.path === '/register') return true
return token ? true : '/login'
})7.3 请求封装:utils/request.js
基于 axios 封装,所有接口请求都经过这里:
flowchart LR
P["页面调用<br/>api 函数"] --> Q["请求拦截器<br/>自动带上 token"]
Q --> S["后端"]
S --> R{"响应拦截器<br/>code 是否为 200"}
R -->|是| OK["返回数据给页面"]
R -->|否| E["弹出错误提示"]
R -->|401| L["清除登录信息<br/>跳转登录页"]- 请求拦截器:本地有 token 就自动加上
Authorization: Bearer token请求头,页面里不用每次手动写 - 响应拦截器:
code不是 200 时统一弹出错误提示;遇到 401 自动退出登录并跳转;后端没启动时提示「网络异常,请检查后端服务」
7.4 登录状态存储:utils/auth.js 与 utils/user.js
auth.js:负责把 token 和用户信息存到localStorage,以及读取和清除user.js:提供useUser(),用响应式变量保存当前用户信息。修改个人资料后调用updateUser,顶栏的名字和头像会立刻更新
7.5 布局与菜单:layouts/Layout.vue
左侧菜单根据用户角色显示不同的菜单项:
<el-menu-item index="/manager/user" v-if="userInfo?.role === 'admin'">
前端隐藏菜单不等于权限控制。 学生看不到「用户管理」菜单,但如果直接调用用户管理接口,真正拦住他的是后端的 get_current_admin。前端负责体验,后端负责安全,这一点后续开发 AI 功能时同样适用。
8. 小结
- 运行顺序:数据库 → 后端 → 前端,三者都跑起来才能正常登录
- 后端分为接口层、数据校验、业务层、模型层,配合统一返回格式和统一异常处理
- 登录后得到 JWT,之后每次请求都带上它;后端通过
get_current_user从 token 中解析当前用户,通过get_current_admin校验管理员 - 前端通过路由守卫拦截未登录访问,通过请求封装统一处理 token 和错误
- 前端隐藏菜单负责体验,后端校验权限负责安全