api_data_order_guide.md 46 KB

数据订单 API 前端开发指南

模块说明: 数据订单 API 提供数据需求订单的创建、分析、审批、驳回、完成等全生命周期管理功能。当用户在数据服务列表中找不到所需数据时,可以发起数据订单,系统会通过 LLM 提取实体并检测业务领域图谱的连通性。

基础路径: /api/dataservice

版本: 2.1.0 (更新于 2026-01-09)


目录


功能概述

数据订单功能允许用户:

  1. 提交数据需求: 描述需要什么样的数据
  2. 智能分析: 系统通过 LLM 自动提取业务领域和数据字段
  3. 连通性检测: 在业务领域图谱中检测实体间的关联关系
  4. 审批流程: 支持人工审批、驳回和补充信息
  5. 自动资源生成: 审批通过后自动创建 BusinessDomain 和 DataFlow 节点
  6. 数据产品关联: 数据流程完成后关联生成的数据产品

业务流程

状态流转图

                              ┌─────────────────┐
                              │    新增订单      │
                              │    (pending)    │
                              └────────┬────────┘
                                       │ 发起分析
                                       ▼
                              ┌─────────────────┐
                              │     分析中      │
                              │   (analyzing)   │
                              └────────┬────────┘
                                       │
                   ┌───────────────────┼───────────────────┐
                   │ 分析通过          │                   │ 分析有问题
                   ▼                   │                   ▼
          ┌─────────────────┐          │          ┌─────────────────┐
          │     待审批       │          │          │   待人工处理     │
          │(pending_approval)│          │          │ (manual_review) │
          └────────┬────────┘          │          └────────┬────────┘
                   │                   │                   │
       ┌───────────┴───────────┐       │                   │ 修改后
       │ 审批通过              │审批驳回│                   │ 重新分析
       ▼                       ▼       │                   │
┌─────────────────┐   ┌─────────────────┐                  │
│     加工中       │   │     已驳回      │                  │
│  (processing)   │   │   (rejected)    │◄─────────────────┘
└────────┬────────┘   └─────────────────┘
         │ 数据工厂回调
         ▼
┌─────────────────┐
│  数据产品就绪    │
│    (onboard)    │
└────────┬────────┘
         │ 标记完成
         ▼
┌─────────────────┐
│     已完成       │
│   (completed)   │
└─────────────────┘

典型流程

步骤 操作 状态变化 说明
1 用户创建订单 pending 订单初始状态
2 发起分析 pendinganalyzing 调用 LLM 提取实体
3a 分析通过 analyzingpending_approval 等待人工审批确认
3b 分析有问题 analyzingmanual_review 需要人工修改
4 人工修改订单 保持 manual_review 修改描述或提取结果
5 重新分析 manual_reviewanalyzing 再次进行分析
6a 审批通过 pending_approvalprocessing 自动生成资源
6b 审批驳回 pending_approvalrejected 订单被驳回
7 数据工厂回调 processingonboard 数据产品生产完成
8 标记完成 onboardcompleted 订单最终完成

通用说明

响应格式

所有接口返回统一的 JSON 格式:

{
  "code": 200,
  "message": "操作成功",
  "data": { ... }
}
字段 类型 说明
code number 状态码,200 表示成功,其他表示失败
message string 操作结果描述信息
data object | array | null 返回的数据内容

错误码说明

状态码 说明 常见场景
200 成功 操作成功完成
400 请求参数错误 缺少必填字段、状态不允许操作、驳回原因为空
404 资源不存在 数据订单 ID 不存在
500 服务器内部错误 数据库连接失败、LLM 调用失败、图谱查询异常

订单状态说明

状态值 中文标签 说明 可执行操作
pending 待处理 订单刚创建,等待分析 分析、更新、删除
analyzing 分析中 正在进行 LLM 提取和图谱分析 -
pending_approval 待审批 分析通过,等待人工审批 审批通过、驳回
processing 加工中 审批通过,正在生成数据流/数据产品 -
onboard 数据产品就绪 数据流程完成,产品可用 完成
completed 已完成 订单处理完成 删除
rejected 已驳回 订单被驳回 删除
need_supplement 待补充 需要用户补充信息 更新、分析
manual_review 待人工处理 需要人工修改 更新、分析、审批、驳回

Axios 配置

建议的 Axios 全局配置:

// src/utils/request.js
import axios from 'axios'
import { ElMessage } from 'element-plus'

const request = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:5050',
  timeout: 60000, // 分析接口可能需要较长时间
  headers: {
    'Content-Type': 'application/json'
  }
})

// 响应拦截器
request.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code !== 200) {
      ElMessage.error(res.message || '请求失败')
      return Promise.reject(new Error(res.message || 'Error'))
    }
    return res
  },
  error => {
    ElMessage.error(error.message || '网络错误')
    return Promise.reject(error)
  }
)

export default request

接口列表


1. 获取数据订单列表

