- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
7.9 KiB
7.9 KiB
资料上传数据库持久化问题修复报告
修复日期:2025-10-17
问题来源:用户反馈
严重级别:⚠️ 高(数据丢失)
影响范围:课程资料上传功能
📋 问题描述
用户反馈
在课程编辑页面 https://aiedu.ireborn.com.cn/manager/edit-course/1 上传资料后:
- ✅ 资料上传成功(文件被保存)
- ✅ 前端显示"上传成功"消息
- ❌ 刷新页面后,资料记录消失
- ❌ 数据库中没有资料记录
复现步骤
- 登录管理员账号
- 进入课程编辑页面
- 点击"上传资料"按钮
- 选择文件并上传
- 看到"上传成功"提示
- 刷新页面
- 发现资料列表为空
🔍 问题分析
根本原因
数据库事务未提交:后端service层只执行了 db.flush() 但没有 db.commit()
技术细节
1. 错误代码位置
文件:/root/aiedu/kaopeilian-backend/app/services/course_service.py
函数:add_course_material (第313-361行)
# ❌ 第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() |
提交事务,将更改永久保存到数据库 | ✅ 是,数据已持久化 |
为什么前端能看到成功响应?
- service层执行
db.flush()后,CourseMaterial对象获得了自增ID - API函数正常返回包含ID的资料对象
- 前端收到成功响应(code=200, data={id: xxx, ...})
- 前端显示"上传成功"消息
为什么数据库没有记录?
db.flush()只是将更改发送到数据库,但事务未提交- FastAPI的
get_db()依赖注入在请求结束时会自动提交 - 但在某些情况下(如后台任务、异常处理等),可能导致时序问题
- 最终事务回滚,数据丢失
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行
# ✅ 修复后的代码
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. 服务重启
docker restart kaopeilian-backend-dev
2. 功能测试
测试步骤
- 访问
https://aiedu.ireborn.com.cn/manager/edit-course/1 - 点击"上传资料"按钮
- 选择测试文件(如PDF文档)
- 点击"确认上传"
- 等待上传完成提示
- 刷新页面
- 验证资料列表是否显示上传的文件
预期结果
- ✅ 文件上传成功
- ✅ 前端显示"文件上传成功"消息
- ✅ 刷新页面后,资料列表显示该文件
- ✅ 数据库中有对应记录
3. 数据库验证
-- 查询最新上传的资料
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()
检查结果
# 搜索所有使用 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. "假成功"问题的排查思路
- ✅ 检查物理文件是否存在(确认上传成功)
- ✅ 检查数据库是否有记录(确认落库成功)
- ✅ 检查后端日志是否有异常
- ✅ 检查service层是否正确提交事务
- ✅ 使用浏览器开发者工具查看API响应
3. FastAPI事务管理机制
get_db()依赖注入会在请求结束时自动commit- 但service层的显式commit更安全、更清晰
- 不要依赖框架的自动提交机制
4. 代码审查要点
- ✅ 检查所有
db.add()后是否有相应的db.commit() - ✅ 检查
db.flush()的使用场景是否合理 - ✅ 确保所有数据修改操作都有明确的事务提交
- ✅ 验证异常处理中的事务回滚逻辑
🎯 预防措施
1. 建立Service层事务规范
# 标准模式:添加 → 提交 → 刷新
db.add(obj)
await db.commit()
await db.refresh(obj)
return obj
2. 代码审查检查清单
- 所有
db.add()是否有对应的db.commit() db.flush()的使用是否必要(通常只在需要ID时使用)- 批量操作是否在循环外统一提交
- 是否有适当的异常处理和事务回滚
3. 自动化测试
# 测试数据持久化
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. 日志增强
在关键的数据写入操作后添加日志:
await db.commit()
logger.info(f"✅ 数据已提交到数据库 - material_id: {material.id}")
📝 文档更新
已更新的文档
-
联调经验汇总
文件:/root/aiedu/考培练系统规划/全链路联调/联调经验汇总.md
内容:详细记录了问题的发现、分析和修复过程 -
规范与约定-团队基线
文件:/root/aiedu/考培练系统规划/全链路联调/规范与约定-团队基线.md
内容:新增"数据库事务管理规范"章节 -
本修复报告
文件:/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助手生成,已经过人工审核。