data_factory_api.md 43 KB

Data Factory API 前端开发说明文档

概述

Data Factory API 提供了与 n8n 工作流引擎的集成接口,支持工作流的查询、状态监控、执行记录查看和工作流触发等功能。

版本: v1.0
基础URL: /api/datafactory
内容类型: application/json
字符编码: UTF-8

通用响应格式

所有API接口都遵循统一的响应格式:

{
  "code": 200,           // HTTP状态码
  "message": "操作成功",  // 操作结果消息
  "data": {}             // 返回的具体数据
}

状态码说明

状态码 含义 说明
200 成功 请求成功执行
400 请求错误 请求参数错误或缺失
401 认证失败 n8n API Key 无效
403 权限不足 无权访问该资源
404 未找到 请求的资源不存在
500 服务器错误 服务器内部错误
503 服务不可用 n8n 服务连接失败

API 接口详情

1. 获取工作流列表

接口描述: 获取 n8n 系统中的工作流列表,支持分页、搜索和状态过滤。

请求信息

  • HTTP方法: GET
  • 请求路径: /workflows
  • 请求头: Content-Type: application/json

请求参数

参数名 类型 必填 默认值 说明
page int 1 页码,从1开始
page_size int 20 每页数量
active string - 过滤活跃状态:true/false
search string - 按名称搜索关键词
tags string - 按标签过滤,多个标签用逗号分隔

响应数据

{
  "code": 200,
  "message": "获取工作流列表成功",
  "data": {
    "items": [
      {
        "id": "1PCBqwesRXiFcfJ1",
        "name": "Simple RAG",
        "active": false,
        "tags": ["AI", "RAG"],
        "created_at": "2025-12-01 10:00:00",
        "updated_at": "2025-12-03 03:14:49"
      },
      {
        "id": "9w5VhCRlRrjFqDpX",
        "name": "My workflow 2",
        "active": true,
        "tags": [],
        "created_at": "2025-10-28 11:00:00",
        "updated_at": "2025-10-28 12:06:12"
      }
    ],
    "total": 3,
    "page": 1,
    "page_size": 20,
    "total_pages": 1
  }
}

响应字段说明

字段 类型 说明
items array 工作流列表
items[].id string 工作流唯一标识
items[].name string 工作流名称
items[].active boolean 是否激活状态
items[].tags array 标签列表
items[].created_at string 创建时间
items[].updated_at string 更新时间
total int 总记录数
page int 当前页码
page_size int 每页数量
total_pages int 总页数

Vue 代码示例

