Skip to content

消防管家 OpenAPI 文档 V1.0

1. 概述

本文档目的是方便第三方应用系统对接消防管家,包括但不限于将事件接入到消防管家或者从消防管家订阅事件。

2. 前置条件

向消防管家(简称 IOC 工作台,IOC)申请应用 AK、SK,用来鉴定请求合法性。

2.1 对接前准备工作

  • 找交付经理申请应用的 AK、SK:提供第三方系统名称
  • 数据接入:第三方系统在边端,和消防管家在同一个局域网内,可通过 HTTP 或 RabbitMQ 方式接入;如果第三方应用在云端,则需要通过云端 RabbitMQ 方式接入,需要提供云端 RabbitMQ 信息
  • 数据订阅:如需从消防管家订阅数据,需要提供 HTTP/HTTPS 推送地址或者 RabbitMQ 信息(IP、Port、VirtualHost、User、Password)

数据订阅以 HTTP/HTTPS 订阅的,第三方系统需要对推送请求鉴权,鉴权机制请查阅 OpenAPI 通用鉴权机制

2.2 OpenAPI 通用鉴权机制

请求接口时,携带三个请求头:AuthorizationDateContent-Type

  • Authorization:格式为 appkey:sign,将 appKey 和 sign 签名用冒号连接,appkey 为申请应用 AK,sign 为通过下文算法计算出来
  • Date:为请求时的时间,格式为:EEE, dd MMM yyyy HH:mm:ss zzz,如 Wed, 19 Nov 2025 02:41:20 GMT
  • Content-Type:固定为 application/json

注:对于 Content-Type 的值,在请求头中传输的值可能后面会多一个 ;charset=utf-8。第三方服务最好将 Content-Type 的值做截取操作,只取 ; 前的字符串,避免校验不通过。

2.2.1 sign 签名算法

  1. 拼接字符串:将 HTTP 方法、Content-Type(固定为 application/json)、日期字符串和路径资源按以下格式拼接:

    HTTP方法\nContent-Type\n日期字符串\n路径资源
  2. HmacSHA1 加密:使用 appSecret 对拼接后的字符串进行 HmacSHA1 加密

  3. Base64 编码:对加密结果进行 Base64 编码

  4. 截取签名:从 Base64 编码结果中截取第5位开始的10个字符作为签名

Java 代码参考:详见附录 7.3 附录三:鉴权相关参考代码


3. 名称解释

  • 事件:泛指系统或服务中发生的任何状态变化,可能是正常的(如服务重启),也可能是异常的(如服务器宕机)
  • 告警:是事件的一种特殊类型,当系统检测到异常(如 CPU 使用率超过阈值)时,会主动发出通知,要求立即处理

本文中的事件通常是指告警。


4. 系统交互图介绍


5. 接口文档

5.1 查询事件类型列表

接口定义和请求方式

  • 接口地址:/api/open/queryIncidentTypeList
  • 请求方式:POST

请求参数

字段名称数据类型备注
priority事件等级Integer0:提示 1:一般 2:重要 3:紧急
pageSize页大小Integer
pageNum页码Integer
scene场景Integer1-报事事件(YQBSFW),2-设备事件(YQSBSS),3-AI事件(AIYJ),4-业务事件(YWSJ),5-消防事件(XFSJ)

返回参数

字段名称数据类型备注
code状态码String200:成功
isOK是否成功Booleantrue:成功; false:失败
data承载数据Object

data 中包含的对象信息

