瀏覽代碼

init alarmenginestarter

lixing 3 年之前
父節點
當前提交
fb5c6a0ded
共有 15 個文件被更改,包括 1271 次插入112 次删除
  1. 5 9
      AlarmDataStarter/pom.xml
  2. 2 2
      AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/feign/client/AlarmClient.java
  3. 0 70
      AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/jms/JmsConfig.java
  4. 0 18
      AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/jms/model/OrderStateMessage.java
  5. 2 2
      AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/service/AlarmConfigServiceImpl.java
  6. 3 3
      AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/utils/DmpResultUtil.java
  7. 3 8
      AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/utils/Obj2JSONObject.java
  8. 64 0
      AlarmEngineStarter/pom.xml
  9. 143 0
      AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/jms/JmsConfig.java
  10. 55 0
      AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/jms/model/DmpMessage.java
  11. 78 0
      AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/netty/NettyAlarmMessage.java
  12. 406 0
      AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/netty/NettyAlarmMsgBaseHandler.java
  13. 105 0
      AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/netty/NettyAlarmServer.java
  14. 131 0
      AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/service/BaseService.java
  15. 274 0
      AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/service/NettyAlarmService.java

+ 5 - 9
AlarmDataStarter/pom.xml

@@ -55,16 +55,12 @@
             <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
             <version>2.2.1.RELEASE</version>
         </dependency>
-        <!--rabbitmq -->
+
+        <!-- fastjson -->
         <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-starter-amqp</artifactId>
-            <exclusions>
-                <exclusion>
-                    <groupId>org.springframework.boot</groupId>
-                    <artifactId>spring-boot-starter-logging</artifactId>
-                </exclusion>
-            </exclusions>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.71</version>
         </dependency>
     </dependencies>
 </project>

+ 2 - 2
AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/feign/client/AlarmClient.java

@@ -1,11 +1,11 @@
 package com.persagy.apm.energyalarmstarter.alarmdata.feign.client;
 
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.persagy.apm.energyalarmstarter.alarmdata.constant.RequestUrlConstant;
 import com.persagy.apm.energyalarmstarter.alarmdata.feign.AlarmUrlParam;
 import com.persagy.apm.energyalarmstarter.alarmdata.feign.DmpResult;
 import com.persagy.apm.energyalarmstarter.alarmdata.feign.fallback.AlarmClientFallbackFactory;
-import org.springframework.boot.configurationprocessor.json.JSONArray;
-import org.springframework.boot.configurationprocessor.json.JSONObject;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.cloud.openfeign.SpringQueryMap;
 import org.springframework.web.bind.annotation.PostMapping;

+ 0 - 70
AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/jms/JmsConfig.java

@@ -1,70 +0,0 @@
-package com.persagy.apm.energyalarmstarter.alarmdata.jms;
-
-import com.rabbitmq.client.Channel;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.amqp.core.*;
-import org.springframework.amqp.rabbit.annotation.RabbitListener;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * @description:报警定义消息通知
- * @author:LuoGuangyi
- * @company:PersagyTechnologyCo.,Ltd
- * @since:2020/10/20 002016:30
- * @version:V1.0
- **/
-@Slf4j
-@Configuration
-public class JmsConfig {
-//    @Autowired
-//    OrderStateChangeService orderStateChangeService;
-
-    /**
-     * 工单状态变化exchange, 下一期升级为对接集成框架
-     */
-    private String orderStateExchange = "workorder_state_publish_exchange";
-    /**
-     * 工单状态变化queue
-     */
-    private String orderStateQueue = "order_state_queue";
-
-
-    @Bean
-    public FanoutExchange orderStateExchange() {
-        return new FanoutExchange(orderStateExchange);
-    }
-
-    @Bean
-    public Queue orderStateQueue() {
-        return new Queue(orderStateQueue, true);
-    }
-
-    @Bean
-    public Binding orderStateBinding() {
-        return BindingBuilder.bind(orderStateQueue()).to(orderStateExchange());
-    }
-
-    @RabbitListener(queues = "order_state_queue")    //监听器监听指定的Queue
-    public void processOrderState(String message, Channel channel, Message msg) {
-//        log.info("============================== Receive:" + message);
-//        try {
-//            OrderStateMessage orderStateMessage = StringUtil.transferItemToDTO(message, OrderStateMessage.class);
-//            log.info("currentThread:{}", Thread.currentThread().getId());
-//            log.info("order_id: {}", orderStateMessage.getOrder_id());
-//            log.info("order_state: {}", orderStateMessage.getOrder_state());
-//            // 根据工单状态消息更新报警记录状态
-//            orderStateChangeService.updateAlarmWhenOrderStateChange(orderStateMessage);
-//            // 手动确认消息已消费
-//            channel.basicAck(msg.getMessageProperties().getDeliveryTag(),false);
-//        } catch (Exception e) {
-//            log.error("工单状态消息消费失败: {}", e.getMessage());
-//            try {
-//                // 将消费失败的消息移除消息队列,避免重复消费
-//                channel.basicReject(msg.getMessageProperties().getDeliveryTag(),false);
-//            } catch (IOException ex) {
-//                log.error("工单状态消息从队列中移除失败,{}", ex.getMessage());
-//            }
-//        }
-    }
-}

+ 0 - 18
AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/jms/model/OrderStateMessage.java

@@ -1,18 +0,0 @@
-package com.persagy.apm.energyalarmstarter.alarmdata.jms.model;
-
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * @description:
- * @author: lixing
- * @company: Persagy Technology Co.,Ltd
- * @since: 2020/12/15 11:22 上午
- * @version: V1.0
- **/
-@Getter
-@Setter
-public class OrderStateMessage {
-    private String order_id;
-    private int order_state;
-}

+ 2 - 2
AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/service/AlarmConfigServiceImpl.java

@@ -1,5 +1,7 @@
 package com.persagy.apm.energyalarmstarter.alarmdata.service;
 
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.persagy.apm.energyalarmstarter.alarmdata.feign.DmpResult;
 import com.persagy.apm.energyalarmstarter.alarmdata.feign.client.AlarmClient;
 import com.persagy.apm.energyalarmstarter.alarmdata.model.dto.AlarmCondition;