分页获取数据订单列表,支持搜索和状态过滤。

请求信息

项目 说明
URL GET /api/dataservice/orderlist
Method GET
Content-Type -

请求参数 (Query String)

参数名 类型 必填 默认值 说明
page integer 1 页码
page_size integer 20 每页数量
search string "" 搜索关键词(匹配订单编号、标题、描述)
status string - 状态过滤,见订单状态说明

响应数据

{
  "code": 200,
  "message": "获取数据订单列表成功",
  "data": {
    "list": [
      {
        "id": 1,
        "order_no": "DO202601090001",
        "title": "员工与部门关联数据",
        "description": "需要获取员工信息和所属部门的关联数据",
        "extracted_domains": ["员工", "部门"],
        "extracted_fields": ["员工ID", "姓名", "部门ID", "部门名称"],
        "extraction_purpose": "用于人力资源分析报表",
        "graph_analysis": {
          "matched_domains": [...],
          "matched_fields": [...],
          "connection_analysis": {...}
        },
        "can_connect": true,
        "connection_path": {...},
        "status": "pending_approval",
        "status_label": "待审批",
        "reject_reason": null,
        "result_product_id": null,
        "result_dataflow_id": null,
        "created_by": "张三",
        "created_at": "2026-01-09T09:00:00",
        "updated_at": "2026-01-09T09:15:00",
        "processed_by": null,
        "processed_at": null
      }
    ],
    "pagination": {
      "page": 1,
      "page_size": 20,
      "total": 45,
      "total_pages": 3
    }
  }
}

数据订单字段说明

字段 类型 说明
id integer 订单唯一 ID
order_no string 订单编号,格式: DO + YYYYMMDD + 4位序列号
title string 订单标题
description string 需求描述
extracted_domains array | null LLM 提取的业务领域列表
extracted_fields array | null LLM 提取的数据字段列表
extraction_purpose string | null LLM 提取的数据用途
graph_analysis object | null 图谱连通性分析结果
can_connect boolean | null 是否可在图谱中连通
connection_path object | null 连通路径详情
status string 订单状态
status_label string 状态中文标签
reject_reason string | null 驳回原因(仅状态为 rejected 时有值)
result_product_id integer | null 生成的数据产品 ID
result_dataflow_id integer | null 生成的数据流 ID
created_by string 创建人
created_at string 创建时间(ISO 8601 格式)
updated_at string 更新时间
processed_by string | null 处理人
processed_at string | null 处理时间

Vue 接入示例

import request from '@/utils/request'

// 获取订单列表
const fetchOrders = async (params) => {
  const res = await request.get('/api/dataservice/orderlist', { params })
  return res.data
}

// 使用示例
const { list, pagination } = await fetchOrders({
  page: 1,
  page_size: 20,
  status: 'pending_approval'
})

2. 获取数据订单详情

根据 ID 获取单个数据订单的详细信息。

请求信息

项目 说明
URL GET /api/dataservice/orders/{order_id}/detail
Method GET
Content-Type -

路径参数

参数名 类型 必填 说明
order_id integer 数据订单 ID

响应数据

{
  "code": 200,
  "message": "获取数据订单详情成功",
  "data": {
    "id": 1,
    "order_no": "DO202601090001",
    "title": "员工与部门关联数据",
    "description": "需要获取员工信息和所属部门的关联数据...",
    "extracted_domains": ["员工", "部门"],
    "extracted_fields": ["员工ID", "姓名", "部门ID", "部门名称"],
    "extraction_purpose": "用于人力资源分析报表",
    "graph_analysis": {...},
    "can_connect": true,
    "connection_path": {...},
    "status": "pending_approval",
    "status_label": "待审批",
    ...
  }
}

Vue 接入示例

const fetchOrderDetail = async (orderId) => {
  const res = await request.get(`/api/dataservice/orders/${orderId}/detail`)
  return res.data
}

3. 创建数据订单

创建新的数据需求订单。

请求信息

项目 说明
URL POST /api/dataservice/neworder
Method POST
Content-Type application/json

请求体参数

参数名 类型 必填 默认值 说明
title string - 订单标题,最大 200 字符
description string - 需求描述,详细说明需要什么数据
created_by string "user" 创建人标识

请求示例

{
  "title": "员工与部门关联数据",
  "description": "需要获取员工信息和所属部门的关联数据,包括:\n1. 员工基本信息(ID、姓名、入职日期)\n2. 部门信息(部门ID、部门名称)\n3. 部门层级关系\n\n用途:生成人力资源分析报表",
  "created_by": "张三"
}

响应数据

{
  "code": 200,
  "message": "创建数据订单成功",
  "data": {
    "id": 1,
    "order_no": "DO202601090001",
    "title": "员工与部门关联数据",
    "description": "...",
    "status": "pending",
    "status_label": "待处理",
    ...
  }
}

Vue 接入示例