字段名称数据类型备注
id事件类型idString字符串长度为64个字符以下
scene场景编码Integer1-报事事件,2-设备事件,3-AI事件,4-业务事件,5-消防事件
sceneStr场景String场景编码对应的场景名称
originParentCode原始父编码String字符串长度为128个字符以下
originCode原始编码String字符串长度为128个字符以下
parentName父名称String字符串长度为64个字符以下
name名称String字符串长度为64个字符以下
code唯一标识String字符串长度为128个字符以下
businessName业务事件类型String字符串长度为64个字符以下
businessKey对应事件类型业务标识String字符串长度为128个字符以下
thresholdDuration分钟Integer
thresholdTimes次数Integer
status是否启用Integer1-是 0-否
autoReport是否自动派单标识Integer1-是 0-否
autoReportStr是否自动派单String
aiCheckai验单标识Integer0-否,1-是
aiCheckStrai验单String
dispatchType调度类型StringworkOrder-工单, robot-移动巡检(可多选,多个使用','拼接)
autoDistributeTo自动派单对象StringworkOrder-工单, robot-移动巡检
deleted是否删除Integer0-否,1-是
priority事件等级标识Integer0提示 1一般,2重要,3紧急
priorityStr事件等级String事件等级标识对应的名称
createTime创建时间String
updateTime更新时间String
voiceScene提示声音类型Integer事件在消防管家上报时,系统的提示音类型
typeOriginCode一级事件类型codeString字符串长度为64个字符以下
typeParentName一级事件类型nameString字符串长度为64个字符以下
isWindow是否弹窗Integer1-是 0-否

请求示例

(略)

响应报文示例

json
{
  "isOk": true,
  "msg": null,
  "cause": null,
  "code": "200",
  "data": {
    "records": [
      {
        "id": "-7661345827197248065",
        "scene": 3,
        "sceneStr": "AI事件",
        "originParentCode": "AISB_RYSD",
        "originCode": "AISB_RYSD",
        "parentName": "AI识别-人员摔倒",
        "name": "AI识别-人员摔倒",
        "code": "AISB_RYSD",
        "businessName": "AI识别-人员摔倒",
        "businessKey": "AISB_RYSD",
        "thresholdDuration": 0,
        "thresholdTimes": 0,
        "status": 0,
        "autoReport": 0,
        "autoReportStr": "否",
        "aiCheck": 1,
        "aiCheckStr": "是",
        "dispatchType": "workOrder",
        "autoDistributeTo": "workOrder",
        "deleted": 0,
        "priority": 2,
        "priorityStr": "重要",
        "createTime": "2025-11-18 14:36:02",
        "updateTime": "2025-11-18 15:23:09",
        "voiceScene": 1,
        "typeOriginCode": "AISB_RYSD",
        "typeParentName": "AI识别-人员摔倒",
        "isWindow": 0
      }
    ],
    "total": 79,
    "size": 10,
    "current": 1,
    "orders": [],
    "optimizeCountSql": true,
    "searchCount": true,
    "countId": null,
    "maxLimit": null,
    "pages": 8
  }
}

5.2 查询事件类型详情

接口定义和请求方式

  • 接口地址:/api/open/queryIncidentTypeDetail
  • 请求方式:POST

请求参数

字段名称数据类型备注
secondTypeCode二级事件编码String

返回参数

与 5.1 返回参数中 data 对象结构一致。

请求示例

json
{
  "secondTypeCode": "AISB_RYSD"
}

响应报文示例

json
{
  "isOk": true,
  "msg": null,
  "cause": null,
  "code": "200",
  "data": {
    "id": "-7661345827197248065",
    "scene": 3,
    "sceneStr": "AI事件",
    "originParentCode": "AISB_RYSD",
    "originCode": "AISB_RYSD",
    "parentName": "AI识别-人员摔倒",
    "name": "AI识别-人员摔倒",
    "code": "AISB_RYSD",
    "businessName": "AI识别-人员摔倒",
    "businessKey": "AISB_RYSD",
    "thresholdDuration": 0,
    "thresholdTimes": 0,
    "status": 0,
    "autoReport": 0,
    "autoReportStr": "否",
    "aiCheck": 1,
    "aiCheckStr": "是",
    "dispatchType": "workOrder",
    "autoDistributeTo": "workOrder",
    "deleted": 0,
    "priority": 2,
    "priorityStr": "重要",
    "createTime": "2025-11-18 14:36:02",
    "updateTime": "2025-11-18 15:23:09",
    "voiceScene": 1,
    "typeOriginCode": "AISB_RYSD",
    "typeParentName": "AI识别-人员摔倒",
    "isWindow": 0
  }
}

5.3 分页查询事件列表

接口定义和请求方式

  • 接口地址:/api/open/queryIncidentList
  • 请求方式:POST

请求参数

字段名称数据类型备注
pageNum页码Integer
pageSize每页数据量Integer
code事件编号String字符串长度大概为64个字符以下
typeId事件类型等级idList<String>
incidentType事件状态String0:待处理 1:已结束 2:处理中
startTime创建时间(开始)String
endTime创建时间(结束)String
eventStartTime关闭事件(开始)String
eventEndTime关闭时间(结束)String

