完整的系统集成和API对接指南
版本:v1.0.2 | 更新时间:2026年4月

目录

  1. 集成概述
  2. 系统集成
  3. API对接
  4. 数据对接
  5. 用户认证
  6. 部署配置

1. 集成概述

1.1 集成目标

本指南旨在帮助开发团队将Ricon组态系统快速集成到现有项目中,实现以下目标:

  • 无缝集成 - 与现有系统无缝对接
  • 数据互通 - 实现实时数据监控和展示
  • 用户统一 - 统一用户认证和权限管理
  • 界面统一 - 保持整体界面风格一致
  • 运维简化 - 简化部署和维护流程

1.2 集成方式

Ricon组态系统支持多种集成方式:

集成方式 适用场景 技术复杂度 推荐程度
iframe嵌入 快速集成、独立部署 ⭐⭐⭐
API对接 深度集成、数据互通 ⭐⭐⭐⭐⭐
组件化集成 前端框架集成 ⭐⭐⭐⭐
混合模式 综合需求 中-高 ⭐⭐⭐⭐⭐

1.3 集成架构

┌─────────────────────────────────────────────────┐
│                现有系统                          │
├─────────────────────────────────────────────────┤
│  ┌─────────────┐    ┌─────────────────────────┐  │
│  │  用户系统   │    │     业务系统           │  │
│  │  - 登录认证 │    │     - 业务逻辑         │  │
│  │  - 权限管理 │    │     - 数据处理         │  │
│  └─────────────┘    └─────────────────────────┘  │
│           │                   │                  │
│           └───────┬───────────┘                  │
│                   │                              │
│          ┌────────▼─────────┐                   │
│          │   API网关层       │                   │
│          │  - 统一认证       │                   │
│          │  - 路由转发       │                   │
│          └────────┬─────────┘                   │
│                   │                              │
│          ┌────────▼─────────┐                   │
│          │ Ricon组态系统    │                   │
│          │  - 编辑器        │                   │
│          │  - 监控页面      │                   │
│          │  - 数据通信      │                   │
│          └──────────────────┘                   │
└─────────────────────────────────────────────────┘

2. 系统集成

2.1 文件部署

2.1.1 目录结构
项目根目录/
├── ricon/                           # Ricon组态系统
│   ├── assets/                      # 静态资源
│   │   ├── css/                     # 样式文件
│   │   ├── js/                      # JavaScript文件
│   │   │   ├── common/               # 公共工具
│   │   │   ├── core/                 # 核心模块
│   │   │   │   ├── stage/            # 场景操作模块(14个)
│   │   │   │   ├── webSocketClient.js
│   │   │   │   ├── mqttClient.js
│   │   │   │   ├── httpClient.js
│   │   │   │   └── stageView.js
│   │   │   └── modules/             # 业务模块
│   │   │       ├── editor.js       # 编辑器
│   │   │       └── view.js         # 监控页面
│   │   ├── images/                # 图片资源
│   │   └── json/                  # 配置文件
│   ├── config/                     # 配置文件
│   │   └── apiClient.js            # API客户端配置
│   ├── modules/                    # 组件编辑模块
│   │   └── edit/                   # 组件编辑器
│   ├── pages/                      # 功能页面
│   ├── editor.html                 # 编辑器页面
│   ├── view.html                   # 监控页面
│   └── preview.html                # 预览页面
└── docs/                          # 文档目录
2.1.2 部署步骤

步骤1:上传文件

# 将Ricon组态系统文件上传到Web服务器
scp -r ricon/ user@server:/var/www/html/

步骤2:设置权限

# 设置文件权限
chmod -R 755 /var/www/html/ricon/
chown -R www-data:www-data /var/www/html/ricon/

步骤3:配置Web服务器
参考第6章的部署配置章节。

2.2 系统集成方式

2.2.1 iframe嵌入(快速集成)

优点:

  • 集成简单,无需修改现有系统
  • 独立部署,不影响现有系统
  • 维护方便

集成示例:

<!DOCTYPE html>
<html>
<head>
    <title>组态监控</title>
    <style>
        .ricon-container {
            width: 100%;
            height: 800px;
            border: 1px solid #ddd;
        }
    </style>
</head>
<body>
    <h1>设备监控</h1>
    <iframe
        src="http://your-server/ricon/view.html?stageId=stage001&token=your_token&userId=user001"
        class="ricon-container"
        frameborder="0"
        allowfullscreen>
    </iframe>
</body>
</html>

URL参数说明:

  • stageId: 场景ID(必填)
  • token: 用户认证Token(必填)
  • userId: 用户ID(必填)
  • width: 画布宽度(可选)
  • height: 画布高度(可选)
  • zoom: 缩放比例(可选,默认1)
2.2.2 API对接(深度集成)

