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

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

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