@@ -11,8 +13,6 @@ import com.persagy.apm.energyalarmstarter.alarmdata.model.vo.DmpUpsertVO;
 import com.persagy.apm.energyalarmstarter.alarmdata.utils.DmpResultUtil;
 import com.persagy.apm.energyalarmstarter.alarmdata.utils.Obj2JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.configurationprocessor.json.JSONArray;
-import org.springframework.boot.configurationprocessor.json.JSONObject;
 import org.springframework.stereotype.Service;
 
 import java.util.List;

+ 3 - 3
AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/utils/DmpResultUtil.java

@@ -1,8 +1,8 @@
 package com.persagy.apm.energyalarmstarter.alarmdata.utils;
 
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.persagy.apm.energyalarmstarter.alarmdata.feign.DmpResult;
-import org.springframework.boot.configurationprocessor.json.JSONArray;
-import org.springframework.boot.configurationprocessor.json.JSONObject;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -51,7 +51,7 @@ public class DmpResultUtil {
         DmpResult<List<T>> newResult = copyBasicInfo(result);
         List<T> resultData = new ArrayList<>();
         JSONArray data = result.getData();
-        for (int i = 0; i < data.length(); i++) {
+        for (int i = 0; i < data.size(); i++) {
             JSONObject dataObj = data.getJSONObject(i);
             T reverse = Obj2JSONObject.reverse(dataObj, clazz);
             resultData.add(reverse);

+ 3 - 8
AlarmDataStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmdata/utils/Obj2JSONObject.java

@@ -1,7 +1,6 @@
 package com.persagy.apm.energyalarmstarter.alarmdata.utils;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.springframework.boot.configurationprocessor.json.JSONObject;
+import com.alibaba.fastjson.JSONObject;
 
 /**
  * 对象转换为JSONObject
@@ -11,14 +10,10 @@ import org.springframework.boot.configurationprocessor.json.JSONObject;
  **/
 public class Obj2JSONObject {
     public static JSONObject convert(Object o) throws Exception{
-        ObjectMapper objectMapper = new ObjectMapper();
-        String tmp = objectMapper.writeValueAsString(o);
-        return new JSONObject(tmp);
+        return (JSONObject)JSONObject.toJSON(o);
     }
 
     public static <T> T reverse(JSONObject jsonObject, Class<T> clazz) throws Exception{
-        String json = jsonObject.toString();
-        ObjectMapper objectMapper = new ObjectMapper();
-        return objectMapper.readValue(json, clazz);
+        return JSONObject.toJavaObject(jsonObject, clazz);
     }
 }

+ 64 - 0
AlarmEngineStarter/pom.xml

@@ -15,5 +15,69 @@
         <maven.compiler.source>8</maven.compiler.source>
         <maven.compiler.target>8</maven.compiler.target>
     </properties>
+    <dependencies>
+        <!-- jackson -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-annotations</artifactId>
+            <version>2.11.3</version>
+        </dependency>
 
+        <!-- lombok -->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- starter 标配 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-autoconfigure</artifactId>
+        </dependency>
+
+        <!--rabbitmq -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-amqp</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- fastjson -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.71</version>
+        </dependency>
+
+        <!-- netty -->
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+            <version>4.1.42.Final</version>
+        </dependency>
+
+        <!-- lang -->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+        </dependency>
+
+        <!-- alarmDataStarter-->
+        <dependency>
+            <groupId>com.persagy.apm</groupId>
+            <artifactId>AlarmDataStarter</artifactId>
+            <version>v1.0.0</version>
+        </dependency>
+    </dependencies>
 </project>

+ 143 - 0
AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/jms/JmsConfig.java

@@ -0,0 +1,143 @@
+package com.persagy.apm.energyalarmstarter.alarmengine.jms;
+
+import com.persagy.dmp.starter.alarm.communication.mq.model.DmpMessage;
+import com.persagy.dmp.starter.alarm.communication.mq.model.OrderStateMessage;
+import com.persagy.dmp.starter.alarm.communication.netty.NettyAlarmMsgBaseHandler;
+import com.persagy.dmp.starter.alarm.service.OrderStateChangeService;
+import com.persagy.dmp.starter.alarm.util.StringUtil;
+import com.rabbitmq.client.Channel;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.*;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.io.IOException;
+
+/**
+ * @description:报警定义消息通知
+ * @author:LuoGuangyi
+ * @company:PersagyTechnologyCo.,Ltd
+ * @since:2020/10/20 002016:30
+ * @version:V1.0
+ **/
+@Slf4j
+@Configuration
+public class JmsConfig {
+    @Autowired
+    OrderStateChangeService orderStateChangeService;
+
+    /**
+     * NettyAlarmMsgBaseHandler本身不进行ioc注入,这里注入的是他的子类。
+     * 子类在实际项目中创建,starter中没有实例。
+     */
+    @Autowired
+    private NettyAlarmMsgBaseHandler msgHandler;
+    /**
+     * 报警定义变化类型
+     */
+    private static final String ALARM_CONFIGS_CHANGE = "alarmConfigsChange";
+    /**
+     * 报警交换器
+     */
+    @Value("${dmp.alarm.exchange}")
+    private String dmpAlarmExchange;
+    /**
+     * 报警路由键
+     */
+    @Value("${dmp.alarm.routingKey}")
+    private String alarmRoutingKey;
+    /**
+     * 报警队列
+     */
+    @Value("${dmp.alarm.queue}")
+    private String alarmQueue;
+
+    /**
+     * 工单状态变化exchange, 下一期升级为对接集成框架
+     */
+    private String orderStateExchange = "workorder_state_publish_exchange";
+    /**
+     * 工单状态变化queue
+     */
+    private String orderStateQueue = "order_state_queue";
+
+
+    @Bean
+    public Queue alarmQueue() {
+        return new Queue(alarmQueue, true);
+    }
+
+    @Bean
+    public TopicExchange alarmExchange() {
+        return new TopicExchange(dmpAlarmExchange);
+    }
+
+    @Bean
+    public Binding alarmBinding() {
+        return BindingBuilder.bind(alarmQueue()).to(alarmExchange()).with(alarmRoutingKey);
+    }
+
+    @Bean
+    public FanoutExchange orderStateExchange() {
+        return new FanoutExchange(orderStateExchange);
+    }
+
+    @Bean
+    public Queue orderStateQueue() {
+        return new Queue(orderStateQueue, true);
+    }
+
+    @Bean
+    public Binding orderStateBinding() {
+        return BindingBuilder.bind(orderStateQueue()).to(orderStateExchange());
+    }
+
+    @RabbitListener(queues = "order_state_queue")    //监听器监听指定的Queue
+    public void processOrderState(String message, Channel channel, Message msg) {
+        log.info("============================== Receive:" + message);
+        try {
+            OrderStateMessage orderStateMessage = StringUtil.transferItemToDTO(message, OrderStateMessage.class);
+            log.info("currentThread:{}", Thread.currentThread().getId());
+            log.info("order_id: {}", orderStateMessage.getOrder_id());
+            log.info("order_state: {}", orderStateMessage.getOrder_state());
+            // 根据工单状态消息更新报警记录状态
+            orderStateChangeService.updateAlarmWhenOrderStateChange(orderStateMessage);
+            // 手动确认消息已消费
+            channel.basicAck(msg.getMessageProperties().getDeliveryTag(),false);
+        } catch (Exception e) {
+            log.error("工单状态消息消费失败: {}", e.getMessage());
+            try {
+                // 将消费失败的消息移除消息队列,避免重复消费
+                channel.basicReject(msg.getMessageProperties().getDeliveryTag(),false);
+            } catch (IOException ex) {
+                log.error("工单状态消息从队列中移除失败,{}", ex.getMessage());
+            }
+        }
+    }
+
+    @RabbitHandler
+    @RabbitListener(queues = "${dmp.alarm.queue}")
+    public void planQueues(String msg, Channel channel, Message message) {
+        try {
+            log.info("============================== Receive:" + msg);
+            DmpMessage dmpMessage = StringUtil.transferItemToDTO(msg, DmpMessage.class);
+            //报警定义变化
+            if (ALARM_CONFIGS_CHANGE.equals(dmpMessage.getType())) {
+                log.info("================收到一条报警定义变化通知==============");
+                log.info(msg);
+                try {
+                    msgHandler.incrementSyncAlarmConfig(dmpMessage);
+                } catch (Exception e) {
+                    log.error("error", e);
+                }
+            }
+            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
+        } catch (Exception e) {
+            log.error("消息消费失败,{}", e.getMessage());
+        }
+    }
+}

+ 55 - 0
AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/jms/model/DmpMessage.java

@@ -0,0 +1,55 @@
+package com.persagy.apm.energyalarmstarter.alarmengine.jms.model;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+
+/**
+ * @description:中台消息队列格式
+ * @author:LuoGuangyi
+ * @company:PersagyTechnologyCo.,Ltd
+ * @since:2020/10/28 002816:10
+ * @version:V1.0
+ **/
+@Data
+public class DmpMessage {
+    private String mid;
+    /**
+     *  type = alarmConfigsChange
+     *  报警消息变化
+     */
+    private String type;
+    private String groupCode;
+    private String projectId;
+    private String targetId;
+    private Integer int1;
+    private Integer int2;
+    private String str1;
+    private String str2;
+    private String sendTime;
+    private String expireTime;
+    private String appId;
+    private String userId;
+    /**
+     * 分别存放新增和修改的报警定义条目
+     * {
+     *     "createdConfigUniques": [
+     *         {
+     *             "itemCode": "报警条目编码",
+     *             "objId": "对象id"
+     *         }
+     *     ],
+     *     "deletedConfigUniques": [
+     *         {
+     *             "itemCode": "报警条目编码",
+     *             "objId": "对象id"
+     *         }
+     *     ]
+     * }
+     */
+    private JSONObject exts;
+
+    @Override
+    public String toString() {
+        return JSONObject.toJSONString(this);
+    }
+}

+ 78 - 0
AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/netty/NettyAlarmMessage.java

@@ -0,0 +1,78 @@
+package com.persagy.apm.energyalarmstarter.alarmengine.netty;
+
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * @description: netty报警消息格式定义
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/11/30 10:52 上午
+ * @version: V1.0
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class NettyAlarmMessage<T> {
+    /**
+     * 唯一标识
+     */
+    @JSONField()
+    private long streamId;
+    @JSONField()
+    private int version = 1;
+
+    /**
+     * 操作类型:
+     * 200-建立连接,200的source是projectId
+     * 1-请求、2 -响应、3-通知、
+     * 4-边缘端获取报警定义、
+     * 5-边缘端主动推送报警记录、
+     * 6-边缘端主动更新报警记录状态、
+     * 7-云端推送修改的报警定义给边缘端(增量新增修改报警定义)、
+     * 8-云端把报警记录的id推送到边缘端
+     * 9-边缘端取报警定义,云端推送给边缘端的标记(全量报警定义)
+     * 10-云端推送删除的报警定义给边缘端(增量删除报警定义)、
+     */
+    @JSONField()
+    private int opCode;
+
+    /**
+     * 请求来源
+     */
+    @JSONField()
+    private String source = "group";
+
+    /**
+     * 用于项目控制程序清除之前的时间和命令
+     */
+    @JSONField()
+    private String clearBeforeTimeFlag;
+
+    /**
+     * 传输内容
+     */
+    @JSONField(jsonDirect = true)
+    private List<T> content;
+
+    /**
+     * 成功标识
+     */
+    @JSONField()
+    private Boolean success;
+
+    @Override
+    public String toString() {
+        return JSONObject.toJSONString(this);
+    }
+
+    public NettyAlarmMessage(int opCode, List<T> content) {
+        this.opCode = opCode;
+        this.content = content;
+    }
+}

+ 406 - 0
AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/netty/NettyAlarmMsgBaseHandler.java

@@ -0,0 +1,406 @@
+package com.persagy.apm.energyalarmstarter.alarmengine.netty;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.persagy.dmp.starter.alarm.communication.mq.model.DmpMessage;
+import com.persagy.dmp.starter.alarm.service.NettyAlarmService;
+import com.persagy.dmp.starter.alarm.util.StringUtil;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInboundHandlerAdapter;
+import io.netty.channel.group.ChannelGroup;
+import io.netty.channel.group.DefaultChannelGroup;
+import io.netty.util.concurrent.GlobalEventExecutor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StringUtils;
+
+import java.net.SocketAddress;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/**
+ * @description: Netty报警息处理中心
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/11/30 10:31 上午
+ * @version: V1.0
+ */
+@Slf4j
+@ChannelHandler.Sharable
+public class NettyAlarmMsgBaseHandler extends ChannelInboundHandlerAdapter {
+
+    public static final String allProjects = "allProjects";
+
+    @Autowired
+    private NettyAlarmService nettyAlarmService;
+    /**
+     * 装每个客户端的地址及对应的管道
+     */
+    public Map<String, Channel> socketChannelMap = new ConcurrentHashMap<>();
+
+    /**
+     * @description: 根据项目id获取对应的通信通道
+     * @param: projectId
+     * @return: io.netty.channel.Channel
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/3 3:03 下午
+     * @version: V1.0
+     */
+    private Channel getChannel(String projectId) {
+        // 项目上的消息只推送给一个边缘端来处理
+        Channel channel = socketChannelMap.get(projectId);
+        if (channel == null) {
+            channel = socketChannelMap.get(allProjects);
+        }
+        return channel;
+    }
+
+    /**
+     * 保留所有与服务器建立连接的channel对象
+     */
+    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
+
+    @Override
+    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
+        log.info("netty通道注册完成,ChannelHandlerContext信息:{}", ctx.toString());
+        SocketAddress socketAddress = ctx.channel().remoteAddress();
+        String remoteAddress = socketAddress.toString();
+        System.out.println("--某个客户端绑定地址:" + remoteAddress + "--");
+    }
+
+    /**
+     * @description: 边缘端请求连接
+     * @param: nettyMessage
+     * @param: channelHandlerContext
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 11:06 上午
+     * @version: V1.0
+     */
+    private void connected(NettyAlarmMessage nettyMessage, ChannelHandlerContext channelHandlerContext) {
+        String source = nettyMessage.getSource();
+        if (StringUtils.isEmpty(source)) {
+            if (socketChannelMap.size() > 0) {
+                throw new RuntimeException("已经有projectId!=0的边缘端连接到云端,本次连接失效");
+            }
+            socketChannelMap.put(allProjects, channelHandlerContext.channel());
+        } else {
+            if (socketChannelMap.get(allProjects) != null) {
+                throw new RuntimeException("已经有projectId=0的边缘端连接到云端,本次连接失效");
+            }
+            String[] projectIds = source.split(",");
+            // 一个项目只能对应一个channel
+            for (String projectId : projectIds) {
+                // 保留旧的通道,因为当新通道请求连接时,可能老通道已经产生了通信消息
+                if (!socketChannelMap.containsKey(projectId)) {
+                    socketChannelMap.put(projectId, channelHandlerContext.channel());
+                }
+            }
+        }
+    }
+
+    /**
+     * @description: 发送全部报警定义到边缘端
+     * @param: nettyMessage
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 3:26 下午
+     * @version: V1.0
+     */
+    public void sendAllAlarmConfigs(NettyAlarmMessage nettyMessage) throws Exception {
+        List<JSONObject> dataList = nettyMessage.getContent();
+        JSONObject data = dataList.get(0);
+        String projectId = data.getString("projectId");
+        if (StringUtils.isEmpty(projectId)) {
+            data.put("projectId", 0);
+        } else {
+            data.put("projectId", projectId.split(","));
+        }
+
+        data.put("userId", "system");
+
+        JSONArray alarmConfigs = nettyAlarmService.queryAlarmConfig(data);
+        if (alarmConfigs == null || alarmConfigs.size() <= 0) {
+            return;
+        }
+        // 查询到的报警定义按项目id分组,发送到对应的边缘端
+        Map<String, List<Object>> groups = alarmConfigs.stream().collect(
+                Collectors.groupingBy(alarmConfig ->
+                        ((JSONObject) alarmConfig).getString("projectId")
+                )
+        );
+
+        // 全量同步报警定义,同一个边缘端只能发送一条消息,如果有多条,只有最后一条生效。
+        // 因此这里需要先将需要给边缘端发送的消息整理在一起
+        Map<Channel, List<Object>> channelGroups = new HashMap<>();
+
+        groups.forEach((tmpProjectId, alarmConfigList) -> {
+            Channel channel = getChannel(projectId);
+            if (channelGroups.containsKey(channel)) {
+                // 把要发送的报警定义追加到通道中
+                channelGroups.get(channel).addAll(alarmConfigList);
+            } else {
+                channelGroups.put(channel, alarmConfigList);
+            }
+        });
+
+        channelGroups.forEach((channel, alarmConfigList) -> {
+            sendMessage(channel, new NettyAlarmMessage(9, alarmConfigList).toString());
+        });
+    }
+
+    /**
+     * @description: 创建报警记录,并发送报警记录id至边缘端
+     * @param: nettyMessage
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 3:33 下午
+     * @version: V1.0
+     */
+    public void createAlarmRecordAndSendRecordId(NettyAlarmMessage nettyMessage) throws Exception {
+        List<JSONObject> dataList = nettyMessage.getContent();
+        JSONObject data = dataList.get(0);
+        data.put("userId", "system");
+        String alarmRecordId = nettyAlarmService.createAlarm(data);
+
+        JSONObject record = new JSONObject();
+        record.put("id", alarmRecordId);
+        record.put("objId", data.getString("objId"));
+        record.put("itemCode", data.getString("itemCode"));
+        List<JSONObject> records = new ArrayList<>();
+        records.add(record);
+        String projectId = data.getString("projectId");
+        sendMessage(projectId, new NettyAlarmMessage(8, records).toString());
+    }
+
+    /**
+     * @description: 更新报警记录
+     * @param: message
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 4:13 下午
+     * @version: V1.0
+     */
+    public void updateAlarmRecord(NettyAlarmMessage message) throws Exception {
+        List<JSONObject> dataList = message.getContent();
+        JSONObject data = dataList.get(0);
+        data.put("userId", "system");
+        nettyAlarmService.updateAlarmRecord(data);
+    }
+
+    /**
+     * @description: 增量同步报警定义
+     * @param: dmpMessage
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/1 11:45 上午
+     * @version: V1.0
+     */
+    public void incrementSyncAlarmConfig(DmpMessage dmpMessage) throws Exception {
+        Map<String, JSONArray> changedAlarmConfigs = nettyAlarmService.queryChangedAlarmConfigs(dmpMessage);
+        JSONArray createdConfigUniques = changedAlarmConfigs.get("createdConfigUniques");
+        JSONArray deletedConfigUniques = changedAlarmConfigs.get("deletedConfigUniques");
+        if (!CollectionUtils.isEmpty(deletedConfigUniques)) {
+            // 通过netty发送给边缘端 10-云端推送删除的报警定义给边缘端(增量删除报警定义)
+            sendMessage(dmpMessage.getProjectId(), new NettyAlarmMessage(10, deletedConfigUniques).toString());
+        }
+        if (!CollectionUtils.isEmpty(createdConfigUniques)) {
+            // 通过netty发送给边缘端 7-云端推送修改的报警定义给边缘端(增量新增修改报警定义)
+            sendMessage(dmpMessage.getProjectId(), new NettyAlarmMessage(7, createdConfigUniques).toString());
+        }
+    }
+
+    /**
+     * @param channelHandlerContext
+     * @param msg
+     * @description: 接受客户端收到的数据
+     * @return: void
+     * @exception:
+     * @author: shiliqiang
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/10/21 10:20
+     * @version: V1.0
+     */
+    @Override
+    public void channelRead(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
+        log.info("收到[" + channelHandlerContext.channel().remoteAddress() + "]消息:" + msg);
+        try {
+            NettyAlarmMessage nettyMessage = StringUtil.transferItemToDTO(msg.toString(), NettyAlarmMessage.class);
+            /* 操作类型:1-请求、2 -响应、3-通知、
+             * 4-边缘端获取报警定义、
+             * 5-边缘端主动推送报警记录、
+             * 6-边缘端主动更新报警记录状态、
+             * 7-云端推送修改的报警定义给边缘端、
+             * 9-边缘端取报警定义,云端推送给边缘端的标记
+             */
+            int opCode = nettyMessage.getOpCode();
+            List<JSONObject> dataList = nettyMessage.getContent();
+            switch (opCode) {
+                case 200:
+                    /* 接收到边缘端创建连接请求,存储连接请求的projectId和channel映射关系,
+                     * 以后向边缘端发送请求时,通过projectId获取到对应的channel
+                     */
+                    connected(nettyMessage, channelHandlerContext);
+                    break;
+                case 4:
+                    // 向边缘端全量发送报警定义
+                    sendAllAlarmConfigs(nettyMessage);
+                    break;
+                case 5:
+                    // 创建报警记录并发送报警记录id到边缘端
+                    createAlarmRecordAndSendRecordId(nettyMessage);
+                    break;
+                case 6:
+                    // 更新报警记录状态
+                    updateAlarmRecord(nettyMessage);
+                    break;
+                case 11:
+                    // 报警仍在继续
+                    alarmContinue(nettyMessage);
+                    break;
+                default:
+                    log.info("边缘端发来的参数无效,参数值为:" + opCode);
+                    break;
+            }
+
+        } catch (Exception e) {
+            log.error("channelRead error", e);
+        }
+    }
+
+    /**
+     * @description: 报警仍在持续的处理方法
+     * @param: nettyMessage
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/17 4:44 下午
+     * @version: V1.0
+     */
+    public void alarmContinue(NettyAlarmMessage nettyMessage) {
+        log.info("报警仍在继续:{}", nettyMessage.toString());
+    }
+
+    @Override
+    public void channelReadComplete(ChannelHandlerContext ctx) {
+        ctx.flush();
+    }
+
+
+    /**
+     * 在读取操作期间,有异常抛出时会调用。
+     *
+     * @param ctx
+     * @param cause
+     * @throws Exception
+     */
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
+        log.error(cause.getMessage());
+        cause.printStackTrace();
+        ctx.close();
+    }
+
+    /**
+     * 新增连接
+     */
+    @Override
+    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+        // TODO Auto-generated method stub
+        //NettyServer.socketChannelMap.put(ctx.channel().remoteAddress().toString(), ctx.channel());
+        channelGroup.add(ctx.channel());
+        log.info("当前连接数:[{}],新建立连接为[{}]...", channelGroup.size(), ctx.channel().remoteAddress().toString());
+        super.handlerAdded(ctx);
+    }
+
+    /**
+     * 断开连接
+     */
+    @Override
+    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+        //chanel可以理解成Connection
+        Channel channel = ctx.channel();
+        Set<Map.Entry<String, Channel>> channelList = socketChannelMap.entrySet();
+        for (Map.Entry<String, Channel> channelEntry : channelList) {
+            if (channelEntry.getValue() == channel) {
+                socketChannelMap.remove(channelEntry.getKey());
+                log.warn("----项目ID[{}],地址[{}] ----离开---", channelEntry.getKey(), channel.remoteAddress().toString());
+            }
+        }
+        log.warn("----客户端[{}] ----离开", channel.remoteAddress().toString());
+    }
+
+    /**
+     * 该方法只会在通道建立时调用一次,连接生效
+     */
+    @Override
+    public void channelActive(ChannelHandlerContext ctx) throws Exception {
+        Channel channel = ctx.channel();
+
+    }
+
+    /**
+     * 连接是否有效
+     */
+    @Override
+    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+
+    }
+
+    /**
+     * @param msg
+     * @Title: sendMessage
+     * @Description: 服务端给所有客户端发送消息
+     */
+    public void sendMessageToAll(Object msg) {
+        channelGroup.writeAndFlush(msg.toString());
+    }
+
+    /**
+     * @param msg
+     * @Title: sendMessage
+     * @Description: 服务端给某个客户端发送消息
+     */
+    public void sendMessage(String projectId, String msg) {
+        Channel channel = getChannel(projectId);
+        log.info("projectId: {}", projectId);
+        sendMessage(channel, msg);
+    }
+
+    /**
+     * @description: 发送消息
+     * @param: channel 通道
+     * @param: msg 消息
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/17 12:55 下午
+     * @version: V1.0
+     */
+    public void sendMessage(Channel channel, String msg) {
+        if (channel != null) {
+            channel.writeAndFlush(msg);
+        } else {
+            log.error("消息通道未建立,无法发送消息!");
+        }
+    }
+}