集成流程:

  1. 用户认证集成

    // 获取用户Token
    function getUserToken(userId) {
        // 调用现有系统的用户认证接口
        return existingSystemAPI.getToken(userId);
    }
    
  2. 场景管理集成

    // 保存场景
    function saveStage(stageData, stageId) {
        const token = getUserToken(currentUser.id);
        const url = `http://your-server/api/saveStage`;
    
        return fetch(url, {
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${token}`,
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                stageId: stageId,
                stageName: stageData.name,
                stageDatajson: JSON.stringify(stageData),
                dataKeyArray: getDataKeyArray(stageData),
                stageBase64: await generateStageThumbnail(),
                remarks: stageData.remarks
            })
        });
    }
    
  3. 数据通信集成

    // WebSocket数据推送
    function setupDataPush() {
        const ws = new WebSocket('ws://your-server:8081/data');
    
        ws.onmessage = function(event) {
            const data = JSON.parse(event.data);
            // 转发给Ricon组态系统
            window.postMessage({
                type: 'ricon-data-update',
                data: data
            }, '*');
        };
    }
    
2.2.3 组件化集成(前端框架)

Vue.js集成示例:

// RiconViewer.vue
<template>
  <div class="ricon-viewer">
    <iframe
      ref="iframe"
      :src="iframeSrc"
      frameborder="0"
      @load="onIframeLoad"
    />
  </div>
</template>

<script>
export default {
  name: 'RiconViewer',
  props: {
    stageId: {
      type: String,
      required: true
    },
    token: {
      type: String,
      required: true
    },
    userId: {
      type: String,
      required: true
    },
    width: {
      type: Number,
      default: 1920
    },
    height: {
      type: Number,
      default: 1080
    }
  },
  computed: {
    iframeSrc() {
      return `http://your-server/ricon/view.html?stageId=${this.stageId}&token=${this.token}&userId=${this.userId}&width=${this.width}&height=${this.height}`;
    }
  },
  methods: {
    onIframeLoad() {
      // iframe加载完成后的处理
      console.log('Ricon viewer loaded');
    },
    sendDataUpdate(data) {
      // 向Ricon发送数据更新
      if (this.$refs.iframe) {
        this.$refs.iframe.contentWindow.postMessage({
          type: 'ricon-data-update',
          data: data
        }, '*');
      }
    }
  }
};
</script>

<style scoped>
.ricon-viewer {
  width: 100%;
  height: 100%;
}
.ricon-viewer iframe {
  width: 100%;
  height: 100%;
  border: none;
}
</style>

React集成示例:

import React, { useRef, useEffect } from 'react';

const RiconViewer = ({ stageId, token, userId, width = 1920, height = 1080 }) => {
  const iframeRef = useRef(null);

  const iframeSrc = `http://your-server/ricon/view.html?stageId=${stageId}&token=${token}&userId=${userId}&width=${width}&height=${height}`;

  useEffect(() => {
    const iframe = iframeRef.current;
    if (iframe) {
      iframe.addEventListener('load', () => {
        console.log('Ricon viewer loaded');
      });
    }
  }, []);

  const sendDataUpdate = (data) => {
    if (iframeRef.current) {
      iframeRef.current.contentWindow.postMessage({
        type: 'ricon-data-update',
        data: data
      }, '*');
    }
  };

  return (
    <div className="ricon-viewer">
      <iframe
        ref={iframeRef}
        src={iframeSrc}
        frameBorder="0"
        style={{ width: '100%', height: '100%', border: 'none' }}
      />
    </div>
  );
};

export default RiconViewer;
2.2.4 混合模式集成

混合模式结合了iframe嵌入和API对接的优点,适合复杂的集成场景:

  1. 使用iframe嵌入 - 快速集成编辑器和监控页面
  2. 使用API对接 - 实现数据互通和用户认证
  3. 使用事件通信 - 实现iframe与父页面的通信

示例:

// 父页面
const iframe = document.getElementById('ricon-iframe');

// 向iframe发送消息
function sendMessageToRicon(type, data) {
  iframe.contentWindow.postMessage({ type, data }, '*');
}

// 接收iframe消息
window.addEventListener('message', (event) => {
  if (event.origin !== 'http://your-server') return;
  
  const { type, data } = event.data;
  switch (type) {
    case 'ricon-stage-saved':
      console.log('场景保存成功:', data);
      break;
    case 'ricon-data-updated':
      console.log('数据更新:', data);
      break;
  }
});

// 示例:保存场景
sendMessageToRicon('save-stage', { stageId: 'stage001' });

3. API对接

3.1 API接口规范

3.1.1 基础规范

URL结构:

{baseUrl}/{apiPrefix}/{module}/{action}

示例:

  • http://api.example.com:8080/ztgl/hmgl/stageEdit
  • http://api.example.com:8080/api/v1/device/data

请求方法:

  • GET:获取数据
  • POST:创建/更新数据
  • PUT:更新数据
  • DELETE:删除数据

Content-Type:

  • application/json:JSON格式
  • application/x-www-form-urlencoded:表单格式
3.1.2 认证方式

Bearer Token认证:

Authorization: Bearer {token}

Token获取:

// 获取Token的API调用示例
async function getAuthToken(username, password) {
    const response = await fetch('/api/auth/login', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            username: username,
            password: password
        })
    });

    const data = await response.json();
    return data.token;
}

3.2 场景管理API

3.2.1 保存场景

接口地址: POST /api/saveStage

请求参数:

{
    stageId: "stage_001",           // 场景ID(修改时必填)
    stageName: "智慧园区监控",        // 场景名称
    stageDatajson: "{...}",        // 场景JSON数据
    dataKeyArray: "key1,key2,key3", // 数据点列表
    stageBase64: "data:image/png;base64,...", // 缩略图
    remarks: "园区主入口监控界面"      // 备注信息
}

实现示例:

async function saveStage(stageData, stageId = null) {
    const token = await getAuthToken();

    const response = await fetch('/api/saveStage', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            stageId: stageId,
            stageName: stageData.name || '未命名场景',
            stageDatajson: JSON.stringify(stageData),
            dataKeyArray: Object.keys(stageData.attrs?.dataKeyArray || {}).join(','),
            stageBase64: await generateStageThumbnail(),
            remarks: stageData.remarks || ''
        })
    });

    return await response.json();
}
3.2.2 加载场景

接口地址: GET /api/selectStageById

请求参数:

  • stageId:场景ID(URL参数)

实现示例:

async function loadStage(stageId) {
    const token = await getAuthToken();

    const response = await fetch(`/api/selectStageById?stageId=${stageId}`, {
        method: 'GET',
        headers: {
            'Authorization': `Bearer ${token}`
        }
    });

    const data = await response.json();
    if (data.code === 200 && data.data && data.data.stageDatajson) {
        return JSON.parse(data.data.stageDatajson);
    }
    throw new Error('Failed to load stage');
}
3.2.3 获取场景列表

