前言
在 Week1 中,我们完成了 Python 基础语法、FastAPI 入门和 DeepSeek API 的调用,实现了一个能在终端聊天的 AI 助手。
Week2 的目标是:把这个终端聊天工具,变成一个真正的 Web 应用。
具体来说,Week2 要完成以下几件事:
- 搭建 FastAPI 后端框架,实现用户认证和多会话管理
- 使用 Vue 3 开发前端界面,实现组件化架构
- 集成 WebSocket 实现实时通信
- 使用 Docker 容器化部署
一、技术选型
| 层面 | 技术 | 选择理由 |
|---|---|---|
| 后端框架 | FastAPI | 高性能、异步支持、自动生成 API 文档 |
| 数据库 | SQLite + SQLAlchemy | 轻量级、无需额外安装数据库服务 |
| 认证 | JWT | 无状态、适合前后端分离 |
| 前端框架 | Vue 3 + TypeScript | 组合式 API、类型安全 |
| 构建工具 | Vite | 极快的冷启动和热更新 |
| 实时通信 | WebSocket | 双向通信、低延迟 |
| 容器化 | Docker + Docker Compose | 一键部署、环境一致性 |
二、项目架构
bash
week2/
├── backend/ # 后端代码
│ ├── main.py # FastAPI 主程序
│ ├── database.py # 数据库模型
│ ├── auth.py # 认证逻辑
│ ├── dependencies.py # 依赖注入
│ ├── migrate_add_pinned.py # 数据库迁移脚本
│ ├── requirements.txt # Python 依赖
│ ├── .env # 环境变量(需自行创建)
│ └── chat.db # SQLite 数据库(自动生成)
│
├── frontend/ # 前端代码
│ ├── src/
│ │ ├── components/ # Vue 组件
│ │ │ ├── LoginForm.vue # 登录/注册表单
│ │ │ ├── Sidebar.vue # 侧边栏
│ │ │ ├── ChatArea.vue # 聊天主区域
│ │ │ ├── MessageList.vue # 消息列表
│ │ │ ├── ChatInput.vue # 输入框
│ │ │ └── __tests__/ # 组件测试
│ │ ├── App.vue # 根组件
│ │ └── main.ts # 入口文件
│ ├── package.json # 依赖配置
│ └── vite.config.ts # Vite 配置
│
├── Dockerfile.backend # 后端 Docker 配置
├── Dockerfile.frontend # 前端 Docker 配置
├── docker-compose.yml # Docker Compose 配置
├── nginx.conf # Nginx 配置
└── README.md # 项目文档
三、核心功能实现
3.1 用户认证系统
用户认证是 Web 应用的基础。我选择了 JWT(JSON Web Token)方案,因为它天然适合前后端分离架构。
认证流程:
- 用户注册 → 后端返回 JWT Token
- 用户登录 → 后端验证密码 → 返回 JWT Token
- 前端将 Token 存储在 localStorage
- 每次请求在 Header 中携带 Token
- 后端通过依赖注入验证 Token
关键代码(auth.py):
python
from datetime import datetime, timedelta
from jose import jwt
from passlib.context import CryptContext
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 # 24小时
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def create_access_token(data: dict):
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
async def get_current_user(token: str = Depends(oauth2_scheme), db: AsyncSession = Depends(get_db)):
# 解析 Token → 查询用户 → 返回用户对象
...
遇到的坑:
- 前端请求
/api/sessions时返回 401,原因是get_current_user函数中的Depends(get_db)写成了Depends(get_db())(多了括号),导致数据库会话未正确注入。 - 修复后,所有接口都通过
current_user: User = Depends(get_current_user)进行鉴权保护。
3.2 多会话管理
会话模型(database.py):
ini
# 会话表
class Session(Base):
__tablename__ = "sessions"
id = Column(String(36), primary_key=True)
name = Column(String(100),default="新对话")
user_id = Column(Integer, ForeignKey("users.id"))
pinned = Column(Integer, default=0) # 0=未置顶,1=已置顶
created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now)
前端会话列表组件(Sidebar.vue):
- 显示所有会话
- 支持搜索过滤
- 支持置顶(星标)
- 支持双击重命名
- 支持删除
消息持久化:
每条消息都保存在数据库中,刷新页面后通过 GET /api/history?session_id=${sessionId} 接口重新加载,确保数据不丢失。
3.3 流式响应(SSE)
AI 回复通常需要几秒钟,如果等全部生成完再返回,用户体验很差。流式响应可以让用户边生成边看到内容。
实现原理:
- 前端通过 POST 请求发送消息
- 后端使用
StreamingResponse返回事件流 - 前端使用
EventSource或fetch的ReadableStream逐步读取 - 每收到一段内容,就更新页面上的消息
关键代码(后端main.py):
ini
# ---- 新增的流式接口 ----
@app.post("/chat/stream")
async def chat_stream(
request: ChatRequest,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user)
):
try:
messages = [{"role": "system", "content": "你是一个友善的AI助手。"}]
for msg in request.history:
messages.append(msg)
messages.append({"role": "user", "content": request.message})
# 调用 DeepSeek 流式 API
stream = client.chat.completions.create(
model="deepseek-v4-flash",
messages=messages,
stream=True
)
# 定义一个生成器,逐块产生 SSE 格式的数据
async def generate():
full_reply = "" # 用于累积完整回复
for chunk in stream:
if chunk.choices[0].delta.content is not None:
content = chunk.choices[0].delta.content
full_reply += content
# SSE 格式:data: <json>\n\n
yield f"data: {json.dumps({'content': content})}\n\n"
# 使用注入的 db 会话
db.add(ChatMessage(role="user", content=request.message, session_id=request.session_id))
db.add(ChatMessage(role="assistant", content=full_reply, session_id=request.session_id))
await db.commit()
# 发送结束标志
yield "data: [DONE]\n\n"
return StreamingResponse(
generate(),
media_type="text/event-stream"
)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
前端处理(ChatArea.vue):
php
const response = await fetch('/api/chat/stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${props.token}`
},
body: JSON.stringify({
message: text,
history: [],
session_id: props.currentSessionId || 'default'
}),
signal: controller.value.signal
})
3.4 WebSocket 实时通信
除了流式响应,我还需要 WebSocket 来处理更复杂的实时场景(如多用户在线状态、消息广播等)。
WebSocket 连接管理:
python
python
class ConnectionManager:
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
async def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
Token 验证:
WebSocket 连接时通过 URL 参数传递 Token:
bash
纯文本
ws://localhost:8000/ws?token=eyJhbGciOiJIUzI1NiIs...
3.5 前端组件化
为了提高代码的可维护性和复用性,我将前端拆分为 5 个核心组件:
| 组件 | 职责 |
|---|---|
| LoginForm.vue | 登录/注册表单 |
| Sidebar.vue | 会话列表、搜索、置顶 |
| ChatArea.vue | 聊天主区域,协调消息列表和输入框 |
| MessageList.vue | 消息渲染(Markdown) |
| ChatInput.vue | 输入框、发送按钮、停止生成 |
四、Docker 容器化部署
4.1 环境准备
部署的第一步是安装 Docker Desktop。但我的 Windows 10 版本是 18363,不满足 Docker 的要求(需要 22H2 以上)。
升级过程:
- 使用微软官方升级工具升级到 Windows 10 22H2
- 安装 WSL2(Windows Subsystem for Linux)
- 安装 Docker Desktop
升级耗时: 约 1 小时(下载 + 安装 + 多次重启)