+ 105 - 0
AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/netty/NettyAlarmServer.java

@@ -0,0 +1,105 @@
+package com.persagy.apm.energyalarmstarter.alarmengine.netty;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioChannelOption;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
+import io.netty.handler.codec.LengthFieldPrepender;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.handler.codec.string.StringEncoder;
+import io.netty.handler.logging.LogLevel;
+import io.netty.handler.logging.LoggingHandler;
+import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.util.concurrent.DefaultThreadFactory;
+import io.netty.util.concurrent.UnorderedThreadPoolEventExecutor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @description: netty报警服务端
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/11/30 10:54 上午
+ * @version: V1.0
+ */
+@Component
+@Slf4j
+public class NettyAlarmServer {
+    @Autowired
+    private com.persagy.dmp.starter.alarm.communication.netty.NettyAlarmMsgBaseHandler nettyAlarmMsgBaseHandler;
+
+    @Value("${group.alarm.port}")
+    public int port;
+
+    /**
+     * 用于接收客户端的TCP连接
+     */
+    EventLoopGroup bossGroup = null;
+    /**
+     * 处理I/O相关的读写操作,或者执行系统Task、定时任务Task等。
+     */
+    EventLoopGroup workGroup = null;
+    ChannelFuture channelFuture = null;
+
+    /**
+     * @Title: start
+     * @Description: 启动netty服务端
+     */
+    public void start() {
+        log.info("NettyServer开始初始化...");
+        bossGroup = new NioEventLoopGroup();
+        workGroup = new NioEventLoopGroup();
+        UnorderedThreadPoolEventExecutor businessGroup = new UnorderedThreadPoolEventExecutor(10, new DefaultThreadFactory("business"));
+
+        try {
+            // (服务端启动类)ServerBootstrap负责初始化netty服务器,并且开始监听端口的socket请求
+            ServerBootstrap startNetty = new ServerBootstrap();
+            startNetty.group(bossGroup, workGroup)
+                    .channel(NioServerSocketChannel.class)
+                    .option(NioChannelOption.SO_BACKLOG, 1024)
+                    .childOption(NioChannelOption.TCP_NODELAY, true)
+                    .handler(new LoggingHandler(LogLevel.INFO))
+                    .childHandler(new ChannelInitializer<SocketChannel>() {
+                        @Override
+                        protected void initChannel(SocketChannel ch) throws Exception {
+                            // 解决粘包和拆包问题,增加的编码和解码器
+                            ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
+                            ch.pipeline().addLast(new LengthFieldPrepender(4));
+                            // the encoder and decoder are static as these are sharable
+                            ch.pipeline().addLast(new StringDecoder());
+                            ch.pipeline().addLast(new StringEncoder());
+                            // 为监听客户端read/write事件的Channel添加用户自定义的ChannelHandler
+                            ch.pipeline().addLast(businessGroup, nettyAlarmMsgBaseHandler);
+
+                            // 增加超时检查
+                            ch.pipeline().addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS));
+                        }
+                    });
+            channelFuture = startNetty.bind(new InetSocketAddress(port)).sync();
+
+            log.info("NettyServer初始化完成,启动netty服务端");
+            channelFuture.channel().closeFuture().sync();
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            // 关闭主线程组
+            bossGroup.shutdownGracefully();
+            // 关闭工作线程组
+            workGroup.shutdownGracefully();
+            businessGroup.shutdownGracefully();
+        }
+    }
+
+
+}