返回参数

字段名称数据类型备注
code状态码String
isOk是否成功Booleantrue:成功;false:失败
data承载数据Object

data 中包含的对象信息

字段名称数据类型备注
current当前页码Integer
size当前页码数据量Integer
pages页码总数Integer
total数据量总数Integer
records数据详情List<IncidentDto>

IncidentDto 中包含的事件字段

字段名称数据类型备注
code事件编号String字符串长度在64个字符以下
originIncidentCode第三方事件编号String
name事件名称String
range项目String
position位置String
typeKey事件类型String事件对应的二级事件编码,字符串长度在128个字符以下
typeName事件类型名称String字符串长度在64个字符以下
incidentType事件状态String"0"--待处理;"1"--已结束;"2"--处理中
createTime创建时间String
eventEndTime关闭时间String
alarmDuration持续时间String已换算成X天X小时X分X秒

请求示例

json
{
  "id": "",
  "code": "",
  "typeId": ["1980462046801563649"],
  "incidentType": 1,
  "queryType": 1,
  "pageNum": 1,
  "pageSize": 15,
  "startTime": "2025-12-03 00:00:00",
  "endTime": "2025-12-05 23:59:59",
  "eventStartTime": "",
  "eventEndTime": ""
}

响应报文示例

(略,详见原文档)


5.4 查询事件详情

接口定义和请求方式

  • 接口地址:/api/open/queryIncidentDetail
  • 请求方式:POST

请求参数

字段名称数据类型备注
id事件idString

请求示例

json
{
  "id": "1995297557426274304"
}

返回参数

字段名称数据类型备注
incidentDetail事件详情信息Object
logOperationList调度记录List<LogOperationDto>
records事件备注List<TempRecordModel>

incidentDetail 包含字段

字段名称数据类型备注
id事件idString
name事件名称String
description事件描述String
source事件来源String
deviceName报事设备名称String
position位置String
createTime上报时间String
pictureUrl图片StringJSON数组格式字符串

LogOperationDto 包含字段

字段名称数据类型备注
eventId事件编号String
workOrderNo工单编号String
eventStatus事件状态String
eventName事件名称String
eventClosed工单是否关闭Booleantrue:关闭;false:未关闭
eventHandler工单处理人String
eventHandlerMobile工单处理人手机号String
eventTime工单处理时间String
eventType事件类型String事件sj 任务rw
etlCode唯一编号String来源-工单编号-项目编号
source来源String
content节点描述String
sipCodesip号String
online是否在线String0:不在线;1:在线
reasonOptionAI事件关闭-原因选项String风险已解除;识别错误
reasonDescriptionAI事件关闭-原因手动输入的描述String
robotId机器人idString
robotCameraId机器人绑定摄像头idString

TempRecordModel 包含字段

字段名称数据类型备注
id事件idString
content内容String
operator操作人String
phone操作人手机号String
createTime创建时间String

6. 事件订阅

6.1 增量事件订阅

实时推送事件,事件状态变化,都进行事件推送。包括待处理,进行中,关闭三种状态。

支持 HTTP 和 RabbitMQ 方式推送,需要应用方提供 HTTP 接口地址或 RabbitMQ 配置信息。

RabbitMQ 方式订阅:RabbitMQ 的配置规则参考 7.2 附录二:RabbitMQ配置相关规则

HTTP 方式订阅

  • HTTP 接口地址:第三方应用提供
  • 请求方式:POST
  • 请求参数:无

返回参数

字段名称数据类型备注
objectType订阅类型String1-事件;此处固定为1
dataType数据类型String1-增量数据;2-全量数据
data数据详情List<Object>增量事件订阅时此处列表为1个元素

data 中包含的对象信息