const createOrder = async (data) => {
  const res = await request.post('/api/dataservice/neworder', data)
  return res.data
}

// 使用示例
const newOrder = await createOrder({
  title: '员工与部门关联数据',
  description: '需要获取员工信息...'
})

4. 更新数据订单

更新数据订单信息,支持修改标题、描述和 LLM 提取结果。

注意: 只允许在 pendingmanual_reviewneed_supplement 状态下修改订单。

请求信息

项目 说明
URL PUT /api/dataservice/orders/{order_id}/update
Method PUT
Content-Type application/json

路径参数

参数名 类型 必填 说明
order_id integer 数据订单 ID

请求体参数

参数名 类型 必填 说明
title string 订单标题
description string 需求描述
extracted_domains array 提取的业务领域列表
extracted_fields array 提取的数据字段列表
extraction_purpose string 数据用途

请求示例

{
  "title": "员工与部门关联数据(更新)",
  "description": "需要获取员工信息和所属部门的关联数据,补充:需要包含员工的岗位信息",
  "extracted_domains": ["员工", "部门", "岗位"],
  "extracted_fields": ["员工ID", "姓名", "部门ID", "部门名称", "岗位名称"]
}

响应数据

{
  "code": 200,
  "message": "更新数据订单成功",
  "data": {
    "id": 1,
    "order_no": "DO202601090001",
    "title": "员工与部门关联数据(更新)",
    "description": "...",
    "extracted_domains": ["员工", "部门", "岗位"],
    "extracted_fields": ["员工ID", "姓名", "部门ID", "部门名称", "岗位名称"],
    "status": "manual_review",
    "status_label": "待人工处理",
    ...
  }
}

错误响应

状态不允许修改 (400):

{
  "code": 400,
  "message": "订单状态 processing 不允许修改,只有 ['pending', 'manual_review', 'need_supplement'] 状态可以修改",
  "data": null
}

Vue 接入示例

<template>
  <el-dialog v-model="dialogVisible" title="编辑订单" width="600px">
    <el-form :model="form" :rules="rules" ref="formRef" label-width="120px">
      <el-form-item label="订单标题" prop="title">
        <el-input v-model="form.title" />
      </el-form-item>
      <el-form-item label="需求描述" prop="description">
        <el-input v-model="form.description" type="textarea" :rows="4" />
      </el-form-item>
      <el-form-item label="业务领域">
        <el-select v-model="form.extracted_domains" multiple allow-create filterable>
          <el-option
            v-for="domain in domainOptions"
            :key="domain"
            :label="domain"
            :value="domain"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="数据字段">
        <el-select v-model="form.extracted_fields" multiple allow-create filterable>
          <el-option
            v-for="field in fieldOptions"
            :key="field"
            :label="field"
            :value="field"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="数据用途">
        <el-input v-model="form.extraction_purpose" />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="handleSubmit" :loading="submitting">
        保存修改
      </el-button>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import request from '@/utils/request'

const emit = defineEmits(['success'])
const dialogVisible = ref(false)
const submitting = ref(false)
const currentOrderId = ref(null)
const formRef = ref(null)

const form = reactive({
  title: '',
  description: '',
  extracted_domains: [],
  extracted_fields: [],
  extraction_purpose: ''
})

const open = (order) => {
  currentOrderId.value = order.id
  form.title = order.title
  form.description = order.description
  form.extracted_domains = order.extracted_domains || []
  form.extracted_fields = order.extracted_fields || []
  form.extraction_purpose = order.extraction_purpose || ''
  dialogVisible.value = true
}

const handleSubmit = async () => {
  submitting.value = true
  try {
    const data = {}
    if (form.title) data.title = form.title
    if (form.description) data.description = form.description
    if (form.extracted_domains.length) data.extracted_domains = form.extracted_domains
    if (form.extracted_fields.length) data.extracted_fields = form.extracted_fields
    if (form.extraction_purpose) data.extraction_purpose = form.extraction_purpose

    await request.put(`/api/dataservice/orders/${currentOrderId.value}/update`, data)
    ElMessage.success('更新成功')
    dialogVisible.value = false
    emit('success')
  } catch (error) {
    ElMessage.error(error.message || '更新失败')
  } finally {
    submitting.value = false
  }
}

defineExpose({ open })
</script>

5. 分析数据订单

触发 LLM 实体提取和业务领域图谱连通性分析。

请求信息

项目 说明
URL POST /api/dataservice/orders/{order_id}/analyze
Method POST
Content-Type -

路径参数

参数名 类型 必填 说明
order_id integer 数据订单 ID

请求体

无需请求体。

响应数据

分析成功后返回更新后的订单数据:

