# 解析任务管理类API接口操作说明文档

> **版本**: v1.0  
> **更新时间**: 2025-01-18  
> **适用对象**: 前端开发工程师  

## 📋 目录

1. [接口概览](#接口概览)
2. [通用说明](#通用说明)
3. [接口详细说明](#接口详细说明)
   - [1. 获取解析任务列表](#1-获取解析任务列表)
   - [2. 获取解析任务详情](#2-获取解析任务详情)
   - [3. 新增解析任务](#3-新增解析任务)
   - [4. 执行解析任务](#4-执行解析任务)
   - [5. 处理解析结果](#5-处理解析结果)
4. [前端集成示例](#前端集成示例)
5. [错误处理](#错误处理)
6. [最佳实践](#最佳实践)

---

## 接口概览

| 序号 | 接口名称 | HTTP方法 | 接口路径 | 功能描述 |
|------|----------|----------|----------|----------|
| 1 | 获取解析任务列表 | GET | `/api/data-parse/get-parse-tasks` | 分页查询解析任务列表，支持过滤 |
| 2 | 获取解析任务详情 | GET | `/api/data-parse/get-parse-task-detail` | 根据任务名称获取详细信息 |
| 3 | 新增解析任务 | POST | `/api/data-parse/add-parse-task` | 创建新的解析任务并上传文件 |
| 4 | 执行解析任务 | POST | `/api/data-parse/execute-parse-task` | 执行指定的批量解析任务 |
| 5 | 处理解析结果 | POST | `/api/data-parse/add-parsed-talents` | 将解析结果写入人才数据库 |

---

## 通用说明

### 🌐 服务器信息
- **开发环境**: `http://localhost:5000`
- **生产环境**: `http://192.168.3.143:5000`
- **API前缀**: `/api/data-parse`

### 🔒 认证方式
- 当前版本暂无认证要求
- 后续版本可能需要Bearer Token

### 📊 响应格式
所有接口均返回统一的JSON格式：

```json
{
  "success": boolean,      // 请求是否成功
  "message": string,       // 响应消息
  "data": object | null    // 响应数据
}
```

### 📋 任务类型说明

| 任务类型 | 代码值 | 支持文件格式 | 存储目录 | 说明 |
|----------|--------|--------------|----------|------|
| 名片 | `"名片"` | JPG, PNG | `talent_photos/` | 名片图片解析 |
| 简历 | `"简历"` | PDF | `resume_files/` | 简历文档解析 |
| 新任命 | `"新任命"` | MD | `appointment_files/` | 任命文档解析 |
| 招聘 | `"招聘"` | 无需文件 | 无 | 数据库记录处理 |
| 杂项 | `"杂项"` | 任意格式 | `misc_files/` | 其他类型文件 |

---

## 接口详细说明

### 1. 获取解析任务列表

#### 📝 基本信息
- **接口路径**: `GET /api/data-parse/get-parse-tasks`
- **功能**: 分页查询解析任务列表，支持按任务类型和状态过滤
- **内容类型**: `application/json`

#### 📥 请求参数

| 参数名 | 类型 | 必填 | 默认值 | 说明 | 示例值 |
|--------|------|------|--------|------|--------|
| `page` | integer | 否 | 1 | 页码，从1开始 | `1` |
| `per_page` | integer | 否 | 10 | 每页记录数，最大100 | `20` |
| `task_type` | string | 否 | - | 任务类型过滤 | `"名片"` |
| `task_status` | string | 否 | - | 任务状态过滤 | `"待解析"` |

#### 📤 响应结果

```json
{
  "success": true,
  "message": "获取解析任务列表成功",
  "data": {
    "tasks": [
      {
        "id": 123,
        "task_name": "parse_task_20250118_a1b2c3d4",
        "task_status": "待解析",
        "task_type": "名片",
        "task_source": {
          "minio_paths_json": [
            "talent_photos/talent_photo_20250118_143012_a1b2c3d4.jpg"
          ],
          "upload_time": "2025-01-18T14:30:25.123456"
        },
        "collection_count": 5,
        "parse_count": 0,
        "parse_result": null,
        "created_at": "2025-01-18 14:30:25",
        "created_by": "api_user",
        "updated_at": "2025-01-18 14:30:25",
        "updated_by": "api_user"
      }
    ],
    "pagination": {
      "page": 1,
      "per_page": 10,
      "total": 25,
      "pages": 3,
      "has_next": true,
      "has_prev": false
    }
  }
}
```

#### 🎯 前端调用示例

```javascript
// 使用fetch API
async function getParseTaskList(page = 1, perPage = 10, taskType = null, taskStatus = null) {
  const params = new URLSearchParams();
  params.append('page', page);
  params.append('per_page', perPage);
  if (taskType) params.append('task_type', taskType);
  if (taskStatus) params.append('task_status', taskStatus);
  
  try {
    const response = await fetch(`/api/data-parse/get-parse-tasks?${params}`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      }
    });
    
    const result = await response.json();
    
    if (result.success) {
      console.log('任务列表获取成功:', result.data);
      return result.data;
    } else {
      console.error('获取失败:', result.message);
      throw new Error(result.message);
    }
  } catch (error) {
    console.error('请求异常:', error);
    throw error;
  }
}

// 使用示例
getParseTaskList(1, 20, '名片', '待解析')
  .then(data => {
    console.log('总任务数:', data.pagination.total);
    console.log('任务列表:', data.tasks);
  })
  .catch(error => {
    console.error('获取任务列表失败:', error);
  });
```

#### 📊 状态码说明

| HTTP状态码 | 业务状态 | 说明 |
|------------|----------|------|
| 200 | success: true | 查询成功 |
| 400 | success: false | 请求参数错误 |
| 500 | success: false | 服务器内部错误 |

---

### 2. 获取解析任务详情

#### 📝 基本信息
- **接口路径**: `GET /api/data-parse/get-parse-task-detail`
- **功能**: 根据任务名称获取解析任务的详细信息
- **内容类型**: `application/json`

#### 📥 请求参数

| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| `task_name` | string | 是 | 任务名称 | `"parse_task_20250118_a1b2c3d4"` |

#### 📤 响应结果

```json
{
  "success": true,
  "message": "成功获取任务 parse_task_20250118_a1b2c3d4 的详细信息",
  "data": {
    "id": 123,
    "task_name": "parse_task_20250118_a1b2c3d4",
    "task_status": "解析完成",
    "task_type": "名片",
    "task_source": {
      "minio_paths_json": [
        "talent_photos/talent_photo_20250118_143012_a1b2c3d4.jpg",
        "talent_photos/talent_photo_20250118_143015_b2c3d4e5.jpg"
      ],
      "upload_time": "2025-01-18T14:30:25.123456"
    },
    "collection_count": 2,
    "parse_count": 2,
    "parse_result": {
      "summary": {
        "total_files": 2,
        "success_count": 2,
        "failed_count": 0,
        "success_rate": 100.0
      },
      "results": [
        {
          "index": 0,
          "success": true,
          "data": {
            "name_zh": "张三",
            "title_zh": "技术总监",
            "hotel_zh": "北京万豪酒店",
            "mobile": "13800138000",
            "email": "zhangsan@marriott.com"
          }
        }
      ]
    },
    "created_at": "2025-01-18 14:30:25",
    "created_by": "api_user",
    "updated_at": "2025-01-18 15:45:30",
    "updated_by": "system"
  }
}
```

#### 🎯 前端调用示例

```javascript
// 使用fetch API
async function getParseTaskDetail(taskName) {
  if (!taskName) {
    throw new Error('任务名称不能为空');
  }
  
  const params = new URLSearchParams();
  params.append('task_name', taskName);
  
  try {
    const response = await fetch(`/api/data-parse/get-parse-task-detail?${params}`, {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      }
    });
    
    const result = await response.json();
    
    if (result.success) {
      console.log('任务详情获取成功:', result.data);
      return result.data;
    } else {
      console.error('获取失败:', result.message);
      throw new Error(result.message);
    }
  } catch (error) {
    console.error('请求异常:', error);
    throw error;
  }
}

// 使用示例
getParseTaskDetail('parse_task_20250118_a1b2c3d4')
  .then(taskDetail => {
    console.log('任务状态:', taskDetail.task_status);
    console.log('解析结果:', taskDetail.parse_result);
  })
  .catch(error => {
    console.error('获取任务详情失败:', error);
  });
```

#### 📊 状态码说明

| HTTP状态码 | 业务状态 | 说明 |
|------------|----------|------|
| 200 | success: true | 查询成功 |
| 400 | success: false | 请求参数错误 |
| 404 | success: false | 任务不存在 |
| 500 | success: false | 服务器内部错误 |

---

### 3. 新增解析任务

#### 📝 基本信息
- **接口路径**: `POST /api/data-parse/add-parse-task`
- **功能**: 创建新的解析任务并上传文件到MinIO存储
- **内容类型**: `multipart/form-data`

#### 📥 请求参数

| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| `task_type` | string | 是 | 任务类型 | `"名片"` |
| `files` | File[] | 否* | 文件数组 | `[file1.jpg, file2.png]` |
| `created_by` | string | 否 | 创建者 | `"frontend_user"` |

> **注意**: 除"招聘"类型外，其他类型必须上传文件

#### 📤 响应结果

```json
{
  "success": true,
  "message": "解析任务创建成功，所有文件上传完成",
  "data": {
    "task_info": {
      "id": 124,
      "task_name": "parse_task_20250118_b2c3d4e5",
      "task_status": "待解析",
      "task_type": "名片",
      "task_source": {
        "minio_paths_json": [
          "talent_photos/talent_photo_20250118_143012_a1b2c3d4.jpg"
        ],
        "upload_time": "2025-01-18T14:30:25.123456"
      },
      "collection_count": 2,
      "parse_count": 0,
      "parse_result": null,
      "created_by": "frontend_user",
      "updated_by": "frontend_user",
      "created_at": "2025-01-18 14:30:25",
      "updated_at": "2025-01-18 14:30:25"
    },
    "upload_summary": {
      "task_type": "名片",
      "total_files": 2,
      "uploaded_count": 2,
      "failed_count": 0,
      "uploaded_files": [
        {
          "original_filename": "business_card1.jpg",
          "minio_path": "talent_photos/talent_photo_20250118_143012_a1b2c3d4.jpg",
          "relative_path": "talent_photos/talent_photo_20250118_143012_a1b2c3d4.jpg",
          "file_size": 256000
        }
      ],
      "failed_uploads": []
    }
  }
}
```

#### 🎯 前端调用示例

```javascript
// 使用fetch API和FormData
async function createParseTask(taskType, files, createdBy = 'frontend_user') {
  const formData = new FormData();
  
  // 添加任务类型
  formData.append('task_type', taskType);
  
  // 添加创建者
  formData.append('created_by', createdBy);
  
  // 添加文件（如果有的话）
  if (files && files.length > 0) {
    for (let i = 0; i < files.length; i++) {
      formData.append('files', files[i]);
    }
  }
  
  try {
    const response = await fetch('/api/data-parse/add-parse-task', {
      method: 'POST',
      body: formData
      // 注意：不要设置Content-Type，让浏览器自动设置multipart/form-data
    });
    
    const result = await response.json();
    
    if (result.success) {
      console.log('任务创建成功:', result.data);
      return result.data;
    } else {
      console.error('创建失败:', result.message);
      throw new Error(result.message);
    }
  } catch (error) {
    console.error('请求异常:', error);
    throw error;
  }
}

// HTML文件上传示例
function setupFileUpload() {
  const fileInput = document.getElementById('fileInput');
  const taskTypeSelect = document.getElementById('taskTypeSelect');
  const uploadBtn = document.getElementById('uploadBtn');
  
  uploadBtn.addEventListener('click', async () => {
    const taskType = taskTypeSelect.value;
    const files = fileInput.files;
    
    if (!taskType) {
      alert('请选择任务类型');
      return;
    }
    
    if (taskType !== '招聘' && files.length === 0) {
      alert('请选择要上传的文件');
      return;
    }
    
    try {
      const result = await createParseTask(taskType, files);
      alert(`任务创建成功！任务ID: ${result.task_info.id}`);
      
      // 清空文件选择
      fileInput.value = '';
    } catch (error) {
      alert(`任务创建失败: ${error.message}`);
    }
  });
}

// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', setupFileUpload);
```

#### 🌐 HTML示例

```html
<!DOCTYPE html>
<html>
<head>
    <title>创建解析任务</title>
</head>
<body>
    <div>
        <label for="taskTypeSelect">任务类型:</label>
        <select id="taskTypeSelect">
            <option value="">请选择任务类型</option>
            <option value="名片">名片</option>
            <option value="简历">简历</option>
            <option value="新任命">新任命</option>
            <option value="招聘">招聘</option>
            <option value="杂项">杂项</option>
        </select>
    </div>
    
    <div>
        <label for="fileInput">选择文件:</label>
        <input type="file" id="fileInput" multiple accept="image/*,.pdf,.md">
    </div>
    
    <button id="uploadBtn">创建任务</button>
    
    <script src="parse-task.js"></script>
</body>
</html>
```

#### 📊 状态码说明

| HTTP状态码 | 业务状态 | 说明 |
|------------|----------|------|
| 200 | success: true | 所有文件上传成功，任务创建成功 |
| 206 | success: true | 部分文件上传成功，任务创建成功 |
| 400 | success: false | 请求参数错误 |
| 500 | success: false | 服务器内部错误 |

---

### 4. 执行解析任务

#### 📝 基本信息
- **接口路径**: `POST /api/data-parse/execute-parse-task`
- **功能**: 根据任务类型执行相应的批量处理函数
- **内容类型**: `application/json`

#### 📥 请求参数

| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| `task_type` | string | 是 | 任务类型 | `"名片"` |
| `data` | array | 是 | 数据列表，格式因任务类型而异 | `["path1.jpg", "path2.jpg"]` |
| `publish_time` | string | 否* | 发布时间（仅新任命任务需要） | `"2025-01-18"` |
| `process_type` | string | 否 | 处理类型（仅杂项任务） | `"table"` |
| `id` | integer | 是 | 解析任务ID | `123` |

> **注意**: 新任命任务必须提供`publish_time`参数

#### 📋 不同任务类型的data格式

| 任务类型 | data格式 | 示例 |
|----------|----------|------|
| 名片 | MinIO路径数组 | `["talent_photos/card1.jpg", "talent_photos/card2.jpg"]` |
| 简历 | PDF文件路径数组 | `["resume_files/resume1.pdf", "resume_files/resume2.pdf"]` |
| 新任命 | Markdown文件路径数组 | `["appointment_files/appointment1.md"]` |
| 招聘 | 招聘数据对象数组 | `[{"name": "张三", "position": "经理"}]` |
| 杂项 | 文件路径数组 | `["misc_files/file1.xlsx", "misc_files/file2.docx"]` |

#### 📤 响应结果

```json
{
  "success": true,
  "message": "批量处理完成，全部 2 个文件处理成功",
  "data": {
    "summary": {
      "total_files": 2,
      "success_count": 2,
      "failed_count": 0,
      "success_rate": 100.0
    },
    "results": [
      {
        "index": 0,
        "filename": "card1.jpg",
        "success": true,
        "error": null,
        "data": {
          "name_zh": "张三",
          "title_zh": "技术总监",
          "hotel_zh": "北京万豪酒店",
          "mobile": "13800138000",
          "email": "zhangsan@marriott.com",
          "career_path": [
            {
              "date": "2025-01-18",
              "hotel_zh": "北京万豪酒店",
              "title_zh": "技术总监",
              "source": "business_card"
            }
          ]
        },
        "message": "处理成功"
      }
    ],
    "processed_time": "2025-01-18T15:30:45.123456"
  }
}
```

#### 🎯 前端调用示例

```javascript
// 执行解析任务
async function executeParseTask(taskType, data, taskId, publishTime = null, processType = 'table') {
  const requestBody = {
    task_type: taskType,
    data: data,
    id: taskId
  };
  
  // 新任命任务需要发布时间
  if (taskType === '新任命' && publishTime) {
    requestBody.publish_time = publishTime;
  }
  
  // 杂项任务可以指定处理类型
  if (taskType === '杂项') {
    requestBody.process_type = processType;
  }
  
  try {
    const response = await fetch('/api/data-parse/execute-parse-task', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(requestBody)
    });
    
    const result = await response.json();
    
    if (result.success) {
      console.log('解析任务执行成功:', result.data);
      return result.data;
    } else {
      console.error('执行失败:', result.message);
      throw new Error(result.message);
    }
  } catch (error) {
    console.error('请求异常:', error);
    throw error;
  }
}

// 使用示例
const taskData = [
  "talent_photos/talent_photo_20250118_143012_a1b2c3d4.jpg",
  "talent_photos/talent_photo_20250118_143015_b2c3d4e5.jpg"
];

executeParseTask('名片', taskData, 123)
  .then(result => {
    console.log('处理结果:', result);
    
    // 检查处理统计
    const summary = result.summary;
    console.log(`总共${summary.total_files}个文件，成功${summary.success_count}个，失败${summary.failed_count}个`);
    
    // 处理每个结果
    result.results.forEach((item, index) => {
      if (item.success) {
        console.log(`第${index + 1}个文件处理成功:`, item.data);
      } else {
        console.error(`第${index + 1}个文件处理失败:`, item.error);
      }
    });
  })
  .catch(error => {
    console.error('解析任务执行失败:', error);
  });

// 新任命任务示例
const appointmentData = ["appointment_files/appointment_20250118.md"];
executeParseTask('新任命', appointmentData, 124, '2025-01-18')
  .then(result => {
    console.log('新任命解析完成:', result);
  })
  .catch(error => {
    console.error('新任命解析失败:', error);
  });
```

#### 📊 状态码说明

| HTTP状态码 | 业务状态 | 说明 |
|------------|----------|------|
| 200 | success: true | 全部处理成功 |
| 206 | success: true | 部分处理成功 |
| 400 | success: false | 请求参数错误 |
| 500 | success: false | 服务器内部错误 |

---

### 5. 处理解析结果

#### 📝 基本信息
- **接口路径**: `POST /api/data-parse/add-parsed-talents`
- **功能**: 处理execute-parse-task API的响应数据，将人才信息写入business_cards表
- **内容类型**: `application/json`

#### 📥 请求参数

请求体为execute-parse-task API的完整返回数据：

```json
{
  "success": true,
  "message": "处理完成",
  "data": {
    "summary": {
      "total_files": 5,
      "success_count": 4,
      "failed_count": 1,
      "success_rate": 80.0
    },
    "results": [
      {
        "index": 0,
        "success": true,
        "data": {
          "name_zh": "张三",
          "title_zh": "经理",
          "hotel_zh": "某酒店",
          "image_path": "talent_photos/image1.jpg"
        }
      }
    ],
    "processed_time": "2025-01-18T10:30:00"
  }
}
```

#### 📤 响应结果

```json
{
  "success": true,
  "message": "批量处理完成，全部 4 条人才数据写入成功",
  "data": {
    "summary": {
      "total_files": 4,
      "success_count": 4,
      "failed_count": 0,
      "success_rate": 100.0,
      "original_summary": {
        "total_files": 5,
        "success_count": 4,
        "failed_count": 1,
        "success_rate": 80.0
      }
    },
    "results": [
      {
        "index": 0,
        "original_index": 0,
        "success": true,
        "error": null,
        "data": {
          "id": 1001,
          "name_zh": "张三",
          "action": "created",
          "message": "成功创建新的人才记录"
        },
        "message": "成功处理人员: 张三"
      }
    ],
    "processed_time": "2025-01-18T16:45:30.789012",
    "original_api_response": {
      // 保留原始API响应数据用于调试
    }
  }
}
```

#### 🎯 前端调用示例

```javascript
// 处理解析结果
async function processParseResults(executeTaskResponse) {
  if (!executeTaskResponse || typeof executeTaskResponse !== 'object') {
    throw new Error('API响应数据格式错误');
  }
  
  try {
    const response = await fetch('/api/data-parse/add-parsed-talents', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(executeTaskResponse)
    });
    
    const result = await response.json();
    
    if (result.success) {
      console.log('解析结果处理成功:', result.data);
      return result.data;
    } else {
      console.error('处理失败:', result.message);
      throw new Error(result.message);
    }
  } catch (error) {
    console.error('请求异常:', error);
    throw error;
  }
}

// 完整的解析流程示例
async function completeParseWorkflow(taskType, data, taskId, publishTime = null) {
  try {
    // 第一步：执行解析任务
    console.log('步骤1: 执行解析任务...');
    const executeResult = await executeParseTask(taskType, data, taskId, publishTime);
    
    // 第二步：处理解析结果
    console.log('步骤2: 处理解析结果...');
    const processResult = await processParseResults(executeResult);
    
    // 第三步：显示最终结果
    console.log('解析流程完成!');
    console.log('原始解析统计:', executeResult.summary);
    console.log('数据写入统计:', processResult.summary);
    
    return {
      executeResult,
      processResult
    };
    
  } catch (error) {
    console.error('解析流程失败:', error);
    throw error;
  }
}

// 使用示例
const taskData = ["talent_photos/card1.jpg", "talent_photos/card2.jpg"];

completeParseWorkflow('名片', taskData, 123)
  .then(results => {
    const { executeResult, processResult } = results;
    
    // 显示处理统计
    console.log(`原始解析: ${executeResult.summary.success_count}/${executeResult.summary.total_files} 成功`);
    console.log(`数据写入: ${processResult.summary.success_count}/${processResult.summary.total_files} 成功`);
    
    // 显示详细结果
    processResult.results.forEach((item, index) => {
      if (item.success) {
        console.log(`人员${index + 1}: ${item.message}`);
      } else {
        console.error(`人员${index + 1}: ${item.error}`);
      }
    });
  })
  .catch(error => {
    console.error('完整解析流程失败:', error);
  });
```

#### 📊 状态码说明

| HTTP状态码 | 业务状态 | 说明 |
|------------|----------|------|
| 200 | success: true | 全部处理成功 |
| 206 | success: true | 部分处理成功 |
| 400 | success: false | 请求参数错误 |
| 500 | success: false | 服务器内部错误 |

---

## 前端集成示例

### 🎨 Vue.js 组件示例

```vue
<template>
  <div class="parse-task-manager">
    <h2>解析任务管理</h2>
    
    <!-- 创建任务 -->
    <div class="create-task-section">
      <h3>创建新任务</h3>
      <el-form ref="taskForm" :model="taskForm" label-width="120px">
        <el-form-item label="任务类型">
          <el-select v-model="taskForm.taskType" placeholder="请选择任务类型">
            <el-option label="名片" value="名片"></el-option>
            <el-option label="简历" value="简历"></el-option>
            <el-option label="新任命" value="新任命"></el-option>
            <el-option label="招聘" value="招聘"></el-option>
            <el-option label="杂项" value="杂项"></el-option>
          </el-select>
        </el-form-item>
        
        <el-form-item label="文件上传" v-if="taskForm.taskType !== '招聘'">
          <el-upload
            ref="upload"
            :file-list="fileList"
            :auto-upload="false"
            :multiple="true"
            :on-change="handleFileChange"
            :on-remove="handleFileRemove">
            <el-button size="small" type="primary">选择文件</el-button>
          </el-upload>
        </el-form-item>
        
        <el-form-item>
          <el-button type="primary" @click="createTask" :loading="loading">创建任务</el-button>
        </el-form-item>
      </el-form>
    </div>
    
    <!-- 任务列表 -->
    <div class="task-list-section">
      <h3>任务列表</h3>
      <el-table :data="taskList" style="width: 100%">
        <el-table-column prop="id" label="ID" width="80"></el-table-column>
        <el-table-column prop="task_name" label="任务名称" width="200"></el-table-column>
        <el-table-column prop="task_type" label="类型" width="100"></el-table-column>
        <el-table-column prop="task_status" label="状态" width="100"></el-table-column>
        <el-table-column prop="collection_count" label="文件数" width="80"></el-table-column>
        <el-table-column prop="parse_count" label="解析数" width="80"></el-table-column>
        <el-table-column prop="created_at" label="创建时间" width="180"></el-table-column>
        <el-table-column label="操作">
          <template slot-scope="scope">
            <el-button size="mini" @click="viewDetail(scope.row)">详情</el-button>
            <el-button size="mini" type="primary" @click="executeTask(scope.row)" 
                       :disabled="scope.row.task_status !== '待解析'">执行</el-button>
          </template>
        </el-table-column>
      </el-table>
      
      <!-- 分页 -->
      <el-pagination
        @current-change="handlePageChange"
        :current-page="currentPage"
        :page-size="pageSize"
        :total="total"
        layout="total, prev, pager, next">
      </el-pagination>
    </div>
    
    <!-- 任务详情对话框 -->
    <el-dialog title="任务详情" :visible.sync="detailVisible" width="80%">
      <div v-if="currentTask">
        <h4>基本信息</h4>
        <p><strong>任务名称:</strong> {{ currentTask.task_name }}</p>
        <p><strong>任务类型:</strong> {{ currentTask.task_type }}</p>
        <p><strong>任务状态:</strong> {{ currentTask.task_status }}</p>
        <p><strong>文件数量:</strong> {{ currentTask.collection_count }}</p>
        <p><strong>解析数量:</strong> {{ currentTask.parse_count }}</p>
        
        <h4 v-if="currentTask.parse_result">解析结果</h4>
        <pre v-if="currentTask.parse_result">{{ JSON.stringify(currentTask.parse_result, null, 2) }}</pre>
      </div>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: 'ParseTaskManager',
  data() {
    return {
      loading: false,
      taskForm: {
        taskType: '',
      },
      fileList: [],
      taskList: [],
      currentPage: 1,
      pageSize: 10,
      total: 0,
      detailVisible: false,
      currentTask: null
    }
  },
  mounted() {
    this.loadTaskList();
  },
  methods: {
    // 文件选择处理
    handleFileChange(file, fileList) {
      this.fileList = fileList;
    },
    
    handleFileRemove(file, fileList) {
      this.fileList = fileList;
    },
    
    // 创建任务
    async createTask() {
      if (!this.taskForm.taskType) {
        this.$message.error('请选择任务类型');
        return;
      }
      
      if (this.taskForm.taskType !== '招聘' && this.fileList.length === 0) {
        this.$message.error('请选择要上传的文件');
        return;
      }
      
      this.loading = true;
      
      try {
        const files = this.fileList.map(item => item.raw);
        const result = await this.createParseTask(this.taskForm.taskType, files);
        
        this.$message.success('任务创建成功');
        this.resetForm();
        this.loadTaskList();
        
      } catch (error) {
        this.$message.error(`任务创建失败: ${error.message}`);
      } finally {
        this.loading = false;
      }
    },
    
    // 重置表单
    resetForm() {
      this.taskForm.taskType = '';
      this.fileList = [];
      this.$refs.upload.clearFiles();
    },
    
    // 加载任务列表
    async loadTaskList() {
      try {
        const data = await this.getParseTaskList(this.currentPage, this.pageSize);
        this.taskList = data.tasks;
        this.total = data.pagination.total;
      } catch (error) {
        this.$message.error(`加载任务列表失败: ${error.message}`);
      }
    },
    
    // 分页处理
    handlePageChange(page) {
      this.currentPage = page;
      this.loadTaskList();
    },
    
    // 查看详情
    async viewDetail(task) {
      try {
        this.currentTask = await this.getParseTaskDetail(task.task_name);
        this.detailVisible = true;
      } catch (error) {
        this.$message.error(`获取任务详情失败: ${error.message}`);
      }
    },
    
    // 执行任务
    async executeTask(task) {
      if (!task.task_source || !task.task_source.minio_paths_json) {
        this.$message.error('任务数据不完整，无法执行');
        return;
      }
      
      try {
        this.$loading = this.$loading.service({
          lock: true,
          text: '正在执行解析任务...',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)'
        });
        
        // 执行解析
        const executeResult = await this.executeParseTask(
          task.task_type, 
          task.task_source.minio_paths_json, 
          task.id
        );
        
        // 处理结果
        const processResult = await this.processParseResults(executeResult);
        
        this.$message.success(`解析完成，成功处理 ${processResult.summary.success_count} 条记录`);
        this.loadTaskList();
        
      } catch (error) {
        this.$message.error(`任务执行失败: ${error.message}`);
      } finally {
        this.$loading.close();
      }
    },
    
    // API调用方法
    async createParseTask(taskType, files, createdBy = 'vue_user') {
      // 实现createParseTask方法
      // ... (参考前面的JavaScript示例)
    },
    
    async getParseTaskList(page, perPage, taskType = null, taskStatus = null) {
      // 实现getParseTaskList方法
      // ... (参考前面的JavaScript示例)
    },
    
    async getParseTaskDetail(taskName) {
      // 实现getParseTaskDetail方法
      // ... (参考前面的JavaScript示例)
    },
    
    async executeParseTask(taskType, data, taskId, publishTime = null) {
      // 实现executeParseTask方法
      // ... (参考前面的JavaScript示例)
    },
    
    async processParseResults(executeTaskResponse) {
      // 实现processParseResults方法
      // ... (参考前面的JavaScript示例)
    }
  }
}
</script>

<style scoped>
.parse-task-manager {
  padding: 20px;
}

.create-task-section,
.task-list-section {
  margin-bottom: 30px;
  padding: 20px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
}

.el-pagination {
  margin-top: 20px;
  text-align: center;
}

pre {
  background-color: #f5f5f5;
  padding: 10px;
  border-radius: 4px;
  overflow-x: auto;
  max-height: 400px;
}
</style>
```

### 🔧 工具类封装

```javascript
// parseTaskAPI.js - API调用工具类
class ParseTaskAPI {
  constructor(baseURL = '/api/data-parse') {
    this.baseURL = baseURL;
  }
  
  // 通用请求方法
  async request(url, options = {}) {
    const fullURL = `${this.baseURL}${url}`;
    
    try {
      const response = await fetch(fullURL, {
        headers: {
          'Content-Type': 'application/json',
          ...options.headers
        },
        ...options
      });
      
      const result = await response.json();
      
      if (result.success) {
        return result.data;
      } else {
        throw new Error(result.message);
      }
    } catch (error) {
      console.error(`API请求失败: ${fullURL}`, error);
      throw error;
    }
  }
  
  // GET请求（用于查询类接口）
  async get(url, params = {}) {
    const queryString = new URLSearchParams(params).toString();
    const fullURL = queryString ? `${url}?${queryString}` : url;
    
    return this.request(fullURL, { method: 'GET' });
  }
  
  // POST请求（用于操作类接口）
  async post(url, data) {
    return this.request(url, {
      method: 'POST',
      body: JSON.stringify(data)
    });
  }
  
  // FormData POST请求（用于文件上传）
  async postFormData(url, formData) {
    const fullURL = `${this.baseURL}${url}`;
    
    try {
      const response = await fetch(fullURL, {
        method: 'POST',
        body: formData
      });
      
      const result = await response.json();
      
      if (result.success) {
        return result.data;
      } else {
        throw new Error(result.message);
      }
    } catch (error) {
      console.error(`FormData请求失败: ${fullURL}`, error);
      throw error;
    }
  }
  
  // 1. 获取解析任务列表
  async getTaskList(page = 1, perPage = 10, taskType = null, taskStatus = null) {
    const params = { page, per_page: perPage };
    if (taskType) params.task_type = taskType;
    if (taskStatus) params.task_status = taskStatus;
    
    return this.get('/get-parse-tasks', params);
  }
  
  // 2. 获取解析任务详情
  async getTaskDetail(taskName) {
    if (!taskName) {
      throw new Error('任务名称不能为空');
    }
    
    return this.get('/get-parse-task-detail', { task_name: taskName });
  }
  
  // 3. 新增解析任务
  async createTask(taskType, files = [], createdBy = 'api_user') {
    const formData = new FormData();
    formData.append('task_type', taskType);
    formData.append('created_by', createdBy);
    
    if (files && files.length > 0) {
      for (let i = 0; i < files.length; i++) {
        formData.append('files', files[i]);
      }
    }
    
    return this.postFormData('/add-parse-task', formData);
  }
  
  // 4. 执行解析任务
  async executeTask(taskType, data, taskId, publishTime = null, processType = 'table') {
    const requestBody = {
      task_type: taskType,
      data: data,
      id: taskId
    };
    
    if (taskType === '新任命' && publishTime) {
      requestBody.publish_time = publishTime;
    }
    
    if (taskType === '杂项') {
      requestBody.process_type = processType;
    }
    
    return this.post('/execute-parse-task', requestBody);
  }
  
  // 5. 处理解析结果
  async processResults(executeTaskResponse) {
    if (!executeTaskResponse || typeof executeTaskResponse !== 'object') {
      throw new Error('API响应数据格式错误');
    }
    
    return this.post('/add-parsed-talents', executeTaskResponse);
  }
  
  // 组合方法：完整的解析流程
  async completeParseWorkflow(taskType, data, taskId, publishTime = null) {
    try {
      // 执行解析任务
      const executeResult = await this.executeTask(taskType, data, taskId, publishTime);
      
      // 处理解析结果
      const processResult = await this.processResults(executeResult);
      
      return {
        executeResult,
        processResult
      };
    } catch (error) {
      console.error('完整解析流程失败:', error);
      throw error;
    }
  }
}

// 导出实例
export default new ParseTaskAPI();

// 使用示例：
// import parseTaskAPI from './parseTaskAPI.js';
// 
// // 获取任务列表
// const taskList = await parseTaskAPI.getTaskList(1, 20, '名片');
// 
// // 创建任务
// const createResult = await parseTaskAPI.createTask('名片', files);
// 
// // 执行完整流程
// const result = await parseTaskAPI.completeParseWorkflow('名片', data, taskId);
```

---

## 错误处理

### 🚨 常见错误类型

| 错误类型 | HTTP状态码 | 错误信息示例 | 处理建议 |
|----------|------------|--------------|----------|
| 参数错误 | 400 | `"缺少task_type参数"` | 检查请求参数完整性 |
| 文件格式错误 | 400 | `"不支持的文件格式"` | 检查文件类型是否匹配任务类型 |
| 任务不存在 | 404 | `"未找到任务名称为 xxx 的记录"` | 确认任务名称正确 |
| 服务器错误 | 500 | `"数据库连接失败"` | 联系后端开发人员 |
| 部分成功 | 206 | `"部分文件上传成功"` | 检查失败的文件，重新上传 |

### 🛠️ 错误处理最佳实践

```javascript
// 统一错误处理函数
function handleAPIError(error, context = '') {
  console.error(`${context}失败:`, error);
  
  // 根据错误类型给出不同的用户提示
  if (error.message.includes('网络')) {
    return '网络连接异常，请检查网络后重试';
  } else if (error.message.includes('参数')) {
    return '请求参数错误，请检查输入信息';
  } else if (error.message.includes('权限')) {
    return '权限不足，请联系管理员';
  } else if (error.message.includes('不存在')) {
    return '请求的资源不存在';
  } else {
    return `操作失败: ${error.message}`;
  }
}

// 带重试机制的API调用
async function apiCallWithRetry(apiFunction, maxRetries = 3, delay = 1000) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await apiFunction();
    } catch (error) {
      if (i === maxRetries - 1) {
        throw error;
      }
      
      // 如果是网络错误或服务器错误，等待后重试
      if (error.message.includes('fetch') || error.message.includes('500')) {
        console.log(`第${i + 1}次重试，${delay}ms后重新请求...`);
        await new Promise(resolve => setTimeout(resolve, delay));
        delay *= 2; // 指数退避
      } else {
        throw error; // 其他错误直接抛出
      }
    }
  }
}

// 使用示例
try {
  const result = await apiCallWithRetry(
    () => parseTaskAPI.getTaskList(1, 10),
    3,
    1000
  );
  console.log('获取任务列表成功:', result);
} catch (error) {
  const userMessage = handleAPIError(error, '获取任务列表');
  alert(userMessage);
}
```

---

## 最佳实践

### 🎯 前端开发建议

1. **参数验证**
   ```javascript
   // 在调用API前进行客户端验证
   function validateTaskType(taskType) {
     const validTypes = ['名片', '简历', '新任命', '招聘', '杂项'];
     if (!validTypes.includes(taskType)) {
       throw new Error(`无效的任务类型: ${taskType}`);
     }
   }
   
   function validateFiles(files, taskType) {
     if (taskType === '招聘') {
       return; // 招聘任务不需要文件
     }
     
     if (!files || files.length === 0) {
       throw new Error('请选择要上传的文件');
     }
     
     // 文件格式验证
     const allowedFormats = {
       '名片': ['jpg', 'jpeg', 'png'],
       '简历': ['pdf'],
       '新任命': ['md'],
       '杂项': ['*'] // 支持所有格式
     };
     
     const formats = allowedFormats[taskType];
     if (formats && !formats.includes('*')) {
       files.forEach(file => {
         const ext = file.name.split('.').pop().toLowerCase();
         if (!formats.includes(ext)) {
           throw new Error(`${taskType}任务不支持${ext}格式文件`);
         }
       });
     }
   }
   ```

2. **进度显示**
   ```javascript
   // 文件上传进度显示
   function showUploadProgress(current, total) {
     const percentage = Math.round((current / total) * 100);
     console.log(`上传进度: ${current}/${total} (${percentage}%)`);
     
     // 更新UI进度条
     updateProgressBar(percentage);
   }
   
   // 解析进度显示
   function showParseProgress(results) {
     const completed = results.filter(r => r.success || r.error).length;
     const total = results.length;
     const percentage = Math.round((completed / total) * 100);
     
     console.log(`解析进度: ${completed}/${total} (${percentage}%)`);
   }
   ```

3. **缓存策略**
   ```javascript
   // 任务列表缓存
   class TaskListCache {
     constructor(ttl = 5 * 60 * 1000) { // 5分钟TTL
       this.cache = new Map();
       this.ttl = ttl;
     }
     
     getKey(page, perPage, taskType, taskStatus) {
       return `${page}_${perPage}_${taskType || ''}_${taskStatus || ''}`;
     }
     
     get(page, perPage, taskType, taskStatus) {
       const key = this.getKey(page, perPage, taskType, taskStatus);
       const item = this.cache.get(key);
       
       if (!item) return null;
       
       if (Date.now() - item.timestamp > this.ttl) {
         this.cache.delete(key);
         return null;
       }
       
       return item.data;
     }
     
     set(page, perPage, taskType, taskStatus, data) {
       const key = this.getKey(page, perPage, taskType, taskStatus);
       this.cache.set(key, {
         data,
         timestamp: Date.now()
       });
     }
     
     clear() {
       this.cache.clear();
     }
   }
   
   const taskListCache = new TaskListCache();
   ```

4. **状态管理**
   ```javascript
   // 使用Vuex或类似状态管理
   const parseTaskStore = {
     state: {
       taskList: [],
       currentTask: null,
       loading: false,
       error: null
     },
     
     mutations: {
       SET_TASK_LIST(state, tasks) {
         state.taskList = tasks;
       },
       SET_CURRENT_TASK(state, task) {
         state.currentTask = task;
       },
       SET_LOADING(state, loading) {
         state.loading = loading;
       },
       SET_ERROR(state, error) {
         state.error = error;
       }
     },
     
     actions: {
       async loadTaskList({ commit }, { page, perPage, taskType, taskStatus }) {
         commit('SET_LOADING', true);
         commit('SET_ERROR', null);
         
         try {
           const data = await parseTaskAPI.getTaskList(page, perPage, taskType, taskStatus);
           commit('SET_TASK_LIST', data.tasks);
           return data;
         } catch (error) {
           commit('SET_ERROR', error.message);
           throw error;
         } finally {
           commit('SET_LOADING', false);
         }
       }
     }
   };
   ```

### 📱 响应式设计

```css
/* 移动端适配 */
@media (max-width: 768px) {
  .parse-task-manager {
    padding: 10px;
  }
  
  .el-table {
    font-size: 12px;
  }
  
  .el-table-column {
    min-width: 80px;
  }
  
  .create-task-section,
  .task-list-section {
    padding: 15px;
    margin-bottom: 20px;
  }
}

/* 大屏优化 */
@media (min-width: 1200px) {
  .parse-task-manager {
    max-width: 1200px;
    margin: 0 auto;
  }
}
```

### 🔍 调试工具

```javascript
// 开发环境调试工具
if (process.env.NODE_ENV === 'development') {
  window.parseTaskDebug = {
    // 显示API调用日志
    enableAPILogging() {
      const originalRequest = parseTaskAPI.request;
      parseTaskAPI.request = function(...args) {
        console.group(`API调用: ${args[0]}`);
        console.log('参数:', args[1]);
        console.time('请求耗时');
        
        return originalRequest.apply(this, args)
          .then(result => {
            console.log('响应:', result);
            console.timeEnd('请求耗时');
            console.groupEnd();
            return result;
          })
          .catch(error => {
            console.error('错误:', error);
            console.timeEnd('请求耗时');
            console.groupEnd();
            throw error;
          });
      };
    },
    
    // 模拟API响应
    mockAPI(endpoint, response) {
      const originalRequest = parseTaskAPI.request;
      parseTaskAPI.request = function(url, options) {
        if (url.includes(endpoint)) {
          console.log(`Mock API: ${endpoint}`, response);
          return Promise.resolve(response);
        }
        return originalRequest.apply(this, arguments);
      };
    }
  };
}
```

---

## 📞 技术支持

如有任何问题，请联系：

- **后端开发团队**: backend@company.com
- **API文档维护**: api-docs@company.com
- **技术支持**: tech-support@company.com

---

> **文档更新记录**  
> - v1.0 (2025-01-18): 初始版本，包含5个解析任务管理接口的完整说明
> - 下次更新预计: 根据接口变更情况更新 