解析任务API接口文档
概述
本文档描述了DataOps平台中用于查询解析任务的两个API接口:
get_parse_tasks
: 获取解析任务列表(支持分页和过滤)
get_parse_task_detail
: 获取解析任务详情
这些接口用于查询和管理通过网页解析功能创建的解析任务记录。
1. 获取解析任务列表
接口信息
- 接口路径:
/api/data_parse/get-parse-tasks
- 请求方法:
GET
- 接口描述: 获取解析任务列表,支持分页查询和条件过滤
请求参数
参数名 |
类型 |
必填 |
默认值 |
描述 |
page |
int |
否 |
1 |
页码,从1开始 |
per_page |
int |
否 |
10 |
每页记录数,最大100 |
task_type |
string |
否 |
- |
任务类型过滤(如:"门墩儿新任命") |
task_status |
string |
否 |
- |
任务状态过滤(如:"completed") |
请求示例
# 基础查询
GET /api/data_parse/get-parse-tasks
# 分页查询
GET /api/data_parse/get-parse-tasks?page=2&per_page=20
# 条件过滤
GET /api/data_parse/get-parse-tasks?task_type=门墩儿新任命&task_status=completed
# 组合查询
GET /api/data_parse/get-parse-tasks?page=1&per_page=10&task_type=门墩儿新任命
返回数据结构
{
"success": true,
"message": "获取解析任务列表成功",
"data": {
"tasks": [
{
"id": 1,
"task_name": "20250714_a1b2c3d4",
"task_status": "completed",
"task_type": "门墩儿新任命",
"task_source": "网页解析",
"collection_count": 5,
"parse_count": 4,
"created_at": "2025-01-14T10:30:00Z",
"created_by": "system",
"updated_at": "2025-01-14T10:35:00Z",
"updated_by": "system"
}
],
"pagination": {
"page": 1,
"per_page": 10,
"total": 25,
"pages": 3,
"has_prev": false,
"has_next": true
}
}
}
前端JavaScript示例
// 使用fetch API
async function getParseTasksList(page = 1, perPage = 10, taskType = '', taskStatus = '') {
try {
const params = new URLSearchParams({
page: page.toString(),
per_page: perPage.toString()
});
if (taskType) params.append('task_type', taskType);
if (taskStatus) params.append('task_status', taskStatus);
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.tasks);
console.log('分页信息:', result.data.pagination);
return result.data;
} else {
console.error('获取失败:', result.message);
return null;
}
} catch (error) {
console.error('请求异常:', error);
return null;
}
}
// 使用示例
getParseTasksList(1, 10, '门墩儿新任命', 'completed');
前端Vue.js示例
<template>
<div class="parse-tasks-list">
<!-- 筛选条件 -->
<div class="filters">
<select v-model="filters.taskType" @change="loadTasks">
<option value="">全部类型</option>
<option value="门墩儿新任命">门墩儿新任命</option>
<option value="门墩儿招聘">门墩儿招聘</option>
</select>
<select v-model="filters.taskStatus" @change="loadTasks">
<option value="">全部状态</option>
<option value="completed">已完成</option>
<option value="pending">待处理</option>
</select>
</div>
<!-- 任务列表 -->
<div class="tasks-table">
<table>
<thead>
<tr>
<th>任务名称</th>
<th>任务类型</th>
<th>状态</th>
<th>采集人数</th>
<th>解析人数</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="task in tasks" :key="task.id">
<td>{{ task.task_name }}</td>
<td>{{ task.task_type }}</td>
<td>{{ task.task_status }}</td>
<td>{{ task.collection_count }}</td>
<td>{{ task.parse_count }}</td>
<td>{{ formatDate(task.created_at) }}</td>
<td>
<button @click="viewDetail(task.task_name)">查看详情</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 分页 -->
<div class="pagination">
<button @click="prevPage" :disabled="!pagination.has_prev">上一页</button>
<span>第 {{ pagination.page }} 页,共 {{ pagination.pages }} 页</span>
<button @click="nextPage" :disabled="!pagination.has_next">下一页</button>
</div>
</div>
</template>
<script>
export default {
name: 'ParseTasksList',
data() {
return {
tasks: [],
pagination: {},
filters: {
taskType: '',
taskStatus: ''
},
currentPage: 1,
perPage: 10
}
},
mounted() {
this.loadTasks();
},
methods: {
async loadTasks() {
try {
const params = new URLSearchParams({
page: this.currentPage.toString(),
per_page: this.perPage.toString()
});
if (this.filters.taskType) params.append('task_type', this.filters.taskType);
if (this.filters.taskStatus) params.append('task_status', this.filters.taskStatus);
const response = await fetch(`/api/data_parse/get-parse-tasks?${params}`);
const result = await response.json();
if (result.success) {
this.tasks = result.data.tasks;
this.pagination = result.data.pagination;
} else {
this.$message.error(result.message);
}
} catch (error) {
this.$message.error('加载任务列表失败');
}
},
prevPage() {
if (this.pagination.has_prev) {
this.currentPage--;
this.loadTasks();
}
},
nextPage() {
if (this.pagination.has_next) {
this.currentPage++;
this.loadTasks();
}
},
viewDetail(taskName) {
this.$router.push(`/parse-task-detail?task_name=${taskName}`);
},
formatDate(dateString) {
return new Date(dateString).toLocaleString('zh-CN');
}
}
}
</script>
测试数据
{
"success": true,
"message": "获取解析任务列表成功",
"data": {
"tasks": [
{
"id": 1,
"task_name": "20250714_a1b2c3d4",
"task_status": "completed",
"task_type": "门墩儿新任命",
"task_source": "网页解析",
"collection_count": 5,
"parse_count": 4,
"created_at": "2025-01-14T10:30:00Z",
"created_by": "system",
"updated_at": "2025-01-14T10:35:00Z",
"updated_by": "system"
},
{
"id": 2,
"task_name": "20250714_b2c3d4e5",
"task_status": "completed",
"task_type": "门墩儿新任命",
"task_source": "网页解析",
"collection_count": 3,
"parse_count": 3,
"created_at": "2025-01-14T11:15:00Z",
"created_by": "system",
"updated_at": "2025-01-14T11:20:00Z",
"updated_by": "system"
}
],
"pagination": {
"page": 1,
"per_page": 10,
"total": 25,
"pages": 3,
"has_prev": false,
"has_next": true
}
}
}
返回状态码
状态码 |
描述 |
示例响应 |
200 |
查询成功 |
{"success": true, "message": "获取解析任务列表成功", "data": {...}} |
400 |
请求参数错误 |
{"success": false, "message": "页码必须大于0", "data": null} |
500 |
服务器内部错误 |
{"success": false, "message": "数据库连接失败", "data": null} |
2. 获取解析任务详情
接口信息
- 接口路径:
/api/data_parse/get-parse-task-detail
- 请求方法:
GET
- 接口描述: 根据任务名称获取解析任务的详细信息
请求参数
参数名 |
类型 |
必填 |
默认值 |
描述 |
task_name |
string |
是 |
- |
任务名称(如:"20250714_a1b2c3d4") |
请求示例
# 基础查询
GET /api/data_parse/get-parse-task-detail?task_name=20250714_a1b2c3d4
# URL编码示例(如果任务名称包含特殊字符)
GET /api/data_parse/get-parse-task-detail?task_name=20250714_a1b2c3d4
返回数据结构
{
"success": true,
"message": "获取解析任务详情成功",
"data": {
"id": 1,
"task_name": "20250714_a1b2c3d4",
"task_status": "completed",
"task_type": "门墩儿新任命",
"task_source": "网页解析",
"collection_count": 5,
"parse_count": 4,
"parse_result": {
"success_count": 4,
"failed_count": 1,
"persons": [
{
"name_zh": "张三",
"name_en": "Zhang San",
"title_zh": "总经理",
"title_en": "General Manager",
"hotel_zh": "北京万豪酒店",
"hotel_en": "Beijing Marriott Hotel",
"brand_group": "万豪",
"mobile": "13800138000",
"email": "zhangsan@marriott.com",
"pic_url": "https://example.com/photo1.jpg",
"career_path": [
{
"date": "2025-01-14",
"hotel_zh": "北京万豪酒店",
"hotel_en": "Beijing Marriott Hotel",
"title_zh": "总经理",
"title_en": "General Manager"
}
]
}
],
"errors": [
{
"person_index": 5,
"error_message": "缺少必要的职位信息"
}
]
},
"created_at": "2025-01-14T10:30:00Z",
"created_by": "system",
"updated_at": "2025-01-14T10:35:00Z",
"updated_by": "system"
}
}
前端JavaScript示例
// 使用fetch API
async function getParseTaskDetail(taskName) {
try {
const params = new URLSearchParams({
task_name: taskName
});
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);
return null;
}
} catch (error) {
console.error('请求异常:', error);
return null;
}
}
// 使用示例
getParseTaskDetail('20250714_a1b2c3d4');
前端Vue.js示例
<template>
<div class="parse-task-detail">
<div v-if="loading" class="loading">加载中...</div>
<div v-else-if="taskDetail" class="detail-content">
<!-- 基本信息 -->
<div class="basic-info">
<h2>任务基本信息</h2>
<div class="info-grid">
<div class="info-item">
<label>任务名称:</label>
<span>{{ taskDetail.task_name }}</span>
</div>
<div class="info-item">
<label>任务类型:</label>
<span>{{ taskDetail.task_type }}</span>
</div>
<div class="info-item">
<label>任务状态:</label>
<span :class="getStatusClass(taskDetail.task_status)">
{{ getStatusText(taskDetail.task_status) }}
</span>
</div>
<div class="info-item">
<label>采集人数:</label>
<span>{{ taskDetail.collection_count }}</span>
</div>
<div class="info-item">
<label>解析人数:</label>
<span>{{ taskDetail.parse_count }}</span>
</div>
<div class="info-item">
<label>创建时间:</label>
<span>{{ formatDate(taskDetail.created_at) }}</span>
</div>
</div>
</div>
<!-- 解析结果 -->
<div class="parse-result" v-if="taskDetail.parse_result">
<h2>解析结果</h2>
<!-- 统计信息 -->
<div class="result-stats">
<div class="stat-item success">
<span class="label">成功:</span>
<span class="value">{{ taskDetail.parse_result.success_count }}</span>
</div>
<div class="stat-item failed">
<span class="label">失败:</span>
<span class="value">{{ taskDetail.parse_result.failed_count }}</span>
</div>
</div>
<!-- 人员列表 -->
<div class="persons-list" v-if="taskDetail.parse_result.persons">
<h3>解析成功的人员</h3>
<div class="person-cards">
<div v-for="(person, index) in taskDetail.parse_result.persons"
:key="index"
class="person-card">
<div class="person-photo" v-if="person.pic_url">
<img :src="person.pic_url" :alt="person.name_zh" />
</div>
<div class="person-info">
<h4>{{ person.name_zh }} ({{ person.name_en }})</h4>
<p><strong>职位:</strong> {{ person.title_zh }} ({{ person.title_en }})</p>
<p><strong>酒店:</strong> {{ person.hotel_zh }} ({{ person.hotel_en }})</p>
<p><strong>品牌:</strong> {{ person.brand_group }}</p>
<p v-if="person.mobile"><strong>手机:</strong> {{ person.mobile }}</p>
<p v-if="person.email"><strong>邮箱:</strong> {{ person.email }}</p>
</div>
</div>
</div>
</div>
<!-- 错误信息 -->
<div class="errors-list" v-if="taskDetail.parse_result.errors && taskDetail.parse_result.errors.length > 0">
<h3>解析失败的记录</h3>
<div class="error-items">
<div v-for="(error, index) in taskDetail.parse_result.errors"
:key="index"
class="error-item">
<span class="error-index">第{{ error.person_index }}个人员:</span>
<span class="error-message">{{ error.error_message }}</span>
</div>
</div>
</div>
</div>
</div>
<div v-else class="error-message">
{{ errorMessage || '任务不存在或加载失败' }}
</div>
</div>
</template>
<script>
export default {
name: 'ParseTaskDetail',
data() {
return {
taskDetail: null,
loading: true,
errorMessage: ''
}
},
mounted() {
const taskName = this.$route.query.task_name;
if (taskName) {
this.loadTaskDetail(taskName);
} else {
this.errorMessage = '缺少任务名称参数';
this.loading = false;
}
},
methods: {
async loadTaskDetail(taskName) {
try {
this.loading = true;
const params = new URLSearchParams({
task_name: taskName
});
const response = await fetch(`/api/data_parse/get-parse-task-detail?${params}`);
const result = await response.json();
if (result.success) {
this.taskDetail = result.data;
} else {
this.errorMessage = result.message;
}
} catch (error) {
this.errorMessage = '加载任务详情失败';
} finally {
this.loading = false;
}
},
getStatusClass(status) {
const statusMap = {
'completed': 'status-completed',
'pending': 'status-pending',
'failed': 'status-failed'
};
return statusMap[status] || 'status-default';
},
getStatusText(status) {
const statusMap = {
'completed': '已完成',
'pending': '待处理',
'failed': '失败'
};
return statusMap[status] || status;
},
formatDate(dateString) {
return new Date(dateString).toLocaleString('zh-CN');
}
}
}
</script>
<style scoped>
.parse-task-detail {
padding: 20px;
}
.basic-info {
margin-bottom: 30px;
}
.info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 15px;
margin-top: 15px;
}
.info-item {
display: flex;
align-items: center;
}
.info-item label {
font-weight: bold;
margin-right: 10px;
min-width: 80px;
}
.status-completed { color: #52c41a; }
.status-pending { color: #faad14; }
.status-failed { color: #ff4d4f; }
.result-stats {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.stat-item {
padding: 10px 15px;
border-radius: 4px;
font-weight: bold;
}
.stat-item.success {
background-color: #f6ffed;
border: 1px solid #b7eb8f;
color: #52c41a;
}
.stat-item.failed {
background-color: #fff2f0;
border: 1px solid #ffccc7;
color: #ff4d4f;
}
.person-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
gap: 20px;
margin-top: 15px;
}
.person-card {
border: 1px solid #e8e8e8;
border-radius: 8px;
padding: 15px;
display: flex;
gap: 15px;
}
.person-photo img {
width: 80px;
height: 80px;
border-radius: 4px;
object-fit: cover;
}
.person-info h4 {
margin: 0 0 10px 0;
color: #1890ff;
}
.person-info p {
margin: 5px 0;
font-size: 14px;
}
.error-items {
margin-top: 15px;
}
.error-item {
padding: 10px;
background-color: #fff2f0;
border: 1px solid #ffccc7;
border-radius: 4px;
margin-bottom: 10px;
}
.error-index {
font-weight: bold;
color: #ff4d4f;
margin-right: 10px;
}
.loading, .error-message {
text-align: center;
padding: 50px;
color: #666;
}
</style>
测试数据
{
"success": true,
"message": "获取解析任务详情成功",
"data": {
"id": 1,
"task_name": "20250714_a1b2c3d4",
"task_status": "completed",
"task_type": "门墩儿新任命",
"task_source": "网页解析",
"collection_count": 5,
"parse_count": 4,
"parse_result": {
"success_count": 4,
"failed_count": 1,
"persons": [
{
"name_zh": "张三",
"name_en": "Zhang San",
"title_zh": "总经理",
"title_en": "General Manager",
"hotel_zh": "北京万豪酒店",
"hotel_en": "Beijing Marriott Hotel",
"brand_group": "万豪",
"mobile": "13800138000",
"email": "zhangsan@marriott.com",
"pic_url": "https://example.com/photo1.jpg",
"career_path": [
{
"date": "2025-01-14",
"hotel_zh": "北京万豪酒店",
"hotel_en": "Beijing Marriott Hotel",
"title_zh": "总经理",
"title_en": "General Manager"
}
]
},
{
"name_zh": "李四",
"name_en": "Li Si",
"title_zh": "市场总监",
"title_en": "Marketing Director",
"hotel_zh": "上海希尔顿酒店",
"hotel_en": "Shanghai Hilton Hotel",
"brand_group": "希尔顿",
"mobile": "13900139000",
"email": "lisi@hilton.com",
"pic_url": "https://example.com/photo2.jpg",
"career_path": [
{
"date": "2025-01-14",
"hotel_zh": "上海希尔顿酒店",
"hotel_en": "Shanghai Hilton Hotel",
"title_zh": "市场总监",
"title_en": "Marketing Director"
}
]
}
],
"errors": [
{
"person_index": 5,
"error_message": "缺少必要的职位信息"
}
]
},
"created_at": "2025-01-14T10:30:00Z",
"created_by": "system",
"updated_at": "2025-01-14T10:35:00Z",
"updated_by": "system"
}
}
返回状态码
状态码 |
描述 |
示例响应 |
200 |
查询成功 |
{"success": true, "message": "获取解析任务详情成功", "data": {...}} |
400 |
请求参数错误 |
{"success": false, "message": "任务名称参数不能为空", "data": null} |
404 |
任务不存在 |
{"success": false, "message": "未找到指定的解析任务", "data": null} |
500 |
服务器内部错误 |
{"success": false, "message": "数据库查询失败", "data": null} |
使用场景
1. 任务监控面板
- 定期调用
get_parse_tasks
接口获取最新任务状态
- 显示任务执行统计和成功率
- 提供任务筛选和搜索功能
2. 任务详情查看
- 点击任务列表中的任务,调用
get_parse_task_detail
查看详情
- 显示解析结果和错误信息
- 支持重新处理失败的记录
3. 数据分析
- 通过API获取历史任务数据
- 分析解析成功率和常见错误
- 生成任务执行报告
注意事项
- 分页限制:
get_parse_tasks
接口的per_page
参数最大值为100,避免单次查询数据量过大
- 任务名称格式: 任务名称通常为日期+UUID格式,如
20250714_a1b2c3d4
- 解析结果:
parse_result
字段包含完整的解析数据,数据量可能较大
- 时间格式: 所有时间字段均为ISO 8601格式的UTC时间
- 错误处理: 建议在前端实现适当的错误处理和重试机制
更新日志
- 2025-01-14: 初始版本,支持基础的任务查询功能
- 2025-01-14: 添加分页和过滤功能
- 2025-01-14: 完善错误处理和返回状态码