4.2 Docker 配置
后端 Dockerfile:
sql
dockerfile
FROM python:3.13-slim
WORKDIR /app
COPY backend/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY backend/ .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
前端 Dockerfile(多阶段构建):
sql
dockerfile
FROM node:20-alpine AS build
WORKDIR /app
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
docker-compose.yml:
yaml
yaml
services:
backend:
build:
context: .
dockerfile: Dockerfile.backend
ports:
- "8000:8000"
volumes:
- ./backend/chat.db:/app/chat.db
frontend:
build:
context: .
dockerfile: Dockerfile.frontend
ports:
- "80:80"
depends_on:
- backend
4.3 踩坑记录
坑1:Nginx proxy_pass 末尾斜杠
bash
nginx
# ❌ 错误写法
proxy_pass http://backend:8000/; # 末尾带斜杠
# ✅ 正确写法
proxy_pass http://backend:8000; # 末尾不带斜杠
带斜杠会导致路径被重写,例如 /api/register 变成 /register,导致 405 错误。

五、测试
5.1 后端测试
使用 pytest + httpx 编写 API 测试:
python
python
@pytest.mark.asyncio
async def test_register_and_login():
async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
# 注册
resp = await ac.post("/register", data={"username": "testuser", "password": "test123"})
assert resp.status_code == 200
assert "access_token" in resp.json()
# 登录
resp = await ac.post("/token", data={"username": "testuser", "password": "test123"})
assert resp.status_code == 200
5.2 前端测试
使用 Vitest + @vue/test-utils 编写组件测试:
scss
typescript
describe('LoginForm', () => {
it('renders correctly', () => {
const wrapper = mount(LoginForm)
expect(wrapper.find('h1').text()).toBe('🤖 AI 助手')
expect(wrapper.find('.auth-submit').text()).toBe('登录')
})
})
六、项目成果
功能清单
- ✅ 用户注册/登录(JWT 认证)
- ✅ 多会话管理(创建、切换、删除、搜索、置顶、重命名)
- ✅ 流式 AI 响应(SSE)
- ✅ Markdown 渲染
- ✅ 消息持久化(刷新不丢失)
- ✅ 会话导出(JSON / Markdown)
- ✅ WebSocket 实时通信
- ✅ Docker 容器化部署
技术亮点
- 前后端分离架构 --- RESTful API + Vue SPA
- 异步编程 --- FastAPI async/await + SQLAlchemy async
- 实时通信 --- WebSocket + SSE 流式响应
- 安全认证 --- JWT Token + 密码哈希
- 组件化设计 --- Vue 3 Composition API
- 容器化部署 --- Docker + Docker Compose + Nginx
项目截图