接口地址: GET /api/stageList

请求参数:

实现示例:

async function getStageList() {
    const token = await getAuthToken();

    const response = await fetch('/api/stageList', {
        method: 'GET',
        headers: {
            'Authorization': `Bearer ${token}`
        }
    });

    const data = await response.json();
    return data.rows || [];
}
3.2.4 删除场景

接口地址: POST /api/deleteStage

请求参数:

{
    stageId: "stage_001"  // 场景ID
}

实现示例:

async function deleteStage(stageId) {
    const token = await getAuthToken();

    const response = await fetch('/api/deleteStage', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ stageId: stageId })
    });

    return await response.json();
}

3.3 模板管理API

3.3.1 保存模板

接口地址: POST /api/saveStageModule

请求参数:

{
    stageModule_id: "template_001", // 模板ID(修改时必填)
    stageModuleName: "工业监控模板", // 模板名称
    stageDatajson: "{...}",        // 场景JSON数据
    stageBase64: "data:image/png;base64,..." // 缩略图
}

实现示例:

async function saveTemplate(stageData, templateName, templateId = null) {
    const token = await getAuthToken();

    const response = await fetch('/api/saveStageModule', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            stageModule_id: templateId || '',
            stageModuleName: templateName,
            stageDatajson: JSON.stringify(stageData),
            stageBase64: await generateStageThumbnail()
        })
    });

    return await response.json();
}
3.3.2 获取模板列表

接口地址: GET /api/getMyStageModuleList

请求参数:

  • userId:用户ID(URL参数)

实现示例:

async function getTemplateList(userId) {
    const token = await getAuthToken();

    const response = await fetch(`/api/getMyStageModuleList?userId=${userId}`, {
        method: 'GET',
        headers: {
            'Authorization': `Bearer ${token}`
        }
    });

    const data = await response.json();
    return data.code === 200 ? data.data : [];
}
3.3.3 删除模板

接口地址: POST /api/deleteStageModule

请求参数:

{
    moduleStageId: "template_001"  // 模板ID
}

实现示例:

async function deleteTemplate(templateId) {
    const token = await getAuthToken();

    const response = await fetch('/api/deleteStageModule', {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ moduleStageId: templateId })
    });

    return await response.json();
}

3.4 硬件数据接口

3.4.1 获取硬件数据树

接口地址: GET /api/device/hardware-data

请求参数:

响应格式:

[
    {
        "id": "device001",
        "name": "新智慧园区",
        "children": [
            {
                "id": "device001a",
                "name": "园区大门",
                "children": [
                    {
                        "id": "d1a001",
                        "name": "车辆计数器",
                        "type": "counter"
                    }
                ]
            }
        ]
    }
]

实现示例:

async function getHardwareData() {
    const token = await getAuthToken();

    const response = await fetch('/api/device/hardware-data', {
        method: 'GET',
        headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
        }
    });

    const data = await response.json();
    return data.data || data;
}
3.4.2 获取设备数据点

接口地址: GET /api/device/points

请求参数:

  • deviceId:设备ID(URL参数)

实现示例:

async function getDevicePoints(deviceId) {
    const token = await getAuthToken();

    const response = await fetch(`/api/device/points?deviceId=${deviceId}`, {
        method: 'GET',
        headers: {
            'Authorization': `Bearer ${token}`
        }
    });

    const data = await response.json();
    return data.code === 200 ? data.data : [];
}
3.4.3 实时数据推送

WebSocket接口: ws://your-server:8080/ws

数据格式:

{
    "MESSAGETYPE": "01",
    "MESSAGECONTENT": {
        "d1a001": 25.5,
        "d2a001": 100,
        "d3a001": "运行"
    },
    "STAGEID": "场景ID",
    "TS": 1699123456789
}

实现示例:

class DataPushService {
    constructor(url) {
        this.url = url;
        this.ws = null;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
        this.callbacks = [];
    }

    connect() {
        try {
            this.ws = new WebSocket(this.url);

            this.ws.onopen = () => {
                console.log('WebSocket connected');
                this.reconnectAttempts = 0;
                // 发送认证信息
                this.ws.send(JSON.stringify({
                    type: 'auth',
                    token: getAuthToken()
                }));
            };

            this.ws.onmessage = (event) => {
                const data = JSON.parse(event.data);
                this.handleData(data);
            };

            this.ws.onclose = () => {
                console.log('WebSocket disconnected');
                this.reconnect();
            };

            this.ws.onerror = (error) => {
                console.error('WebSocket error:', error);
            };

        } catch (error) {
            console.error('Failed to connect WebSocket:', error);
            this.reconnect();
        }
    }

    handleData(data) {
        if (data.MESSAGETYPE === '01') {
            // 业务数据
            this.onBusinessData(data.MESSAGECONTENT);
        } else if (data.MESSAGETYPE === '02') {
            // 提醒数据
            this.onAlertData(data.MESSAGECONTENT);
        } else if (data.MESSAGETYPE === '04') {
            // 命令响应
            this.onCommandResponse(data.MESSAGECONTENT);
        }
    }

    onBusinessData(content) {
        // 处理业务数据,更新组态画面
        window.postMessage({
            type: 'ricon-data-update',
            data: content
        }, '*');
        
        // 调用注册的回调函数
        this.callbacks.forEach(callback => {
            if (callback.type === 'data') {
                callback.fn(content);
            }
        });
    }

    onAlertData(content) {
        // 处理提醒数据
        console.log('Alert data:', content);
        
        // 调用注册的回调函数
        this.callbacks.forEach(callback => {
            if (callback.type === 'alert') {
                callback.fn(content);
            }
        });
    }

    onCommandResponse(content) {
        // 处理命令响应
        console.log('Command response:', content);
        
        // 调用注册的回调函数
        this.callbacks.forEach(callback => {
            if (callback.type === 'command') {
                callback.fn(content);
            }
        });
    }

    reconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            setTimeout(() => {
                console.log(`Reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
                this.connect();
            }, 3000 * this.reconnectAttempts);
        }
    }

    disconnect() {
        if (this.ws) {
            this.ws.close();
        }
    }

    // 注册回调函数
    on(type, fn) {
        this.callbacks.push({ type, fn });
    }

    // 发送控制命令
    sendCommand(stageId, key, value, cmd = 'WRITE') {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify({
                type: 'command',
                stageId: stageId,
                cmd: cmd,
                key: key,
                value: value
            }));
        }
    }
}

// 使用示例
const dataService = new DataPushService('ws://your-server:8080/ws');
dataService.connect();

// 监听数据更新
dataService.on('data', (data) => {
    console.log('Received data:', data);
    // 更新UI
});

// 发送控制命令
dataService.sendCommand('stage_001', 'd1a001', 1, 'OPEN');

4. 数据对接

4.1 数据格式规范

4.1.1 场景数据格式

场景JSON结构:

{
    "attrs": {
        "id": "stage_001",
        "width": 1920,
        "height": 1080,
        "backgroundColor": "#ffffff",
        "isStageMove": "1",
        "communicationConfig": {
            "websocket": {
                "enabled": true,
                "url": "ws://localhost:8080/ws",
                "heartbeat": 10,
                "reconnect": "1"
            },
            "mqtt": {
                "enabled": false,
                "url": "ws://localhost:9001",
                "topic": "/data/device",
                "clientId": "mqtt_client_12345",
                "username": "",
                "password": ""
            },
            "http": {
                "enabled": false,
                "url": "http://localhost:8080/api/data",
                "interval": 5,
                "method": "GET"
            }
        },
        "dataKeyArray": {
            "a03": "",
            "a04": "",
            "d3a002": ""
        }
    },
    "className": "Stage",
    "children": [
        {
            "attrs": {},
            "className": "Layer",
            "children": [
                {
                    "attrs": {
                        "id": "text_001",
                        "x": 100,
                        "y": 100,
                        "width": 200,
                        "height": 50,
                        "text": "设备状态",
                        "fontSize": 16,
                        "fill": "#333333",
                        "dataKey": [
                            {
                                "key": "device001",
                                "name": "设备001",
                                "type": "string",
                                "unit": "",
                                "scale": "1",
                                "condition": ""
                            }
                        ]
                    },
                    "className": "Text",
                    "children": []
                }
            ]
        }
    ]
}
4.1.2 实时数据格式

标准数据格式:

{
    "devicecode1": "value1",
    "devicecode2": "value2",
    "temperature": 25.5,
    "pressure": 1013.25,
    "status": "normal"
}

WebSocket数据格式:

{
    "MESSAGETYPE": "01",
    "MESSAGECONTENT": {
        "d1a001": 25.5,
        "d2a001": 100,
        "d3a001": "运行"
    },
    "STAGEID": "stage_001",
    "TS": 1699123456789
}

数据更新机制:

  • 键名(key)必须与组件绑定的数据点一致
  • 值可以是字符串、数值、布尔值等基本类型
  • 系统会自动更新绑定了对应数据点的组件
  • 支持嵌套对象和数组格式的数据

4.2 数据通信方式

4.2.1 WebSocket实时通信

连接配置:

// config/apiClient.js
var SystemConfig = {
    // WebSocket配置
    webSocket: {
        url: "ws://your-server:8080/ws",
        heartbeat: 10, // 心跳间隔(秒)
        reconnect: true, // 自动重连
        maxReconnectAttempts: 5 // 最大重连次数
    }
};

服务端推送格式:

{
    "MESSAGETYPE": "01", // 消息类型:01=数据更新,02=提醒,04=命令响应
    "MESSAGECONTENT": {
        "a03": 100,
        "a04": "ON",
        "d3a002": 25.5
    },
    "STAGEID": "stage_001", // 场景ID
    "TS": 1699123456789 // 时间戳
}

客户端接收处理:

// assets/js/core/webSocketClient.js
handleMessage(event) {
    try {
        const data = JSON.parse(event.data);

        if (data.MESSAGETYPE === "01") {
            // 业务数据
            this.updateStageData(data.MESSAGECONTENT);
        } else if (data.MESSAGETYPE === "02") {
            // 提醒数据
            this.handleAlert(data.MESSAGECONTENT);
        } else if (data.MESSAGETYPE === "04") {
            // 命令响应
            this.handleCommandResponse(data.MESSAGECONTENT);
        }
    } catch (error) {
        console.error('WebSocket message parse error:', error);
    }
}

updateStageData(data) {
    // 更新场景数据
    if (window.stageOper && stageOper.stage) {
        stageOper.stage.attrs.dataKeyArray = {
            ...stageOper.stage.attrs.dataKeyArray,
            ...data
        };
        
        // 触发组件数据更新
        this.updateComponents(data);
    }
}
4.2.2 MQTT通信

配置参数:

  • MQTT地址:ws://your-server:9001(WebSocket协议)
  • 客户端ID:mqtt_client_${Date.now()}
  • 订阅主题:/data/device
  • QoS等级:1
  • 认证信息:用户名和密码(可选)

消息格式:

{
    "a03": 100,
    "a04": "ON",
    "d3a002": 25.5
}

实现代码:

// assets/js/core/mqttClient.js
class MqttClient {
    constructor(config) {
        this.config = config;
        this.client = null;
        this.reconnectAttempts = 0;
        this.maxReconnectAttempts = 5;
    }

    connect() {
        const options = {
            connectTimeout: 4000,
            clientId: this.config.clientId || `mqtt_client_${Date.now()}`,
            username: this.config.username,
            password: this.config.password,
            clean: true
        };

        this.client = mqtt.connect(this.config.url, options);

        this.client.on('connect', () => {
            console.log('MQTT connected');
            this.reconnectAttempts = 0;
            this.client.subscribe(this.config.topic, { qos: this.config.qos || 1 });
        });

        this.client.on('message', (topic, message) => {
            try {
                const data = JSON.parse(message.toString());
                this.updateStageData(data);
            } catch (error) {
                console.error('MQTT message parse error:', error);
            }
        });

        this.client.on('error', (error) => {
            console.error('MQTT error:', error);
        });

        this.client.on('reconnect', () => {
            console.log('MQTT reconnecting...');
        });

        this.client.on('offline', () => {
            console.log('MQTT offline');
            this.reconnect();
        });
    }

    updateStageData(data) {
        // 更新场景数据
        if (window.stageOper && stageOper.stage) {
            stageOper.stage.attrs.dataKeyArray = {
                ...stageOper.stage.attrs.dataKeyArray,
                ...data
            };
            
            // 触发组件数据更新
            this.updateComponents(data);
        }
    }

    reconnect() {
        if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            setTimeout(() => {
                console.log(`MQTT reconnecting... (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
                this.connect();
            }, 3000 * this.reconnectAttempts);
        }
    }