<template>
  <div class="workflow-list">
    <!-- 搜索栏 -->
    <div class="search-bar">
      <el-input
        v-model="searchKeyword"
        placeholder="搜索工作流名称"
        clearable
        @clear="fetchWorkflows"
        @keyup.enter="fetchWorkflows"
      >
        <template #append>
          <el-button @click="fetchWorkflows">
            <el-icon><Search /></el-icon>
          </el-button>
        </template>
      </el-input>
      
      <el-select v-model="activeFilter" placeholder="状态筛选" clearable @change="fetchWorkflows">
        <el-option label="全部" value="" />
        <el-option label="活跃" value="true" />
        <el-option label="停用" value="false" />
      </el-select>
    </div>

    <!-- 工作流表格 -->
    <el-table :data="workflows" v-loading="loading" stripe>
      <el-table-column prop="id" label="ID" width="180" />
      <el-table-column prop="name" label="工作流名称" />
      <el-table-column prop="active" label="状态" width="100">
        <template #default="{ row }">
          <el-tag :type="row.active ? 'success' : 'info'">
            {{ row.active ? '活跃' : '停用' }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="tags" label="标签" width="200">
        <template #default="{ row }">
          <el-tag v-for="tag in row.tags" :key="tag" size="small" class="tag-item">
            {{ tag }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="updated_at" label="更新时间" width="180" />
      <el-table-column label="操作" width="200" fixed="right">
        <template #default="{ row }">
          <el-button type="primary" link @click="viewDetail(row.id)">详情</el-button>
          <el-button type="primary" link @click="viewStatus(row.id)">状态</el-button>
          <el-button type="primary" link @click="viewExecutions(row.id)">执行记录</el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 分页 -->
    <el-pagination
      v-model:current-page="pagination.page"
      v-model:page-size="pagination.pageSize"
      :total="pagination.total"
      :page-sizes="[10, 20, 50, 100]"
      layout="total, sizes, prev, pager, next, jumper"
      @size-change="fetchWorkflows"
      @current-change="fetchWorkflows"
    />
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Search } from '@element-plus/icons-vue'
import axios from 'axios'

const loading = ref(false)
const workflows = ref([])
const searchKeyword = ref('')
const activeFilter = ref('')

const pagination = reactive({
  page: 1,
  pageSize: 20,
  total: 0
})

const fetchWorkflows = async () => {
  loading.value = true
  try {
    const params = {
      page: pagination.page,
      page_size: pagination.pageSize
    }
    
    if (searchKeyword.value) {
      params.search = searchKeyword.value
    }
    if (activeFilter.value) {
      params.active = activeFilter.value
    }

    const response = await axios.get('/api/datafactory/workflows', { params })
    
    if (response.data.code === 200) {
      workflows.value = response.data.data.items
      pagination.total = response.data.data.total
    } else {
      ElMessage.error(response.data.message)
    }
  } catch (error) {
    ElMessage.error('获取工作流列表失败')
    console.error(error)
  } finally {
    loading.value = false
  }
}

const viewDetail = (id) => {
  // 跳转到详情页
  router.push(`/datafactory/workflow/${id}`)
}

const viewStatus = (id) => {
  // 跳转到状态页
  router.push(`/datafactory/workflow/${id}/status`)
}

const viewExecutions = (id) => {
  // 跳转到执行记录页
  router.push(`/datafactory/workflow/${id}/executions`)
}

onMounted(() => {
  fetchWorkflows()
})
</script>

<style scoped>
.workflow-list {
  padding: 20px;
}

.search-bar {
  display: flex;
  gap: 16px;
  margin-bottom: 20px;
}

.search-bar .el-input {
  width: 300px;
}

.tag-item {
  margin-right: 4px;
}

.el-pagination {
  margin-top: 20px;
  justify-content: flex-end;
}
</style>

2. 获取工作流详情

接口描述: 根据工作流ID获取详细信息,包括节点列表和配置。

请求信息

  • HTTP方法: GET
  • 请求路径: /workflows/{workflow_id}
  • 请求头: Content-Type: application/json

路径参数

参数名 类型 必填 说明
workflow_id string 工作流ID

响应数据

{
  "code": 200,
  "message": "获取工作流详情成功",
  "data": {
    "id": "1PCBqwesRXiFcfJ1",
    "name": "Simple RAG",
    "active": false,
    "tags": ["AI", "RAG"],
    "created_at": "2025-12-01 10:00:00",
    "updated_at": "2025-12-03 03:14:49",
    "nodes_count": 5,
    "nodes": [
      {
        "id": "node-1",
        "name": "Start",
        "type": "n8n-nodes-base.manualTrigger",
        "type_version": 1,
        "position": [250, 300],
        "disabled": false
      },
      {
        "id": "node-2",
        "name": "HTTP Request",
        "type": "n8n-nodes-base.httpRequest",
        "type_version": 4,
        "position": [450, 300],
        "disabled": false
      }
    ],
    "settings": {
      "executionOrder": "v1"
    }
  }
}

响应字段说明

字段 类型 说明
id string 工作流ID
name string 工作流名称
active boolean 是否激活
tags array 标签列表
created_at string 创建时间
updated_at string 更新时间
nodes_count int 节点数量
nodes array 节点列表
nodes[].id string 节点ID
nodes[].name string 节点名称
nodes[].type string 节点类型
nodes[].type_version int 节点版本
nodes[].position array 节点位置 [x, y]
nodes[].disabled boolean 是否禁用
settings object 工作流设置

Vue 代码示例

<template>
  <div class="workflow-detail" v-loading="loading">
    <el-page-header @back="goBack" :title="workflow.name">
      <template #content>
        <div class="header-content">
          <el-tag :type="workflow.active ? 'success' : 'info'" size="large">
            {{ workflow.active ? '活跃' : '停用' }}
          </el-tag>
        </div>
      </template>
      <template #extra>
        <el-button-group>
          <el-button 
            v-if="!workflow.active" 
            type="success" 
            @click="activateWorkflow"
          >
            激活
          </el-button>
          <el-button 
            v-else 
            type="warning" 
            @click="deactivateWorkflow"
          >
            停用
          </el-button>
        </el-button-group>
      </template>
    </el-page-header>

    <el-descriptions :column="2" border class="detail-info">
      <el-descriptions-item label="工作流ID">{{ workflow.id }}</el-descriptions-item>
      <el-descriptions-item label="节点数量">{{ workflow.nodes_count }}</el-descriptions-item>
      <el-descriptions-item label="创建时间">{{ workflow.created_at }}</el-descriptions-item>
      <el-descriptions-item label="更新时间">{{ workflow.updated_at }}</el-descriptions-item>
      <el-descriptions-item label="标签" :span="2">
        <el-tag v-for="tag in workflow.tags" :key="tag" class="tag-item">
          {{ tag }}
        </el-tag>
        <span v-if="!workflow.tags?.length">无</span>
      </el-descriptions-item>
    </el-descriptions>

    <el-card class="nodes-card">
      <template #header>
        <span>节点列表</span>
      </template>
      <el-table :data="workflow.nodes" stripe>
        <el-table-column prop="name" label="节点名称" />
        <el-table-column prop="type" label="节点类型" />
        <el-table-column prop="type_version" label="版本" width="80" />
        <el-table-column prop="disabled" label="状态" width="100">
          <template #default="{ row }">
            <el-tag :type="row.disabled ? 'danger' : 'success'" size="small">
              {{ row.disabled ? '禁用' : '启用' }}
            </el-tag>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import axios from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus'

const route = useRoute()
const router = useRouter()
const loading = ref(false)
const workflow = ref({})

const workflowId = route.params.id

const fetchWorkflowDetail = async () => {
  loading.value = true
  try {
    const response = await axios.get(`/api/datafactory/workflows/${workflowId}`)
    
    if (response.data.code === 200) {
      workflow.value = response.data.data
    } else {
      ElMessage.error(response.data.message)
    }
  } catch (error) {
    ElMessage.error('获取工作流详情失败')
    console.error(error)
  } finally {
    loading.value = false
  }
}

const activateWorkflow = async () => {
  try {
    await ElMessageBox.confirm('确定要激活该工作流吗?', '提示', {
      type: 'warning'
    })
    
    const response = await axios.post(`/api/datafactory/workflows/${workflowId}/activate`)
    
    if (response.data.code === 200) {
      ElMessage.success('工作流已激活')
      fetchWorkflowDetail()
    } else {
      ElMessage.error(response.data.message)
    }
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('激活失败')
    }
  }
}