{
  "code": 200,
  "message": "数据订单分析完成",
  "data": {
    "id": 1,
    "order_no": "DO202601090001",
    "title": "员工与部门关联数据",
    "extracted_domains": ["员工", "部门"],
    "extracted_fields": ["员工ID", "姓名", "部门ID", "部门名称"],
    "extraction_purpose": "用于人力资源分析报表",
    "graph_analysis": {
      "matched_domains": [
        { "id": 101, "name_zh": "员工信息", "name_en": "employee" },
        { "id": 102, "name_zh": "部门信息", "name_en": "department" }
      ],
      "matched_fields": [...],
      "connection_analysis": {
        "can_connect": true,
        "common_fields": [...]
      }
    },
    "can_connect": true,
    "connection_path": {...},
    "status": "pending_approval",
    "status_label": "待审批"
  }
}

分析结果说明:

结果 说明 后续状态
can_connect: true 所有实体都能在图谱中连通 pending_approval (待人工审批)
can_connect: false 存在无法连通的实体 manual_review (待人工处理)

Vue 接入示例

const analyzeOrder = async (orderId) => {
  const loadingInstance = ElLoading.service({
    text: '正在分析订单,可能需要几秒钟...',
    background: 'rgba(0, 0, 0, 0.7)'
  })

  try {
    const res = await request.post(`/api/dataservice/orders/${orderId}/analyze`)
    loadingInstance.close()

    if (res.data.can_connect) {
      ElMessage.success('分析完成,实体可连通,请进行审批确认!')
    } else {
      ElMessage.warning('分析完成,部分实体无法连通,请修改后重新分析')
    }

    return res.data
  } catch (error) {
    loadingInstance.close()
    ElMessage.error(error.message || '分析失败')
    throw error
  }
}

6. 审批通过订单

审批通过数据订单,系统将自动生成 BusinessDomain 和 DataFlow 资源。

注意: 只允许从 pending_approvalmanual_review 状态审批。

请求信息

项目 说明
URL POST /api/dataservice/orders/{order_id}/approve
Method POST
Content-Type application/json

路径参数

参数名 类型 必填 说明
order_id integer 数据订单 ID

请求体参数

参数名 类型 必填 默认值 说明
processed_by string "admin" 处理人标识

请求示例

{
  "processed_by": "管理员A"
}

响应数据

{
  "code": 200,
  "message": "数据订单审批通过,资源已生成",
  "data": {
    "order": {
      "id": 1,
      "order_no": "DO202601090001",
      "status": "processing",
      "status_label": "加工中",
      "result_dataflow_id": 156,
      "processed_by": "管理员A",
      "processed_at": "2026-01-09T10:00:00",
      ...
    },
    "generated_resources": {
      "target_business_domain_id": 201,
      "dataflow_id": 156,
      "input_domain_ids": [101, 102]
    }
  }
}

生成资源说明

字段 类型 说明
target_business_domain_id integer 新创建的目标 BusinessDomain 节点 ID
dataflow_id integer 新创建的 DataFlow 节点 ID
input_domain_ids array 输入的 BusinessDomain 节点 ID 列表

错误响应

状态不允许审批 (400):

{
  "code": 400,
  "message": "订单状态 pending 不允许审批,只有 ['pending_approval', 'manual_review'] 状态可以审批",
  "data": null
}

Vue 接入示例

const approveOrder = async (orderId, processedBy = 'admin') => {
  try {
    await ElMessageBox.confirm(
      '确定要审批通过该订单吗?通过后将自动生成数据流程资源。',
      '审批确认',
      { type: 'warning' }
    )

    const res = await request.post(`/api/dataservice/orders/${orderId}/approve`, {
      processed_by: processedBy
    })

    ElMessage.success('审批通过,资源已生成')
    
    // 可以显示生成的资源信息
    console.log('生成的 DataFlow ID:', res.data.generated_resources.dataflow_id)
    
    return res.data
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '审批失败')
    }
  }
}

7. 驳回订单

驳回数据订单,需要提供驳回原因。

请求信息

项目 说明
URL POST /api/dataservice/orders/{order_id}/reject
Method POST
Content-Type application/json

路径参数

参数名 类型 必填 说明
order_id integer 数据订单 ID

请求体参数

参数名 类型 必填 默认值 说明
reason string - 驳回原因
processed_by string "admin" 处理人标识

请求示例

{
  "reason": "需求描述不够清晰,请补充具体需要的数据字段和业务场景",
  "processed_by": "管理员A"
}

响应数据

{
  "code": 200,
  "message": "数据订单已驳回",
  "data": {
    "id": 1,
    "order_no": "DO202601090001",
    "status": "rejected",
    "status_label": "已驳回",
    "reject_reason": "需求描述不够清晰,请补充具体需要的数据字段和业务场景",
    "processed_by": "管理员A",
    "processed_at": "2026-01-09T10:00:00",
    ...
  }
}

Vue 接入示例