    disconnect() {
        if (this.client) {
            this.client.end();
        }
    }
}

// 使用示例
const mqttConfig = {
    url: 'ws://your-server:9001',
    topic: '/data/device',
    qos: 1
};

const mqttClient = new MqttClient(mqttConfig);
mqttClient.connect();
4.2.3 HTTP轮询

配置参数:

  • 请求地址:http://your-server:8080/api/data
  • 请求方法:GET/POST
  • 轮询间隔:5秒
  • 请求头:{"Content-Type": "application/json"}
  • 请求参数:根据API要求设置

请求示例:

// assets/js/core/httpClient.js
class HttpClient {
    constructor(config) {
        this.config = config;
        this.pollingInterval = null;
    }

    startPolling() {
        this.pollingInterval = setInterval(async () => {
            try {
                const response = await fetch(this.config.url, {
                    method: this.config.method || 'GET',
                    headers: this.config.headers || {
                        'Content-Type': 'application/json'
                    },
                    body: this.config.method === 'POST' ?
                        JSON.stringify(this.config.body) : null
                });

                if (response.ok) {
                    const data = await response.json();
                    // 处理响应数据
                    if (data.MESSAGETYPE === '01') {
                        this.updateStageData(data.MESSAGECONTENT);
                    } else {
                        this.updateStageData(data.data || data);
                    }
                }
            } catch (error) {
                console.error('HTTP request error:', error);
            }
        }, (this.config.interval || 5) * 1000);
    }

    stopPolling() {
        if (this.pollingInterval) {
            clearInterval(this.pollingInterval);
            this.pollingInterval = null;
        }
    }

    updateStageData(data) {
        // 更新场景数据
        if (window.stageOper && stageOper.stage) {
            stageOper.stage.attrs.dataKeyArray = {
                ...stageOper.stage.attrs.dataKeyArray,
                ...data
            };
            
            // 触发组件数据更新
            this.updateComponents(data);
        }
    }
}

// 使用示例
const httpConfig = {
    url: 'http://your-server:8080/api/data',
    method: 'GET',
    interval: 5
};

const httpClient = new HttpClient(httpConfig);
httpClient.startPolling();

4.3 数据绑定

4.3.1 组件数据绑定

绑定方式:

  1. 单个数据点绑定 - 组件绑定一个数据点
  2. 多个数据点绑定 - 组件绑定多个数据点
  3. 条件绑定 - 根据数据值显示不同状态
  4. 表达式绑定 - 使用表达式计算显示值

绑定配置:

// 组件属性中的数据绑定配置
"dataKey": [
    {
        "key": "device001",     // 数据点ID
        "name": "设备001",      // 显示名称
        "type": "number",       // 数据类型
        "unit": "°C",           // 单位
        "scale": "1",           // 缩放比例
        "condition": "< 30",     // 条件判断
        "expression": "value * 2" // 表达式计算
    }
]

条件配置:

// 条件颜色配置
"whereStatusColorTable": [
    {
        "color": "#52C41A", // 绿色
        "value": "< 30"
    },
    {
        "color": "#FAAD14", // 黄色
        "value": ">= 30 && < 50"
    },
    {
        "color": "#F5222D", // 红色
        "value": ">= 50"
    }
]
4.3.2 数据更新处理

更新流程:

数据接收 → 格式验证 → 数据过滤 → 组件查找 → 属性更新 → 画面重绘

更新逻辑:

// assets/js/core/stageView.js
updateStageData(newData) {
    // 数据验证
    if (!newData || typeof newData !== 'object') {
        return;
    }

    // 遍历场景中的组件
    this.stage.find('Group').forEach(layer => {
        layer.children.forEach(component => {
            const dataKey = component.attrs.dataKey;
            if (dataKey && Array.isArray(dataKey)) {
                dataKey.forEach(binding => {
                    if (newData.hasOwnProperty(binding.key)) {
                        // 更新组件数据
                        this.updateComponentData(component, binding.key, newData[binding.key]);
                    }
                });
            }
        });
    });
}

