Files
iot-device-management-frontend/apps/web-antd/src/api/aiot/device/index.ts
2026-03-23 17:02:30 +08:00

302 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* AIoT 设备管理 API
*
* 所有请求通过 wvpRequestClient 发送到 WVP 视频平台后端,
* 由 Vite 代理按路径分发并 rewrite
* /aiot/device/proxy/* → WVP /api/proxy/*
* /aiot/device/user/* → WVP /api/user/*
* /aiot/device/server/* → WVP /api/server/media_server/*
* /aiot/device/* → WVP /api/ai/*
*/
import { useAppConfig } from '@vben/hooks';
import { getWvpToken, wvpRequestClient } from '#/api/aiot/request';
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
// ==================== 类型定义 ====================
export namespace AiotDeviceApi {
/** 分页响应结构 */
export interface PageResult<T> {
list: T[];
total: number;
}
/** 摄像头(拉流代理) */
export interface Camera {
id?: number;
type?: string; // 'default' | 'ffmpeg'
app?: string;
stream?: string;
cameraCode?: string; // 摄像头唯一编码
cameraName?: string; // 摄像头名称(用户自定义)
edgeDeviceId?: string; // 绑定的边缘设备ID
srcUrl?: string;
timeout?: number;
rtspType?: string; // '0'=TCP, '1'=UDP, '2'=Multicast
enable?: boolean;
enableAudio?: boolean;
enableMp4?: boolean;
enableDisableNoneReader?: boolean;
relatesMediaServerId?: string;
ffmpegCmdKey?: string;
pulling?: boolean;
mediaServerId?: string;
streamKey?: string;
createTime?: string;
}
/** ROI 区域 */
export interface Roi {
id?: number;
roiId?: string;
cameraId?: string;
deviceId?: string;
name?: string;
roiType?: string; // 'rectangle' | 'polygon'
coordinates?: string;
color?: string;
priority?: number;
enabled?: number; // 0 | 1
description?: string;
algorithms?: RoiAlgoBinding[];
}
/** ROI 算法绑定(详情嵌套结构) */
export interface RoiAlgoBinding {
bind: AlgoBind;
algorithm?: Algorithm;
}
/** 算法绑定记录 */
export interface AlgoBind {
bindId?: string;
roiId?: string;
algoCode?: string;
enabled?: number; // 0 | 1
params?: string;
}
/** 算法定义 */
export interface Algorithm {
id?: number;
algoCode?: string;
algoName?: string;
description?: string;
isActive?: boolean;
paramSchema?: string;
}
/** 媒体服务器 */
export interface MediaServer {
id: string;
ip: string;
}
}
// ==================== 摄像头管理 ====================
/** 摄像头列表(分页) */
export function getCameraList(params: {
page: number;
count: number;
query?: string;
pulling?: boolean;
}) {
return wvpRequestClient.get<AiotDeviceApi.PageResult<AiotDeviceApi.Camera>>(
'/aiot/device/proxy/list',
{ params },
);
}
/** 新增摄像头 */
export function addCamera(data: Partial<AiotDeviceApi.Camera>) {
return wvpRequestClient.post('/aiot/device/proxy/add', data);
}
/** 编辑摄像头 */
export function updateCamera(data: Partial<AiotDeviceApi.Camera>) {
return wvpRequestClient.post('/aiot/device/proxy/update', data);
}
/** 保存摄像头(新增/编辑,有 id 为编辑) */
export function saveCamera(data: Partial<AiotDeviceApi.Camera>) {
return data.id ? updateCamera(data) : addCamera(data);
}
/** 删除摄像头 */
export function deleteCamera(id: number) {
return wvpRequestClient.delete('/aiot/device/proxy/delete', {
params: { id },
});
}
/** 开始拉流 */
export function startCamera(id: number) {
return wvpRequestClient.get('/aiot/device/proxy/start', { params: { id } });
}
/** 停止拉流 */
export function stopCamera(id: number) {
return wvpRequestClient.get('/aiot/device/proxy/stop', { params: { id } });
}
/** 在线媒体服务器列表 */
export function getMediaServerList() {
return wvpRequestClient.get<AiotDeviceApi.MediaServer[]>(
'/aiot/device/server/online/list',
);
}
/** 摄像头选项列表(用于下拉搜索选择) */
export function getCameraOptions() {
return wvpRequestClient.get<
{ cameraCode: string; cameraName: string }[]
>('/aiot/device/camera/options');
}
// ==================== ROI 区域管理 ====================
/** ROI 列表(分页) */
export function getRoiList(params: {
page: number;
count: number;
cameraId?: string;
deviceId?: string;
query?: string;
}) {
return wvpRequestClient.get<AiotDeviceApi.PageResult<AiotDeviceApi.Roi>>(
'/aiot/device/roi/list',
{ params },
);
}
/** ROI 详情(含算法绑定) */
export function getRoiDetail(id: number) {
return wvpRequestClient.get<AiotDeviceApi.Roi>(`/aiot/device/roi/${id}`);
}
/** 某摄像头的所有 ROI */
export function getRoiByCameraId(cameraId: string) {
return wvpRequestClient.get<AiotDeviceApi.Roi[]>(
'/aiot/device/roi/channel',
{ params: { cameraId } },
);
}
/** 保存 ROI新增/编辑) */
export function saveRoi(data: Partial<AiotDeviceApi.Roi>) {
return wvpRequestClient.post('/aiot/device/roi/save', data);
}
/** 删除 ROI */
export function deleteRoi(roiId: string) {
return wvpRequestClient.delete(`/aiot/device/roi/delete/${roiId}`);
}
/**
* 获取摄像头截图 URL
*
* 非 force 模式:直接返回 /snap/image 代理 URL无时间戳浏览器自动缓存
* force 模式:先触发边缘端截图,再返回带时间戳的代理 URL 破缓存
*/
export async function getSnapUrl(cameraCode: string, force = false): Promise<string> {
const token = await getWvpToken();
if (force) {
// 强制刷新:先触发边缘端截图(等待完成)
try {
await wvpRequestClient.get('/aiot/device/roi/snap', {
params: { cameraCode, force: true },
});
} catch {
// 截图请求可能超时,但 COS 上可能已有图片,继续返回代理 URL
}
// 加时间戳破浏览器缓存
return (
`${apiURL}/aiot/device/roi/snap/image` +
`?cameraCode=${encodeURIComponent(cameraCode)}` +
`&access-token=${encodeURIComponent(token)}` +
`&t=${Date.now()}`
);
}
// 非 force使用代理端点不加时间戳浏览器自动缓存
return (
`${apiURL}/aiot/device/roi/snap/image` +
`?cameraCode=${encodeURIComponent(cameraCode)}` +
`&access-token=${encodeURIComponent(token)}`
);
}
// ==================== 算法管理 ====================
/** 算法列表 */
export function getAlgorithmList() {
return wvpRequestClient.get<AiotDeviceApi.Algorithm[]>(
'/aiot/device/algorithm/list',
);
}
// ==================== 算法绑定 ====================
/** 绑定算法到 ROI */
export function bindAlgo(data: { roiId: string; algoCode: string }) {
return wvpRequestClient.post('/aiot/device/roi/bindAlgo', data);
}
/** 解绑算法 */
export function unbindAlgo(bindId: string) {
return wvpRequestClient.delete('/aiot/device/roi/unbindAlgo', {
params: { bindId },
});
}
/** 更新算法绑定参数 */
export function updateAlgoParams(data: {
bindId: string;
params?: string;
enabled?: number;
}) {
return wvpRequestClient.post('/aiot/device/roi/updateAlgoParams', data);
}
// ==================== 配置推送 ====================
/** 推送配置到边缘端 */
export function pushConfig(cameraId: string) {
return wvpRequestClient.post('/aiot/device/config/push', null, {
params: { cameraId },
});
}
/** 一次性推送全部配置到本地Edge */
export function pushAllConfig() {
return wvpRequestClient.post<Record<string, any>>(
'/aiot/device/config/push-all',
);
}
/** 导出摄像头配置 JSON */
export function exportConfig(cameraId: string) {
return wvpRequestClient.get<Record<string, any>>(
'/aiot/device/config/export',
{ params: { cameraId } },
);
}
// ==================== 告警图片代理 ====================
/**
* 构造告警图片代理 URL通过 WVP 下载 COS 图片后返回字节流)
* @param imagePath COS 对象路径或完整 URL
*/
export async function getAlertImageUrl(imagePath: string): Promise<string> {
if (!imagePath) return '';
const token = await getWvpToken();
return (
`${apiURL}/aiot/device/alert/image` +
`?imagePath=${encodeURIComponent(imagePath)}` +
`&access-token=${encodeURIComponent(token)}`
);
}