You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4.9 KiB

附件上传功能"Failed to create attachment"问题修复详细说明

问题现象

当创建任务并添加附件时,显示"Failed to create attachment"错误。

问题分析

1. 原始问题定位

  • 前端设置了 :auto-upload="false",需要手动触发上传
  • 上传成功后的数据处理可能存在字段匹配问题
  • 附件关联到任务的API调用可能失败

2. 数据流分析

后端 UploadFile API 响应格式:

{
  "code": 200,
  "message": "File uploaded successfully",
  "data": {
    "filename": "1736303317123456789_test.pdf",
    "filepath": "./uploads/1736303317123456789_test.pdf", 
    "size": 12345,
    "mime_type": "application/pdf"
  }
}

后端 TaskAttachment 模型字段:

type TaskAttachment struct {
    ID         uint   `json:"id"`
    TaskID     uint   `json:"task_id"`
    FileName   string `json:"file_name"`     // 注意这里是下划线格式
    FilePath   string `json:"file_path"`
    FileSize   int64  `json:"file_size"`
    FileType   string `json:"file_type"`
    UploadedBy uint   `json:"uploaded_by"`
    CreatedAt  time.Time `json:"created_at"`
}

修复方案

1. 增强前端上传成功处理

const handleUploadSuccess = (response: any, file: any, fileList: any[]) => {
  console.log('Upload success - raw response:', response)
  
  // 检查响应格式
  if (!response || !response.data) {
    ElMessage.error('文件上传响应格式错误')
    return
  }
  
  ElMessage.success('文件上传成功')
  
  // 将上传成功的文件信息添加到taskForm.attachments
  const attachmentData = {
    file_name: response.data.filename || file.name,        // ✓ 字段匹配
    file_path: response.data.filepath || response.data.path || '',
    file_size: response.data.size || file.size || 0,
    file_type: response.data.mime_type || file.type || ''
  }
  
  taskForm.attachments.push(attachmentData)
}

2. 优化文件上传时机控制

const saveTask = async () => {
  // 检查是否有需要上传的文件
  const hasUnuploadedFiles = fileList.value.some((file: any) => !file.response)
  
  if (hasUnuploadedFiles) {
    console.log('Found unuploaded files, uploading...')
    if (uploadRef.value) {
      await uploadRef.value.submit()
      // 等待确保上传完成
      await new Promise(resolve => setTimeout(resolve, 1000))
    }
  }
  
  // 创建任务...
  // 然后处理附件关联...
}

3. 增强附件关联错误处理

// 如果有附件,需要关联到任务
if (taskForm.attachments && taskForm.attachments.length > 0) {
  for (let i = 0; i < taskForm.attachments.length; i++) {
    const attachment = taskForm.attachments[i]
    try {
      await taskApi.addTaskAttachment(taskId, {
        file_name: attachment.file_name,
        file_path: attachment.file_path,
        file_size: attachment.file_size,
        file_type: attachment.file_type,
        uploaded_by: 1
      })
    } catch (attachmentError: any) {
      // 详细错误处理,附件失败不影响主任务创建
      let errorMsg = `附件 "${attachment.file_name}" 添加失败`
      if (attachmentError.response?.data?.message) {
        errorMsg += `: ${attachmentError.response.data.message}`
      }
      ElMessage.warning(errorMsg)
    }
  }
}

4. 改进文件移除处理

const handleFileRemove = (file: any, fileList: any[]) => {
  const index = taskForm.attachments.findIndex(
    (attachment: any) => {
      const uploadedFileName = file.response?.data?.filename || file.name
      return attachment.file_name === uploadedFileName
    }
  )
  
  if (index > -1) {
    taskForm.attachments.splice(index, 1)
  }
}

测试方法

1. 基本附件上传测试

  1. 启动前后端服务器
  2. 访问 http://localhost:5173/tasks
  3. 点击"创建任务"
  4. 填写基本信息,拖拽文件到上传区域
  5. 点击"确定"创建任务
  6. 观察控制台调试信息和网络请求

2. 异常情况测试

  • 测试大文件上传
  • 测试不支持的文件格式
  • 测试网络中断情况
  • 测试后端服务异常情况

3. 调试信息

修复后的代码会在浏览器控制台输出详细调试信息:

  • 上传成功时的原始响应
  • 构造的附件数据
  • 任务创建成功后的ID
  • 每个附件的关联结果

关键改进点

  1. 响应格式验证:增加了对上传响应格式的检查
  2. 字段映射确认:确保前端构造的数据与后端模型字段一致
  3. 错误隔离:附件添加失败不影响主任务创建
  4. 调试增强:添加了详细的调试日志
  5. 时序控制:确保文件上传完成后再进行任务创建

预期结果

修复后,创建带附件的任务应该:

  1. 文件正常上传到服务器
  2. 任务创建成功
  3. 附件正确关联到任务
  4. 即使个别附件失败,也不影响整体流程
  5. 用户得到明确的成功/失败反馈