const deactivateWorkflow = async () => {
  try {
    await ElMessageBox.confirm('确定要停用该工作流吗?', '提示', {
      type: 'warning'
    })
    
    const response = await axios.post(`/api/datafactory/workflows/${workflowId}/deactivate`)
    
    if (response.data.code === 200) {
      ElMessage.success('工作流已停用')
      fetchWorkflowDetail()
    } else {
      ElMessage.error(response.data.message)
    }
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('停用失败')
    }
  }
}

const goBack = () => {
  router.back()
}

onMounted(() => {
  fetchWorkflowDetail()
})
</script>

<style scoped>
.workflow-detail {
  padding: 20px;
}

.header-content {
  display: flex;
  align-items: center;
}

.detail-info {
  margin: 20px 0;
}

.nodes-card {
  margin-top: 20px;
}

.tag-item {
  margin-right: 8px;
}
</style>

3. 获取工作流状态

接口描述: 获取工作流的运行状态和最近执行情况统计。

请求信息

  • HTTP方法: GET
  • 请求路径: /workflows/{workflow_id}/status
  • 请求头: Content-Type: application/json

路径参数

参数名 类型 必填 说明
workflow_id string 工作流ID

响应数据

{
  "code": 200,
  "message": "获取工作流状态成功",
  "data": {
    "workflow_id": "9w5VhCRlRrjFqDpX",
    "name": "My workflow 2",
    "active": true,
    "status": "active",
    "status_label": "运行中",
    "recent_executions": {
      "total": 5,
      "success": 4,
      "error": 1
    },
    "last_execution": {
      "id": "12345",
      "workflow_id": "9w5VhCRlRrjFqDpX",
      "workflow_name": "My workflow 2",
      "status": "success",
      "status_label": "成功",
      "mode": "trigger",
      "started_at": "2025-12-24 10:30:00",
      "finished_at": "2025-12-24 10:30:05",
      "retry_of": null,
      "retry_success_id": null
    },
    "updated_at": "2025-10-28 12:06:12"
  }
}

响应字段说明

字段 类型 说明
workflow_id string 工作流ID
name string 工作流名称
active boolean 是否激活
status string 状态码:active/inactive
status_label string 状态显示文本
recent_executions object 最近执行统计
recent_executions.total int 最近执行总数
recent_executions.success int 成功次数
recent_executions.error int 失败次数
last_execution object 最后一次执行信息(可能为null)
updated_at string 更新时间

Vue 代码示例

<template>
  <div class="workflow-status" v-loading="loading">
    <el-card class="status-card">
      <template #header>
        <div class="card-header">
          <span>{{ status.name }}</span>
          <el-tag :type="status.active ? 'success' : 'info'" size="large">
            {{ status.status_label }}
          </el-tag>
        </div>
      </template>
      
      <el-row :gutter="20">
        <el-col :span="8">
          <el-statistic title="最近执行总数" :value="status.recent_executions?.total || 0" />
        </el-col>
        <el-col :span="8">
          <el-statistic title="成功次数" :value="status.recent_executions?.success || 0">
            <template #suffix>
              <el-icon color="#67C23A"><SuccessFilled /></el-icon>
            </template>
          </el-statistic>
        </el-col>
        <el-col :span="8">
          <el-statistic title="失败次数" :value="status.recent_executions?.error || 0">
            <template #suffix>
              <el-icon color="#F56C6C"><CircleCloseFilled /></el-icon>
            </template>
          </el-statistic>
        </el-col>
      </el-row>
    </el-card>

    <el-card class="last-execution-card" v-if="status.last_execution">
      <template #header>
        <span>最后一次执行</span>
      </template>
      <el-descriptions :column="2" border>
        <el-descriptions-item label="执行ID">
          {{ status.last_execution.id }}
        </el-descriptions-item>
        <el-descriptions-item label="状态">
          <el-tag :type="getStatusType(status.last_execution.status)">
            {{ status.last_execution.status_label }}
          </el-tag>
        </el-descriptions-item>
        <el-descriptions-item label="开始时间">
          {{ status.last_execution.started_at }}
        </el-descriptions-item>
        <el-descriptions-item label="结束时间">
          {{ status.last_execution.finished_at }}
        </el-descriptions-item>
        <el-descriptions-item label="执行模式">
          {{ status.last_execution.mode }}
        </el-descriptions-item>
      </el-descriptions>
    </el-card>

    <el-empty v-else description="暂无执行记录" />
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { useRoute } from 'vue-router'
import { SuccessFilled, CircleCloseFilled } from '@element-plus/icons-vue'
import axios from 'axios'

