| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- import { Hono } from "hono";
- import debug from "debug";
- import type {
- DeviceInstance,
- DeviceType,
- DeviceTreeNode,
- MapViewDevice,
- DeviceMapStats,
- DeviceMapDataResponse,
- DeviceTreeStats
- } from "../client/share/monitorTypes.ts";
- import {
- DeviceStatus,
- DeviceTreeNodeType,
- DeviceTreeNodeStatus
- } from "../client/share/monitorTypes.ts";
- import {
- EnableStatus,
- DeleteStatus,
- } from "../client/share/types.ts";
- import type { Variables, WithAuth } from "./middlewares.ts";
- const log = {
- api: debug("api:monitor"),
- };
- // 定义扩展的设备实例接口,包含从数据库查询返回的额外字段
- interface DeviceInstanceExtended extends DeviceInstance {
- type_code: string;
- name?: string;
- device_status?: DeviceStatus;
- description?: string;
- longitude?: number;
- latitude?: number;
- }
- // 定义设备统计数据接口
- interface DeviceStatisticsDB {
- type_code: string;
- total: number;
- online: number;
- offline: number;
- error: number;
- }
- // 定义本地统计对象接口
- interface DeviceStats {
- total: number;
- online: number;
- offline: number;
- error: number;
- }
- // 监控数据API路由
- export function createMonitorMapRoutes(withAuth: WithAuth) {
- const monitorRoutes = new Hono<{ Variables: Variables }>();
-
- // 获取设备地图监控数据列表
- monitorRoutes.get("/", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
-
- // 获取分页参数
- const page = Number(c.req.query("page")) || 1;
- const limit = Number(c.req.query("limit")) || 10;
- const offset = (page - 1) * limit;
-
- // 获取筛选参数
- const deviceId = c.req.query("device_id") ? Number(c.req.query("device_id")) : undefined;
- const deviceName = c.req.query("device_name");
- const protocol = c.req.query("protocol");
- const address = c.req.query("address");
- const metricType = c.req.query("metric_type");
- const status = c.req.query("status") ? Number(c.req.query("status")) : undefined;
-
- // 构建查询
- let query = apiClient.database
- .table("device_monitor_data")
- .orderBy("collect_time", "desc");
-
- // 应用筛选条件
- if (deviceId) {
- query = query.where("device_id", deviceId);
- }
-
- if (deviceName) {
- query = query.where("device_name", "like", `%${deviceName}%`);
- }
-
- if (protocol) {
- query = query.where("protocol", protocol);
- }
-
- if (address) {
- query = query.where("address", "like", `%${address}%`);
- }
-
- if (metricType) {
- query = query.where("metric_type", metricType);
- }
-
- if (status !== undefined) {
- query = query.where("status", status);
- }
-
- // 克隆查询以获取总数
- const countQuery = query.clone();
-
- // 执行分页查询
- const monitorData = await query.limit(limit).offset(offset);
-
- // 获取总数
- const count = await countQuery.count();
-
- return c.json({
- data: monitorData,
- total: Number(count),
- page,
- pageSize: limit,
- });
- } catch (error) {
- log.api("获取设备地图监控数据失败:", error);
- return c.json({ error: "获取设备地图监控数据失败" }, 500);
- }
- });
-
- // 获取设备树数据
- monitorRoutes.get("/devices/tree", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
-
- // 获取查询参数
- const status = c.req.query('status');
- const keyword = c.req.query('keyword');
-
- // 获取所有设备分类
- const deviceTypes = await apiClient.database
- .table("device_types")
- .where("is_deleted", DeleteStatus.NOT_DELETED)
- .where("is_enabled", EnableStatus.ENABLED)
- .orderBy("id", "asc") as DeviceType[];
-
- // 构建设备查询
- let deviceQuery = apiClient.database
- .raw(`
- SELECT d.*, t.code as type_code, a.asset_name as name, a.device_status as device_status
- FROM device_instances d
- LEFT JOIN device_types t ON d.type_id = t.id
- LEFT JOIN zichan_info a ON d.id = a.id
- WHERE d.is_deleted = ? AND a.is_deleted = ?
- `, [DeleteStatus.NOT_DELETED, DeleteStatus.NOT_DELETED]);
-
- // 根据状态过滤
- if (status && status !== 'all') {
- deviceQuery = apiClient.database
- .raw(`
- SELECT d.*, t.code as type_code, a.asset_name as name, a.device_status as device_status
- FROM device_instances d
- LEFT JOIN device_types t ON d.type_id = t.id
- LEFT JOIN zichan_info a ON d.id = a.id
- WHERE d.is_deleted = ? AND a.is_deleted = ?
- AND a.device_status = ?
- `, [DeleteStatus.NOT_DELETED, DeleteStatus.NOT_DELETED,
- status === 'error' ? DeviceStatus.FAULT :
- status === 'normal' ? DeviceStatus.NORMAL :
- status === 'offline' ? DeviceStatus.OFFLINE : null]);
- }
-
- // 查询设备
- let devices = await deviceQuery as DeviceInstanceExtended[];
-
- // 根据关键词过滤
- if (keyword) {
- const lowercaseKeyword = keyword.toLowerCase();
- devices = devices.filter(device =>
- device.name?.toLowerCase().includes(lowercaseKeyword) ||
- device.id.toString().includes(lowercaseKeyword));
- }
-
- // 构建树结构
- const filteredTypes = deviceTypes.filter(type => {
- // 筛选当前分类下的设备
- const typeDevices = devices.filter(device => device.type_code === type.code);
- // 如果没有匹配的设备且有关键词或状态过滤,则跳过此分类
- return !(typeDevices.length === 0 && (keyword || (status && status !== 'all')));
- });
-
- const treeData: DeviceTreeNode[] = filteredTypes.map(type => {
- // 筛选当前分类下的设备
- const typeDevices = devices.filter(device => device.type_code === type.code);
-
- // 设备节点
- const deviceNodes: DeviceTreeNode[] = typeDevices.map(device => ({
- key: `device-${device.id}`,
- title: device.name || `设备${device.id}`,
- type: DeviceTreeNodeType.DEVICE,
- status: device.device_status === DeviceStatus.NORMAL ? DeviceTreeNodeStatus.NORMAL :
- device.device_status === DeviceStatus.FAULT ? DeviceTreeNodeStatus.ERROR :
- device.device_status === DeviceStatus.OFFLINE ? DeviceTreeNodeStatus.OFFLINE : DeviceTreeNodeStatus.WARNING,
- icon: type.image_url || null,
- isLeaf: true,
- }));
-
- return {
- key: type.code,
- title: type.name,
- type: DeviceTreeNodeType.CATEGORY,
- icon: type.image_url || null,
- children: deviceNodes
- };
- });
-
- return c.json({
- data: treeData
- });
- } catch (error) {
- log.api("获取设备树数据失败:", error);
- return c.json({ error: "获取设备树数据失败" }, 500);
- }
- });
-
- // 获取设备统计数据
- monitorRoutes.get("/devices/tree/statistics", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
-
- // 获取所有设备分类
- const deviceTypes = await apiClient.database
- .table("device_types")
- .where("is_deleted", DeleteStatus.NOT_DELETED)
- .where("is_enabled", EnableStatus.ENABLED);
-
- // 获取设备统计
- const devicesStats = await apiClient.database
- .raw(`
- SELECT
- t.code as type_code,
- COUNT(*) as total,
- SUM(CASE WHEN a.device_status = ? THEN 1 ELSE 0 END) as online,
- SUM(CASE WHEN a.device_status = ? THEN 1 ELSE 0 END) as offline,
- SUM(CASE WHEN a.device_status = ? THEN 1 ELSE 0 END) as error
- FROM device_instances d
- LEFT JOIN device_types t ON d.type_id = t.id
- LEFT JOIN zichan_info a ON d.id = a.id
- WHERE d.is_deleted = ? AND a.is_deleted = ?
- GROUP BY t.code
- `, [DeviceStatus.NORMAL, DeviceStatus.OFFLINE, DeviceStatus.FAULT, DeleteStatus.NOT_DELETED, DeleteStatus.NOT_DELETED]) as DeviceStatisticsDB[];
-
- // 构建统计数据
- const statistics: DeviceTreeStats = {};
-
- deviceTypes.forEach(type => {
- const stats = devicesStats.find((stat: DeviceStatisticsDB) =>
- stat.type_code === type.code
- );
- statistics[type.code] = {
- total: stats ? Number(stats.total) : 0,
- online: stats ? Number(stats.online) : 0,
- offline: stats ? Number(stats.offline) : 0,
- error: stats ? Number(stats.error) : 0
- };
- });
-
- return c.json({
- data: statistics
- });
- } catch (error) {
- log.api("获取设备统计数据失败:", error);
- return c.json({ error: "获取设备统计数据失败" }, 500);
- }
- });
-
- // 获取设备地图数据
- monitorRoutes.get("/devices/map", withAuth, async (c) => {
- try {
- const apiClient = c.get('apiClient');
-
- // 获取筛选参数
- const typeCode = c.req.query("type_code");
- const status = c.req.query("device_status");
- const keyword = c.req.query("keyword");
- const deviceId = c.req.query("device_id");
-
- // 构建查询
- const query = apiClient.database
- .raw(`
- SELECT
- d.id, t.code as type_code, d.remark as description,
- a.asset_name as name, a.device_status as device_status,
- a.longitude, a.latitude
- FROM device_instances d
- LEFT JOIN device_types t ON d.type_id = t.id
- LEFT JOIN zichan_info a ON d.id = a.id
- WHERE d.is_deleted = ? AND a.is_deleted = ?
- AND a.longitude IS NOT NULL AND a.latitude IS NOT NULL
- `, [DeleteStatus.NOT_DELETED, DeleteStatus.NOT_DELETED]);
-
- // 将原始数据转换为设备地图数据
- let devices = await query as DeviceInstanceExtended[];
-
- // 获取设备类型图标
- const deviceTypes = await apiClient.database
- .table("device_types")
- .where("is_deleted", DeleteStatus.NOT_DELETED);
-
- // 构建图标映射
- const iconMap: Record<string, string> = {};
- deviceTypes.forEach(type => {
- iconMap[type.code] = type.image_url || '';
- });
-
- // 应用筛选
- if (typeCode) {
- devices = devices.filter(d => d.type_code === typeCode);
- }
-
- if (status) {
- const statusNum = Number(status);
- devices = devices.filter(d => d.device_status === statusNum);
- }
-
- // 设备ID精确匹配
- if (deviceId) {
- const deviceIdNum = Number(deviceId);
- devices = devices.filter(d => d.id === deviceIdNum);
- }
-
- if (keyword) {
- const keywordLower = keyword.toLowerCase();
- devices = devices.filter(d =>
- (d.name && d.name.toLowerCase().includes(keywordLower)) ||
- (d.description && d.description.toLowerCase().includes(keywordLower))
- );
- }
-
- // 将设备转换为地图标记
- const mapDevices = devices.map(device => ({
- ...device,
- longitude: Number(device.longitude || 0),
- latitude: Number(device.latitude || 0),
- isOnline: device.device_status === DeviceStatus.NORMAL ? '1' : '0',
- image_url: iconMap[device.type_code] || '',
- })) as MapViewDevice[];
-
- // 生成统计数据
- const stats: DeviceMapStats = {
- total: devices.length,
- online: devices.filter(d => d.device_status === DeviceStatus.NORMAL).length,
- offline: devices.filter(d => d.device_status === DeviceStatus.OFFLINE).length,
- error: devices.filter(d => d.device_status === DeviceStatus.FAULT).length,
- };
-
- return c.json({
- data: mapDevices,
- stats: stats
- } as DeviceMapDataResponse);
- } catch (error) {
- log.api("获取设备地图数据失败:", error);
- return c.json({ error: "获取设备地图数据失败" }, 500);
- }
- });
-
- return monitorRoutes;
- }
|