|
|
@@ -0,0 +1,1836 @@
|
|
|
+# 数据订单 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="请详细描述需要什么数据,包括: 1. 涉及的业务领域(如员工、部门、项目等) 2. 需要的具体字段 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 文档 |
|
|
|
+
|