<template>
  <el-dialog v-model="dialogVisible" title="驳回订单" width="500px">
    <el-form :model="form" :rules="rules" ref="formRef">
      <el-form-item label="驳回原因" prop="reason">
        <el-input
          v-model="form.reason"
          type="textarea"
          :rows="4"
          placeholder="请输入驳回原因,将通知订单创建人"
        />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="danger" @click="submitReject" :loading="rejecting">
        确认驳回
      </el-button>
    </template>
  </el-dialog>
</template>

<script setup>
import { ref, reactive } from 'vue'
import { ElMessage } from 'element-plus'
import request from '@/utils/request'

const emit = defineEmits(['success'])
const dialogVisible = ref(false)
const rejecting = ref(false)
const currentOrderId = ref(null)
const formRef = ref(null)

const form = reactive({
  reason: ''
})

const rules = {
  reason: [
    { required: true, message: '请输入驳回原因', trigger: 'blur' },
    { min: 5, message: '驳回原因至少5个字符', trigger: 'blur' }
  ]
}

const open = (order) => {
  currentOrderId.value = order.id
  form.reason = ''
  dialogVisible.value = true
}

const submitReject = async () => {
  const valid = await formRef.value.validate()
  if (!valid) return

  rejecting.value = true
  try {
    await request.post(`/api/dataservice/orders/${currentOrderId.value}/reject`, {
      reason: form.reason
    })

    ElMessage.success('订单已驳回')
    dialogVisible.value = false
    emit('success')
  } catch (error) {
    ElMessage.error(error.message || '驳回失败')
  } finally {
    rejecting.value = false
  }
}

defineExpose({ open })
</script>

8. 设置数据产品就绪(onboard)

数据工厂回调接口:当数据流程执行完成后,调用此接口将订单状态更新为"数据产品就绪"。

注意: 只允许从 processing 状态转换为 onboard 状态。此接口通常由数据工厂(n8n 工作流)自动调用。

请求信息

项目 说明
URL POST /api/dataservice/orders/{order_id}/onboard
Method POST
Content-Type application/json

路径参数

参数名 类型 必填 说明
order_id integer 数据订单 ID

请求体参数

参数名 类型 必填 默认值 说明
product_id integer null 生成的数据产品 ID
dataflow_id integer null 数据流 ID
processed_by string "n8n-workflow" 处理人标识

请求示例

{
  "product_id": 15,
  "dataflow_id": 156,
  "processed_by": "n8n-workflow-001"
}

响应数据

{
  "code": 200,
  "message": "数据订单已设置为数据产品就绪状态",
  "data": {
    "id": 1,
    "order_no": "DO202601090001",
    "status": "onboard",
    "status_label": "数据产品就绪",
    "result_product_id": 15,
    "result_dataflow_id": 156,
    "processed_by": "n8n-workflow-001",
    "processed_at": "2026-01-09T14:30:00",
    ...
  }
}

错误响应

状态不允许操作 (400):

{
  "code": 400,
  "message": "订单状态 pending_approval 不允许设置为 onboard,只有 processing 状态可以转换",
  "data": null
}

Vue 接入示例

// 此接口通常由后端/数据工厂调用,前端一般不直接调用
// 如需手动触发,可使用以下代码:
const setOrderOnboard = async (orderId, productId, dataflowId) => {
  const res = await request.post(`/api/dataservice/orders/${orderId}/onboard`, {
    product_id: productId,
    dataflow_id: dataflowId,
    processed_by: 'manual'
  })
  return res.data
}

9. 完成订单

将订单标记为最终完成状态。

注意: 只允许从 onboard(数据产品就绪)状态标记完成。

请求信息

项目 说明
URL POST /api/dataservice/orders/{order_id}/complete
Method POST
Content-Type application/json

路径参数

参数名 类型 必填 说明
order_id integer 数据订单 ID

请求体参数

参数名 类型 必填 默认值 说明
processed_by string "user" 处理人标识

请求示例

{
  "processed_by": "张三"
}

响应数据

{
  "code": 200,
  "message": "数据订单已完成",
  "data": {
    "id": 1,
    "order_no": "DO202601090001",
    "status": "completed",
    "status_label": "已完成",
    "result_product_id": 15,
    "result_dataflow_id": 156,
    "processed_by": "张三",
    "processed_at": "2026-01-09T15:00:00",
    ...
  }
}

错误响应

状态不允许完成 (400):

{
  "code": 400,
  "message": "订单状态 processing 不允许标记完成,只有 onboard 状态可以标记完成",
  "data": null
}

Vue 接入示例

const completeOrder = async (orderId) => {
  try {
    await ElMessageBox.confirm(
      '确定要标记该订单为完成吗?',
      '完成确认',
      { type: 'info' }
    )

    const res = await request.post(`/api/dataservice/orders/${orderId}/complete`, {
      processed_by: currentUser.value.name
    })

    ElMessage.success('订单已完成')
    return res.data
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '操作失败')
    }
  }
}

10. 删除订单

删除数据订单记录(软删除)。

请求信息

项目 说明
URL PUT /api/dataservice/orders/{order_id}/delete
Method PUT
Content-Type -

