# 前端队列状态 API 接口文档 ## 概述 本文档说明队列状态查询的 API 接口,前端可以通过这些接口获取队列统计信息和文档排队位置。 **重要更新 (2026-01-29)**: - `documents` 字段现在从**数据库**查询,而不是 Redis metrics - 即使 Worker 未启动,也能正确显示等待处理的文档数量 - 新增 `waiting_ids` 和 `processing_details` 字段 --- ## 接口 1:查询队列整体状态 **用途**:获取全局队列统计信息(排队数、处理数) **请求**: ```http GET /api/v2/system/queue/status Authorization: Bearer ``` **响应**: ```json { "success": true, "timestamp": "2026-01-28T16:45:00.123456", "queue": { "pending_tasks": 5, "processing_tasks": 2, "available_slots": 2, "max_concurrent": 4 }, "documents": { "waiting": 3, "waiting_ids": [101, 102, 103], "processing": 2, "processing_ids": [123, 456], "processing_details": [ {"id": 123, "status": "Cutting"}, {"id": 456, "status": "Extractioning"} ] } } ``` **响应字段说明**: | 字段 | 类型 | 说明 | |------|------|------| | `success` | boolean | 请求是否成功 | | `timestamp` | string | 查询时间 | | `queue.pending_tasks` | number | Celery 队列中等待的任务数 | | `queue.processing_tasks` | number | 正在处理的任务数(通过并发许可统计) | | `queue.available_slots` | number | 可用的处理槽位 | | `queue.max_concurrent` | number | 最大并发数 | | `documents.waiting` | number | **等待处理的文档数**(数据库 status='Queued') | | `documents.waiting_ids` | number[] | **等待中的文档ID列表** | | `documents.processing` | number | **正在处理的文档数**(数据库 status in ['Cutting', 'Extractioning', 'Evaluationing']) | | `documents.processing_ids` | number[] | 正在处理的文档ID列表 | | `documents.processing_details` | object[] | **处理中文档的详细状态** | **数据来源说明**: | 字段 | 数据来源 | 说明 | |------|----------|------| | `queue.*` | Redis | 从 Celery 队列和并发控制器获取 | | `documents.*` | PostgreSQL | 从数据库 documents 表查询 | > ⚠️ **注意**:即使 Worker 未启动,`documents.waiting` 也会正确显示等待处理的文档数量。 --- ## 接口 2:查询单个文档位置 **用途**:查询指定文档在队列中的位置、预估等待时间、以及细分处理状态 **请求**: ```http GET /api/v2/system/queue/position/{document_id} Authorization: Bearer ``` **响应示例 - 排队中**: ```json { "success": true, "document_id": 456, "status": "Queued", "position": 3, "ahead_count": 2, "total_in_queue": 5, "estimated_wait_minutes": 4, "message": "文档在队列中排第 3 位,前面有 2 个文档" } ``` **响应示例 - OCR 识别中**: ```json { "success": true, "document_id": 123, "status": "Cutting", "position": 0, "ahead_count": 0, "message": "OCR 识别中" } ``` **响应示例 - AI 分析中**: ```json { "success": true, "document_id": 123, "status": "Extractioning", "position": 0, "ahead_count": 0, "message": "AI 分析中" } ``` **响应示例 - 规则检查中**: ```json { "success": true, "document_id": 123, "status": "Evaluationing", "position": 0, "ahead_count": 0, "message": "规则检查中" } ``` **响应示例 - 处理完成**: ```json { "success": true, "document_id": 789, "status": "Processed", "position": null, "ahead_count": 0, "message": "文档处理完成" } ``` **响应示例 - 处理失败**: ```json { "success": true, "document_id": 789, "status": "Failed", "position": null, "ahead_count": 0, "message": "文档处理失败" } ``` **响应字段说明**: | 字段 | 类型 | 说明 | |------|------|------| | `success` | boolean | 请求是否成功 | | `document_id` | number | 文档ID | | `status` | string | **细分状态**(见下表) | | `position` | number | 在队列中的位置(从1开始,处理中或完成时为0或null) | | `ahead_count` | number | 前面排队的文档数 | | `total_in_queue` | number | 队列中的总文档数(仅排队时返回) | | `estimated_wait_minutes` | number | 预估等待时间(分钟,仅排队时返回) | | `message` | string | 状态描述 | **status 字段可能的值**: | 状态值 | 说明 | 进度 | 建议显示 | |--------|------|------|----------| | `Queued` | 排队中 | 0% | 显示排队位置和预估时间 | | `Cutting` | OCR 识别中 | 33% | 显示进度条 + "正在识别文档..." | | `Extractioning` | AI 分析中 | 66% | 显示进度条 + "正在分析内容..." | | `Evaluationing` | 规则检查中 | 90% | 显示进度条 + "正在检查规则..." | | `Processed` | 处理完成 | 100% | 显示完成提示,可跳转查看结果 | | `Failed` | 处理失败 | - | 显示错误提示,可查看详情 | --- ## 文档状态流转图 ``` 上传文档 │ ▼ ┌─────────┐ │ Queued │ ← 文档初始状态(等待 Worker 处理) └────┬────┘ │ Worker 开始处理 ▼ ┌─────────┐ │ Cutting │ ← OCR 识别阶段 └────┬────┘ │ ▼ ┌──────────────┐ │Extractioning │ ← AI 分析阶段 └──────┬───────┘ │ ▼ ┌──────────────┐ │Evaluationing │ ← 规则检查阶段 └──────┬───────┘ │ ├────────────┐ ▼ ▼ ┌──────────┐ ┌────────┐ │Processed │ │ Failed │ └──────────┘ └────────┘ ``` --- ## 前端集成建议 ### 1. 上传后立即显示队列状态 ```javascript // 上传文档后 const uploadResult = await uploadDocument(file); const documentId = uploadResult.result.id; // 立即查询队列状态 const position = await api.get(`/system/queue/position/${documentId}`); if (position.status === 'Queued') { showNotification(`文档已加入队列,前面有 ${position.ahead_count} 个文档`); } ``` ### 2. 轮询文档处理状态 ```javascript const pollDocumentStatus = async (documentId) => { const poll = async () => { const result = await api.get(`/system/queue/position/${documentId}`); switch (result.status) { case 'Queued': updateProgress(0, `排队中,前面还有 ${result.ahead_count} 个文档`); break; case 'Cutting': updateProgress(33, 'OCR 识别中...'); break; case 'Extractioning': updateProgress(66, 'AI 分析中...'); break; case 'Evaluationing': updateProgress(90, '规则检查中...'); break; case 'Processed': updateProgress(100, '处理完成'); return result; // 停止轮询 case 'Failed': showError('处理失败'); return result; // 停止轮询 } // 继续轮询 setTimeout(poll, 5000); }; return poll(); }; ``` ### 3. 显示全局队列状态 ```javascript // 在页面顶部或侧边栏显示队列状态 const QueueStatusBadge = () => { const [status, setStatus] = useState(null); useEffect(() => { const fetchStatus = async () => { const data = await api.get('/system/queue/status'); setStatus(data); }; fetchStatus(); const interval = setInterval(fetchStatus, 10000); // 每 10 秒刷新 return () => clearInterval(interval); }, []); if (!status) return null; return (
等待: {status.documents.waiting} 处理中: {status.documents.processing}
); }; ``` --- ## 轮询建议 | 场景 | 推荐间隔 | 说明 | |------|----------|------| | 队列整体状态 | 10 秒 | 用于全局状态显示 | | 单个文档位置 | 5 秒 | 用于进度跟踪 | | 文档排队中 | 5 秒 | 显示排队位置变化 | | 文档处理中 | 3 秒 | 更频繁更新进度 | **停止轮询条件**:当 `status` 变为 `Processed` 或 `Failed` 时停止轮询 --- ## 错误响应 所有接口在发生错误时返回: ```json { "detail": "错误描述信息" } ``` **HTTP 状态码**: - `200` - 成功 - `401` - 未授权(Token 无效或过期) - `500` - 服务器内部错误 --- ## 常见问题 ### Q1: 为什么 `queue.pending_tasks` 和 `documents.waiting` 数量不一致? **A**: - `queue.pending_tasks` 统计的是 Celery 队列中的**任务数** - `documents.waiting` 统计的是数据库中 `status='Queued'` 的**文档数** - 一个文档可能对应多个任务,或者有些任务不是文档处理任务 ### Q2: Worker 未启动时会显示什么? **A**: - `queue.pending_tasks` 会显示队列中等待的任务数 - `documents.waiting` 会正确显示等待处理的文档数(从数据库查询) - `queue.processing_tasks` 和 `documents.processing` 为 0 ### Q3: 如何判断系统是否正常运行? **A**: - 如果 `documents.waiting > 0` 但 `queue.processing_tasks == 0` 且持续较长时间,可能 Worker 未启动 - 正常情况下,应该有文档在处理(`processing > 0`)或所有文档已处理完(`waiting == 0`)