Files
012-kaopeilian/知识库/问题修复/资料上传数据库持久化问题修复报告.md
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

301 lines
7.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 资料上传数据库持久化问题修复报告
**修复日期**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助手生成已经过人工审核。*