updateComponentData(component, key, value) {
    // 数据类型转换和格式化
    let displayValue = value;

    // 应用缩放
    if (component.attrs.dataKey) {
        const binding = component.attrs.dataKey.find(b => b.key === key);
        if (binding) {
            // 应用缩放
            if (binding.scale && !isNaN(binding.scale)) {
                displayValue = parseFloat(displayValue) * parseFloat(binding.scale);
            }

            // 应用表达式
            if (binding.expression) {
                try {
                    // 安全地计算表达式
                    const expression = binding.expression.replace(/value/g, displayValue);
                    displayValue = eval(expression);
                } catch (error) {
                    console.error('Expression evaluation error:', error);
                }
            }

            // 格式化数值
            if (typeof displayValue === 'number') {
                displayValue = displayValue.toFixed(1);
            }

            // 添加单位
            if (binding.unit) {
                displayValue = `${displayValue} ${binding.unit}`;
            }
        }
    }

    // 更新组件显示
    if (component.className === 'Text') {
        component.setText(displayValue);
    } else if (component.className === 'Circle') {
        // 处理圆形组件(如指示灯)
        this.updateCircleComponent(component, key, value);
    } else if (component.className === 'Rect') {
        // 处理矩形组件
        this.updateRectComponent(component, key, value);
    }

    // 触发重绘
    component.getLayer().batchDraw();
}

updateCircleComponent(component, key, value) {
    // 根据数据值更新圆形组件的颜色
    if (component.attrs.whereStatusColorTable) {
        const colorTable = component.attrs.whereStatusColorTable;
        let color = component.attrs.fill || '#999999';

        for (const item of colorTable) {
            try {
                // 安全地计算条件
                const condition = item.value.replace(/value/g, value);
                if (eval(condition)) {
                    color = item.color;
                    break;
                }
            } catch (error) {
                console.error('Condition evaluation error:', error);
            }
        }

        component.fill(color);
    }
}
4.3.3 数据绑定最佳实践
  1. 数据点命名规范

    • 使用有意义的名称
    • 统一命名格式(如:设备类型_设备ID_数据点)
    • 避免使用特殊字符
  2. 数据类型处理

    • 明确指定数据类型
    • 处理数据类型转换
    • 处理空值和异常值
  3. 性能优化

    • 减少数据点数量
    • 合理设置数据更新频率
    • 使用批量更新
  4. 错误处理

    • 处理数据解析错误
    • 处理网络连接错误
    • 提供默认值
  5. 安全性

    • 验证数据来源
    • 防止注入攻击
    • 加密敏感数据

5. 用户认证

5.1 认证方式

5.1.1 Token认证

认证流程:

用户登录 → 获取Token → Token传递 → 接口认证 → 访问授权

Token格式:

  • 推荐使用JWT格式
  • 包含用户ID、过期时间等信息
  • 使用HTTPS传输保证安全
  • Token有效期通常为24小时

Token传递方式:

  1. URL参数传递(监控页面)

    view.html?stageId=xxx&token=your_token&userId=user001
    
  2. 请求头传递(API调用)

    Authorization: Bearer {token}
    
  3. Cookie传递(浏览器环境)

    • 自动携带,无需手动处理
    • 支持跨域设置
5.1.2 会话管理

会话配置:

// config/apiClient.js
var SystemConfig = {
    // Token配置
    token: {
        // Token过期时间(毫秒)
        expireTime: 24 * 60 * 60 * 1000, // 24小时
        // 自动刷新Token
        autoRefresh: true,
        // Token刷新阈值(剩余时间小于此值时刷新)
        refreshThreshold: 30 * 60 * 1000 // 30分钟
    }
};

Token管理工具类:

class TokenManager {
    constructor() {
        this.token = null;
        this.refreshTimer = null;
        this.storageKey = 'ricon_auth_token';
    }

    // 初始化Token
    init() {
        // 从本地存储加载Token
        this.token = localStorage.getItem(this.storageKey);
        if (this.token) {
            this.scheduleTokenRefresh();
        }
    }

    // 设置Token
    setToken(token) {
        this.token = token;
        // 存储到本地
        localStorage.setItem(this.storageKey, token);
        // 安排Token刷新
        this.scheduleTokenRefresh();
    }

    // 获取Token
    getToken() {
        return this.token;
    }

    // 清除Token
    clearToken() {
        this.token = null;
        localStorage.removeItem(this.storageKey);
        if (this.refreshTimer) {
            clearTimeout(this.refreshTimer);
            this.refreshTimer = null;
        }
    }

    // 安排Token刷新
    scheduleTokenRefresh() {
        if (this.refreshTimer) {
            clearTimeout(this.refreshTimer);
        }

        // 解析Token获取过期时间
        const decodedToken = this.decodeToken(this.token);
        if (!decodedToken.exp) {
            return;
        }

        const expireTime = decodedToken.exp * 1000; // 转换为毫秒
        const currentTime = Date.now();
        const timeUntilExpiry = expireTime - currentTime;

        // 在过期前30分钟刷新Token
        const refreshTime = timeUntilExpiry - (30 * 60 * 1000);

        if (refreshTime > 0) {
            this.refreshTimer = setTimeout(() => {
                this.refreshToken();
            }, refreshTime);
        }
    }

    // 刷新Token
    async refreshToken() {
        try {
            const response = await fetch('/api/auth/refresh', {
                method: 'POST',
                headers: {
                    'Authorization': `Bearer ${this.token}`
                }
            });

            const data = await response.json();
            if (data.code === 200) {
                this.setToken(data.token);
                return true;
            }
            return false;
        } catch (error) {
            console.error('Token refresh failed:', error);
            // 重定向到登录页面
            window.location.href = '/login';
            return false;
        }
    }

    // 解码Token
    decodeToken(token) {
        try {
            const base64Url = token.split('.')[1];
            const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
            return JSON.parse(atob(base64));
        } catch (error) {
            return {};
        }
    }

    // 检查Token是否有效
    isTokenValid() {
        if (!this.token) {
            return false;
        }

        const decodedToken = this.decodeToken(this.token);
        if (!decodedToken.exp) {
            return false;
        }

        const currentTime = Date.now() / 1000;
        return decodedToken.exp > currentTime;
    }
}

// 使用示例
const tokenManager = new TokenManager();
tokenManager.init();

// 获取Token用于API调用
function getAuthToken() {
    return tokenManager.getToken();
}

5.2 权限控制

5.2.1 功能权限