字段名称数据类型备注
code事件编码String长度不超过64个字符
name事件名称String长度不超过32个字符
originIncidentCode第三方事件编码String长度不超过64个字符
projectCode项目编码String长度不超过32个字符
projectName项目名称String长度不超过32个字符
position位置String
pictureUrl事件图片String图片url地址
typeKey事件类型(二级事件编码)String长度不超过64个字符
typeName事件类型名称(二级事件名称)String长度不超过32个字符
incidentType事件状态String0:待处理 1:已完成 2:处理中
createTime事件创建时间String
eventEndTime事件关闭时间String
alarmDuration事件持续时间String已换算成X天X小时X分钟X秒
alarmDurationLong事件持续时间Long单位:秒
deviceId报事设备编码String长度不超过64个字符
deviceName报事设备名称String长度不超过32个字符
thirdCode第三方设备编码String长度不超过64个字符
thirdName第三方设备名称String长度不超过64个字符
workOrderCode工单编码String长度不超过64个字符
cameraIdList关联摄像头编码List<String>

请求示例

响应报文示例:

json
{
  "objectType": "1",
  "dataType": "1",
  "data": [
    {
      "code": "20251106AIYJ000527",
      "originIncidentCode": "738093083545669",
      "name": "周界入侵",
      "projectCode": "P0000",
      "projectName": "测试项目",
      "position": "测试位置",
      "pictureUrl": "[\"https://spfile-uat.icloudcity.com/spioc/202511/18818ffed6116b378/1430a7687bbad2b98cd.jpg\"]",
      "typeKey": "AIYJ_XQZJRQ",
      "typeName": "周界入侵",
      "incidentType": "1",
      "createTime": "2025-11-06 10:11:00",
      "eventEndTime": "2025-11-06 10:13:16",
      "alarmDuration": "0天00小时2分钟16秒",
      "alarmDurationLong": 136,
      "deviceId": "IOTPR00001124200",
      "deviceName": "A-TEST-ANGG-2001",
      "thirdCode": "7321429522836889600",
      "thirdName": "A-TEST-ANGG-2001"
    }
  ]
}

7. 附录

7.1 附录一:二级事件类型编码和名称对应关系

AI 事件类型以及编码

二级事件类型名称二级事件类型编码
周界入侵AIYJ_XQZJRQ
机动车违停AIYJ_JDCWT
车辆拥堵AIYJ_JDCDS
消防通道占用AIYJ_XFTDDFZW
人员拥堵AIYJ_CRKYD
垃圾桶满溢AIYJ_LJTMY
桶边垃圾AIYJ_TBLJ
公共区域垃圾AIYJ_GGQYLJ
员工离岗AIYJ_YGLG
人员翻越AIYJ_RYFY
室内明火检测AIYJ_SNMHJC
烟雾检测AIYJ_YWJC
专用车位占用AIYJ_ZYCWZY
消防设备异常AIYJ_MHQQS
抽烟识别AIYJ_CYSB
人员摔倒AIYJ_RYSD
人员逗留AIYJ_RYDL
非机动车违停AIYJ_FJDCWT
机动车滞留AIYJ_JDCZL
玩手机识别AIYJ_WSJSB
横幅识别AIYJ_HFSB
散落垃圾AIYJ_SLLJ

7.2 附录二:RabbitMQ 配置相关规则

事件订阅和事件类型订阅

RabbitMQ 配置规则建议:

  • exchange 类型:Topic 类型
  • exchange 命名规则ioc_{appId}
  • routingkey 命名规则ioc_incident_out.${projectCode}
  • queue 命名规则建议ioc_incident_queue_out_${appId}.${projectCode}

注:对于 queue 的命名可根据实际情况是否需要拼接项目编码,由第三方系统自己创建,并绑定 exchange。


7.3 附录三:鉴权相关参考代码

Java 代码参考

java
package com.onewo.openapi.auth;

import com.onewo.spsms.management.util.OkHttpUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

/**
 * @author yangming
 */
@Slf4j
public class OpenApiClientAuth {

    protected OpenApiClientAuth() {
    }

