# 数据订单 API 前端开发指南

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

---

## 目录

- [功能概述](#功能概述)
- [业务流程](#业务流程)
- [通用说明](#通用说明)
  - [响应格式](#响应格式)
  - [错误码说明](#错误码说明)
  - [订单状态说明](#订单状态说明)
  - [Axios 配置](#axios-配置)
- [接口列表](#接口列表)
  1. [获取数据订单列表](#1-获取数据订单列表)
  2. [获取数据订单详情](#2-获取数据订单详情)
  3. [创建数据订单](#3-创建数据订单)
  4. [分析数据订单](#4-分析数据订单)
  5. [审批通过订单](#5-审批通过订单)
  6. [驳回订单](#6-驳回订单)
  7. [完成订单](#7-完成订单)
  8. [删除订单](#8-删除订单)
- [API 模块封装](#api-模块封装)
- [完整页面示例](#完整页面示例)
- [常见问题](#常见问题)

---

## 功能概述

数据订单功能允许用户：

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

---

## 业务流程

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   待处理    │───>│   分析中    │───>│  加工中/    │───>│   已完成    │
│  (pending)  │    │ (analyzing) │    │ 待人工处理  │    │ (completed) │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
       │                  │                  │
       │                  │                  │
       v                  v                  v
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   已驳回    │    │   待补充    │    │   已更新    │
│ (rejected)  │    │(need_supple)│    │  (updated)  │
└─────────────┘    └─────────────┘    └─────────────┘
```

**典型流程:**
1. 用户创建订单 → `pending`
2. 触发分析 → `analyzing`
3. 分析完成后:
   - 如果可连通 → `processing` (可自动提交数据任务)
   - 如果不可连通 → `need_supplement` 或 `manual_review`
4. 审批通过 → `processing`
5. 完成 → `completed`

---

## 通用说明

### 响应格式

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

```json
{
  "code": 200,
  "message": "操作成功",
  "data": { ... }
}
```

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

### 错误码说明

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

### 订单状态说明

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

### Axios 配置

建议的 Axios 全局配置：

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

const request = axios.create({
  baseURL: process.env.VUE_APP_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 | 否 | - | 状态过滤，见[订单状态说明](#订单状态说明) |

#### 响应数据

```json
{
  "code": 200,
  "message": "获取数据订单列表成功",
  "data": {
    "list": [
      {
        "id": 1,
        "order_no": "DO202412260001",
        "title": "员工与部门关联数据",
        "description": "需要获取员工信息和所属部门的关联数据，包括部门层级",
        "extracted_domains": ["员工", "部门", "组织架构"],
        "extracted_fields": ["员工ID", "姓名", "部门ID", "部门名称"],
        "extraction_purpose": "用于人力资源分析报表",
        "graph_analysis": {
          "matched_domains": 3,
          "matched_fields": 4,
          "connection_score": 0.85
        },
        "can_connect": true,
        "connection_path": {
          "nodes": ["员工", "部门"],
          "relationships": ["属于"]
        },
        "status": "completed",
        "status_label": "已完成",
        "reject_reason": null,
        "result_product_id": 15,
        "result_dataflow_id": 28,
        "created_by": "张三",
        "created_at": "2024-12-26T09:00:00",
        "updated_at": "2024-12-26T14:30:00",
        "processed_by": "admin",
        "processed_at": "2024-12-26T14:30:00"
      }
    ],
    "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 接入示例

```vue
<template>
  <div class="data-order-list">
    <!-- 搜索栏 -->
    <div class="search-bar">
      <el-input
        v-model="searchParams.search"
        placeholder="搜索订单标题或描述..."
        @keyup.enter="fetchOrders"
        clearable
        style="width: 300px"
      />
      <el-select v-model="searchParams.status" placeholder="状态筛选" clearable>
        <el-option label="待处理" value="pending" />
        <el-option label="分析中" value="analyzing" />
        <el-option label="加工中" value="processing" />
        <el-option label="已完成" value="completed" />
        <el-option label="已驳回" value="rejected" />
        <el-option label="待补充" value="need_supplement" />
        <el-option label="待人工处理" value="manual_review" />
      </el-select>
      <el-button type="primary" @click="fetchOrders">查询</el-button>
      <el-button type="success" @click="showCreateDialog">新建订单</el-button>
    </div>

    <!-- 数据表格 -->
    <el-table :data="orderList" v-loading="loading" border>
      <el-table-column prop="order_no" label="订单编号" width="160" />
      <el-table-column prop="title" label="标题" min-width="200" />
      <el-table-column prop="status" label="状态" width="120">
        <template #default="{ row }">
          <el-tag :type="getStatusType(row.status)">
            {{ row.status_label }}
          </el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="can_connect" label="可连通" width="80">
        <template #default="{ row }">
          <el-icon v-if="row.can_connect === true" color="#67C23A">
            <Check />
          </el-icon>
          <el-icon v-else-if="row.can_connect === false" color="#F56C6C">
            <Close />
          </el-icon>
          <span v-else>-</span>
        </template>
      </el-table-column>
      <el-table-column prop="created_by" label="创建人" width="100" />
      <el-table-column prop="created_at" label="创建时间" width="180">
        <template #default="{ row }">
          {{ formatDate(row.created_at) }}
        </template>
      </el-table-column>
      <el-table-column label="操作" width="250" fixed="right">
        <template #default="{ row }">
          <el-button size="small" @click="handleDetail(row.id)">详情</el-button>
          <el-button
            v-if="row.status === 'pending'"
            size="small"
            type="primary"
            @click="handleAnalyze(row.id)"
          >
            分析
          </el-button>
          <el-button
            v-if="row.status === 'manual_review'"
            size="small"
            type="success"
            @click="handleApprove(row.id)"
          >
            审批
          </el-button>
          <el-button
            v-if="['pending', 'completed', 'rejected'].includes(row.status)"
            size="small"
            type="danger"
            @click="handleDelete(row.id)"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>

    <!-- 分页 -->
    <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>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { Check, Close } from '@element-plus/icons-vue'
import request from '@/utils/request'
import dayjs from 'dayjs'

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 fetchOrders = async () => {
  loading.value = true
  try {
    const params = { ...searchParams }
    if (!params.status) delete params.status
    if (!params.search) delete params.search
    
    const res = await request.get('/api/dataservice/orderlist', { params })
    orderList.value = res.data.list
    pagination.value = res.data.pagination
  } finally {
    loading.value = false
  }
}

const getStatusType = (status) => {
  const types = {
    pending: 'info',
    analyzing: 'warning',
    processing: 'primary',
    completed: 'success',
    rejected: 'danger',
    need_supplement: 'warning',
    manual_review: 'warning',
    updated: 'info'
  }
  return types[status] || 'info'
}

const formatDate = (dateStr) => {
  return dateStr ? dayjs(dateStr).format('YYYY-MM-DD HH:mm:ss') : '-'
}

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

<style scoped>
.search-bar {
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
}

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

---

### 2. 获取数据订单详情

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

#### 请求信息

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

#### 路径参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `order_id` | integer | 是 | 数据订单 ID |

#### 响应数据

```json
{
  "code": 200,
  "message": "获取数据订单详情成功",
  "data": {
    "id": 1,
    "order_no": "DO202412260001",
    "title": "员工与部门关联数据",
    "description": "需要获取员工信息和所属部门的关联数据，包括部门层级结构，用于生成人力资源分析报表。",
    "extracted_domains": ["员工", "部门", "组织架构"],
    "extracted_fields": ["员工ID", "姓名", "部门ID", "部门名称", "上级部门"],
    "extraction_purpose": "用于人力资源分析报表",
    "graph_analysis": {
      "matched_domains": [
        { "name": "员工", "node_id": "domain_001", "match_score": 1.0 },
        { "name": "部门", "node_id": "domain_002", "match_score": 1.0 }
      ],
      "matched_fields": [
        { "name": "员工ID", "field_id": "field_001", "domain": "员工" },
        { "name": "部门ID", "field_id": "field_002", "domain": "部门" }
      ],
      "connection_analysis": {
        "paths_found": 2,
        "shortest_path_length": 1
      }
    },
    "can_connect": true,
    "connection_path": {
      "nodes": ["员工", "部门"],
      "relationships": ["属于"],
      "path_detail": [
        { "from": "员工", "to": "部门", "relation": "属于", "common_fields": ["部门ID"] }
      ]
    },
    "status": "completed",
    "status_label": "已完成",
    "reject_reason": null,
    "result_product_id": 15,
    "result_dataflow_id": 28,
    "created_by": "张三",
    "created_at": "2024-12-26T09:00:00",
    "updated_at": "2024-12-26T14:30:00",
    "processed_by": "admin",
    "processed_at": "2024-12-26T14:30:00"
  }
}
```

#### 错误响应

**订单不存在 (404):**
```json
{
  "code": 404,
  "message": "数据订单不存在",
  "data": null
}
```

#### Vue 接入示例

```vue
<template>
  <el-drawer v-model="visible" title="订单详情" size="600px">
    <el-descriptions :column="1" border v-if="order">
      <el-descriptions-item label="订单编号">{{ order.order_no }}</el-descriptions-item>
      <el-descriptions-item label="标题">{{ order.title }}</el-descriptions-item>
      <el-descriptions-item label="状态">
        <el-tag :type="getStatusType(order.status)">{{ order.status_label }}</el-tag>
      </el-descriptions-item>
      <el-descriptions-item label="需求描述">
        <div class="description-text">{{ order.description }}</div>
      </el-descriptions-item>
      
      <!-- LLM 提取结果 -->
      <el-descriptions-item label="提取的业务领域" v-if="order.extracted_domains">
        <el-tag v-for="domain in order.extracted_domains" :key="domain" class="tag-item">
          {{ domain }}
        </el-tag>
      </el-descriptions-item>
      <el-descriptions-item label="提取的数据字段" v-if="order.extracted_fields">
        <el-tag v-for="field in order.extracted_fields" :key="field" type="info" class="tag-item">
          {{ field }}
        </el-tag>
      </el-descriptions-item>
      <el-descriptions-item label="数据用途" v-if="order.extraction_purpose">
        {{ order.extraction_purpose }}
      </el-descriptions-item>
      
      <!-- 连通性分析 -->
      <el-descriptions-item label="图谱连通性">
        <el-tag v-if="order.can_connect === true" type="success">可连通</el-tag>
        <el-tag v-else-if="order.can_connect === false" type="danger">不可连通</el-tag>
        <span v-else>未分析</span>
      </el-descriptions-item>
      
      <!-- 驳回原因 -->
      <el-descriptions-item label="驳回原因" v-if="order.reject_reason">
        <el-alert :title="order.reject_reason" type="error" :closable="false" />
      </el-descriptions-item>
      
      <!-- 关联结果 -->
      <el-descriptions-item label="生成的数据产品" v-if="order.result_product_id">
        <el-link type="primary" @click="goToProduct(order.result_product_id)">
          查看数据产品 #{{ order.result_product_id }}
        </el-link>
      </el-descriptions-item>
      
      <!-- 审计信息 -->
      <el-descriptions-item label="创建人">{{ order.created_by }}</el-descriptions-item>
      <el-descriptions-item label="创建时间">{{ formatDate(order.created_at) }}</el-descriptions-item>
      <el-descriptions-item label="处理人" v-if="order.processed_by">
        {{ order.processed_by }}
      </el-descriptions-item>
      <el-descriptions-item label="处理时间" v-if="order.processed_at">
        {{ formatDate(order.processed_at) }}
      </el-descriptions-item>
    </el-descriptions>
    
    <!-- 操作按钮 -->
    <div class="drawer-footer" v-if="order">
      <el-button
        v-if="order.status === 'pending'"
        type="primary"
        @click="handleAnalyze"
        :loading="analyzing"
      >
        开始分析
      </el-button>
      <el-button
        v-if="order.status === 'manual_review'"
        type="success"
        @click="handleApprove"
      >
        审批通过
      </el-button>
      <el-button
        v-if="order.status === 'manual_review'"
        type="danger"
        @click="showRejectDialog"
      >
        驳回
      </el-button>
    </div>
  </el-drawer>
</template>

<script setup>
import { ref } from 'vue'
import request from '@/utils/request'
import dayjs from 'dayjs'

const props = defineProps({
  orderId: { type: Number, default: null }
})

const visible = ref(false)
const order = ref(null)
const analyzing = ref(false)

const fetchDetail = async () => {
  if (!props.orderId) return
  try {
    const res = await request.get(`/api/dataservice/orders/${props.orderId}`)
    order.value = res.data
    visible.value = true
  } catch (error) {
    console.error('获取订单详情失败:', error)
  }
}

const formatDate = (dateStr) => {
  return dateStr ? dayjs(dateStr).format('YYYY-MM-DD HH:mm:ss') : '-'
}

defineExpose({ fetchDetail })
</script>

<style scoped>
.tag-item {
  margin-right: 8px;
  margin-bottom: 4px;
}

.description-text {
  white-space: pre-wrap;
  word-break: break-word;
}

.drawer-footer {
  margin-top: 24px;
  display: flex;
  gap: 12px;
}
</style>
```

---

### 3. 创建数据订单

创建新的数据需求订单。

#### 请求信息

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

#### 请求体参数

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

#### 请求示例

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

#### 响应数据

```json
{
  "code": 200,
  "message": "创建数据订单成功",
  "data": {
    "id": 1,
    "order_no": "DO202412260001",
    "title": "员工与部门关联数据",
    "description": "需要获取员工信息和所属部门的关联数据...",
    "extracted_domains": null,
    "extracted_fields": null,
    "extraction_purpose": null,
    "graph_analysis": null,
    "can_connect": null,
    "connection_path": null,
    "status": "pending",
    "status_label": "待处理",
    "reject_reason": null,
    "result_product_id": null,
    "result_dataflow_id": null,
    "created_by": "张三",
    "created_at": "2024-12-26T09:00:00",
    "updated_at": "2024-12-26T09:00:00",
    "processed_by": null,
    "processed_at": null
  }
}
```

#### 错误响应

**缺少必填字段 (400):**
```json
{
  "code": 400,
  "message": "缺少必填字段: title",
  "data": null
}
```

**请求体为空 (400):**
```json
{
  "code": 400,
  "message": "请求数据不能为空",
  "data": null
}
```

#### Vue 接入示例

```vue
<template>
  <el-dialog v-model="dialogVisible" title="创建数据订单" width="600px">
    <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
      <el-form-item label="订单标题" prop="title">
        <el-input
          v-model="form.title"
          placeholder="请输入订单标题，例如：员工与部门关联数据"
          maxlength="200"
          show-word-limit
        />
      </el-form-item>
      <el-form-item label="需求描述" prop="description">
        <el-input
          v-model="form.description"
          type="textarea"
          :rows="8"
          placeholder="请详细描述需要什么数据，包括：&#10;1. 涉及的业务领域（如员工、部门、项目等）&#10;2. 需要的具体字段&#10;3. 数据用途"
          maxlength="2000"
          show-word-limit
        />
      </el-form-item>
      <el-form-item label="创建人">
        <el-input v-model="form.created_by" placeholder="可选，默认为当前用户" />
      </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 formRef = ref(null)
const submitting = ref(false)

const form = reactive({
  title: '',
  description: '',
  created_by: ''
})

const rules = {
  title: [
    { required: true, message: '请输入订单标题', trigger: 'blur' },
    { max: 200, message: '标题不能超过200个字符', trigger: 'blur' }
  ],
  description: [
    { required: true, message: '请输入需求描述', trigger: 'blur' },
    { min: 10, message: '描述至少需要10个字符', trigger: 'blur' }
  ]
}

const open = () => {
  form.title = ''
  form.description = ''
  form.created_by = ''
  dialogVisible.value = true
}

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

  submitting.value = true
  try {
    const data = {
      title: form.title,
      description: form.description
    }
    if (form.created_by) {
      data.created_by = form.created_by
    }

    const res = await request.post('/api/dataservice/neworder', data)
    ElMessage.success('订单创建成功')
    dialogVisible.value = false
    emit('success', res.data)
  } catch (error) {
    ElMessage.error(error.message || '创建失败')
  } finally {
    submitting.value = false
  }
}

defineExpose({ open })
</script>
```

---

### 4. 分析数据订单

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

#### 请求信息

| 项目 | 说明 |
|------|------|
| **URL** | `POST /api/dataservice/orders/{order_id}/analyze` |
| **Method** | POST |
| **Content-Type** | application/json |

#### 路径参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `order_id` | integer | 是 | 数据订单 ID |

#### 请求体

无需请求体。

#### 响应数据

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

```json
{
  "code": 200,
  "message": "数据订单分析完成",
  "data": {
    "id": 1,
    "order_no": "DO202412260001",
    "title": "员工与部门关联数据",
    "description": "...",
    "extracted_domains": ["员工", "部门", "组织架构"],
    "extracted_fields": ["员工ID", "姓名", "部门ID", "部门名称"],
    "extraction_purpose": "用于人力资源分析报表",
    "graph_analysis": {
      "matched_domains": [
        { "name": "员工", "node_id": "domain_001", "match_score": 1.0 },
        { "name": "部门", "node_id": "domain_002", "match_score": 1.0 }
      ],
      "matched_fields": 4,
      "unmatched_domains": ["组织架构"],
      "connection_analysis": {
        "paths_found": 2,
        "shortest_path_length": 1
      }
    },
    "can_connect": true,
    "connection_path": {
      "nodes": ["员工", "部门"],
      "relationships": ["属于"],
      "common_fields": ["部门ID"]
    },
    "status": "processing",
    "status_label": "加工中",
    "updated_at": "2024-12-26T09:15:00"
  }
}
```

**分析结果说明：**

| 结果 | 说明 | 后续状态 |
|------|------|----------|
| `can_connect: true` | 所有实体都能在图谱中连通 | `processing` (可自动生成数据流) |
| `can_connect: false` | 存在无法连通的实体 | `need_supplement` 或 `manual_review` |

#### 错误响应

**订单不存在:**
```json
{
  "code": 404,
  "message": "数据订单不存在",
  "data": null
}
```

**分析失败:**
```json
{
  "code": 500,
  "message": "分析数据订单失败: LLM 服务调用超时",
  "data": null
}
```

#### Vue 接入示例

```javascript
const analyzeOrder = async (orderId) => {
  try {
    // 显示分析中提示
    const loadingInstance = ElLoading.service({
      text: '正在分析订单，可能需要几秒钟...',
      background: 'rgba(0, 0, 0, 0.7)'
    })

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

    // 根据分析结果显示不同提示
    if (res.data.can_connect) {
      ElMessage.success('分析完成，实体可连通！')
    } else {
      ElMessage.warning('分析完成，部分实体无法连通，需要补充信息或人工处理')
    }

    // 刷新订单详情
    fetchDetail(orderId)
    
    return res.data
  } catch (error) {
    ElMessage.error(error.message || '分析失败')
    throw error
  }
}
```

---

### 5. 审批通过订单

审批通过数据订单，将状态更新为 `processing`。

#### 请求信息

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

#### 路径参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `order_id` | integer | 是 | 数据订单 ID |

#### 请求体参数

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

#### 请求示例

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

#### 响应数据

```json
{
  "code": 200,
  "message": "数据订单审批通过",
  "data": {
    "id": 1,
    "order_no": "DO202412260001",
    "status": "processing",
    "status_label": "加工中",
    "processed_by": "管理员A",
    "processed_at": "2024-12-26T10:00:00",
    "updated_at": "2024-12-26T10:00:00"
  }
}
```

#### 错误响应

**订单状态不允许审批 (400):**
```json
{
  "code": 400,
  "message": "当前状态不允许审批操作",
  "data": null
}
```

#### Vue 接入示例

```javascript
const approveOrder = async (orderId) => {
  try {
    await ElMessageBox.confirm(
      '确定要审批通过该订单吗？通过后将开始生成数据流。',
      '审批确认',
      { type: 'warning' }
    )

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

    ElMessage.success('审批通过')
    fetchOrders() // 刷新列表
    
    return res.data
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '审批失败')
    }
  }
}
```

---

### 6. 驳回订单

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

#### 请求信息

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

#### 路径参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `order_id` | integer | 是 | 数据订单 ID |

#### 请求体参数

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

#### 请求示例

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

#### 响应数据

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

#### 错误响应

**驳回原因为空 (400):**
```json
{
  "code": 400,
  "message": "驳回原因不能为空",
  "data": null
}
```

#### Vue 接入示例

```vue
<template>
  <el-dialog v-model="rejectDialogVisible" title="驳回订单" width="500px">
    <el-form :model="rejectForm" :rules="rejectRules" ref="rejectFormRef">
      <el-form-item label="驳回原因" prop="reason">
        <el-input
          v-model="rejectForm.reason"
          type="textarea"
          :rows="4"
          placeholder="请输入驳回原因，将通知订单创建人"
        />
      </el-form-item>
    </el-form>
    <template #footer>
      <el-button @click="rejectDialogVisible = 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 request from '@/utils/request'

const rejectDialogVisible = ref(false)
const rejectFormRef = ref(null)
const rejecting = ref(false)
const currentOrderId = ref(null)

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

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

const showRejectDialog = (orderId) => {
  currentOrderId.value = orderId
  rejectForm.reason = ''
  rejectDialogVisible.value = true
}

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

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

    ElMessage.success('订单已驳回')
    rejectDialogVisible.value = false
    fetchOrders() // 刷新列表
  } catch (error) {
    ElMessage.error(error.message || '驳回失败')
  } finally {
    rejecting.value = false
  }
}

defineExpose({ showRejectDialog })
</script>
```

---

### 7. 完成订单

将订单标记为完成，可关联生成的数据产品和数据流。

#### 请求信息

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

#### 路径参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `order_id` | integer | 是 | 数据订单 ID |

#### 请求体参数

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

#### 请求示例

```json
{
  "product_id": 15,
  "dataflow_id": 28,
  "processed_by": "system"
}
```

#### 响应数据

```json
{
  "code": 200,
  "message": "数据订单已完成",
  "data": {
    "id": 1,
    "order_no": "DO202412260001",
    "status": "completed",
    "status_label": "已完成",
    "result_product_id": 15,
    "result_dataflow_id": 28,
    "processed_by": "system",
    "processed_at": "2024-12-26T14:30:00",
    "updated_at": "2024-12-26T14:30:00"
  }
}
```

#### Vue 接入示例

```javascript
const completeOrder = async (orderId, productId = null, dataflowId = null) => {
  try {
    const res = await request.post(`/api/dataservice/orders/${orderId}/complete`, {
      product_id: productId,
      dataflow_id: dataflowId,
      processed_by: 'system'
    })

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

---

### 8. 删除订单

删除数据订单记录。

#### 请求信息

| 项目 | 说明 |
|------|------|
| **URL** | `DELETE /api/dataservice/orders/{order_id}` |
| **Method** | DELETE |
| **Content-Type** | - |

#### 路径参数

| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| `order_id` | integer | 是 | 数据订单 ID |

#### 响应数据

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

#### 错误响应

**订单不存在 (404):**
```json
{
  "code": 404,
  "message": "数据订单不存在",
  "data": null
}
```

#### Vue 接入示例

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

    await request.delete(`/api/dataservice/orders/${orderId}`)
    ElMessage.success('删除成功')
    
    // 从列表中移除
    orderList.value = orderList.value.filter(o => o.id !== orderId)
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '删除失败')
    }
  }
}
```

---

## API 模块封装

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

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

const BASE_URL = '/api/dataservice'

export const dataOrderApi = {
  /**
   * 获取数据订单列表
   * @param {Object} params - 查询参数
   * @param {number} params.page - 页码
   * @param {number} params.page_size - 每页数量
   * @param {string} params.search - 搜索关键词
   * @param {string} params.status - 状态过滤
   */
  getOrders(params) {
    return request.get(`${BASE_URL}/orderlist`, { params })
  },

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

  /**
   * 创建数据订单
   * @param {Object} data - 订单数据
   * @param {string} data.title - 订单标题
   * @param {string} data.description - 需求描述
   * @param {string} [data.created_by] - 创建人
   */
  createOrder(data) {
    return request.post(`${BASE_URL}/neworder`, data)
  },

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

  /**
   * 审批通过订单
   * @param {number} orderId - 订单 ID
   * @param {string} [processedBy] - 处理人
   */
  approveOrder(orderId, processedBy = 'admin') {
    return request.post(`${BASE_URL}/orders/${orderId}/approve`, {
      processed_by: processedBy
    })
  },

  /**
   * 驳回订单
   * @param {number} orderId - 订单 ID
   * @param {string} reason - 驳回原因
   * @param {string} [processedBy] - 处理人
   */
  rejectOrder(orderId, reason, processedBy = 'admin') {
    return request.post(`${BASE_URL}/orders/${orderId}/reject`, {
      reason,
      processed_by: processedBy
    })
  },

  /**
   * 完成订单
   * @param {number} orderId - 订单 ID
   * @param {Object} [options] - 可选参数
   * @param {number} [options.productId] - 数据产品 ID
   * @param {number} [options.dataflowId] - 数据流 ID
   * @param {string} [options.processedBy] - 处理人
   */
  completeOrder(orderId, options = {}) {
    return request.post(`${BASE_URL}/orders/${orderId}/complete`, {
      product_id: options.productId,
      dataflow_id: options.dataflowId,
      processed_by: options.processedBy || 'system'
    })
  },

  /**
   * 删除订单
   * @param {number} orderId - 订单 ID
   */
  deleteOrder(orderId) {
    return request.delete(`${BASE_URL}/orders/${orderId}`)
  }
}

export default dataOrderApi
```

---

## 完整页面示例

以下是一个完整的数据订单管理页面示例：

```vue
<!-- 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
        row-key="id"
      >
        <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="description" label="描述" min-width="250" show-overflow-tooltip />
        <el-table-column prop="status" label="状态" width="120" 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="280" fixed="right">
          <template #default="{ row }">
            <el-button-group>
              <el-button size="small" @click="showDetail(row.id)">
                详情
              </el-button>
              <el-button
                v-if="row.status === 'pending'"
                size="small"
                type="primary"
                @click="handleAnalyze(row)"
              >
                分析
              </el-button>
              <el-button
                v-if="row.status === 'manual_review'"
                size="small"
                type="success"
                @click="handleApprove(row)"
              >
                审批
              </el-button>
              <el-button
                v-if="row.status === 'manual_review'"
                size="small"
                type="warning"
                @click="showRejectDialog(row)"
              >
                驳回
              </el-button>
              <el-button
                v-if="canDelete(row.status)"
                size="small"
                type="danger"
                @click="handleDelete(row)"
              >
                删除
              </el-button>
            </el-button-group>
          </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="handleCreateSuccess" />

    <!-- 订单详情抽屉 -->
    <OrderDetailDrawer ref="detailDrawerRef" @refresh="fetchOrders" />

    <!-- 驳回对话框 -->
    <RejectOrderDialog ref="rejectDialogRef" @success="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 detailDrawerRef = ref(null)
const rejectDialogRef = 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: '分析中',
  processing: '加工中',
  completed: '已完成',
  rejected: '已驳回',
  need_supplement: '待补充',
  manual_review: '待人工处理',
  updated: '已更新'
}

// 获取订单列表
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
  } catch (error) {
    console.error('获取订单列表失败:', error)
  } finally {
    loading.value = false
  }
}

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

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

// 判断是否可删除
const canDelete = (status) => {
  return ['pending', 'completed', 'rejected'].includes(status)
}

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

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

// 显示驳回对话框
const showRejectDialog = (order) => {
  rejectDialogRef.value.open(order)
}

// 分析订单
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}" 吗？`,
      '审批确认',
      { type: 'info' }
    )

    await dataOrderApi.approveOrder(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)
    ElMessage.success('删除成功')
    fetchOrders()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error(error.message || '删除失败')
    }
  }
}

// 创建成功回调
const handleCreateSuccess = (newOrder) => {
  ElMessage.success(`订单 ${newOrder.order_no} 创建成功`)
  fetchOrders()
}

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;
}

:deep(.el-button-group) {
  display: flex;
  flex-wrap: nowrap;
}
</style>
```

---

## 常见问题

### Q1: 分析接口超时怎么办？

**A**: 分析接口涉及 LLM 调用，可能需要较长时间。建议：
1. 设置较长的 `timeout`（如 60 秒）
2. 显示 loading 状态给用户
3. 后端可能需要优化 LLM 调用效率

### Q2: 什么情况下订单会变成 `need_supplement`？

**A**: 当 LLM 提取的实体在业务领域图谱中无法完全匹配时，订单会变成 `need_supplement` 状态，提示用户补充更多信息。

### Q3: 如何判断订单是否可以自动生成数据流？

**A**: 当 `can_connect` 为 `true` 时，表示所有实体都能在图谱中连通，理论上可以自动生成数据流。但具体实现取决于后端数据流生成逻辑。

### Q4: 订单编号的格式是什么？

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

### Q5: 可以修改已提交的订单吗？

**A**: 目前 API 不支持直接修改订单。如果需要修改，可以：
1. 删除原订单并创建新订单
2. 后续版本可能会增加编辑功能

---

## 更新日志

| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0.0 | 2024-12-29 | 初始版本，包含完整的数据订单 API 文档 |

