532 lines
13 KiB
Markdown
532 lines
13 KiB
Markdown
# AI 告警平台与芋道前端对接文档
|
||
|
||
> 本文档指导如何在芋道前端项目(yudao-ui-admin-vben)中集成告警平台功能。
|
||
|
||
## 一、项目概述
|
||
|
||
### 1.1 告警平台后端
|
||
- **技术栈**: FastAPI + SQLite + 阿里云 OSS
|
||
- **职责**: 接收边缘端告警、存储告警证据、提供 REST API
|
||
- **端口**: 8000
|
||
|
||
### 1.2 芋道前端
|
||
- **技术栈**: Vue 3 + Vben Admin + Ant Design Vue
|
||
- **职责**: 告警列表展示、详情查看、人工处理
|
||
- **复用**: 用户体系、权限管理、页面布局
|
||
|
||
---
|
||
|
||
## 二、后端 API 接口
|
||
|
||
### 2.1 基础信息
|
||
- **Base URL**: `http://localhost:8000`
|
||
- **文档地址**: `http://localhost:8000/docs`
|
||
|
||
### 2.2 接口列表
|
||
|
||
#### 2.2.1 健康检查
|
||
```http
|
||
GET /health
|
||
```
|
||
**响应**:
|
||
```json
|
||
{
|
||
"status": "healthy",
|
||
"database": "healthy"
|
||
}
|
||
```
|
||
|
||
#### 2.2.2 创建告警(边缘端调用)
|
||
```http
|
||
POST /api/v1/alerts
|
||
Content-Type: multipart/form-data
|
||
```
|
||
**请求参数**:
|
||
- `data` (FormField): JSON 字符串
|
||
```json
|
||
{
|
||
"camera_id": "cam-001",
|
||
"roi_id": "roi-01",
|
||
"alert_type": "leave_post",
|
||
"algorithm": "LeavePostAlgorithm",
|
||
"confidence": 85,
|
||
"duration_minutes": 5,
|
||
"trigger_time": "2024-01-20T10:30:00Z",
|
||
"message": "离岗告警"
|
||
}
|
||
```
|
||
- `snapshot` (File, 可选): 告警抓拍图片
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"alert_no": "ALT20240120103000ABC12345",
|
||
"camera_id": "cam-001",
|
||
"roi_id": "roi-01",
|
||
"alert_type": "leave_post",
|
||
"algorithm": "LeavePostAlgorithm",
|
||
"confidence": 85,
|
||
"duration_minutes": 5,
|
||
"trigger_time": "2024-01-20T10:30:00Z",
|
||
"message": "离岗告警",
|
||
"snapshot_url": "https://your-bucket.oss-cn-hangzhou.aliyuncs.com/alerts/xxx.jpg",
|
||
"status": "pending",
|
||
"created_at": "2024-01-20T10:30:00Z"
|
||
}
|
||
```
|
||
|
||
#### 2.2.3 查询告警列表
|
||
```http
|
||
GET /api/v1/alerts
|
||
```
|
||
**查询参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| cameraId | string | 否 | 摄像头ID |
|
||
| alertType | string | 否 | 告警类型 |
|
||
| status | string | 否 | 处理状态 |
|
||
| startTime | datetime | 否 | 开始时间 |
|
||
| endTime | datetime | 否 | 结束时间 |
|
||
| page | int | 否 | 页码,默认1 |
|
||
| pageSize | int | 否 | 每页条数,默认20 |
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"alerts": [...],
|
||
"total": 100,
|
||
"page": 1,
|
||
"pageSize": 20
|
||
}
|
||
```
|
||
|
||
#### 2.2.4 获取告警详情
|
||
```http
|
||
GET /api/v1/alerts/{alertId}
|
||
```
|
||
|
||
#### 2.2.5 处理告警
|
||
```http
|
||
PUT /api/v1/alerts/{alertId}/handle
|
||
```
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"status": "confirmed",
|
||
"remark": "确认为真实告警"
|
||
}
|
||
```
|
||
**状态枚举**: `pending` | `confirmed` | `ignored` | `resolved`
|
||
|
||
#### 2.2.6 获取告警统计
|
||
```http
|
||
GET /api/v1/alerts/statistics
|
||
```
|
||
**响应**:
|
||
```json
|
||
{
|
||
"total": 100,
|
||
"pending": 10,
|
||
"confirmed": 50,
|
||
"ignored": 20,
|
||
"resolved": 20,
|
||
"by_type": {
|
||
"leave_post": 80,
|
||
"intrusion": 20
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、芋道前端集成步骤
|
||
|
||
### 3.1 创建 API 文件
|
||
|
||
在 `apps/web-antd/src/api/` 目录下创建 `alert` 目录,并添加 `alert.ts`:
|
||
|
||
```typescript
|
||
import { requestClient } from '#/api/request';
|
||
|
||
export namespace AlertApi {
|
||
export interface Alert {
|
||
id: number;
|
||
alertNo: string;
|
||
cameraId: string;
|
||
roiId?: string;
|
||
alertType: string;
|
||
algorithm?: string;
|
||
confidence?: number;
|
||
durationMinutes?: number;
|
||
triggerTime: string;
|
||
message?: string;
|
||
snapshotUrl?: string;
|
||
status: 'pending' | 'confirmed' | 'ignored' | 'resolved';
|
||
handleRemark?: string;
|
||
handledBy?: string;
|
||
handledAt?: string;
|
||
aiAnalysis?: Record<string, any>;
|
||
createdAt: string;
|
||
updatedAt: string;
|
||
}
|
||
|
||
export interface AlertListParams {
|
||
pageNo: number;
|
||
pageSize: number;
|
||
cameraId?: string;
|
||
alertType?: string;
|
||
status?: string;
|
||
startTime?: string;
|
||
endTime?: string;
|
||
}
|
||
|
||
export interface AlertListResponse {
|
||
alerts: Alert[];
|
||
total: number;
|
||
page: number;
|
||
pageSize: number;
|
||
}
|
||
|
||
export interface AlertStatistics {
|
||
total: number;
|
||
pending: number;
|
||
confirmed: number;
|
||
ignored: number;
|
||
resolved: number;
|
||
by_type: Record<string, number>;
|
||
}
|
||
|
||
export interface HandleAlertRequest {
|
||
status: 'confirmed' | 'ignored' | 'resolved';
|
||
remark?: string;
|
||
}
|
||
}
|
||
|
||
export function getAlertList(params: AlertApi.AlertListParams) {
|
||
return requestClient.get<AlertApi.AlertListResponse>('/api/v1/alerts', {
|
||
params,
|
||
});
|
||
}
|
||
|
||
export function getAlertDetail(alertId: number) {
|
||
return requestClient.get<AlertApi.Alert>(`/api/v1/alerts/${alertId}`);
|
||
}
|
||
|
||
export function handleAlert(alertId: number, data: AlertApi.HandleAlertRequest) {
|
||
return requestClient.put<AlertApi.Alert>(`/api/v1/alerts/${alertId}/handle`, data, {
|
||
params: { handledBy: 'currentUser' },
|
||
});
|
||
}
|
||
|
||
export function getAlertStatistics() {
|
||
return requestClient.get<AlertApi.AlertStatistics>('/api/v1/alerts/statistics');
|
||
}
|
||
```
|
||
|
||
### 3.2 创建路由配置
|
||
|
||
在 `apps/web-antd/src/router/routes/modules/` 下创建 `alert.ts`:
|
||
|
||
```typescript
|
||
import type { RouteRecordRaw } from 'vue-router';
|
||
|
||
const routes: RouteRecordRaw[] = [
|
||
{
|
||
path: '/alert',
|
||
name: 'AlertCenter',
|
||
meta: {
|
||
title: '告警中心',
|
||
icon: 'lucide:bell',
|
||
keepAlive: true,
|
||
},
|
||
children: [
|
||
{
|
||
path: 'list',
|
||
name: 'AlertList',
|
||
meta: {
|
||
title: '告警列表',
|
||
noCache: true,
|
||
},
|
||
component: () => import('#/views/alert/list/index.vue'),
|
||
},
|
||
],
|
||
},
|
||
];
|
||
|
||
export default routes;
|
||
```
|
||
|
||
将此路由添加到主路由配置中(编辑 `apps/web-antd/src/router/routes/modules/` 下的 `dashboard.ts` 或其他菜单配置)。
|
||
|
||
### 3.3 创建页面组件
|
||
|
||
在 `apps/web-antd/src/views/` 下创建 `alert/list/` 目录:
|
||
|
||
**data.ts**:
|
||
```typescript
|
||
import type { VbenFormSchema } from '#/adapter/form';
|
||
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
|
||
|
||
import { getRangePickerDefaultProps } from '#/utils';
|
||
|
||
export function useAlertFormSchema(): VbenFormSchema[] {
|
||
return [
|
||
{ fieldName: 'cameraId', label: '摄像头ID', component: 'Input', componentProps: { placeholder: '请输入摄像头ID', allowClear: true } },
|
||
{ fieldName: 'alertType', label: '告警类型', component: 'Input', componentProps: { placeholder: '请输入告警类型', allowClear: true } },
|
||
{
|
||
fieldName: 'status',
|
||
label: '处理状态',
|
||
component: 'Select',
|
||
componentProps: {
|
||
options: [
|
||
{ label: '待处理', value: 'pending' },
|
||
{ label: '已确认', value: 'confirmed' },
|
||
{ label: '已忽略', value: 'ignored' },
|
||
{ label: '已解决', value: 'resolved' },
|
||
],
|
||
placeholder: '请选择处理状态',
|
||
allowClear: true,
|
||
},
|
||
},
|
||
{ fieldName: 'createTime', label: '创建时间', component: 'RangePicker', componentProps: { ...getRangePickerDefaultProps(), allowClear: true } },
|
||
];
|
||
}
|
||
|
||
export function useAlertColumns(): VxeTableGridOptions['columns'] {
|
||
return [
|
||
{ type: 'checkbox', width: 40 },
|
||
{ field: 'id', title: '编号', minWidth: 80 },
|
||
{ field: 'alertNo', title: '告警编号', minWidth: 150 },
|
||
{ field: 'cameraId', title: '摄像头', minWidth: 100 },
|
||
{ field: 'alertType', title: '告警类型', minWidth: 100 },
|
||
{ field: 'algorithm', title: '算法', minWidth: 120 },
|
||
{ field: 'confidence', title: '置信度', minWidth: 80, slots: { default: 'confidence' } },
|
||
{ field: 'durationMinutes', title: '离岗时长', minWidth: 100, slots: { default: 'duration' } },
|
||
{ field: 'status', title: '状态', minWidth: 100, slots: { default: 'status' } },
|
||
{ field: 'createdAt', title: '创建时间', minWidth: 180, formatter: 'formatDateTime' },
|
||
{ title: '操作', width: 150, fixed: 'right', slots: { default: 'actions' } },
|
||
];
|
||
}
|
||
```
|
||
|
||
**index.vue**:
|
||
参考芋道现有的 IoT 告警页面 `apps/web-antd/src/views/iot/alert/record/index.vue` 进行修改。
|
||
|
||
---
|
||
|
||
## 四、边缘端对接示例
|
||
|
||
### 4.1 Python (ai_edge 项目)
|
||
|
||
在 `ai_edge` 项目的 `main.py` 中添加告警上报功能:
|
||
|
||
```python
|
||
import requests
|
||
from datetime import datetime
|
||
|
||
class AlertReporter:
|
||
ALERT_PLATFORM_URL = "http://localhost:8000/api/v1/alerts"
|
||
|
||
def report_alert(
|
||
self,
|
||
camera_id: str,
|
||
roi_id: str,
|
||
alert_type: str,
|
||
algorithm: str,
|
||
confidence: float,
|
||
duration_minutes: int,
|
||
message: str,
|
||
screenshot: bytes,
|
||
):
|
||
alert_data = {
|
||
"camera_id": camera_id,
|
||
"roi_id": roi_id,
|
||
"alert_type": alert_type,
|
||
"algorithm": algorithm,
|
||
"confidence": int(confidence * 100),
|
||
"duration_minutes": duration_minutes,
|
||
"trigger_time": datetime.utcnow().isoformat() + "Z",
|
||
"message": message,
|
||
}
|
||
|
||
files = {
|
||
"snapshot": ("alert.jpg", screenshot, "image/jpeg"),
|
||
"data": (None, json.dumps(alert_data), "application/json"),
|
||
}
|
||
|
||
response = requests.post(self.ALERT_PLATFORM_URL, files=files)
|
||
response.raise_for_status()
|
||
return response.json()
|
||
```
|
||
|
||
### 4.2 在推理服务中调用
|
||
|
||
在 `EdgeInferenceService._handle_detections` 方法中调用:
|
||
|
||
```python
|
||
def _handle_detections(self, camera_id: str, roi, frame: VideoFrame, alerts: list):
|
||
for alert in alerts:
|
||
# 上报到云端告警平台
|
||
self._reporter.report_to_platform(
|
||
camera_id=camera_id,
|
||
roi_id=roi.roi_id,
|
||
alert_type=alert.get("alert_type"),
|
||
algorithm="LeavePostAlgorithm",
|
||
confidence=alert.get("confidence", 1.0),
|
||
duration_minutes=alert.get("duration_minutes", 0),
|
||
message=alert.get("message", ""),
|
||
screenshot=frame.image,
|
||
)
|
||
```
|
||
|
||
---
|
||
|
||
## 五、阿里云 OSS 配置
|
||
|
||
### 5.1 配置项
|
||
|
||
在告警平台的 `.env` 文件中配置:
|
||
|
||
```env
|
||
OSS_ACCESS_KEY_ID=your_access_key_id
|
||
OSS_ACCESS_KEY_SECRET=your_access_key_secret
|
||
OSS_ENDPOINT=oss-cn-hangzhou.aliyuncs.com
|
||
OSS_BUCKET_NAME=your_bucket_name
|
||
OSS_URL_PREFIX=https://your-bucket-name.oss-cn-hangzhou.aliyuncs.com
|
||
```
|
||
|
||
### 5.2 权限要求
|
||
|
||
OSS Bucket 需要配置以下权限:
|
||
- **CORS**: 允许前端跨域访问
|
||
- **公共读**: 告警图片需要可以公开访问
|
||
|
||
---
|
||
|
||
## 六、大模型分析配置(可选)
|
||
|
||
### 6.1 配置项
|
||
|
||
```env
|
||
AI_MODEL_ENDPOINT=http://localhost:8001
|
||
AI_MODEL_API_KEY=your_api_key
|
||
```
|
||
|
||
### 6.2 大模型 API 格式
|
||
|
||
告警平台期望大模型服务提供以下接口:
|
||
|
||
```http
|
||
POST /analyze
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"alert_id": 1,
|
||
"snapshot_url": "https://xxx/alert.jpg",
|
||
"alert_info": {
|
||
"camera_id": "cam-001",
|
||
"roi_id": "roi-01",
|
||
"alert_type": "leave_post",
|
||
"confidence": 85,
|
||
"duration_minutes": 5
|
||
}
|
||
}
|
||
```
|
||
|
||
**响应格式**:
|
||
```json
|
||
{
|
||
"risk_level": "HIGH",
|
||
"description": "检测到人员在非工作区域长时间停留,建议现场核实",
|
||
"suggestion": "可能是误报,建议确认监控区域工作安排",
|
||
"confidence_score": 0.92
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 七、部署说明
|
||
|
||
### 7.1 后端部署
|
||
|
||
```bash
|
||
# 1. 安装依赖
|
||
pip install -r requirements.txt
|
||
|
||
# 2. 配置环境变量
|
||
cp .env.example .env
|
||
# 编辑 .env 文件
|
||
|
||
# 3. 启动服务
|
||
python -m app.main
|
||
```
|
||
|
||
### 7.2 使用 Gunicorn 生产部署
|
||
|
||
```bash
|
||
gunicorn app.main:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:8000
|
||
```
|
||
|
||
### 7.3 Docker 部署
|
||
|
||
创建 `Dockerfile`:
|
||
|
||
```dockerfile
|
||
FROM python:3.10-slim
|
||
WORKDIR /app
|
||
COPY requirements.txt .
|
||
RUN pip install -r requirements.txt
|
||
COPY . .
|
||
EXPOSE 8000
|
||
CMD ["python", "-m", "app.main"]
|
||
```
|
||
|
||
---
|
||
|
||
## 八、注意事项
|
||
|
||
1. **芋道项目权限**: 告警相关接口需要芋道后端配合实现用户认证
|
||
2. **跨域配置**: 告警平台需要配置 CORS 以允许芋道前端访问
|
||
3. **图片存储**: 阿里云 OSS 需要配置 CORS 允许前端直接访问图片
|
||
4. **时区处理**: 建议统一使用 UTC 时间,前端展示时转换为本地时间
|
||
5. **大模型服务**: AI 分析功能可选,需要部署大模型服务支持
|
||
|
||
---
|
||
|
||
## 九、目录结构总结
|
||
|
||
```
|
||
# 告警平台后端
|
||
alert_platform/
|
||
├── app/
|
||
│ ├── main.py # FastAPI 入口
|
||
│ ├── config.py # 配置
|
||
│ ├── models.py # SQLAlchemy 模型
|
||
│ ├── schemas.py # Pydantic 模型
|
||
│ ├── services/
|
||
│ │ ├── alert_service.py # 告警业务逻辑
|
||
│ │ ├── oss_storage.py # OSS 存储
|
||
│ │ └── ai_analyzer.py # AI 分析
|
||
│ └── utils/
|
||
│ └── logger.py # 日志
|
||
├── data/ # SQLite 数据库
|
||
├── uploads/ # 本地临时存储
|
||
├── requirements.txt
|
||
├── .env.example
|
||
└── README.md
|
||
|
||
# 芋道前端需要创建的文件
|
||
yudao-ui-admin-vben/
|
||
└── apps/web-antd/src/
|
||
├── api/
|
||
│ └── alert/
|
||
│ └── alert.ts # 告警 API
|
||
├── router/routes/modules/
|
||
│ └── alert.ts # 告警路由
|
||
└── views/alert/
|
||
└── list/
|
||
├── data.ts # 列表配置
|
||
└── index.vue # 列表页面
|
||
```
|