    public static final String AUTHORIZATION = "Authorization";
    public static final String CONTENT_TYPE = "Content-Type";
    public static final String DATE = "Date";
    public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter
            .ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.ENGLISH);

    /**
     * @param httpMethod  请求方式 GET,POST,PUT。。。
     * @param url         http://baidu.com/xxx/xx?aa=11
     * @param appKey      123123
     * @param appSecret   sdfdsf
     * @param contentType 请求类型
     * @return 返回授权说需要的Header参数
     */
    @SneakyThrows
    public static Map<String, String> getSignHeaders(final String httpMethod, final String url,
                                                      final String appKey, final String appSecret,
                                                      final String contentType) {
        final String pathResource = OpenApiClientAuth.getPathResource(url);
        final String contentTypeValue = contentType != null ? contentType : "application/json";
        final String dateValue = DATE_TIME_FORMATTER.format(ZonedDateTime.now(ZoneId.of("GMT")));
        final String sign = OpenApiClientAuth
                .calculateSing(httpMethod, contentTypeValue, dateValue, pathResource, appSecret);
        final String authorizationString = appKey + ":" + sign;
        final Map<String, String> authHeaders = new HashMap<>(4);
        authHeaders.put(OpenApiClientAuth.DATE, dateValue);
        authHeaders.put(OpenApiClientAuth.AUTHORIZATION, authorizationString);
        authHeaders.put(OpenApiClientAuth.CONTENT_TYPE, contentTypeValue);
        return authHeaders;
    }

    protected static String getPathResource(final String url) {
        final String substring = url.substring(url.indexOf("://") + 3);
        return substring.substring(substring.indexOf("/"));
    }

    protected static String calculateSing(final String httpMethodString,
                                          final String contentTypeValue, final String dateValue, final String pathResource,
                                          final String appSecret) throws InvalidKeyException, NoSuchAlgorithmException {
        final String signToString = OpenApiClientAuth
                .builderStringToSign(httpMethodString, contentTypeValue, dateValue, pathResource);
        final String base64HashString = HMAC.hmacSha1Encrypt(signToString, appSecret);
        final String sign = base64HashString.substring(5, 15);
        return sign;
    }

    protected static String builderStringToSign(final String method,
                                                final String contentTypeValue, final String dateValue, final String pathResource) {
        return method
                + "\n"
                + OpenApiClientAuth.handleNullString(contentTypeValue)
                + "\n"
                + OpenApiClientAuth.handleNullString(dateValue)
                + "\n"
                + pathResource;
    }

    protected static String handleNullString(final String str) {
        return !StringUtils.hasText(str) ? "" : str;
    }

    protected static class HMAC {
        private HMAC() {
        }

        private static final String KEY_MAC_SHA1 = "HmacSHA1";

        public static String hmacSha1Encrypt(final String encryptText, final String encryptKey)
                throws NoSuchAlgorithmException, InvalidKeyException {
            final byte[] text = encryptText.getBytes(StandardCharsets.UTF_8);
            final byte[] keyData = encryptKey.getBytes(StandardCharsets.UTF_8);
            final SecretKeySpec secretKey = new SecretKeySpec(keyData, HMAC.KEY_MAC_SHA1);
            final Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
            return new String(Base64.getEncoder().encode(mac.doFinal(text)),
                    StandardCharsets.UTF_8);
        }
    }

    /**
     * get请求
     */
    public static String openApiClientGet(String url, String body, String clientId, String clientSecret) {
        Map<String, String> signHeaders = getSignHeaders(HttpMethod.GET.toString(), url, clientId, clientSecret, MediaType.APPLICATION_JSON_VALUE);
        return OkHttpUtil.get(url, signHeaders, String.class);
    }

    /**
     * post请求
     */
    public static String openApiClientPost(String url, String body, String clientId, String clientSecret) {
        Map<String, String> signHeaders = getSignHeaders(HttpMethod.POST.toString(), url, clientId, clientSecret, MediaType.APPLICATION_JSON_VALUE);
        return OkHttpUtil.post(url, body, signHeaders, String.class);
    }
}

请求示例

java
public static void main(String[] args) {
    String getUrl = "http://替换为实际的IP:30692/api/open/testGet?queryParam=GET参数";
    String postUrl = "http://替换为实际的IP:30692/api/open/testPost";
    String appkey = "替换为实际的appKey";
    String appSecret = "替换为实际的appSecret";

    // 无body示例
    String result1 = OpenApiClientAuth.openApiClientGet(getUrl, null, appkey, appSecret);
    log.info("OpenApi-test GET:{}", result1);

    String result2 = OpenApiClientAuth.openApiClientPost(postUrl, "{\"name\":\"POST参数\"}", appkey, appSecret);
    log.info("OpenApi-test POST:{}", result2);
}