权限级别:

  • 查看权限 - 只能查看监控画面
  • 编辑权限 - 可以编辑场景和组件
  • 管理权限 - 可以管理用户和系统配置

权限验证:

// 权限检查中间件
function checkPermission(requiredPermission) {
    return function(req, res, next) {
        const userPermissions = req.user.permissions || [];

        if (userPermissions.includes(requiredPermission)) {
            next();
        } else {
            res.status(403).json({
                code: 403,
                message: '权限不足'
            });
        }
    };
}

// 使用示例
app.post('/api/saveStage', checkPermission('edit_stage'), saveStageHandler);
app.get('/api/selectStageById', checkPermission('view_stage'), loadStageHandler);
5.2.2 数据权限

数据隔离:

  • 用户只能访问自己有权限的场景
  • 数据按用户或角色进行隔离
  • 支持数据级别的权限控制

实现示例:

// 数据权限过滤
function filterUserData(data, userId) {
    return data.filter(item => {
        // 检查用户是否有权限访问该数据
        return item.userId === userId || item.sharedUsers.includes(userId);
    });
}

// 在API接口中应用
app.get('/api/stageList', async (req, res) => {
    const allStages = await Stage.findAll();
    const userStages = filterUserData(allStages, req.user.id);

    res.json({
        code: 200,
        data: userStages
    });
});
5.2.3 前端权限控制

实现示例:

// 前端权限检查
class PermissionManager {
    constructor() {
        this.permissions = [];
    }

    // 初始化权限
    init(permissions) {
        this.permissions = permissions || [];
    }

    // 检查权限
    hasPermission(permission) {
        return this.permissions.includes(permission);
    }

    // 检查多个权限
    hasAnyPermission(permissions) {
        return permissions.some(permission => this.hasPermission(permission));
    }

    // 检查所有权限
    hasAllPermissions(permissions) {
        return permissions.every(permission => this.hasPermission(permission));
    }

    // 渲染权限相关UI
    renderPermissionBasedUI() {
        // 隐藏无权限的按钮
        if (!this.hasPermission('edit_stage')) {
            document.querySelector('.save-button').style.display = 'none';
        }

        // 禁用无权限的操作
        if (!this.hasPermission('delete_stage')) {
            document.querySelectorAll('.delete-button').forEach(button => {
                button.disabled = true;
                button.title = '无权限操作';
            });
        }
    }
}

// 使用示例
const permissionManager = new PermissionManager();

// 从用户信息初始化权限
async function initPermissions() {
    try {
        const response = await fetch('/api/user/permissions', {
            headers: {
                'Authorization': `Bearer ${getAuthToken()}`
            }
        });

        const data = await response.json();
        if (data.code === 200) {
            permissionManager.init(data.data);
            permissionManager.renderPermissionBasedUI();
        }
    } catch (error) {
        console.error('Failed to load permissions:', error);
    }
}

// 页面加载后初始化权限
window.addEventListener('DOMContentLoaded', initPermissions);

5.3 单点登录集成

5.3.1 SAML集成

配置参数:

const samlConfig = {
    // SAML Identity Provider配置
    idp: {
        entryPoint: 'https://idp.example.com/saml2/sso',
        issuer: 'https://idp.example.com',
        cert: 'MIID...' // IdP证书
    },

    // Service Provider配置
    sp: {
        entityId: 'ricon-sso',
        assertEndpoint: 'https://your-server/saml/assert',
        privateKey: '...', // SP私钥
        cert: '...' // SP证书
    }
};

实现示例:

// SAML登录
function initiateSamlLogin() {
    // 重定向到IdP登录页面
    window.location.href = '/saml/login';
}

// SAML回调处理
function handleSamlCallback() {
    // 从URL获取SAML响应
    const samlResponse = new URLSearchParams(window.location.search).get('SAMLResponse');
    
    if (samlResponse) {
        // 验证SAML响应并获取Token
        fetch('/api/saml/assert', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            body: new URLSearchParams({
                SAMLResponse: samlResponse
            })
        })
        .then(response => response.json())
        .then(data => {
            if (data.code === 200) {
                // 存储Token
                tokenManager.setToken(data.token);
                // 重定向到首页
                window.location.href = '/';
            }
        });
    }
}
5.3.2 OAuth 2.0集成

授权码模式:

// OAuth配置
const oauthConfig = {
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
    authorizationURL: 'https://auth-server.com/oauth/authorize',
    tokenURL: 'https://auth-server.com/oauth/token',
    userInfoURL: 'https://auth-server.com/oauth/userinfo',
    redirectURI: 'https://your-server/auth/callback'
};

// 初始化OAuth登录
function initiateOAuthLogin() {
    const authUrl = `${oauthConfig.authorizationURL}?` +
        `client_id=${oauthConfig.clientId}&` +
        `redirect_uri=${encodeURIComponent(oauthConfig.redirectURI)}&` +
        `response_type=code&` +
        `scope=openid profile email`;

    window.location.href = authUrl;
}

// 处理OAuth回调
async function handleOAuthCallback() {
    const code = new URLSearchParams(window.location.search).get('code');
    
    if (code) {
        try {
            // 获取Access Token
            const tokenResponse = await fetch(oauthConfig.tokenURL, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: new URLSearchParams({
                    grant_type: 'authorization_code',
                    code: code,
                    redirect_uri: oauthConfig.redirectURI,
                    client_id: oauthConfig.clientId,
                    client_secret: oauthConfig.clientSecret
                })
            });

            const tokenData = await tokenResponse.json();
            if (tokenData.access_token) {
                // 获取用户信息
                const userResponse = await fetch(oauthConfig.userInfoURL, {
                    headers: {
                        'Authorization': `Bearer ${tokenData.access_token}`
                    }
                });

                const userData = await userResponse.json();
                
                // 登录到Ricon系统
                const loginResponse = await fetch('/api/auth/oauthLogin', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        provider: 'oauth',
                        accessToken: tokenData.access_token,
                        userInfo: userData
                    })
                });

                const loginData = await loginResponse.json();
                if (loginData.code === 200) {
                    // 存储Token
                    tokenManager.setToken(loginData.token);
                    // 重定向到首页
                    window.location.href = '/';
                }
            }
        } catch (error) {
            console.error('OAuth login failed:', error);
        }
    }
}
5.3.3 集成第三方认证

