版本: v1.0
更新时间: 2025-01-18
适用对象: 前端开发工程师
序号 | 接口名称 | 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/data-parse
所有接口均返回统一的JSON格式:
{
"success": boolean, // 请求是否成功
"message": string, // 响应消息
"data": object | null // 响应数据
}
任务类型 | 代码值 | 支持文件格式 | 存储目录 | 说明 |
---|---|---|---|---|
名片 | "名片" |
JPG, PNG | talent_photos/ |
名片图片解析 |
简历 | "简历" |
resume_files/ |
简历文档解析 | |
新任命 | "新任命" |
MD | appointment_files/ |
任命文档解析 |
招聘 | "招聘" |
无需文件 | 无 | 数据库记录处理 |
杂项 | "杂项" |
任意格式 | misc_files/ |
其他类型文件 |
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 | 否 | - | 任务状态过滤 | "待解析" |
{
"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
}
}
}
// 使用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 | 服务器内部错误 |
GET /api/data-parse/get-parse-task-detail
application/json
参数名 | 类型 | 必填 | 说明 | 示例值 |
---|---|---|---|---|
task_name |
string | 是 | 任务名称 | "parse_task_20250118_a1b2c3d4" |
{
"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"
}
}
// 使用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 | 服务器内部错误 |
POST /api/data-parse/add-parse-task
multipart/form-data
参数名 | 类型 | 必填 | 说明 | 示例值 |
---|---|---|---|---|
task_type |
string | 是 | 任务类型 | "名片" |
files |
File[] | 否* | 文件数组 | [file1.jpg, file2.png] |
created_by |
string | 否 | 创建者 | "frontend_user" |
注意: 除"招聘"类型外,其他类型必须上传文件
{
"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": []
}
}
}
// 使用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);
<!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 | 服务器内部错误 |
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格式 | 示例 |
---|---|---|
名片 | 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"] |
{
"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"
}
}
// 执行解析任务
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 | 服务器内部错误 |
POST /api/data-parse/add-parsed-talents
application/json
请求体为execute-parse-task API的完整返回数据:
{
"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"
}
}
{
"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响应数据用于调试
}
}
}
// 处理解析结果
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 | 服务器内部错误 |
<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>
// 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 | "部分文件上传成功" |
检查失败的文件,重新上传 |
// 统一错误处理函数
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);
}
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}%)`);
}
缓存策略 ```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);
}
}
}
};
/* 移动端适配 */
@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;
}
}
// 开发环境调试工具
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);
};
}
};
}
如有任何问题,请联系:
文档更新记录
- v1.0 (2025-01-18): 初始版本,包含5个解析任务管理接口的完整说明
- 下次更新预计: 根据接口变更情况更新