- 从服务器拉取完整代码 - 按框架规范整理项目结构 - 配置 Drone CI 测试环境部署 - 包含后端(FastAPI)、前端(Vue3)、管理端 技术栈: Vue3 + TypeScript + FastAPI + MySQL
10 KiB
文件上传和AI分析问题修复报告
修复时间
2025-10-16 22:40 - 2025-10-17 06:54
问题描述
用户报告了两个关键问题:
问题1:文件上传后刷新页面消失
- 现象:上传文件后显示成功,也看得到文件,但刷新页面后文件就消失了
- 根因:数据库记录没有真正创建(数据库中没有对应的course_materials记录)
问题2:AI分析知识点未正确启动Dify工作流
- 现象:文件上传后AI分析未自动启动
- 根因:前端代码只改变UI状态,没有真正调用AI分析API
根本原因分析
原因1:http.ts的upload方法双重数据提取
文件:/root/aiedu/kaopeilian-frontend/src/utils/http.ts
问题代码(第392行):
upload<T = any>(url: string, file: File, config?: RequestConfig): Promise<ResponseData<T>> {
// ... FormData构建 ...
return this.instance.post(url, formData, config)
.then((response) => response.data as ResponseData<T>) // ❌ 多余的数据提取
}
数据流转过程:
- 后端返回:
{ code: 200, data: { file_url: '...', file_type: '...', file_size: ... } } - 响应拦截器处理后返回:
{ code: 200, data: { file_url: '...', ... } } - upload方法又提取
.data,最终返回:{ file_url: '...', ... }(丢失code字段) - 前端判断
uploadRes.code === 200失败 → 抛出"文件上传失败"错误
修复措施:
upload<T = any>(url: string, file: File, config?: RequestConfig): Promise<ResponseData<T>> {
// ... FormData构建 ...
return this.instance.post(url, formData, config) // ✅ 直接返回
}
原因2:上传成功后未启动AI分析
文件:/root/aiedu/kaopeilian-frontend/src/views/manager/edit-course.vue
问题代码(第1158-1165行):
// 上传成功后,设置状态为分析中
setTimeout(() => {
const material = materialList.value.find(m => m.id === newMaterial.id)
if (material) {
material.status = 'analyzing' // ❌ 只改变UI状态
ElMessage.info(`正在分析资料"${material.name}"的知识点...`)
}
}, 1000)
问题:只修改了UI状态,没有调用API启动AI分析
修复措施:
// 上传成功后,自动启动AI分析
setTimeout(async () => {
const material = materialList.value.find(m => m.id === newMaterial.id)
if (material) {
console.log('自动启动AI知识点分析:', material.id, material.name)
await analyzeWithAI(material) // ✅ 真正调用AI分析
}
}, 1000)
原因3:AI分析API返回404
现象:POST /api/v1/courses/1/materials/30/analyze 返回404
根因:前端传递的material_id=30在数据库中不存在
- 数据库最新的material_id是26
- material_id=30是前端临时添加到列表的,但数据库记录创建失败
- 因为之前的http.ts bug导致创建资料记录的API调用失败
修复详情
修复1:http.ts的upload方法
文件:/root/aiedu/kaopeilian-frontend/src/utils/http.ts(第392行)
修改前:
}).then((response) => response.data as ResponseData<T>)
修改后:
}) // 直接返回响应拦截器处理后的结果
修复2:自动启动AI分析
文件:/root/aiedu/kaopeilian-frontend/src/views/manager/edit-course.vue(第1158-1165行)
修改前:
setTimeout(() => {
const material = materialList.value.find(m => m.id === newMaterial.id)
if (material) {
material.status = 'analyzing'
ElMessage.info(`正在分析资料"${material.name}"的知识点...`)
}
}, 1000)
修改后:
setTimeout(async () => {
const material = materialList.value.find(m => m.id === newMaterial.id)
if (material) {
console.log('自动启动AI知识点分析:', material.id, material.name)
await analyzeWithAI(material)
}
}, 1000)
验证结果
验证1:upload方法返回正确结构
✅ 修复后,upload() 方法正确返回 { code: 200, data: {...} } 结构
验证2:数据库记录成功创建
✅ 调用 courseApi.addMaterial() 时能正确判断 res.code === 200
✅ 数据库中成功创建 course_materials 记录
✅ 刷新页面后文件不再消失
验证3:AI分析自动启动
✅ 文件上传成功后自动调用 analyzeWithAI(material)
✅ 正确调用 /api/v1/courses/{course_id}/materials/{material_id}/analyze API
✅ Dify工作流成功启动
完整的数据流程
修复后的正确流程
-
用户选择文件并点击"确认上传"
-
前端上传文件到服务器
const uploadRes = await request.upload( `/api/v1/upload/course/${courseId}/materials`, file.raw ) // 返回:{ code: 200, data: { file_url: '...', file_type: '...', file_size: ... } } -
判断上传成功
if (uploadRes.code === 200 && uploadRes.data) { // ✅ 判断通过 -
创建数据库记录
const res = await courseApi.addMaterial(courseId, { name: file.name, file_url: uploadRes.data.file_url, file_type: uploadRes.data.file_type, file_size: uploadRes.data.file_size }) // 返回:{ code: 200, data: { id: 27, name: '...', ... } } -
添加到前端列表
const newMaterial = { id: res.data.id, // ✅ 真实的数据库ID name: res.data.name, status: 'pending', knowledgePoints: [] } materialList.value.push(newMaterial) -
自动启动AI分析
setTimeout(async () => { await analyzeWithAI(material) // 调用:POST /api/v1/courses/{course_id}/materials/{material.id}/analyze }, 1000) -
Dify工作流启动
- 后端接收分析请求
- 调用Dify API上传文件并启动知识点分析工作流
- 返回workflow_run_id给前端
- 工作流完成后自动保存知识点到数据库
-
刷新页面
- 从数据库加载
course_materials - 文件和知识点都正常显示 ✅
- 从数据库加载
技术要点总结
1. 响应拦截器与方法封装的关系
核心原则:
- 响应拦截器负责统一处理所有响应,返回标准的
{code, message, data}结构 - 具体方法(get/post/upload等)只负责构造请求,不应再次处理响应结构
- 保持"单一职责":拦截器处理响应,方法构造请求
常见错误:
// ❌ 错误:双重数据提取
upload() {
return this.instance.post(url, data).then(res => res.data)
// 响应拦截器已返回 {code, data},再次.data会丢失code
}
// ✅ 正确:直接返回
upload() {
return this.instance.post(url, data)
}
2. 异步操作的完整性
原则:
- UI状态更新必须基于实际的后端操作结果
- 不能"假装成功"——只改UI不调API
- 异步任务必须真正启动,不能只显示"进行中"
修复前:
material.status = 'analyzing' // 假装在分析
ElMessage.info('正在分析...') // 但实际没调用API
修复后:
await analyzeWithAI(material) // 真正调用API
// API内部会:
// 1. 设置状态为analyzing
// 2. 调用后端API
// 3. 根据结果更新状态为completed/failed
3. 数据库事务的正确性
虽然这次不是数据库事务的问题,但需要注意:
- FastAPI的
get_db依赖会自动commit - 但前提是API调用成功执行到结束
- 如果API调用失败(如前端判断错误没有调用),数据库操作根本不会发生
相关文件
修改的文件
/root/aiedu/kaopeilian-frontend/src/utils/http.ts- 修复upload方法/root/aiedu/kaopeilian-frontend/src/views/manager/edit-course.vue- 修复AI分析启动
文档更新
/root/aiedu/考培练系统规划/全链路联调/联调经验汇总.md- 记录问题和修复/root/aiedu/考培练系统规划/全链路联调/规范与约定-团队基线.md- 更新团队规范
后端相关文件(已验证)
/root/aiedu/kaopeilian-backend/app/api/v1/upload.py- 文件上传API ✓/root/aiedu/kaopeilian-backend/app/api/v1/courses.py- 资料管理API ✓/root/aiedu/kaopeilian-backend/app/api/v1/knowledge_analysis.py- 知识点分析API ✓/root/aiedu/kaopeilian-backend/app/services/course_service.py- 课程服务 ✓
测试建议
测试步骤
- 打开编辑课程页面
- 点击"上传资料",选择一个PDF文件
- 点击"确认上传"
- 验证点1:上传成功提示,文件出现在列表中
- 验证点2:1秒后显示"AI正在分析文件内容,提取知识点..."
- 验证点3:等待30-180秒,知识点分析完成
- 验证点4:刷新页面,文件和知识点仍然存在
数据库验证
-- 查询最新的资料记录
SELECT id, course_id, name, created_at
FROM course_materials
ORDER BY created_at DESC
LIMIT 5;
-- 查询知识点
SELECT id, course_id, material_id, name, type, source
FROM knowledge_points
WHERE material_id = {新资料ID}
ORDER BY created_at DESC;
日志验证
# 查看后端日志
docker compose logs backend --tail 100 | grep -E "(上传成功|添加课程资料|知识点分析)"
# 查看前端日志
# 浏览器控制台应显示:
# - 开始上传文件: xxx.pdf
# - 文件上传响应: { code: 200, data: {...} }
# - 创建资料记录响应: { code: 200, data: { id: 27, ... } }
# - 自动启动AI知识点分析: 27 xxx.pdf
# - AI分析API响应: { code: 200, data: { status: 'succeeded', ... } }
后续改进建议
-
添加单元测试
- 测试
http.ts的upload方法返回结构 - 测试
courseApi.addMaterial的完整流程
- 测试
-
增强错误处理
- 文件上传失败时显示详细错误信息
- AI分析失败时允许手动重试
-
优化用户体验
- 显示上传进度条
- 显示AI分析进度(轮询任务状态)
- 支持批量上传文件
-
监控和告警
- 记录文件上传失败率
- 记录AI分析成功率
- 异常时发送告警
总结
本次修复解决了两个关键问题:
- ✅ http.ts双重数据提取 - 导致文件上传后无法创建数据库记录
- ✅ AI分析未真正启动 - 导致知识点无法自动提取
核心教训:
- 响应拦截器和方法封装要保持单一职责,避免重复处理
- UI状态必须基于真实的API调用结果,不能"假装成功"
- 异步操作的完整性至关重要,每一步都要真正执行
修复后的完整流程已验证通过,文件上传、数据库记录创建、AI知识点分析都能正常工作。