+ 131 - 0
AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/service/BaseService.java

@@ -0,0 +1,131 @@
+package com.persagy.apm.energyalarmstarter.alarmengine.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.persagy.apm.energyalarmstarter.alarmdata.feign.AlarmUrlParam;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * @description: 提供一些基础方法
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/12/16 10:10 上午
+ * @version: V1.0
+ **/
+public class BaseService {
+    /**
+     * @description: 参数校验
+     * @param: obj
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 3:14 下午
+     * @version: V1.0
+     */
+    public void checkRequestParam(JSONObject obj) throws Exception {
+        String projectId = obj.getString("projectId");
+        String userId = obj.getString("userId");
+        String groupCode = obj.getString("groupCode");
+        if (StringUtils.isBlank(projectId)) {
+            throw new Exception("projectId不能为空");
+        } else if (StringUtils.isBlank(userId)) {
+            throw new Exception("userId不能为空");
+        } else if (StringUtils.isBlank(groupCode)) {
+            throw new Exception("groupCode不能为空");
+        }
+    }
+
+    /**
+     * @description: 获取request中的param
+     * @param: obj
+     * @return: com.persagy.dmp.starter.alarm.feign.AlarmUrlParam
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 3:14 下午
+     * @version: V1.0
+     */
+    public AlarmUrlParam getAlarmUrlParam(JSONObject obj) throws Exception {
+        String projectId = obj.getString("projectId");
+        // 如果projectId为空,设置为0,查询集团下所有项目的数据
+        if (StringUtils.isEmpty(projectId)) {
+            projectId = "0";
+            obj.put("projectId", "0");
+        } else {
+            // 如果projectId为多个项目(用逗号分隔), param中projectId传0
+            String[] projectIds = projectId.split(",");
+            if (projectIds.length > 1) {
+                obj.put("projectId", "0");
+            }
+        }
+        this.checkRequestParam(obj);
+
+        return new AlarmUrlParam(
+                obj.getString("userId"),
+                obj.getString("groupCode"),
+                projectId,
+                obj.getString("appId")
+        );
+    }
+
+    /**
+     * @description: 获取request中的body
+     * @param: obj
+     * @return: com.alibaba.fastjson.JSONObject
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 3:14 下午
+     * @version: V1.0
+     */
+    public JSONObject getRequestBody(JSONObject obj) throws Exception {
+        JSONObject requestBody = new JSONObject();
+        JSONObject criteria = (JSONObject) obj.clone();
+        criteria.remove("appId");
+        criteria.remove("userId");
+        if (!CollectionUtils.isEmpty(criteria.getJSONArray("orders"))) {
+            requestBody.put("orders", criteria.getJSONArray("orders"));
+            criteria.remove("orders");
+        }
+
+        if (!CollectionUtils.isEmpty(criteria.getJSONArray("withColumns"))) {
+            requestBody.put("withColumns", criteria.getJSONArray("withColumns"));
+            criteria.remove("withColumns");
+        }
+
+        if (criteria.containsKey("onlyCount")) {
+            requestBody.put("onlyCount", criteria.getBooleanValue("onlyCount"));
+            criteria.remove("onlyCount");
+        }
+
+        if (criteria.containsKey("page") && criteria.containsKey("size")) {
+            Integer page = criteria.getInteger("page");
+            Integer size = criteria.getInteger("size");
+            requestBody.put("page", page);
+            requestBody.put("size", size);
+            criteria.remove("page");
+            criteria.remove("size");
+        }
+        /* 如果查询条件中包含projectId,进行如下处理 */
+        String projectId = criteria.getString("projectId");
+        // 如果projectId为空或是0,从查询条件中移除
+        if (StringUtils.isEmpty(projectId) || "0".equals(projectId)) {
+            criteria.remove("projectId");
+        } else {
+            // 将projectId转换为数组,适配projectId为多个项目(用逗号分隔)的情况
+            String[] projectIds = projectId.split(",");
+            criteria.put("projectId", projectIds);
+        }
+
+        /* 如果查询条件中包含groupCode, 且groupCode = 0, 从查询条件中移除groupCode */
+        String groupCode = criteria.getString("groupCode");
+        // 如果projectId为空或是0,从查询条件中移除
+        if (StringUtils.isEmpty(groupCode) || "0".equals(groupCode)) {
+            criteria.remove("groupCode");
+        }
+
+        requestBody.put("criteria", criteria);
+        return requestBody;
+    }
+}