const route = useRoute()
const loading = ref(false)
const status = ref({})
let refreshTimer = null

const workflowId = route.params.id

const fetchStatus = async () => {
  loading.value = true
  try {
    const response = await axios.get(`/api/datafactory/workflows/${workflowId}/status`)
    
    if (response.data.code === 200) {
      status.value = response.data.data
    }
  } catch (error) {
    console.error('获取状态失败', error)
  } finally {
    loading.value = false
  }
}

const getStatusType = (statusCode) => {
  const types = {
    success: 'success',
    error: 'danger',
    waiting: 'warning',
    running: 'primary'
  }
  return types[statusCode] || 'info'
}

onMounted(() => {
  fetchStatus()
  // 每30秒自动刷新状态
  refreshTimer = setInterval(fetchStatus, 30000)
})

onUnmounted(() => {
  if (refreshTimer) {
    clearInterval(refreshTimer)
  }
})
</script>

<style scoped>
.workflow-status {
  padding: 20px;
}

.status-card {
  margin-bottom: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.last-execution-card {
  margin-top: 20px;
}
</style>

4. 激活工作流

接口描述: 激活指定的工作流。

请求信息

  • HTTP方法: POST
  • 请求路径: /workflows/{workflow_id}/activate
  • 请求头: Content-Type: application/json

路径参数

参数名 类型 必填 说明
workflow_id string 工作流ID

响应数据

{
  "code": 200,
  "message": "工作流激活成功",
  "data": {
    "workflow_id": "9w5VhCRlRrjFqDpX",
    "active": true,
    "message": "工作流已激活"
  }
}

5. 停用工作流

接口描述: 停用指定的工作流。

请求信息

  • HTTP方法: POST
  • 请求路径: /workflows/{workflow_id}/deactivate
  • 请求头: Content-Type: application/json

路径参数

参数名 类型 必填 说明
workflow_id string 工作流ID

响应数据

{
  "code": 200,
  "message": "工作流停用成功",
  "data": {
    "workflow_id": "9w5VhCRlRrjFqDpX",
    "active": false,
    "message": "工作流已停用"
  }
}

6. 获取工作流执行记录列表

接口描述: 获取指定工作流的执行记录列表。

请求信息

  • HTTP方法: GET
  • 请求路径: /workflows/{workflow_id}/executions
  • 请求头: Content-Type: application/json

路径参数

参数名 类型 必填 说明
workflow_id string 工作流ID

请求参数

参数名 类型 必填 默认值 说明
page int 1 页码
page_size int 20 每页数量
status string - 状态过滤:success/error/waiting

响应数据

{
  "code": 200,
  "message": "获取执行记录列表成功",
  "data": {
    "items": [
      {
        "id": "12345",
        "workflow_id": "9w5VhCRlRrjFqDpX",
        "workflow_name": "My workflow 2",
        "status": "success",
        "status_label": "成功",
        "mode": "trigger",
        "started_at": "2025-12-24 10:30:00",
        "finished_at": "2025-12-24 10:30:05",
        "retry_of": null,
        "retry_success_id": null
      },
      {
        "id": "12344",
        "workflow_id": "9w5VhCRlRrjFqDpX",
        "workflow_name": "My workflow 2",
        "status": "error",
        "status_label": "失败",
        "mode": "manual",
        "started_at": "2025-12-24 09:15:00",
        "finished_at": "2025-12-24 09:15:03",
        "retry_of": null,
        "retry_success_id": null
      }
    ],
    "total": 25,
    "page": 1,
    "page_size": 20,
    "total_pages": 2
  }
}

响应字段说明

字段 类型 说明
items[].id string 执行记录ID
items[].workflow_id string 工作流ID
items[].workflow_name string 工作流名称
items[].status string 状态码:success/error/waiting/running
items[].status_label string 状态显示文本
items[].mode string 执行模式:trigger/manual/webhook等
items[].started_at string 开始时间
items[].finished_at string 结束时间
items[].retry_of string 重试来源执行ID(可能为null)
items[].retry_success_id string 重试成功执行ID(可能为null)

Vue 代码示例

<template>
  <div class="execution-list">
    <!-- 筛选栏 -->
    <div class="filter-bar">
      <el-select v-model="statusFilter" placeholder="状态筛选" clearable @change="fetchExecutions">
        <el-option label="全部" value="" />
        <el-option label="成功" value="success" />
        <el-option label="失败" value="error" />
        <el-option label="等待中" value="waiting" />
      </el-select>
      
      <el-button type="primary" @click="fetchExecutions">
        <el-icon><Refresh /></el-icon>
        刷新
      </el-button>
    </div>

    <!-- 执行记录表格 -->
    <el-table :data="executions" v-loading="loading" stripe>
      <el-table-column prop="id" label="执行ID" width="120" />
      <el-table-column prop="status" label="状态" width="100">
        <template #default="{ row }">
          <el-tag :type="getStatusType(row.status)">
            {{ row.status_label }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="mode" label="执行模式" width="100" />
      <el-table-column prop="started_at" label="开始时间" width="180" />
      <el-table-column prop="finished_at" label="结束时间" width="180" />
      <el-table-column label="耗时" width="100">
        <template #default="{ row }">
          {{ calculateDuration(row.started_at, row.finished_at) }}
        </template>
      </el-table-column>
      <el-table-column label="操作" width="100" fixed="right">
        <template #default="{ row }">
          <el-button type="primary" link @click="viewExecutionDetail(row.id)">
            详情
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 分页 -->
    <el-pagination
      v-model:current-page="pagination.page"
      v-model:page-size="pagination.pageSize"
      :total="pagination.total"
      :page-sizes="[10, 20, 50, 100]"
      layout="total, sizes, prev, pager, next"
      @size-change="fetchExecutions"
      @current-change="fetchExecutions"
    />
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { Refresh } from '@element-plus/icons-vue'
import axios from 'axios'
import dayjs from 'dayjs'

const route = useRoute()
const router = useRouter()
const loading = ref(false)
const executions = ref([])
const statusFilter = ref('')

const workflowId = route.params.id

const pagination = reactive({
  page: 1,
  pageSize: 20,
  total: 0
})

const fetchExecutions = async () => {
  loading.value = true
  try {
    const params = {
      page: pagination.page,
      page_size: pagination.pageSize
    }
    
    if (statusFilter.value) {
      params.status = statusFilter.value
    }

    const response = await axios.get(
      `/api/datafactory/workflows/${workflowId}/executions`,
      { params }
    )
    
    if (response.data.code === 200) {
      executions.value = response.data.data.items
      pagination.total = response.data.data.total
    }
  } catch (error) {
    console.error('获取执行记录失败', error)
  } finally {
    loading.value = false
  }
}

const getStatusType = (status) => {
  const types = {
    success: 'success',
    error: 'danger',
    waiting: 'warning',
    running: 'primary'
  }
  return types[status] || 'info'
}

const calculateDuration = (start, end) => {
  if (!start || !end) return '-'
  const duration = dayjs(end).diff(dayjs(start), 'second')
  if (duration < 60) return `${duration}秒`
  if (duration < 3600) return `${Math.floor(duration / 60)}分${duration % 60}秒`
  return `${Math.floor(duration / 3600)}时${Math.floor((duration % 3600) / 60)}分`
}

const viewExecutionDetail = (executionId) => {
  router.push(`/datafactory/executions/${executionId}`)
}

onMounted(() => {
  fetchExecutions()
})
</script>

<style scoped>
.execution-list {
  padding: 20px;
}

.filter-bar {
  display: flex;
  gap: 16px;
  margin-bottom: 20px;
}

.el-pagination {
  margin-top: 20px;
  justify-content: flex-end;
}
</style>

7. 获取所有执行记录列表

接口描述: 获取所有工作流的执行记录列表(不限定工作流)。

请求信息

  • HTTP方法: GET
  • 请求路径: /executions
  • 请求头: Content-Type: application/json

请求参数

参数名 类型 必填 默认值 说明
page int 1 页码
page_size int 20 每页数量
workflow_id string - 按工作流ID过滤
status string - 状态过滤:success/error/waiting

响应数据

响应格式与 /workflows/{workflow_id}/executions 相同。


8. 获取执行详情

接口描述: 获取单次执行的详细信息,包括各节点的执行结果。

请求信息

  • HTTP方法: GET
  • 请求路径: /executions/{execution_id}
  • 请求头: Content-Type: application/json

路径参数

参数名 类型 必填 说明
execution_id string 执行ID

响应数据

{
  "code": 200,
  "message": "获取执行详情成功",
  "data": {
    "id": "12345",
    "workflow_id": "9w5VhCRlRrjFqDpX",
    "workflow_name": "My workflow 2",
    "status": "success",
    "status_label": "成功",
    "mode": "trigger",
    "started_at": "2025-12-24 10:30:00",
    "finished_at": "2025-12-24 10:30:05",
    "retry_of": null,
    "retry_success_id": null,
    "node_results": [
      {
        "node_name": "Start",
        "start_time": "2025-12-24 10:30:00",
        "execution_time": 10,
        "source": [],
        "data": {
          "main": [[{"json": {"started": true}}]]
        }
      },
      {
        "node_name": "HTTP Request",
        "start_time": "2025-12-24 10:30:01",
        "execution_time": 3500,
        "source": [{"previousNode": "Start"}],
        "data": {
          "main": [[{"json": {"response": "OK"}}]]
        }
      }
    ],
    "error": null
  }
}

响应字段说明

字段 类型 说明
node_results array 各节点执行结果列表
node_results[].node_name string 节点名称
node_results[].start_time string 节点开始执行时间
node_results[].execution_time int 执行耗时(毫秒)
node_results[].source array 数据来源节点
node_results[].data object 节点输出数据
error object 错误信息(成功时为null)

Vue 代码示例

<template>
  <div class="execution-detail" v-loading="loading">
    <el-page-header @back="goBack" title="执行详情">
      <template #content>
        <el-tag :type="getStatusType(execution.status)" size="large">
          {{ execution.status_label }}
        </el-tag>
      </template>
    </el-page-header>

    <el-descriptions :column="2" border class="execution-info">
      <el-descriptions-item label="执行ID">{{ execution.id }}</el-descriptions-item>
      <el-descriptions-item label="工作流">{{ execution.workflow_name }}</el-descriptions-item>
      <el-descriptions-item label="执行模式">{{ execution.mode }}</el-descriptions-item>
      <el-descriptions-item label="状态">
        <el-tag :type="getStatusType(execution.status)">
          {{ execution.status_label }}
        </el-tag>
      </el-descriptions-item>
      <el-descriptions-item label="开始时间">{{ execution.started_at }}</el-descriptions-item>
      <el-descriptions-item label="结束时间">{{ execution.finished_at }}</el-descriptions-item>
    </el-descriptions>

    <!-- 错误信息 -->
    <el-alert
      v-if="execution.error"
      :title="execution.error.message || '执行出错'"
      type="error"
      :description="JSON.stringify(execution.error, null, 2)"
      show-icon
      class="error-alert"
    />

    <!-- 节点执行结果 -->
    <el-card class="node-results-card">
      <template #header>
        <span>节点执行结果</span>
      </template>
      
      <el-timeline>
        <el-timeline-item
          v-for="(node, index) in execution.node_results"
          :key="index"
          :type="getNodeStatusType(node)"
          :timestamp="node.start_time"
        >
          <el-card shadow="hover">
            <div class="node-header">
              <span class="node-name">{{ node.node_name }}</span>
              <el-tag size="small">{{ node.execution_time }}ms</el-tag>
            </div>
            <div class="node-data">
              <el-collapse>
                <el-collapse-item title="输出数据">
                  <pre>{{ JSON.stringify(node.data, null, 2) }}</pre>
                </el-collapse-item>
              </el-collapse>
            </div>
          </el-card>
        </el-timeline-item>
      </el-timeline>
    </el-card>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import axios from 'axios'

const route = useRoute()
const router = useRouter()
const loading = ref(false)
const execution = ref({})

const executionId = route.params.id

const fetchExecutionDetail = async () => {
  loading.value = true
  try {
    const response = await axios.get(`/api/datafactory/executions/${executionId}`)
    
    if (response.data.code === 200) {
      execution.value = response.data.data
    }
  } catch (error) {
    console.error('获取执行详情失败', error)
  } finally {
    loading.value = false
  }
}

const getStatusType = (status) => {
  const types = {
    success: 'success',
    error: 'danger',
    waiting: 'warning',
    running: 'primary'
  }
  return types[status] || 'info'
}

const getNodeStatusType = (node) => {
  // 根据节点执行情况判断状态
  if (node.error) return 'danger'
  return 'success'
}

const goBack = () => {
  router.back()
}

onMounted(() => {
  fetchExecutionDetail()
})
</script>

<style scoped>
.execution-detail {
  padding: 20px;
}

.execution-info {
  margin: 20px 0;
}

.error-alert {
  margin: 20px 0;
}

.node-results-card {
  margin-top: 20px;
}

.node-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}

.node-name {
  font-weight: bold;
}

.node-data pre {
  background: #f5f5f5;
  padding: 10px;
  border-radius: 4px;
  overflow-x: auto;
  font-size: 12px;
}
</style>

9. 触发工作流执行

接口描述: 通过 Webhook 触发工作流执行。

请求信息

  • HTTP方法: POST
  • 请求路径: /workflows/{workflow_id}/execute
  • 请求头: Content-Type: application/json

路径参数

参数名 类型 必填 说明
workflow_id string 工作流ID

请求参数

参数名 类型 必填 说明
webhook_path string Webhook 路径(工作流中配置的路径)
data object 传递给工作流的数据

请求体示例

{
  "webhook_path": "my-webhook-path",
  "data": {
    "user_id": 12345,
    "action": "process",
    "params": {
      "key1": "value1",
      "key2": "value2"
    }
  }
}

响应数据

成功响应:

{
  "code": 200,
  "message": "工作流触发成功",
  "data": {
    "success": true,
    "message": "工作流已通过 Webhook 触发",
    "workflow_id": "9w5VhCRlRrjFqDpX",
    "response": {
      "result": "OK"
    }
  }
}

缺少 webhook_path 响应:

{
  "code": 400,
  "message": "请提供 Webhook 路径以触发工作流",
  "data": {
    "success": false,
    "message": "请提供 Webhook 路径以触发工作流",
    "workflow_id": "9w5VhCRlRrjFqDpX"
  }
}

Vue 代码示例

<template>
  <div class="trigger-workflow">
    <el-card>
      <template #header>
        <span>触发工作流执行</span>
      </template>
      
      <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
        <el-form-item label="Webhook路径" prop="webhook_path">
          <el-input v-model="form.webhook_path" placeholder="请输入Webhook路径" />
          <div class="form-tip">工作流中 Webhook 节点配置的路径</div>
        </el-form-item>
        
        <el-form-item label="传递数据">
          <el-input
            v-model="form.dataJson"
            type="textarea"
            :rows="8"
            placeholder="请输入JSON格式的数据(可选)"
          />
          <div class="form-tip">JSON格式,例如:{"key": "value"}</div>
        </el-form-item>
        
        <el-form-item>
          <el-button type="primary" @click="triggerWorkflow" :loading="loading">
            触发执行
          </el-button>
          <el-button @click="resetForm">重置</el-button>
        </el-form-item>
      </el-form>
    </el-card>

    <!-- 执行结果 -->
    <el-card v-if="result" class="result-card">
      <template #header>
        <span>执行结果</span>
      </template>
      <el-result
        :icon="result.success ? 'success' : 'error'"
        :title="result.success ? '触发成功' : '触发失败'"
        :sub-title="result.message"
      >
        <template #extra>
          <pre v-if="result.response">{{ JSON.stringify(result.response, null, 2) }}</pre>
        </template>
      </el-result>
    </el-card>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
import { useRoute } from 'vue-router'
import axios from 'axios'
import { ElMessage } from 'element-plus'

const route = useRoute()
const formRef = ref()
const loading = ref(false)
const result = ref(null)

const workflowId = route.params.id

const form = reactive({
  webhook_path: '',
  dataJson: ''
})

const rules = {
  webhook_path: [
    { required: true, message: '请输入Webhook路径', trigger: 'blur' }
  ]
}

const triggerWorkflow = async () => {
  try {
    await formRef.value.validate()
  } catch {
    return
  }

  // 解析JSON数据
  let data = {}
  if (form.dataJson) {
    try {
      data = JSON.parse(form.dataJson)
    } catch (e) {
      ElMessage.error('数据格式错误,请输入有效的JSON')
      return
    }
  }

  loading.value = true
  result.value = null

  try {
    const response = await axios.post(`/api/datafactory/workflows/${workflowId}/execute`, {
      webhook_path: form.webhook_path,
      data: data
    })

    if (response.data.code === 200) {
      result.value = response.data.data
      ElMessage.success('工作流触发成功')
    } else {
      result.value = {
        success: false,
        message: response.data.message
      }
      ElMessage.error(response.data.message)
    }
  } catch (error) {
    result.value = {
      success: false,
      message: error.message || '触发失败'
    }
    ElMessage.error('触发工作流失败')
  } finally {
    loading.value = false
  }
}

const resetForm = () => {
  formRef.value?.resetFields()
  result.value = null
}
</script>

<style scoped>
.trigger-workflow {
  padding: 20px;
}

.form-tip {
  font-size: 12px;
  color: #909399;
  margin-top: 4px;
}

.result-card {
  margin-top: 20px;
}

.result-card pre {
  background: #f5f5f5;
  padding: 15px;
  border-radius: 4px;
  overflow-x: auto;
  text-align: left;
}
</style>

10. 健康检查

接口描述: 检查 n8n 服务的连接状态。

请求信息

  • HTTP方法: GET
  • 请求路径: /health
  • 请求头: Content-Type: application/json

响应数据

连接正常:

{
  "code": 200,
  "message": "n8n 服务连接正常",
  "data": {
    "status": "healthy",
    "connected": true,
    "api_url": "https://n8n.citupro.com"
  }
}

连接失败:

{
  "code": 503,
  "message": "n8n 服务连接失败: n8n API 认证失败,请检查 API Key 配置",
  "data": {
    "status": "unhealthy",
    "connected": false,
    "error": "n8n API 认证失败,请检查 API Key 配置",
    "api_url": "https://n8n.citupro.com"
  }
}

Vue 代码示例

<template>
  <div class="health-check">
    <el-card>
      <template #header>
        <div class="card-header">
          <span>n8n 服务状态</span>
          <el-button type="primary" size="small" @click="checkHealth" :loading="loading">
            <el-icon><Refresh /></el-icon>
            检查
          </el-button>
        </div>
      </template>
      
      <el-result
        :icon="health.connected ? 'success' : 'error'"
        :title="health.connected ? '服务正常' : '服务异常'"
      >
        <template #sub-title>
          <p>API地址: {{ health.api_url }}</p>
          <p v-if="health.error" class="error-text">错误: {{ health.error }}</p>
        </template>
      </el-result>
    </el-card>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { Refresh } from '@element-plus/icons-vue'
import axios from 'axios'

const loading = ref(false)
const health = ref({
  connected: false,
  api_url: ''
})

const checkHealth = async () => {
  loading.value = true
  try {
    const response = await axios.get('/api/datafactory/health')
    health.value = response.data.data
  } catch (error) {
    health.value = {
      connected: false,
      error: '无法连接到服务'
    }
  } finally {
    loading.value = false
  }
}

onMounted(() => {
  checkHealth()
})
</script>

<style scoped>
.health-check {
  padding: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.error-text {
  color: #F56C6C;
}
</style>

API 服务封装

建议将 API 调用封装成独立的服务模块,便于统一管理和维护。

api/datafactory.js

import axios from 'axios'

const BASE_URL = '/api/datafactory'

export const datafactoryApi = {
  // 工作流相关
  getWorkflows(params) {
    return axios.get(`${BASE_URL}/workflows`, { params })
  },
  
  getWorkflow(workflowId) {
    return axios.get(`${BASE_URL}/workflows/${workflowId}`)
  },
  
  getWorkflowStatus(workflowId) {
    return axios.get(`${BASE_URL}/workflows/${workflowId}/status`)
  },
  
  activateWorkflow(workflowId) {
    return axios.post(`${BASE_URL}/workflows/${workflowId}/activate`)
  },
  
  deactivateWorkflow(workflowId) {
    return axios.post(`${BASE_URL}/workflows/${workflowId}/deactivate`)
  },
  
  // 执行记录相关
  getWorkflowExecutions(workflowId, params) {
    return axios.get(`${BASE_URL}/workflows/${workflowId}/executions`, { params })
  },
  
  getAllExecutions(params) {
    return axios.get(`${BASE_URL}/executions`, { params })
  },
  
  getExecution(executionId) {
    return axios.get(`${BASE_URL}/executions/${executionId}`)
  },
  
  // 触发执行
  triggerWorkflow(workflowId, data) {
    return axios.post(`${BASE_URL}/workflows/${workflowId}/execute`, data)
  },
  
  // 健康检查
  healthCheck() {
    return axios.get(`${BASE_URL}/health`)
  }
}

使用示例

import { datafactoryApi } from '@/api/datafactory'

// 获取工作流列表
const response = await datafactoryApi.getWorkflows({
  page: 1,
  page_size: 20,
  active: 'true'
})

// 触发工作流
const result = await datafactoryApi.triggerWorkflow('workflow-id', {
  webhook_path: 'my-webhook',
  data: { key: 'value' }
})

路由配置示例

router/datafactory.js

export default [
  {
    path: '/datafactory',
    name: 'DataFactory',
    component: () => import('@/views/datafactory/index.vue'),
    meta: { title: '数据工厂' },
    children: [
      {
        path: '',
        name: 'WorkflowList',
        component: () => import('@/views/datafactory/WorkflowList.vue'),
        meta: { title: '工作流列表' }
      },
      {
        path: 'workflow/:id',
        name: 'WorkflowDetail',
        component: () => import('@/views/datafactory/WorkflowDetail.vue'),
        meta: { title: '工作流详情' }
      },
      {
        path: 'workflow/:id/status',
        name: 'WorkflowStatus',
        component: () => import('@/views/datafactory/WorkflowStatus.vue'),
        meta: { title: '工作流状态' }
      },
      {
        path: 'workflow/:id/executions',
        name: 'WorkflowExecutions',
        component: () => import('@/views/datafactory/ExecutionList.vue'),
        meta: { title: '执行记录' }
      },
      {
        path: 'workflow/:id/trigger',
        name: 'TriggerWorkflow',
        component: () => import('@/views/datafactory/TriggerWorkflow.vue'),
        meta: { title: '触发执行' }
      },
      {
        path: 'executions/:id',
        name: 'ExecutionDetail',
        component: () => import('@/views/datafactory/ExecutionDetail.vue'),
        meta: { title: '执行详情' }
      }
    ]
  }
]

注意事项

  1. 认证配置: 后端已配置 n8n API Key,前端无需额外处理认证
  2. 跨域问题: 后端已配置 CORS,支持跨域请求
  3. 错误处理: 建议在 axios 拦截器中统一处理错误响应
  4. 状态刷新: 工作流状态页面建议设置定时刷新(如30秒)
  5. Webhook路径: 触发工作流时需要提供正确的 Webhook 路径,该路径在 n8n 工作流的 Webhook 节点中配置

更新日志

  • v1.0 (2025-12-24): 初始版本,包含工作流查询、状态监控、执行记录和触发执行功能

技术支持

如有问题请联系后端开发团队或查看详细的 API 文档。

文档版本: v1.0
最后更新: 2025-12-24