七、遇到的问题与解决方案
| 问题 | 解决方案 |
|---|---|
| Windows 版本过低无法安装 Docker | 升级到 Windows 10 22H2 |
| WSL2 安装失败 | 手动下载内核更新包安装 |
| 前端请求后端 404 | 检查 Nginx proxy_pass 配置 |
| 前端请求后端 405 | 修复 proxy_pass 末尾斜杠问题 |
| 前端测试 document is not defined | 配置 vitest.config.ts 的 environment: 'jsdom' |
| 刷新页面会话丢失 | 实现消息持久化到数据库 |
| WebSocket 连接失败 | URL 参数传递 Token 进行验证 |
八、下周计划
Week3 的目标是进一步提升前端体验:
- UI 框架集成 --- 引入 Element Plus 或 Naive UI,美化界面
- 前端路由与状态管理 --- Vue Router + Pinia
- 高级功能 --- 消息编辑/删除、文件上传、主题切换
- 性能优化 --- 虚拟滚动、懒加载、代码分割
九、总结
Week2 是强度很大的一周,从后端 API 设计到前端组件开发,再到 Docker 容器化部署,几乎覆盖了全栈开发的完整流程。
最大的收获不是学会了某个具体的技术,而是建立了 "全栈思维" ------知道一个 Web 应用从前端到后端、从开发到部署的完整链路是怎样的。
最大的教训是 "环境配置是第一生产力" ------在 Windows 上配置 Docker 花了整整一天时间,如果一开始就升级系统,后面会顺利很多。
期待 Week3 的前端深度开发!
附录:项目地址
- GitHub:github.com/qishuixian/...