+ 274 - 0
AlarmEngineStarter/src/main/java/com/persagy/apm/energyalarmstarter/alarmengine/service/NettyAlarmService.java

@@ -0,0 +1,274 @@
+package com.persagy.apm.energyalarmstarter.alarmengine.service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.persagy.apm.energyalarmstarter.alarmdata.feign.AlarmUrlParam;
+import com.persagy.apm.energyalarmstarter.alarmdata.feign.DmpResult;
+import com.persagy.apm.energyalarmstarter.alarmdata.feign.client.AlarmClient;
+import com.persagy.apm.energyalarmstarter.alarmengine.jms.model.DmpMessage;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.CollectionUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @description: 处理netty消息中调用数据中台的逻辑
+ * @author: lixing
+ * @company: Persagy Technology Co.,Ltd
+ * @since: 2020/11/30 2:38 下午
+ * @version: V1.0
+ **/
+@Slf4j
+public abstract class NettyAlarmService extends BaseService {
+    @Autowired
+    AlarmClient alarmClient;
+
+    /**
+     * @description: 查询报警定义
+     * @param: data
+     * @return: com.alibaba.fastjson.JSONArray
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 3:25 下午
+     * @version: V1.0
+     */
+    public JSONArray queryAlarmConfig(JSONObject data) throws Exception {
+        DmpResult<JSONArray> queryResult = alarmClient.queryAlarmConfig(getAlarmUrlParam(data), getRequestBody(data));
+        JSONArray alarmConfigs = queryResult.getData();
+        // 报警定义中的信息点转换为表号、功能号
+        initAlarmConfigInfoCodes(alarmConfigs);
+        return alarmConfigs;
+    }
+
+    /**
+     * @description: 报警定义中的信息点转换为表号、功能号
+     * @param: alarmConfigs
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/1 2:32 下午
+     * @version: V1.0
+     */
+    public void initAlarmConfigInfoCodes(JSONArray alarmConfigs) throws Exception {
+        if (alarmConfigs == null) {
+            return;
+        }
+        for (Object alarmConfig : alarmConfigs) {
+            JSONObject config = (JSONObject) alarmConfig;
+            String classCode = config.getString("classCode");
+            JSONObject condition = config.getJSONObject("condition");
+            JSONArray infoCodeArray = condition.getJSONArray("infoCode");
+
+            JSONArray infoCodes = new JSONArray();
+            for (Object infoCodeObj : infoCodeArray) {
+                JSONObject infoCode = new JSONObject();
+                String infoCodeStr = (String) infoCodeObj;
+                infoCode.put("infoCode", infoCodeStr);
+                infoCode.put("meterId", getMeterId(infoCodeStr, classCode));
+                infoCode.put("funcId", getFuncId(infoCodeStr, classCode));
+                infoCodes.add(infoCode);
+            }
+            condition.put("infoCodes", infoCodes);
+        }
+    }
+
+    /**
+     * @description: 获取表号
+     * @param: infoCode
+     * @param: classCode
+     * @return: java.lang.String
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/1 9:40 上午
+     * @version: V1.0
+     */
+    public abstract String getMeterId(String infoCode, String classCode) throws Exception;
+
+    /**
+     * @description: 获取功能号
+     * @param: infoCode
+     * @param: classCode
+     * @return: java.lang.String
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/1 9:40 上午
+     * @version: V1.0
+     */
+    public abstract String getFuncId(String infoCode, String classCode) throws Exception;
+
+    /**
+     * @description: 获取报警名称
+     * @param: objId
+     * @return: java.lang.String
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 4:00 下午
+     * @version: V1.0
+     */
+    public abstract String getAlarmName(String projectId,String objId,String category);
+
+    /**
+     * @description: 获取报警扩充字段
+     * @param: objId
+     * @return: JSONObject
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 4:00 下午
+     * @version: V1.0
+     */
+    public abstract JSONObject getAlarmSupplement(AlarmUrlParam alarmUrlParam, JSONObject alarmRecord);
+
+    /**
+     * @description: 获取报警等级
+     * @param: JSONObject 创建报警的参数
+     * @return: 报警等级,默认为报警定义的报警等级
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 4:00 下午
+     * @version: V1.0
+     */
+    public String getAlarmLevel(JSONObject data) {
+        // 默认报警等级为低
+        String defaultValue = "3";
+        if (data != null && StringUtils.isNotBlank(data.getString("level"))) {
+            return data.getString("level");
+        }
+        return defaultValue;
+    }
+
+    /**
+     * @description: 获取报警备注
+     * @param: objId
+     * @return: java.lang.String
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 4:00 下午
+     * @version: V1.0
+     */
+    public abstract String getAlarmRemark(AlarmUrlParam data, String objId, String itemCode);
+
+    /**
+     * @description: 创建报警id
+     * @param: data
+     * @return: java.lang.String
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 3:45 下午
+     * @version: V1.0
+     */
+    public String createAlarm(JSONObject data) throws Exception {
+        //报警分类(设备、空间、系统 Eq,Sp,Sy)
+        String category = data.getString("category");
+        String objId = data.getString("objId");
+        String classCode = data.getString("classCode");
+        String projectId = data.getString("projectId");
+        String itemCode = data.getString("itemCode");
+
+        data.put("name", getAlarmName(projectId,objId,category));
+        data.put("remark", getAlarmRemark(getAlarmUrlParam(data), objId, itemCode));
+        data.put("supplement", getAlarmSupplement(getAlarmUrlParam(data), data));
+        data.put("level", getAlarmLevel(data));
+
+        DmpResult<JSONObject> alarmRecord = alarmClient.createAlarmRecord(getAlarmUrlParam(data), data);
+        return alarmRecord.getData().getString("id");
+    }
+
+    /**
+     * @description: 更新报警记录
+     * @param: data
+     * @return: void
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/11/30 4:13 下午
+     * @version: V1.0
+     */
+    public void updateAlarmRecord(JSONObject data) throws Exception {
+        alarmClient.updateAlarmRecord(getAlarmUrlParam(data), data);
+    }
+
+    /**
+     * @description: 报警定义增量变化时,查询发生变化的报警定义
+     * @param: dmpMessage
+     * @return: java.util.Map<java.lang.String, com.alibaba.fastjson.JSONArray>
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/1 12:06 下午
+     * @version: V1.0
+     */
+    public Map<String, JSONArray> queryChangedAlarmConfigs(DmpMessage dmpMessage) throws Exception {
+        String projectId = dmpMessage.getProjectId();
+        JSONObject exts = dmpMessage.getExts();
+        //删除报警定义
+        JSONArray deletedConfigUniques = exts.getJSONArray("deletedConfigUniques");
+        //新增报警定义
+        JSONArray createdConfigUniques = exts.getJSONArray("createdConfigUniques");
+        //修改报警定义
+        JSONArray updatedConfigUniques = exts.getJSONArray("updatedConfigUniques");
+        createdConfigUniques = CollectionUtils.isEmpty(createdConfigUniques) ? new JSONArray() : createdConfigUniques;
+        deletedConfigUniques = CollectionUtils.isEmpty(deletedConfigUniques) ? new JSONArray() : deletedConfigUniques;
+        updatedConfigUniques = CollectionUtils.isEmpty(updatedConfigUniques) ? new JSONArray() : updatedConfigUniques;
+        createdConfigUniques.addAll(updatedConfigUniques);
+        List<String> defineList = createdConfigUniques.stream().map(
+                p -> JSONObject.parseObject(JSONObject.toJSONString(p))
+        ).map(this::getAlarmConfigDefineId).collect(Collectors.toList());
+        deletedConfigUniques = deletedConfigUniques.stream().map(
+                p -> JSONObject.parseObject(JSONObject.toJSONString(p))
+        ).filter(
+                t -> !defineList.contains(getAlarmConfigDefineId(t))
+        ).collect(Collectors.toCollection(JSONArray::new));
+
+        JSONArray alarmConfigArr = new JSONArray();
+        for (Object p : createdConfigUniques) {
+            JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(p));
+            JSONObject data = new JSONObject();
+            data.put("projectId", projectId);
+            data.put("groupCode", dmpMessage.getGroupCode());
+            data.put("itemCode", jsonObject.getString("itemCode"));
+            data.put("objId", jsonObject.getString("objId"));
+            data.put("userId", "system");
+            DmpResult<JSONArray> alarmConfigQueryResult = alarmClient.queryAlarmConfig(getAlarmUrlParam(data), getRequestBody(data));
+            JSONArray tmpAlarmConfigArr = alarmConfigQueryResult.getData();
+            if (!CollectionUtils.isEmpty(tmpAlarmConfigArr)) {
+                // 报警定义中的信息点转换为表号、功能号
+                initAlarmConfigInfoCodes(tmpAlarmConfigArr);
+                alarmConfigArr.addAll(tmpAlarmConfigArr);
+            }
+            log.info("新增和更新的报警定义-------------------:{}", tmpAlarmConfigArr);
+        }
+        Map<String, JSONArray> resultMap = new HashMap<>();
+        resultMap.put("createdConfigUniques", alarmConfigArr);
+        resultMap.put("deletedConfigUniques", deletedConfigUniques);
+        return resultMap;
+    }
+
+    /**
+     * @description: 获取报警定义唯一标识
+     * @param: obj
+     * @return: java.lang.String
+     * @exception:
+     * @author: lixing
+     * @company: Persagy Technology Co.,Ltd
+     * @since: 2020/12/1 11:54 上午
+     * @version: V1.0
+     */
+    public String getAlarmConfigDefineId(JSONObject obj) {
+        return obj.getString("itemCode") + "" + obj.getString("objId");
+    }
+}