微信认证:

// 微信登录
function initiateWechatLogin() {
    const wechatAuthUrl = `https://open.weixin.qq.com/connect/qrconnect?` +
        `appid=YOUR_APP_ID&` +
        `redirect_uri=${encodeURIComponent('https://your-server/auth/wechat/callback')}&` +
        `response_type=code&` +
        `scope=snsapi_login&` +
        `state=ricon_login`;

    // 打开微信登录二维码
    window.open(wechatAuthUrl, 'wechat_login', 'width=800,height=600');
}

// 处理微信回调
function handleWechatCallback(code) {
    // 登录到Ricon系统
    fetch('/api/auth/wechatLogin', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({ code: code })
    })
    .then(response => response.json())
    .then(data => {
        if (data.code === 200) {
            // 存储Token
            tokenManager.setToken(data.token);
            // 重定向到首页
            window.location.href = '/';
        }
    });
}

企业微信认证:

// 企业微信登录
function initiateWecomLogin() {
    const wecomAuthUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?` +
        `appid=YOUR_CORP_ID&` +
        `redirect_uri=${encodeURIComponent('https://your-server/auth/wecom/callback')}&` +
        `response_type=code&` +
        `scope=snsapi_base&` +
        `state=ricon_login`;

    window.location.href = wecomAuthUrl;
}

// 处理企业微信回调
async function handleWecomCallback() {
    const code = new URLSearchParams(window.location.search).get('code');
    
    if (code) {
        try {
            // 登录到Ricon系统
            const response = await fetch('/api/auth/wecomLogin', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ code: code })
            });

            const data = await response.json();
            if (data.code === 200) {
                // 存储Token
                tokenManager.setToken(data.token);
                // 重定向到首页
                window.location.href = '/';
            }
        } catch (error) {
            console.error('WeCom login failed:', error);
        }
    }
}

7. 部署配置

7.1 生产环境部署

7.1.1 服务器配置

推荐配置:

# Nginx配置示例
upstream ricon_backend {
    server 127.0.0.1:8080;
    keepalive 32;
}

server {
    listen 80;
    server_name your-domain.com;
    root /var/www/html/ricon;
    index editor.html;

    # Gzip压缩
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;

    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary "Accept-Encoding";
    }

    # HTML文件不缓存
    location ~* \.html$ {
        expires -1;
        add_header Cache-Control "no-store, no-cache, must-revalidate";
    }

    # API代理
    location /api/ {
        proxy_pass http://ricon_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }

    # WebSocket代理
    location /ws/ {
        proxy_pass http://ricon_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # 主页面路由
    location / {
        try_files $uri $uri/ /editor.html;
    }
}
7.1.2 安全配置

HTTPS配置:

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL证书
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/private.key;

    # SSL配置
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # HSTS
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";

    # 其他配置同上
    ...
}

安全头配置:

# 安全相关HTTP头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; frame-ancestors 'self';" always;

7.2 性能优化

7.2.1 缓存策略

浏览器缓存:

# 静态资源长期缓存
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";

    # 使用文件哈希作为缓存键
    add_header Vary "Accept-Encoding";

    # Gzip压缩
    gzip_static on;
}

服务器缓存:

// Redis缓存配置
const redisConfig = {
    host: 'localhost',
    port: 6379,
    password: 'your-password',
    db: 0
};

// 缓存场景数据
async function getCachedStage(stageId) {
    const cacheKey = `stage:${stageId}`;

    // 尝试从缓存获取
    let stageData = await redisClient.get(cacheKey);
    if (stageData) {
        return JSON.parse(stageData);
    }

    // 从数据库获取
    stageData = await Stage.findById(stageId);

    // 缓存到Redis(1小时过期)
    await redisClient.setex(cacheKey, 3600, JSON.stringify(stageData));

    return stageData;
}
7.2.2 负载均衡

Nginx负载均衡:

upstream ricon_cluster {
    # 轮询算法
    least_conn;

    # 后端服务器
    server 192.168.1.10:8080 weight=3 max_fails=3 fail_timeout=30s;
    server 192.168.1.11:8080 weight=2 max_fails=3 fail_timeout=30s;
    server 192.168.1.12:8080 weight=1 max_fails=3 fail_timeout=30s backup;

    # 健康检查
    check interval=3000 rise=2 fall=5 timeout=1000 type=http;
    check_http_send "HEAD /health HTTP/1.0\r\n\r\n";
    check_http_expect_alive http_2xx http_3xx;
}

会话保持:

upstream ricon_cluster {
    ip_hash;  # IP哈希保持会话
    server 192.168.1.10:8080;
    server 192.168.1.11:8080;
}

7.3 监控告警

7.3.1 系统监控

监控指标:

  • CPU使用率
  • 内存使用率
  • 磁盘使用率
  • 网络流量
  • 请求响应时间
  • 错误率

监控配置:

// Prometheus监控配置
const client = require('prom-client');

// 创建指标
const httpRequestDuration = new client.Histogram({
    name: 'http_request_duration_ms',
    help: 'Duration of HTTP requests in ms',
    labelNames: ['method', 'route', 'status_code'],
    buckets: [10, 50, 100
  
  

# 立即体验

👉 演示地址: [http://www.ricon.cloud:81](http://www.ricon.cloud:81)
🌐 官网地址: [http://www.ricon.cloud](http://www.ricon.cloud)

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