From 159a82aaa966f4c6c0be549b4c87f59637f6e16e Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Fri, 6 Feb 2026 16:40:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(aiot):=20=E6=90=AD=E5=BB=BA=20aiot=20?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=A8=A1=E5=9D=97=E8=B7=AF=E7=94=B1=E5=92=8C?= =?UTF-8?q?=20API=20=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 router/routes/modules/aiot.ts:6 个页面路由 告警列表、摄像头汇总、摄像头管理、ROI配置、实时视频、边缘节点 - 新增 api/aiot/alarm/:告警 API(分页、详情、处理、删除、统计、汇总) - 新增 api/aiot/edge/:边缘设备 API(分页、详情、统计) - 新增 api/aiot/device/:摄像头和 ROI API(调用 WVP 后端) - 新增 api/aiot/video/:视频播放 API(playStart/playStop) - 新增 api/aiot/request.ts:WVP 专用请求客户端(跳过芋道响应拦截器) Co-Authored-By: Claude Opus 4.6 --- apps/web-antd/src/api/aiot/alarm/index.ts | 118 ++++++++++++++++++ apps/web-antd/src/api/aiot/device/index.ts | 99 +++++++++++++++ apps/web-antd/src/api/aiot/edge/index.ts | 55 ++++++++ apps/web-antd/src/api/aiot/request.ts | 35 ++++++ apps/web-antd/src/api/aiot/video/index.ts | 43 +++++++ .../src/router/routes/modules/aiot.ts | 71 +++++++++++ 6 files changed, 421 insertions(+) create mode 100644 apps/web-antd/src/api/aiot/alarm/index.ts create mode 100644 apps/web-antd/src/api/aiot/device/index.ts create mode 100644 apps/web-antd/src/api/aiot/edge/index.ts create mode 100644 apps/web-antd/src/api/aiot/request.ts create mode 100644 apps/web-antd/src/api/aiot/video/index.ts create mode 100644 apps/web-antd/src/router/routes/modules/aiot.ts diff --git a/apps/web-antd/src/api/aiot/alarm/index.ts b/apps/web-antd/src/api/aiot/alarm/index.ts new file mode 100644 index 000000000..40126c09d --- /dev/null +++ b/apps/web-antd/src/api/aiot/alarm/index.ts @@ -0,0 +1,118 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiotAlarmApi { + /** AI 告警 VO */ + export interface Alert { + id?: number; + alertNo?: string; + cameraId?: string; + cameraName?: string; + roiId?: string; + bindId?: string; + deviceId?: string; + alertType?: string; + alertTypeName?: string; + algorithm?: string; + confidence?: number; + durationMinutes?: number; + triggerTime?: string; + message?: string; + bbox?: string; + snapshotUrl?: string; + ossUrl?: string; + status?: string; + statusName?: string; + level?: string; + handleRemark?: string; + handledBy?: string; + handledAt?: string; + workOrderId?: number; + aiAnalysis?: Record; + createdAt?: string; + updatedAt?: string; + logInfo?: AlertLogInfo; + } + + /** 告警日志信息 */ + export interface AlertLogInfo { + receiveTime?: string; + handleTime?: string; + handledBy?: string; + handleRemark?: string; + confidence?: number; + durationMinutes?: number; + bbox?: string; + aiAnalysis?: Record; + } + + /** 摄像头告警汇总 */ + export interface CameraAlertSummary { + cameraId?: string; + cameraName?: string; + totalCount?: number; + pendingCount?: number; + lastAlertTime?: string; + lastAlertType?: string; + lastAlertTypeName?: string; + } + + /** 告警统计 */ + export interface AlertStatistics { + total?: number; + todayCount?: number; + pendingCount?: number; + handledCount?: number; + byType?: Record; + byStatus?: Record; + byLevel?: Record; + } +} + +// ==================== 告警管理 API ==================== + +/** 分页查询告警列表 */ +export function getAlertPage(params: PageParam) { + return requestClient.get>( + '/aiot/alarm/alert/page', + { params }, + ); +} + +/** 获取告警详情 */ +export function getAlert(id: number) { + return requestClient.get( + `/aiot/alarm/alert/get?id=${id}`, + ); +} + +/** 处理告警 */ +export function handleAlert(id: number, status: string, remark?: string) { + return requestClient.put('/aiot/alarm/alert/handle', null, { + params: { id, status, remark }, + }); +} + +/** 删除告警 */ +export function deleteAlert(id: number) { + return requestClient.delete(`/aiot/alarm/alert/delete?id=${id}`); +} + +/** 获取告警统计 */ +export function getAlertStatistics(startTime?: string, endTime?: string) { + return requestClient.get( + '/aiot/alarm/alert/statistics', + { params: { startTime, endTime } }, + ); +} + +// ==================== 摄像头告警汇总 API ==================== + +/** 以摄像头维度获取告警汇总 */ +export function getCameraAlertSummary(params: PageParam) { + return requestClient.get>( + '/aiot/alarm/camera-summary/page', + { params }, + ); +} diff --git a/apps/web-antd/src/api/aiot/device/index.ts b/apps/web-antd/src/api/aiot/device/index.ts new file mode 100644 index 000000000..bcffe8f33 --- /dev/null +++ b/apps/web-antd/src/api/aiot/device/index.ts @@ -0,0 +1,99 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { wvpRequestClient } from '#/api/aiot/request'; + +export namespace AiotDeviceApi { + /** ROI 区域 */ + export interface Roi { + id?: number; + channelId?: string; + channelName?: string; + name?: string; + type?: string; + points?: string; + enabled?: boolean; + createdAt?: string; + updatedAt?: string; + } + + /** ROI 算法绑定 */ + export interface RoiAlgoBind { + id?: number; + roiId?: number; + algorithmId?: number; + algorithmName?: string; + enabled?: boolean; + config?: Record; + } + + /** 算法 */ + export interface Algorithm { + id?: number; + name?: string; + code?: string; + description?: string; + enabled?: boolean; + } + + /** 摄像头通道 */ + export interface Channel { + channelId?: string; + name?: string; + manufacturer?: string; + status?: string; + ptztypeText?: string; + longitudeWgs84?: number; + latitudeWgs84?: number; + } +} + +// ==================== ROI 区域管理 API ==================== + +/** 获取 ROI 列表 */ +export function getRoiPage(params: PageParam) { + return wvpRequestClient.get>( + '/aiot/device/roi/list', + { params }, + ); +} + +/** 保存 ROI */ +export function saveRoi(data: AiotDeviceApi.Roi) { + return wvpRequestClient.post('/aiot/device/roi/save', data); +} + +/** 删除 ROI */ +export function deleteRoi(id: number) { + return wvpRequestClient.delete(`/aiot/device/roi/delete?id=${id}`); +} + +// ==================== 算法绑定 API ==================== + +/** 绑定算法到 ROI */ +export function bindAlgo(data: { roiId: number; algorithmId: number }) { + return wvpRequestClient.post('/aiot/device/roi/bindAlgo', data); +} + +/** 解绑算法 */ +export function unbindAlgo(bindId: number) { + return wvpRequestClient.delete( + `/aiot/device/roi/unbindAlgo?bindId=${bindId}`, + ); +} + +/** 获取算法列表 */ +export function getAlgorithmList() { + return wvpRequestClient.get( + '/aiot/device/algorithm/list', + ); +} + +// ==================== 摄像头通道 API ==================== + +/** 获取摄像头通道列表 */ +export function getChannelPage(params: PageParam) { + return wvpRequestClient.get>( + '/aiot/device/channel/list', + { params }, + ); +} diff --git a/apps/web-antd/src/api/aiot/edge/index.ts b/apps/web-antd/src/api/aiot/edge/index.ts new file mode 100644 index 000000000..39cf9843c --- /dev/null +++ b/apps/web-antd/src/api/aiot/edge/index.ts @@ -0,0 +1,55 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiotEdgeApi { + /** 边缘设备 VO */ + export interface Device { + id?: number; + deviceId?: string; + deviceName?: string; + status?: string; + statusName?: string; + lastHeartbeat?: string; + uptimeSeconds?: number; + framesProcessed?: number; + alertsGenerated?: number; + ipAddress?: string; + streamCount?: number; + configVersion?: string; + extraInfo?: Record; + updatedAt?: string; + } + + /** 设备统计 */ + export interface DeviceStatistics { + total?: number; + online?: number; + offline?: number; + error?: number; + } +} + +// ==================== 边缘设备 API ==================== + +/** 分页查询边缘设备列表 */ +export function getDevicePage(params: PageParam) { + return requestClient.get>( + '/aiot/edge/device/page', + { params }, + ); +} + +/** 获取设备详情 */ +export function getDevice(id: string) { + return requestClient.get( + `/aiot/edge/device/get?id=${id}`, + ); +} + +/** 获取设备统计 */ +export function getDeviceStatistics() { + return requestClient.get( + '/aiot/edge/device/statistics', + ); +} diff --git a/apps/web-antd/src/api/aiot/request.ts b/apps/web-antd/src/api/aiot/request.ts new file mode 100644 index 000000000..638d33689 --- /dev/null +++ b/apps/web-antd/src/api/aiot/request.ts @@ -0,0 +1,35 @@ +/** + * WVP 视频平台专用请求客户端 + * + * WVP 返回原始 JSON(非芋道的 {code:0, data:...} 格式), + * 需要跳过芋道默认的响应拦截器。 + */ +import { useAppConfig } from '@vben/hooks'; +import { preferences } from '@vben/preferences'; +import { RequestClient } from '@vben/request'; +import { useAccessStore } from '@vben/stores'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); + +function createWvpRequestClient(baseURL: string) { + const client = new RequestClient({ + baseURL, + }); + + // 请求头:携带 token 用于 Vite 代理鉴权透传 + client.addRequestInterceptor({ + fulfilled: async (config) => { + const accessStore = useAccessStore(); + const token = accessStore.accessToken; + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + config.headers['Accept-Language'] = preferences.app.locale; + return config; + }, + }); + + return client; +} + +export const wvpRequestClient = createWvpRequestClient(apiURL); diff --git a/apps/web-antd/src/api/aiot/video/index.ts b/apps/web-antd/src/api/aiot/video/index.ts new file mode 100644 index 000000000..4fdd62dae --- /dev/null +++ b/apps/web-antd/src/api/aiot/video/index.ts @@ -0,0 +1,43 @@ +import { wvpRequestClient } from '#/api/aiot/request'; + +export namespace AiotVideoApi { + /** 流信息 */ + export interface StreamInfo { + app?: string; + stream?: string; + ip?: string; + flv?: string; + ws_flv?: string; + rtmp?: string; + hls?: string; + rtsp?: string; + mediaServerId?: string; + tracks?: StreamTrack[]; + } + + export interface StreamTrack { + codec_id?: number; + codec_id_name?: string; + ready?: boolean; + type?: number; + width?: number; + height?: number; + fps?: number; + } +} + +// ==================== 视频播放 API ==================== + +/** 开始播放 */ +export function playStart(deviceId: string, channelId: string) { + return wvpRequestClient.get( + `/aiot/video/play/start/${deviceId}/${channelId}`, + ); +} + +/** 停止播放 */ +export function playStop(deviceId: string, channelId: string) { + return wvpRequestClient.get( + `/aiot/video/play/stop/${deviceId}/${channelId}`, + ); +} diff --git a/apps/web-antd/src/router/routes/modules/aiot.ts b/apps/web-antd/src/router/routes/modules/aiot.ts new file mode 100644 index 000000000..c61ea6746 --- /dev/null +++ b/apps/web-antd/src/router/routes/modules/aiot.ts @@ -0,0 +1,71 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + path: '/aiot', + name: 'AIoT', + meta: { + title: 'AIoT 智能平台', + icon: 'ant-design:robot-outlined', + keepAlive: true, + }, + children: [ + { + path: 'alarm/list', + name: 'AiotAlarmList', + component: () => import('#/views/aiot/alarm/list/index.vue'), + meta: { + title: '告警列表', + icon: 'ant-design:alert-outlined', + }, + }, + { + path: 'alarm/summary', + name: 'AiotAlarmSummary', + component: () => import('#/views/aiot/alarm/summary/index.vue'), + meta: { + title: '摄像头告警汇总', + icon: 'ant-design:video-camera-outlined', + }, + }, + { + path: 'device/camera', + name: 'AiotDeviceCamera', + component: () => import('#/views/aiot/device/camera/index.vue'), + meta: { + title: '摄像头管理', + icon: 'ant-design:camera-outlined', + }, + }, + { + path: 'device/roi', + name: 'AiotDeviceRoi', + component: () => import('#/views/aiot/device/roi/index.vue'), + meta: { + title: 'ROI 区域配置', + icon: 'ant-design:aim-outlined', + }, + }, + { + path: 'video/live', + name: 'AiotVideoLive', + component: () => import('#/views/aiot/video/live/index.vue'), + meta: { + title: '实时视频', + icon: 'ant-design:play-circle-outlined', + }, + }, + { + path: 'edge/node', + name: 'AiotEdgeNode', + component: () => import('#/views/aiot/edge/node/index.vue'), + meta: { + title: '边缘节点管理', + icon: 'ant-design:cluster-outlined', + }, + }, + ], + }, +]; + +export default routes;