路径参数

参数名 类型 必填 说明
order_id integer 数据订单 ID

响应数据

{
  "code": 200,
  "message": "删除数据订单成功",
  "data": {}
}

Vue 接入示例

const deleteOrder = async (orderId) => {
  try {
    await ElMessageBox.confirm(
      '确定要删除该订单吗?此操作不可恢复。',
      '删除确认',
      { type: 'warning', confirmButtonClass: 'el-button--danger' }
    )

    await request.put(`/api/dataservice/orders/${orderId}/delete`)
    ElMessage.success('删除成功')
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '删除失败')
    }
  }
}

API 模块封装

建议将所有数据订单 API 封装到独立模块:

// src/api/dataOrder.js
import request from '@/utils/request'

const BASE_URL = '/api/dataservice'

export const dataOrderApi = {
  /**
   * 获取数据订单列表
   */
  getOrders(params) {
    return request.get(`${BASE_URL}/orderlist`, { params })
  },

  /**
   * 获取数据订单详情
   */
  getOrderDetail(orderId) {
    return request.get(`${BASE_URL}/orders/${orderId}/detail`)
  },

  /**
   * 创建数据订单
   */
  createOrder(data) {
    return request.post(`${BASE_URL}/neworder`, data)
  },

  /**
   * 更新数据订单
   */
  updateOrder(orderId, data) {
    return request.put(`${BASE_URL}/orders/${orderId}/update`, data)
  },

  /**
   * 分析数据订单
   */
  analyzeOrder(orderId) {
    return request.post(`${BASE_URL}/orders/${orderId}/analyze`)
  },

  /**
   * 审批通过订单
   */
  approveOrder(orderId, processedBy = 'admin') {
    return request.post(`${BASE_URL}/orders/${orderId}/approve`, {
      processed_by: processedBy
    })
  },

  /**
   * 驳回订单
   */
  rejectOrder(orderId, reason, processedBy = 'admin') {
    return request.post(`${BASE_URL}/orders/${orderId}/reject`, {
      reason,
      processed_by: processedBy
    })
  },

  /**
   * 设置数据产品就绪状态(数据工厂回调)
   */
  setOrderOnboard(orderId, options = {}) {
    return request.post(`${BASE_URL}/orders/${orderId}/onboard`, {
      product_id: options.productId,
      dataflow_id: options.dataflowId,
      processed_by: options.processedBy || 'n8n-workflow'
    })
  },

  /**
   * 完成订单
   */
  completeOrder(orderId, processedBy = 'user') {
    return request.post(`${BASE_URL}/orders/${orderId}/complete`, {
      processed_by: processedBy
    })
  },

  /**
   * 删除订单
   */
  deleteOrder(orderId) {
    return request.put(`${BASE_URL}/orders/${orderId}/delete`)
  }
}

export default dataOrderApi

完整页面示例

以下是一个完整的数据订单管理页面示例,包含完整的状态流转操作:

