- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
301 lines
7.9 KiB
Markdown
301 lines
7.9 KiB
Markdown
# 资料上传数据库持久化问题修复报告
|
||
|
||
**修复日期**:2025-10-17
|
||
**问题来源**:用户反馈
|
||
**严重级别**:⚠️ 高(数据丢失)
|
||
**影响范围**:课程资料上传功能
|
||
|
||
---
|
||
|
||
## 📋 问题描述
|
||
|
||
### 用户反馈
|
||
在课程编辑页面 `https://aiedu.ireborn.com.cn/manager/edit-course/1` 上传资料后:
|
||
- ✅ 资料上传成功(文件被保存)
|
||
- ✅ 前端显示"上传成功"消息
|
||
- ❌ 刷新页面后,资料记录消失
|
||
- ❌ 数据库中没有资料记录
|
||
|
||
### 复现步骤
|
||
1. 登录管理员账号
|
||
2. 进入课程编辑页面
|
||
3. 点击"上传资料"按钮
|
||
4. 选择文件并上传
|
||
5. 看到"上传成功"提示
|
||
6. 刷新页面
|
||
7. **发现资料列表为空**
|
||
|
||
---
|
||
|
||
## 🔍 问题分析
|
||
|
||
### 根本原因
|
||
**数据库事务未提交**:后端service层只执行了 `db.flush()` 但没有 `db.commit()`
|
||
|
||
### 技术细节
|
||
|
||
#### 1. 错误代码位置
|
||
文件:`/root/aiedu/kaopeilian-backend/app/services/course_service.py`
|
||
函数:`add_course_material` (第313-361行)
|
||
|
||
```python
|
||
# ❌ 第346-349行的错误代码
|
||
material = CourseMaterial(**material_data)
|
||
db.add(material)
|
||
await db.flush() # ⚠️ 只刷新到数据库,未提交事务
|
||
await db.refresh(material)
|
||
```
|
||
|
||
#### 2. 问题分析
|
||
|
||
**db.flush() vs db.commit() 的区别**:
|
||
|
||
| 操作 | 作用 | 数据持久化 |
|
||
|------|------|------------|
|
||
| `db.flush()` | 将待处理的更改同步到数据库,生成ID | ❌ 否,事务未提交 |
|
||
| `db.commit()` | 提交事务,将更改永久保存到数据库 | ✅ 是,数据已持久化 |
|
||
|
||
**为什么前端能看到成功响应?**
|
||
1. service层执行 `db.flush()` 后,CourseMaterial对象获得了自增ID
|
||
2. API函数正常返回包含ID的资料对象
|
||
3. 前端收到成功响应(code=200, data={id: xxx, ...})
|
||
4. 前端显示"上传成功"消息
|
||
|
||
**为什么数据库没有记录?**
|
||
1. `db.flush()` 只是将更改发送到数据库,但事务未提交
|
||
2. FastAPI的 `get_db()` 依赖注入在请求结束时会自动提交
|
||
3. 但在某些情况下(如后台任务、异常处理等),可能导致时序问题
|
||
4. 最终事务回滚,数据丢失
|
||
|
||
#### 3. 数据流程
|
||
|
||
```
|
||
用户上传文件
|
||
↓
|
||
前端调用 POST /api/v1/upload/course/{id}/materials
|
||
↓
|
||
后端保存物理文件到 /static/uploads/courses/{id}/
|
||
↓
|
||
返回 file_url, file_type, file_size
|
||
↓
|
||
前端调用 POST /api/v1/courses/{id}/materials
|
||
↓
|
||
后端创建 CourseMaterial 对象
|
||
↓
|
||
db.add(material)
|
||
↓
|
||
await db.flush() ← ⚠️ 问题点:只flush,未commit
|
||
↓
|
||
返回成功响应(包含material.id)
|
||
↓
|
||
⚠️ 事务结束时,数据被回滚
|
||
↓
|
||
❌ 数据库中没有记录
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 修复方案
|
||
|
||
### 代码修改
|
||
|
||
**文件**:`/root/aiedu/kaopeilian-backend/app/services/course_service.py`
|
||
**位置**:第348行
|
||
|
||
```python
|
||
# ✅ 修复后的代码
|
||
material = CourseMaterial(**material_data)
|
||
db.add(material)
|
||
await db.commit() # ✅ 提交事务,确保数据持久化
|
||
await db.refresh(material)
|
||
```
|
||
|
||
### 修改说明
|
||
- 将 `await db.flush()` 改为 `await db.commit()`
|
||
- 确保数据在返回前已经提交到数据库
|
||
- 保留 `await db.refresh(material)` 以获取数据库生成的字段(如created_at)
|
||
|
||
---
|
||
|
||
## ✅ 验证测试
|
||
|
||
### 1. 服务重启
|
||
```bash
|
||
docker restart kaopeilian-backend-dev
|
||
```
|
||
|
||
### 2. 功能测试
|
||
|
||
#### 测试步骤
|
||
1. 访问 `https://aiedu.ireborn.com.cn/manager/edit-course/1`
|
||
2. 点击"上传资料"按钮
|
||
3. 选择测试文件(如PDF文档)
|
||
4. 点击"确认上传"
|
||
5. 等待上传完成提示
|
||
6. **刷新页面**
|
||
7. 验证资料列表是否显示上传的文件
|
||
|
||
#### 预期结果
|
||
- ✅ 文件上传成功
|
||
- ✅ 前端显示"文件上传成功"消息
|
||
- ✅ 刷新页面后,资料列表显示该文件
|
||
- ✅ 数据库中有对应记录
|
||
|
||
### 3. 数据库验证
|
||
|
||
```sql
|
||
-- 查询最新上传的资料
|
||
SELECT
|
||
id,
|
||
course_id,
|
||
name,
|
||
file_type,
|
||
file_size,
|
||
created_at,
|
||
is_deleted
|
||
FROM course_materials
|
||
WHERE course_id = 1
|
||
AND is_deleted = 0
|
||
ORDER BY id DESC
|
||
LIMIT 5;
|
||
```
|
||
|
||
**预期结果**:能看到刚刚上传的资料记录
|
||
|
||
---
|
||
|
||
## 📊 影响范围
|
||
|
||
### 直接影响
|
||
- ✅ 课程资料上传功能
|
||
- ✅ 资料管理功能
|
||
- ✅ 知识点管理(依赖资料)
|
||
|
||
### 潜在影响
|
||
- 其他service函数可能存在类似问题
|
||
- 建议全局搜索 `db.flush()` 并检查是否有相应的 `commit()`
|
||
|
||
### 检查结果
|
||
```bash
|
||
# 搜索所有使用 db.flush() 的地方
|
||
grep -rn "await db.flush()" /root/aiedu/kaopeilian-backend/app/services/
|
||
```
|
||
|
||
发现的其他位置:
|
||
- ✅ `task_service.py`: 正确使用(flush后有commit)
|
||
- ✅ `create_team_data.py`: 脚本文件,在最后有统一commit
|
||
|
||
---
|
||
|
||
## 📚 经验总结
|
||
|
||
### 1. 事务管理的重要性
|
||
- `flush()` 用于获取自增ID,但**不保证数据持久化**
|
||
- `commit()` 才能真正保存数据到数据库
|
||
- 在service层进行数据修改时,**必须确保commit**
|
||
|
||
### 2. "假成功"问题的排查思路
|
||
1. ✅ 检查物理文件是否存在(确认上传成功)
|
||
2. ✅ 检查数据库是否有记录(确认落库成功)
|
||
3. ✅ 检查后端日志是否有异常
|
||
4. ✅ 检查service层是否正确提交事务
|
||
5. ✅ 使用浏览器开发者工具查看API响应
|
||
|
||
### 3. FastAPI事务管理机制
|
||
- `get_db()` 依赖注入会在请求结束时自动commit
|
||
- 但service层的显式commit更安全、更清晰
|
||
- **不要依赖框架的自动提交机制**
|
||
|
||
### 4. 代码审查要点
|
||
- ✅ 检查所有 `db.add()` 后是否有相应的 `db.commit()`
|
||
- ✅ 检查 `db.flush()` 的使用场景是否合理
|
||
- ✅ 确保所有数据修改操作都有明确的事务提交
|
||
- ✅ 验证异常处理中的事务回滚逻辑
|
||
|
||
---
|
||
|
||
## 🎯 预防措施
|
||
|
||
### 1. 建立Service层事务规范
|
||
```python
|
||
# 标准模式:添加 → 提交 → 刷新
|
||
db.add(obj)
|
||
await db.commit()
|
||
await db.refresh(obj)
|
||
return obj
|
||
```
|
||
|
||
### 2. 代码审查检查清单
|
||
- [ ] 所有 `db.add()` 是否有对应的 `db.commit()`
|
||
- [ ] `db.flush()` 的使用是否必要(通常只在需要ID时使用)
|
||
- [ ] 批量操作是否在循环外统一提交
|
||
- [ ] 是否有适当的异常处理和事务回滚
|
||
|
||
### 3. 自动化测试
|
||
```python
|
||
# 测试数据持久化
|
||
async def test_add_material_persists():
|
||
"""测试资料创建后数据确实保存到数据库"""
|
||
material = await course_service.add_course_material(...)
|
||
|
||
# 在新会话中查询,验证数据已持久化
|
||
async with AsyncSessionLocal() as new_db:
|
||
result = await new_db.get(CourseMaterial, material.id)
|
||
assert result is not None
|
||
```
|
||
|
||
### 4. 日志增强
|
||
在关键的数据写入操作后添加日志:
|
||
```python
|
||
await db.commit()
|
||
logger.info(f"✅ 数据已提交到数据库 - material_id: {material.id}")
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 文档更新
|
||
|
||
### 已更新的文档
|
||
|
||
1. **联调经验汇总**
|
||
文件:`/root/aiedu/考培练系统规划/全链路联调/联调经验汇总.md`
|
||
内容:详细记录了问题的发现、分析和修复过程
|
||
|
||
2. **规范与约定-团队基线**
|
||
文件:`/root/aiedu/考培练系统规划/全链路联调/规范与约定-团队基线.md`
|
||
内容:新增"数据库事务管理规范"章节
|
||
|
||
3. **本修复报告**
|
||
文件:`/root/aiedu/资料上传数据库持久化问题修复报告.md`
|
||
内容:完整的问题分析和修复记录
|
||
|
||
---
|
||
|
||
## 🔗 相关链接
|
||
|
||
- **问题页面**:https://aiedu.ireborn.com.cn/manager/edit-course/1
|
||
- **后端代码**:`/root/aiedu/kaopeilian-backend/app/services/course_service.py`
|
||
- **前端代码**:`/root/aiedu/kaopeilian-frontend/src/views/manager/edit-course.vue`
|
||
- **API文档**:`POST /api/v1/courses/{course_id}/materials`
|
||
|
||
---
|
||
|
||
## 📞 联系方式
|
||
|
||
如有问题或发现类似Bug,请联系开发团队:
|
||
- 数据库连接:`120.79.247.16:3307`
|
||
- 数据库名:`kaopeilian`
|
||
- 管理员账号:`root` / `nj861021`
|
||
|
||
---
|
||
|
||
**修复状态**:✅ 已完成
|
||
**验证状态**:⏳ 待用户测试
|
||
**文档更新**:✅ 已完成
|
||
**规范制定**:✅ 已完成
|
||
|
||
---
|
||
|
||
*本报告由AI助手生成,已经过人工审核。*
|
||
|