<!-- src/views/dataservice/DataOrderManage.vue -->
<template>
  <div class="data-order-manage">
    <!-- 页面标题 -->
    <div class="page-header">
      <h2>数据订单管理</h2>
      <p class="page-description">
        当您在数据服务列表中找不到所需数据时,可以创建数据订单提交您的数据需求。
      </p>
    </div>

    <!-- 搜索栏 -->
    <el-card class="search-card">
      <el-form :inline="true" :model="searchParams">
        <el-form-item label="搜索">
          <el-input
            v-model="searchParams.search"
            placeholder="订单编号/标题/描述"
            clearable
            @keyup.enter="fetchOrders"
          />
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="searchParams.status" placeholder="全部" clearable>
            <el-option
              v-for="(label, value) in statusOptions"
              :key="value"
              :label="label"
              :value="value"
            />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="fetchOrders">
            <el-icon><Search /></el-icon> 查询
          </el-button>
          <el-button @click="resetSearch">重置</el-button>
          <el-button type="success" @click="createDialogRef?.open()">
            <el-icon><Plus /></el-icon> 新建订单
          </el-button>
        </el-form-item>
      </el-form>
    </el-card>

    <!-- 数据表格 -->
    <el-card class="table-card">
      <el-table :data="orderList" v-loading="loading" border stripe>
        <el-table-column prop="order_no" label="订单编号" width="160" fixed />
        <el-table-column prop="title" label="标题" min-width="200" show-overflow-tooltip />
        <el-table-column prop="status" label="状态" width="130" align="center">
          <template #default="{ row }">
            <el-tag :type="getStatusType(row.status)" effect="light">
              {{ row.status_label }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="can_connect" label="可连通" width="90" align="center">
          <template #default="{ row }">
            <el-icon v-if="row.can_connect === true" color="#67C23A" :size="18">
              <CircleCheck />
            </el-icon>
            <el-icon v-else-if="row.can_connect === false" color="#F56C6C" :size="18">
              <CircleClose />
            </el-icon>
            <span v-else class="text-gray">-</span>
          </template>
        </el-table-column>
        <el-table-column prop="created_by" label="创建人" width="100" />
        <el-table-column prop="created_at" label="创建时间" width="170">
          <template #default="{ row }">
            {{ formatDate(row.created_at) }}
          </template>
        </el-table-column>
        <el-table-column label="操作" width="300" fixed="right">
          <template #default="{ row }">
            <el-button size="small" @click="showDetail(row)">详情</el-button>
            
            <!-- 待处理/待人工处理/待补充:可编辑和分析 -->
            <el-button
              v-if="canEdit(row.status)"
              size="small"
              @click="editDialogRef?.open(row)"
            >
              编辑
            </el-button>
            <el-button
              v-if="canAnalyze(row.status)"
              size="small"
              type="primary"
              @click="handleAnalyze(row)"
            >
              分析
            </el-button>
            
            <!-- 待审批/待人工处理:可审批和驳回 -->
            <el-button
              v-if="canApprove(row.status)"
              size="small"
              type="success"
              @click="handleApprove(row)"
            >
              审批
            </el-button>
            <el-button
              v-if="canReject(row.status)"
              size="small"
              type="warning"
              @click="rejectDialogRef?.open(row)"
            >
              驳回
            </el-button>
            
            <!-- 数据产品就绪:可完成 -->
            <el-button
              v-if="row.status === 'onboard'"
              size="small"
              type="success"
              @click="handleComplete(row)"
            >
              完成
            </el-button>
            
            <!-- 可删除状态 -->
            <el-button
              v-if="canDelete(row.status)"
              size="small"
              type="danger"
              @click="handleDelete(row)"
            >
              删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>

      <!-- 分页 -->
      <div class="pagination-wrapper">
        <el-pagination
          v-model:current-page="searchParams.page"
          v-model:page-size="searchParams.page_size"
          :total="pagination.total"
          :page-sizes="[10, 20, 50, 100]"
          layout="total, sizes, prev, pager, next, jumper"
          @current-change="fetchOrders"
          @size-change="fetchOrders"
        />
      </div>
    </el-card>

    <!-- 子组件 -->
    <CreateOrderDialog ref="createDialogRef" @success="fetchOrders" />
    <EditOrderDialog ref="editDialogRef" @success="fetchOrders" />
    <RejectOrderDialog ref="rejectDialogRef" @success="fetchOrders" />
    <OrderDetailDrawer ref="detailDrawerRef" @refresh="fetchOrders" />
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'
import { Search, Plus, CircleCheck, CircleClose } from '@element-plus/icons-vue'
import dayjs from 'dayjs'
import { dataOrderApi } from '@/api/dataOrder'

// 子组件引用
const createDialogRef = ref(null)
const editDialogRef = ref(null)
const rejectDialogRef = ref(null)
const detailDrawerRef = ref(null)

// 状态
const loading = ref(false)
const orderList = ref([])
const pagination = ref({ page: 1, page_size: 20, total: 0, total_pages: 0 })

// 搜索参数
const searchParams = reactive({
  page: 1,
  page_size: 20,
  search: '',
  status: ''
})

// 状态选项
const statusOptions = {
  pending: '待处理',
  analyzing: '分析中',
  pending_approval: '待审批',
  processing: '加工中',
  onboard: '数据产品就绪',
  completed: '已完成',
  rejected: '已驳回',
  need_supplement: '待补充',
  manual_review: '待人工处理'
}

// 获取订单列表
const fetchOrders = async () => {
  loading.value = true
  try {
    const params = { ...searchParams }
    if (!params.status) delete params.status
    if (!params.search) delete params.search

    const res = await dataOrderApi.getOrders(params)
    orderList.value = res.data.list
    pagination.value = res.data.pagination
  } finally {
    loading.value = false
  }
}

// 重置搜索
const resetSearch = () => {
  searchParams.page = 1
  searchParams.search = ''
  searchParams.status = ''
  fetchOrders()
}

// 状态判断函数
const canEdit = (status) => ['pending', 'manual_review', 'need_supplement'].includes(status)
const canAnalyze = (status) => ['pending', 'manual_review', 'need_supplement'].includes(status)
const canApprove = (status) => ['pending_approval', 'manual_review'].includes(status)
const canReject = (status) => ['pending_approval', 'manual_review'].includes(status)
const canDelete = (status) => ['pending', 'completed', 'rejected'].includes(status)

// 获取状态标签类型
const getStatusType = (status) => {
  const types = {
    pending: 'info',
    analyzing: 'warning',
    pending_approval: '',
    processing: 'primary',
    onboard: 'success',
    completed: 'success',
    rejected: 'danger',
    need_supplement: 'warning',
    manual_review: 'warning'
  }
  return types[status] || 'info'
}

// 格式化日期
const formatDate = (dateStr) => {
  return dateStr ? dayjs(dateStr).format('YYYY-MM-DD HH:mm') : '-'
}

// 显示详情
const showDetail = (order) => {
  detailDrawerRef.value?.open(order.id)
}

// 分析订单
const handleAnalyze = async (order) => {
  const loadingInstance = ElLoading.service({
    text: '正在分析订单,请稍候...',
    background: 'rgba(0, 0, 0, 0.7)'
  })

  try {
    const res = await dataOrderApi.analyzeOrder(order.id)
    loadingInstance.close()

    if (res.data.can_connect) {
      ElMessage.success('分析完成,实体可连通,请进行审批确认!')
    } else {
      ElMessage.warning('分析完成,部分实体无法连通,请修改后重新分析')
    }

    fetchOrders()
  } catch (error) {
    loadingInstance.close()
    ElMessage.error(error.message || '分析失败')
  }
}

// 审批通过
const handleApprove = async (order) => {
  try {
    await ElMessageBox.confirm(
      `确定要审批通过订单 "${order.title}" 吗?\n通过后将自动生成数据流程资源。`,
      '审批确认',
      { type: 'info' }
    )

    const res = await dataOrderApi.approveOrder(order.id)
    ElMessage.success(`审批通过,已生成 DataFlow ID: ${res.data.generated_resources.dataflow_id}`)
    fetchOrders()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '审批失败')
    }
  }
}

// 完成订单
const handleComplete = async (order) => {
  try {
    await ElMessageBox.confirm(
      `确定要标记订单 "${order.title}" 为完成吗?`,
      '完成确认',
      { type: 'info' }
    )

    await dataOrderApi.completeOrder(order.id)
    ElMessage.success('订单已完成')
    fetchOrders()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '操作失败')
    }
  }
}

// 删除订单
const handleDelete = async (order) => {
  try {
    await ElMessageBox.confirm(
      `确定要删除订单 "${order.title}" 吗?此操作不可恢复。`,
      '删除确认',
      { type: 'warning', confirmButtonClass: 'el-button--danger' }
    )

    await dataOrderApi.deleteOrder(order.id)  // 使用 PUT 方法调用 /delete 接口
    ElMessage.success('删除成功')
    fetchOrders()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '删除失败')
    }
  }
}

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

<style scoped>
.data-order-manage {
  padding: 20px;
}

.page-header {
  margin-bottom: 20px;
}

.page-header h2 {
  margin: 0 0 8px 0;
  font-size: 20px;
  font-weight: 600;
}

.page-description {
  color: #909399;
  font-size: 14px;
  margin: 0;
}

.search-card {
  margin-bottom: 16px;
}

.table-card {
  min-height: 400px;
}

.pagination-wrapper {
  margin-top: 16px;
  display: flex;
  justify-content: flex-end;
}

.text-gray {
  color: #909399;
}
</style>

常见问题

Q1: 分析接口超时怎么办?

A: 分析接口涉及 LLM 调用,可能需要较长时间。建议:

  1. 设置较长的 timeout(如 60 秒)
  2. 显示 loading 状态给用户
  3. 如果持续超时,检查 LLM 服务配置

Q2: 什么情况下订单会变成 manual_review

A: 当分析结果显示 can_connect: false 时,表示 LLM 提取的实体在业务领域图谱中无法完全连通,订单会变成 manual_review 状态,需要人工修改后重新分析。

Q3: 审批通过后会发生什么?

A: 审批通过后系统会:

  1. 自动创建目标 BusinessDomain 节点
  2. 自动创建 DataFlow 节点
  3. 建立输入 BusinessDomain 与 DataFlow 的 INPUT 关系
  4. 建立 DataFlow 与目标 BusinessDomain 的 OUTPUT 关系
  5. 订单状态更新为 processing
  6. 返回生成的资源 ID

Q4: 什么情况下可以修改订单?

A: 只有以下状态的订单可以修改:

  • pending (待处理)
  • manual_review (待人工处理)
  • need_supplement (待补充)

Q5: onboard 状态是如何触发的?

A: onboard 状态通常由数据工厂(n8n 工作流)在数据流程执行完成后调用 /orders/{id}/onboard 接口触发。这表示数据产品已生产完成,可供使用。

Q6: 订单编号的格式是什么?

A: 格式为 DO + YYYYMMDD + 4位序列号,例如 DO202601090001。每天的序列号从 0001 开始递增。


更新日志

版本 日期 说明
2.1.0 2026-01-09 接口路径规范化:获取订单详情改为 /orders/{id}/detail;更新订单改为 /orders/{id}/update;删除订单改为 PUT /orders/{id}/delete(软删除)
2.0.0 2026-01-09 重构订单流程:新增 pending_approvalonboard 状态;新增更新订单、onboard 回调接口;审批接口新增自动资源生成功能
1.0.0 2024-12-29 初始版本,包含完整的数据订单 API 文档