Browse Source

add 云云对接

18339543638 4 năm trước cách đây
mục cha
commit
61f87308c9
33 tập tin đã thay đổi với 3031 bổ sung182 xóa
  1. 2 2
      jetlinks-components/network-component/mqtt-component/src/main/java/org/jetlinks/community/network/mqtt/gateway/device/MqttServerDeviceGateway.java
  2. 1 0
      jetlinks-components/pom.xml
  3. 15 168
      jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/cluster/RuleClusterUniqueTask.java
  4. 9 1
      jetlinks-core/src/main/java/org/jetlinks/core/cluster/ClusterUniqueTask.java
  5. 87 0
      jetlinks-manager/bridge-manager/pom.xml
  6. 118 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/AliBridgeCodec.java
  7. 278 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultBridgeBootstrap.java
  8. 182 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultBridgeConfigManager.java
  9. 61 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultDeviceConfigManager.java
  10. 179 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultDeviceServiceClient.java
  11. 336 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultUplinkChannelHandler.java
  12. 75 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/entity/AliIotBridgeDeviceConfig.java
  13. 78 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/entity/AliIotBridgeEntity.java
  14. 538 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/exception/AliYunIotExceptionEnums.java
  15. 53 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/message/AliBridgeMessage.java
  16. 73 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/BridgeServer.java
  17. 41 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/Channel.java
  18. 111 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/aliyun/AliBridgeGateway.java
  19. 182 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/aliyun/AliBridgeServer.java
  20. 42 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/aliyun/AliContanst.java
  21. 120 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/aliyun/DefaultAliBridgeChannel.java
  22. 17 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/service/AliBridgeDeviceService.java
  23. 16 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/service/AliBridgeService.java
  24. 68 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/service/AliIotService.java
  25. 124 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/web/AliBridgeServerController.java
  26. 82 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/web/AliIotApiController.java
  27. 49 0
      jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/web/param/AliApiParam.java
  28. 1 1
      jetlinks-manager/pom.xml
  29. 7 6
      jetlinks-standalone/pom.xml
  30. 2 4
      jetlinks-standalone/src/main/java/org/jetlinks/community/standalone/JetLinksApplication.java
  31. 5 0
      jetlinks-standalone/src/main/resources/application.yml
  32. 78 0
      jetlinks-standalone/src/test/java/org/jetlinks/community/BridgeTest.java
  33. 1 0
      pom.xml

+ 2 - 2
jetlinks-components/network-component/mqtt-component/src/main/java/org/jetlinks/community/network/mqtt/gateway/device/MqttServerDeviceGateway.java

@@ -119,8 +119,8 @@ class MqttServerDeviceGateway implements DeviceGateway, MonitorSupportDeviceGate
             .publishOn(Schedulers.parallel())
             .flatMap(this::handleConnection)
             .flatMap(tuple3 -> handleAuthResponse(tuple3.getT1(), tuple3.getT2(), tuple3.getT3()))
-            .doOnNext(this::handleSubscriptionTopic)
-            .doOnNext(this::handleUnSubscriptionTopic)
+//            .doOnNext(this::handleSubscriptionTopic)
+//            .doOnNext(this::handleUnSubscriptionTopic)
             .flatMap(tp -> handleAcceptedMqttConnection(tp.getT1(), tp.getT2(), tp.getT3()), Integer.MAX_VALUE)
             .onErrorContinue((err, obj) -> log.error("处理MQTT连接失败", err))
             .subscriberContext(ReactiveLogger.start("network", mqttServer.getId()))

+ 1 - 0
jetlinks-components/pom.xml

@@ -22,6 +22,7 @@
         <module>notify-component</module>
         <module>logging-component</module>
         <module>rule-engine-component</module>
+        <module>bridge-component</module>
     </modules>
 
     <artifactId>jetlinks-components</artifactId>

+ 15 - 168
jetlinks-components/rule-engine-component/src/main/java/org/jetlinks/community/rule/engine/cluster/RuleClusterUniqueTask.java

@@ -51,7 +51,6 @@ class RuleClusterUniqueTask extends ClusterUniqueTask implements Task ,Serializa
     private long lastStateTime;
 
 
-
     /**
      * 开始时间
      */
@@ -83,16 +82,6 @@ class RuleClusterUniqueTask extends ClusterUniqueTask implements Task ,Serializa
         this.schedulerId = schedulerId;
         this.context = context;
         this.executor = executor;
-//        this.workerId=currentSeverId;
-//        this.id=context.getInstanceId()+"-"+context.getJob().getNodeId();
-//        this.clusterManager=clusterManager;
-//        this.pingTopic=String.format(this.pingTopic,this.id);
-//        this.operationTopic=String.format(this.operationTopic,this.id);
-//        this.lock = redissonClient.getLock("cluster-unique-"+this.getId());
-//        this.redissonClient=redissonClient;
-//        //先创建本地任务,再争夺任务的唯一锁,避免消息漏发
-//        init()
-//            .subscribe();
     }
 
     @Override
@@ -128,84 +117,6 @@ class RuleClusterUniqueTask extends ClusterUniqueTask implements Task ,Serializa
 
     }
 
-
-
-//    private Mono<Void> init(){
-//
-//        return generatePingMsg()
-//            .mergeWith(this.handlePingMsg())
-//            .then();
-//    }
-
-//    /**
-//     * 产生心跳信息
-//     * @return
-//     */
-//    private Mono<Void> generatePingMsg() {
-//        return Flux.interval(Duration.ofSeconds(pingTime/2))
-//            .filter(ignore->{
-//                if (isReplica()) {
-//                    this.heldLockThread.execute(()->{
-//                        if (this.lock.isHeldByThread(Thread.currentThread().getId())) {
-//                            //非工作节点且占据锁,则解锁
-//                            this.lock.unlock();
-//                        }else if(!this.isAlive.get()&&!this.lock.isLocked()){
-//                            //争夺锁
-//                            try {
-//                                //获取锁
-//                                if (this.lock.tryLock(-1, pingTime, TimeUnit.SECONDS)) {
-//                                    //获取锁成功后改写工作节点id
-//                                    this.workerId=this.getCurrentSeverId();
-//                                    this.isAlive.set(true);
-//                                }else {
-//                                    //失败后停止发送心跳(若存在)
-//                                    this.workerId=null;
-//                                    if(this.generatePingMsgDisposable!=null&&!this.generatePingMsgDisposable.isDisposed()){
-//                                        this.generatePingMsgDisposable.dispose();
-//                                    }
-//                                }
-//                            }catch (InterruptedException e){
-//                            }
-//                        }
-//                    });
-//                    return false;
-//                }
-//                return true;
-//            })
-//            .doOnNext(ignore->{
-//                //锁续约
-//                AtomicBoolean finished= new AtomicBoolean(false);
-//                this.heldLockThread.execute(()->{
-//                    try {
-//                        if (this.lock.tryLock(-1,pingTime/2, TimeUnit.SECONDS)) {
-//                            this.workerId=this.getCurrentSeverId();
-//                        }else {
-//                            this.workerId=null;
-//                            if(this.generatePingMsgDisposable!=null&&!this.generatePingMsgDisposable.isDisposed()){
-//                                this.generatePingMsgDisposable.dispose();
-//                            }
-//                        }
-//
-//                    } catch (InterruptedException e) {
-//                    }finally {
-//                        finished.set(true);
-//                    }
-//                });
-//                while (!finished.get()){
-//                }
-//                if(!isReplica()){
-//                    if(State.running.equals(this.taskState)&&!State.running.equals(this.executor.getState())){
-//                        this.executor.start();
-//                    }
-//                    if(this.generatePingMsgDisposable==null||this.generatePingMsgDisposable.isDisposed()){
-//                        this.generatePingMsgDisposable = this.clusterManager.getTopic(pingTopic).publish(Mono.just(this)).subscribe();
-//                    }
-//                }
-//
-//            })
-//            .then();
-//    }
-
     @Override
     public void beMasterPostProcessor() {
         if(State.running.equals(this.taskState)&&!State.running.equals(this.executor.getState())){
@@ -213,19 +124,10 @@ class RuleClusterUniqueTask extends ClusterUniqueTask implements Task ,Serializa
         }
     }
 
+
     @Override
     public Mono<Void> handleMsg(Object message) {
-        if (message instanceof RuleClusterUniqueTask) {
-            //心跳信息
-            RuleClusterUniqueTask task = (RuleClusterUniqueTask) message;
-            if (task.getCurrentSeverId().equals(this.getWorkerId())) {
-                return Mono.empty();
-            }
-            this.setWorkerId(task.getCurrentSeverId());
-            this.taskState = task.taskState;
-            this.lastStateTime = task.lastStateTime;
-            this.startTime = task.startTime;
-        } else if (message instanceof OperationMessage) {
+        if (message instanceof OperationMessage) {
             //操作信息
             OperationMessage operationMessage = (OperationMessage) message;
             if (operationMessage.getFromServerId().equals(this.getWorkerId())) {
@@ -245,63 +147,19 @@ class RuleClusterUniqueTask extends ClusterUniqueTask implements Task ,Serializa
         }
     }
 
-
-//    /**
-//     * 处理消息
-//     *
-//     */
-//    private Mono<Void> handlePingMsg(){
-//        return Flux.interval(Duration.ofSeconds(pingTime/3))
-//            .filter(ignore-> (this.handleDisposable ==null|| this.handleDisposable.isDisposed()))
-//            .doOnNext(ignore->{
-//                    if(this.generatePingMsgDisposable!=null&&!this.generatePingMsgDisposable.isDisposed()){
-//                        if(this.handleDisposable!=null&&!this.handleDisposable.isDisposed()){
-//                            this.handleDisposable.isDisposed();
-//                        }
-//                        return;
-//                    }
-//                    this.handleDisposable = this.clusterManager.getTopic(this.pingTopic)
-//                        .subscribePattern()
-//                        .mergeWith(this.clusterManager.getTopic(this.operationTopic).subscribePattern())
-//                        .timeout(Duration.ofSeconds(Math.multiplyExact(pingTime,2)), Mono.fromRunnable(() -> this.isAlive.set(false)))
-//                        .doOnNext(__->{
-//                            if(isReplica()&&this.executor.getState().equals(State.running)){
-//                                this.taskState=State.running;
-//                                this.executor.pause();
-//                            }
-//                        })
-//                        .publishOn(Schedulers.boundedElastic())
-//                        .flatMap(obj -> {
-//                            if(this.generatePingMsgDisposable!=null&&!this.generatePingMsgDisposable.isDisposed()){
-//                                this.generatePingMsgDisposable.dispose();
-//                            }
-//                            Object message = obj.getMessage();
-//                            this.isAlive.set(true);
-//                            if (message instanceof RuleClusterUniqueTask) {
-//                                //心跳信息
-//                                RuleClusterUniqueTask task = (RuleClusterUniqueTask) message;
-//                                if(task.getCurrentSeverId().equals(this.workerId)){
-//                                    return Mono.empty();
-//                                }
-//                                this.workerId = task.currentSeverId;
-//                                this.taskState = task.taskState;
-//                                this.lastStateTime = task.lastStateTime;
-//                                this.startTime = task.startTime;
-//                            } else if (message instanceof OperationMessage) {
-//                                //操作信息
-//                                OperationMessage operationMessage = (OperationMessage) message;
-//                                if(operationMessage.fromServerId.equals(this.workerId)){
-//                                    return Mono.empty();
-//                                }
-//                                //任务存活
-//                                return operation(operationMessage);
-//                            }
-//                            return Mono.empty();
-//                        })
-//                        .subscribe();
-//                }
-//            ).then();
-//    }
+    @Override
+    public Mono<Void> handlePing(ClusterUniqueTask task) {
+        //心跳信息
+        RuleClusterUniqueTask uniqueTask = (RuleClusterUniqueTask) task;
+        if (task.getCurrentSeverId().equals(this.getWorkerId())) {
+            return Mono.empty();
+        }
+        this.setWorkerId(task.getCurrentSeverId());
+        this.taskState = uniqueTask.taskState;
+        this.lastStateTime = uniqueTask.lastStateTime;
+        this.startTime =  uniqueTask.startTime;
+        return Mono.empty();
+    }
 
     @Override
     public Mono<Void> reload() {
@@ -412,17 +270,6 @@ class RuleClusterUniqueTask extends ClusterUniqueTask implements Task ,Serializa
         return Mono.empty();
     }
 
-
-//    /**
-//     * 副本节点
-//     * @return
-//     */
-//    private boolean isReplica(){
-//        return !this.getCurrentSeverId().equals(this.getWorkerId());
-//    }
-
-
-
     @AllArgsConstructor
     enum TaskOperation implements Serializable {
         /**

+ 9 - 1
jetlinks-core/src/main/java/org/jetlinks/core/cluster/ClusterUniqueTask.java

@@ -26,7 +26,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
  */
 @AllArgsConstructor
 @Data
-public abstract class ClusterUniqueTask implements Serializable {
+public abstract class ClusterUniqueTask<T> implements Serializable {
     private static final long serialVersionUID=1L;
     private String id;
     /**
@@ -176,6 +176,11 @@ public abstract class ClusterUniqueTask implements Serializable {
     public abstract void beforeHandleMsg();
 
 
+    /**
+     * 处理心跳信息
+     */
+    public abstract Mono<Void> handlePing(ClusterUniqueTask task);
+
     /**
      * 处理消息
      *
@@ -204,6 +209,9 @@ public abstract class ClusterUniqueTask implements Serializable {
                             }
                             Object message = obj.getMessage();
                             this.isAlive.set(true);
+                            if (message instanceof ClusterUniqueTask) {
+                                return handlePing((ClusterUniqueTask) message);
+                            }
                             return handleMsg(message);
                         })
                         .subscribe();

+ 87 - 0
jetlinks-manager/bridge-manager/pom.xml

@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <artifactId>jetlinks-manager</artifactId>
+        <groupId>org.jetlinks.community</groupId>
+        <version>1.10.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+
+    <description>云云对接模块</description>
+
+    <artifactId>bridge-manager</artifactId>
+
+
+    <dependencies>
+
+        <!-- https://mvnrepository.com/artifact/com.aliyun/aliyun-java-sdk-iot -->
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-iot</artifactId>
+            <version>${aliyun.iot.sdk.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.aliyun.openservices</groupId>
+            <artifactId>iot-as-bridge-sdk-core</artifactId>
+            <version>${aliyun.bridge.sdk.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jetlinks.community</groupId>
+            <artifactId>gateway-component</artifactId>
+            <version>1.10.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>aliyun-java-sdk-core</artifactId>
+            <version>${aliyun.sdk.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jetlinks.community</groupId>
+            <artifactId>common-component</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-authorization-api</artifactId>
+            <version>${hsweb.framework.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jetlinks</groupId>
+            <artifactId>jetlinks-supports</artifactId>
+            <version>${jetlinks.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.jetlinks</groupId>
+            <artifactId>jetlinks-core</artifactId>
+            <version>${jetlinks.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-starter</artifactId>
+            <version>${hsweb.framework.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>io.r2dbc</groupId>
+            <artifactId>r2dbc-h2</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hswebframework</groupId>
+            <artifactId>hsweb-easy-orm-rdb</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 118 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/AliBridgeCodec.java

@@ -0,0 +1,118 @@
+package org.jetlinks.community.bridge.core;
+
+
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.aliyun.iot.as.bridge.core.model.tsl.*;
+import org.jetlinks.community.bridge.message.AliBridgeMessage;
+import org.jetlinks.core.message.*;
+import org.jetlinks.core.message.event.EventMessage;
+import org.jetlinks.core.message.function.FunctionInvokeMessage;
+import org.jetlinks.core.message.property.ReportPropertyMessage;
+import org.jetlinks.core.message.property.WritePropertyMessageReply;
+import org.jetlinks.core.metadata.types.GeoPoint;
+
+import javax.annotation.Nonnull;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliBridgeCodec.java
+ * @Description TODO
+ * @createTime 2021年11月30日 20:01:00
+ */
+public class AliBridgeCodec{
+
+
+
+    public static DeviceMessage decode(@Nonnull AliBridgeMessage payload,String productKey,String deviceName) {
+        String topic = payload.getTopic();
+        if(topic==null){
+            return null;
+        }
+        if(topic.equals( ThingTopicTemplates.PROPERTY_SET_REPLY_TOPIC_TEMPLATE.getTopic(productKey, deviceName))){
+            //属性设置回复
+        }
+        if(topic.startsWith(ThingTopicTemplates.SERVICE_TOPIC_TEMPLATE_PREFIX.getTopic(productKey,deviceName))){
+            //功能调用回复
+        }
+        return null;
+    }
+
+
+    /**
+     * 属性、事件、标签数据上报
+     * 属性设置;服务调用
+     * @param  jsonObject 消息参数
+     * @param  productKey   产品key
+     * @param  deviceName   设备名称
+     * @return
+     */
+    public static AliBridgeMessage encode(JSONObject jsonObject, String productKey, String deviceName) {
+        String  messageType = String.valueOf(jsonObject.get("messageType"));
+        if(StrUtil.isNullOrUndefined(messageType)){
+            return null;
+        }
+        if(MessageType.ONLINE.name().equals(messageType)){
+            DeviceOnlineMessage message = jsonObject.toJavaObject(DeviceOnlineMessage.class);
+            return AliBridgeMessage.online(message.getDeviceId());
+        }else if(MessageType.DISCONNECT_REPLY.name().equals(messageType)){
+            DisconnectDeviceMessageReply message = jsonObject.toJavaObject(DisconnectDeviceMessageReply.class);
+            return AliBridgeMessage.offline(message.getDeviceId());
+        }
+        else if(MessageType.OFFLINE.name().equals(messageType)){
+            DeviceOnlineMessage message = jsonObject.toJavaObject(DeviceOnlineMessage.class);
+            return AliBridgeMessage.offline(message.getDeviceId());
+        }
+        else if(MessageType.REPORT_PROPERTY.name().equals(messageType)){
+            ReportPropertyMessage message = jsonObject.toJavaObject(ReportPropertyMessage.class);
+            //属性上报
+            String topic = ThingTopicTemplates.PROPERTY_PUB_TOPIC_TEMPLATE.getTopic(productKey, deviceName);
+            ThingPropertyUpdateRequest request = new ThingPropertyUpdateRequest();
+            message.getProperties().forEach(request::addParams);
+            return  AliBridgeMessage.of(topic, JSON.toJSONString(request).getBytes());
+        }
+        else if(MessageType.EVENT.name().equals(messageType)){
+            //事件上报
+            EventMessage message = jsonObject.toJavaObject(EventMessage.class);
+            String topic = ThingTopicTemplates.EVENT_PUB_TOPIC_TEMPLATE.getTopic(productKey, deviceName, message.getEvent());
+            //todo
+            ThingEventRequest request = new ThingEventRequest(message.getEvent(),ThingEventTypes.INFO);
+            return  AliBridgeMessage.of(topic,JSON.toJSONString(request).getBytes());
+        }
+        else if(MessageType.UPDATE_TAG.name().equals(messageType)){
+            //标签信息上报
+            UpdateTagMessage message = jsonObject.toJavaObject(UpdateTagMessage.class);
+            String topic = ThingTopicTemplates.DEVICE_INFO_PUB_TOPIC_TEMPLATE.getTopic(productKey, deviceName);
+            ThingInfoUpdateRequest request = new ThingInfoUpdateRequest();
+            message.getTags().forEach((k,v)->request.addParam(k,String.valueOf(v)));
+            return  AliBridgeMessage.of(topic,JSON.toJSONString(request).getBytes());
+        }
+        else if(MessageType.COORDINATE.name().equals(messageType)){
+            //地理位置标签上报
+            CoordinateMessage message = jsonObject.toJavaObject(CoordinateMessage.class);
+            String topic = ThingTopicTemplates.DEVICE_INFO_PUB_TOPIC_TEMPLATE.getTopic(productKey, deviceName);
+            ThingInfoUpdateRequest request = new ThingInfoUpdateRequest();
+            GeoPoint geoPoint = new GeoPoint( message.getLon(),message.getLat());
+            request.addParam("coordinate", JSON.toJSONString(geoPoint));
+            return  AliBridgeMessage.of(topic,JSON.toJSONString(request).getBytes());
+        }
+        else if(MessageType.WRITE_PROPERTY_REPLY.name().equals(messageType)){
+            //属性设置成功上报
+            WritePropertyMessageReply message = jsonObject.toJavaObject(WritePropertyMessageReply.class);
+            String topic = ThingTopicTemplates.PROPERTY_PUB_TOPIC_TEMPLATE.getTopic(productKey, deviceName);
+            ThingPropertyUpdateRequest request = new ThingPropertyUpdateRequest();
+            message.getProperties().forEach(request::addParams);
+            return  AliBridgeMessage.of(topic, JSON.toJSONString(request).getBytes());
+        }
+        else if(MessageType.INVOKE_FUNCTION.name().equals(messageType)){
+            //服务调用
+            FunctionInvokeMessage message = jsonObject.toJavaObject(FunctionInvokeMessage.class);
+            String topic = ThingTopicTemplates.SERVICE_TOPIC_TEMPLATE_PREFIX.getTopic(productKey, deviceName)
+                + message.getFunctionId();
+            return  AliBridgeMessage.of(topic,null);
+        }
+        return null;
+    }
+}

+ 278 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultBridgeBootstrap.java

@@ -0,0 +1,278 @@
+/**
+ * aliyun.com Inc.
+ * Copyright (c) 2004-2018 All Rights Reserved.
+ */
+package org.jetlinks.community.bridge.core;
+
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import com.alibaba.fastjson.JSON;
+
+import com.aliyun.iot.as.bridge.core.BridgeBootstrapApi;
+import com.aliyun.iot.as.bridge.core.client.DeviceServiceClient;
+import com.aliyun.iot.as.bridge.core.client.Http2MessageClientFactory;
+import com.aliyun.iot.as.bridge.core.client.PopClientFactory;
+import com.aliyun.iot.as.bridge.core.client.callback.DownlinkMessageCallback;
+import com.aliyun.iot.as.bridge.core.client.callback.IoTServiceCallback;
+import com.aliyun.iot.as.bridge.core.config.BridgeConfigConsts;
+import com.aliyun.iot.as.bridge.core.config.BridgeConfigManager;
+import com.aliyun.iot.as.bridge.core.config.ConfigFactory;
+import com.aliyun.iot.as.bridge.core.exception.BootException;
+import com.aliyun.iot.as.bridge.core.exception.BridgeHttpException;
+import com.aliyun.iot.as.bridge.core.handler.DownlinkChannelHandler;
+import com.aliyun.iot.as.bridge.core.handler.OtaUpgradeHandler;
+import com.aliyun.iot.as.bridge.core.handler.PropertySetHandler;
+import com.aliyun.iot.as.bridge.core.handler.ServiceInvokeHandler;
+import com.aliyun.iot.as.bridge.core.model.PopClientConfiguration;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceAuthRequest;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceAuthResponse;
+import com.aliyun.iot.as.bridge.core.util.AssertUtil;
+import com.aliyun.openservices.iot.api.Profile;
+import com.aliyun.openservices.iot.api.exception.IotClientException;
+import com.aliyun.openservices.iot.api.message.MessageClientFactory;
+import com.aliyun.openservices.iot.api.message.api.MessageClient;
+import com.aliyun.openservices.iot.api.message.callback.MessageCallback;
+import com.aliyun.openservices.iot.api.message.entity.Message;
+import com.aliyun.openservices.iot.api.message.entity.MessageToken;
+import com.aliyun.openservices.iot.api.util.StringUtil;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.iot.model.v20180120.QueryDeviceDetailRequest;
+import com.aliyuncs.iot.model.v20180120.QueryDeviceDetailResponse;
+import com.aliyuncs.iot.model.v20180120.QueryDeviceDetailResponse.Data;
+import com.aliyuncs.iot.model.v20180120.RegisterDeviceRequest;
+import com.aliyuncs.iot.model.v20180120.RegisterDeviceResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A lite bridge bootstrap makes it easy to initialize a bridge to Aliyun IoT
+ * platform which can be used for customized protocol. It will NOT start a
+ * socket server.
+ *
+ */
+public class DefaultBridgeBootstrap implements BridgeBootstrapApi {
+
+	private static final Logger logger = LoggerFactory.getLogger(DefaultBridgeBootstrap.class);
+	private DownlinkMessageCallback callback = new DownlinkMessageCallback();
+
+	private BridgeConfigManager bridgeConfigManager;
+
+    public DefaultBridgeBootstrap( BridgeConfigManager bridgeConfigManager) {
+        this.bridgeConfigManager = bridgeConfigManager;
+    }
+
+    @Override
+	public void bootstrap() {
+		bootstrap(null);
+	}
+
+	@Override
+	public void bootstrap(DownlinkChannelHandler handler) {
+		initComponents(handler);
+	}
+
+	@Override
+	public void disconnectBridge() {
+		Http2MessageClientFactory.disconnect();
+	}
+
+	@Override
+	public void reconnectBridge() {
+		initHttp2Client(true);
+		initDownLinkHandler(callback.getDownlinkHandler());
+	}
+
+	@Override
+	public boolean isBridgeConnected() {
+		return Http2MessageClientFactory.isConnected();
+	}
+
+	@Override
+	public void setOtaUpgradeHandler(OtaUpgradeHandler otaUpgradeHandler) {
+		this.callback.setOtaUpgradeHandler(otaUpgradeHandler);
+	}
+
+	@Override
+	public void setPropertySetHandler(PropertySetHandler propertySetHandler) {
+		this.callback.setPropertySetHandler(propertySetHandler);
+	}
+
+	@Override
+	public void setServiceInvokeHandler(ServiceInvokeHandler serviceInvokeHandler) {
+		this.callback.setServiceInvokeHandler(serviceInvokeHandler);
+	}
+
+	private void initComponents(DownlinkChannelHandler handler) {
+		checkParam();
+		checkIfDynamicRegisterBridgeDevice();
+		initHttp2Client(false);
+		initServiceCall();
+		initDownLinkHandler(handler);
+		doOnline();
+	}
+
+	private void checkParam() {
+		String subDeviceConnectMode = bridgeConfigManager.getSubDeviceConnectMode();
+		if(StringUtil.isNotEmpty(subDeviceConnectMode)){
+			try{
+				Integer.parseInt(subDeviceConnectMode);
+			}catch (NumberFormatException e){
+				throw new BootException("Invalid subDeviceConnectMode, must be integer");
+			}
+		}
+		if(bridgeConfigManager.isMultiGwMode()){
+			String networkKey = bridgeConfigManager.getBridgeNetworkKey();
+			if(networkKey == null || networkKey.length() > BridgeConfigConsts.NETWORK_KEY_MAX_LENGTH
+				|| networkKey.length() < BridgeConfigConsts.NETWORK_KEY_MIN_LENGTH){
+				throw new BootException("Invalid networkKey are detected, length of networkKey is required in the range of 6-32");
+			}
+		}
+	}
+
+	private void checkIfDynamicRegisterBridgeDevice() {
+		String instanceId = bridgeConfigManager.getBridgeInstanceId();
+		String productKey = bridgeConfigManager.getBridgeProductKey();
+		String deviceSecret = bridgeConfigManager.getBridgeDeviceSecret();
+		String deviceName = bridgeConfigManager.getBridgeDeviceName();
+		if (StringUtil.isEmpty(deviceName)) {
+			logger.info("bridge deviceName is empty in config, try to use mac address as bridge device");
+			try {
+				deviceName = bridgeConfigManager.getMacAddress();
+				PopClientConfiguration popConfigs = bridgeConfigManager.getPopConfiguration();
+				AssertUtil.isTrue(popConfigs.isComplete(), "bridge deviceName empty && popConfig is not complete!");
+				DefaultAcsClient popClient = PopClientFactory.initClient(popConfigs.getAccessKey(),
+					popConfigs.getAccessSecret(), popConfigs.getName(), popConfigs.getRegion(), popConfigs.getProduct(),
+					popConfigs.getEndpoint());
+				QueryDeviceDetailRequest queryRequest = new QueryDeviceDetailRequest();
+				if(StringUtil.isNotEmpty(instanceId)){
+					queryRequest.setIotInstanceId(instanceId);
+				}
+				queryRequest.setProductKey(productKey);
+				queryRequest.setDeviceName(deviceName);
+				QueryDeviceDetailResponse queryResponse = popClient.getAcsResponse(queryRequest);
+				if (Boolean.TRUE.equals(queryResponse.getSuccess())) {
+					Data deviceDetail = queryResponse.getData();
+					deviceSecret = deviceDetail.getDeviceSecret();
+					bridgeConfigManager.updateBridgeDeviceName(deviceDetail.getDeviceName());
+					bridgeConfigManager.updateBridgeDeviceSecret(deviceDetail.getDeviceSecret());
+					bridgeConfigManager.updateBridgeIotId(deviceDetail.getIotId());
+				} else {
+					logger.warn(String.format("Failed to query device detail[request:%s, response:%s]",
+						JSON.toJSONString(queryRequest), JSON.toJSONString(queryResponse)));
+					logger.info("Start dynamic registration of this bridge, using productKey:{} deviceName:{}",
+						productKey, deviceName);
+					RegisterDeviceRequest registerRequest = new RegisterDeviceRequest();
+					if(StringUtil.isNotEmpty(instanceId)){
+						registerRequest.setIotInstanceId(instanceId);
+					}
+					registerRequest.setProductKey(productKey);
+					registerRequest.setDeviceName(deviceName);
+					RegisterDeviceResponse registerResponse = popClient.getAcsResponse(registerRequest);
+					if (Boolean.TRUE.equals(registerResponse.getSuccess())) {
+						deviceSecret = registerResponse.getData().getDeviceSecret();
+						bridgeConfigManager.updateBridgeDeviceName(registerResponse.getData().getDeviceName());
+						bridgeConfigManager.updateBridgeDeviceSecret(registerResponse.getData().getDeviceSecret());
+						bridgeConfigManager.updateBridgeIotId(registerResponse.getData().getIotId());
+						logger.info("Dynamic registration of this bridge has been finished");
+					} else {
+						logger.error(String.format("Failed to register bridge[request:%s, response:%s]",
+							JSON.toJSONString(registerRequest), JSON.toJSONString(registerResponse)));
+						throw new BootException("Failed to register bridge " + registerResponse.getRequestId());
+					}
+				}
+			} catch (ClientException e) {
+				String msg = "Failed to initialize POP client!";
+				logger.error(msg, e);
+				throw new BootException(msg);
+			}
+		}
+	}
+
+	private void doOnline() {
+        DefaultDeviceServiceClient client = DefaultDeviceServiceClient.getInstance(bridgeConfigManager);
+		DeviceAuthRequest request = new DeviceAuthRequest(bridgeConfigManager.getBridgeProductKey(), bridgeConfigManager.getBridgeDeviceName(),
+			bridgeConfigManager.isMultiGwMode(),bridgeConfigManager.getBridgeNetworkKey(),bridgeConfigManager.getSubDeviceConnectMode());
+		request.setGateway(true);
+		DeviceAuthResponse response = null;
+		try {
+			response = client.auth(request, bridgeConfigManager.getBridgeDeviceSecret());
+		} catch (BridgeHttpException e) {
+			throw new BootException("Bridge failed to pass authorization, network error");
+		}
+		if (response == null || !response.isSuccess() || response.getData() == null
+				|| StringUtil.isEmpty(response.getData().getNetId())) {
+			logger.error("Bridge failed to pass authorization, response: {}", JSON.toJSONString(response));
+			throw new BootException("Bridge failed to pass authorization, server result fail, response " + JSON.toJSONString(response));
+		}
+		bridgeConfigManager.updateBridgeNetId(response.getData().getNetId());
+	}
+
+	private void initServiceCall() {
+		MessageClient client = Http2MessageClientFactory.getClient();
+		AssertUtil.notNull(client, "client must be connected before initServiceCall");
+		String responseTopic = String.format(BridgeConfigConsts.OFFLINE_REPLY_TOPIC_TEMPLATE,
+				bridgeConfigManager.getBridgeProductKey(), bridgeConfigManager.getBridgeDeviceName());
+		CompletableFuture<Boolean> future = client.subscribe(responseTopic, new IoTServiceCallback());
+		try {
+			Boolean result = future.get(60, TimeUnit.SECONDS);
+			if (!Boolean.TRUE.equals(result)) {
+				String msg = "Failed to initialize service call client of IoT platform";
+				logger.error(msg);
+				throw new BootException(msg);
+			}
+		} catch (InterruptedException | ExecutionException | TimeoutException e) {
+			String msg = "Failed to initialize service call client of IoT platform";
+			logger.error(msg, e);
+			throw new BootException(msg);
+		}
+	}
+
+	private void initDownLinkHandler(DownlinkChannelHandler handler) {
+		if(handler == null) {
+			return;
+		}
+		MessageClient client = Http2MessageClientFactory.getClient();
+		String responseTopic = String.format(BridgeConfigConsts.SUB_TOPIC_TEMPLATE,
+				bridgeConfigManager.getBridgeProductKey(), bridgeConfigManager.getBridgeDeviceName());
+		AssertUtil.notNull(callback, "DownlinkMessageCallback is null");
+		callback.setDownlinkHandler(handler);
+		CompletableFuture<Boolean> future = client.subscribe(responseTopic, callback);
+		try {
+			Boolean result = future.get(60, TimeUnit.SECONDS);
+			if (!Boolean.TRUE.equals(result)) {
+				String msg = "Failed to initialize downlink from IoT platform";
+				logger.error(msg);
+				throw new BootException(msg);
+			}
+		} catch (InterruptedException | ExecutionException | TimeoutException e) {
+			String msg = "Failed to initialize downlink from IoT platform";
+			logger.error(msg, e);
+			throw new BootException(msg);
+		}
+	}
+
+	private void initHttp2Client(boolean isReconnect) {
+		String productKey = bridgeConfigManager.getBridgeProductKey();
+		String deviceSecret = bridgeConfigManager.getBridgeDeviceSecret();
+		String deviceName = bridgeConfigManager.getBridgeDeviceName();
+		String http2EndPoint = bridgeConfigManager.getHttp2Endpoint();
+		AssertUtil.notBlank(productKey, "productKey is empty");
+		AssertUtil.notBlank(deviceName, "deviceName is empty");
+		AssertUtil.notBlank(deviceSecret, "deviceSecret is empty");
+		AssertUtil.notBlank(http2EndPoint, "http2 EndPoint is empty");
+		logger.info("Initialize the bridge with {} & {}, {}", productKey, deviceName, http2EndPoint);
+		try {
+			Http2MessageClientFactory.initClient(productKey, deviceName, deviceSecret, http2EndPoint, isReconnect);
+		} catch (IotClientException e) {
+			String msg = "Failed to initialize HTTP2 client!" + e.getMessage();
+			logger.error(msg, e);
+			throw new BootException(msg);
+		}
+	}
+
+}

+ 182 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultBridgeConfigManager.java

@@ -0,0 +1,182 @@
+package org.jetlinks.community.bridge.core;
+
+import com.aliyun.iot.as.bridge.core.config.BridgeConfigConsts;
+import com.aliyun.iot.as.bridge.core.config.BridgeConfigManager;
+import com.aliyun.iot.as.bridge.core.exception.BootException;
+import com.aliyun.iot.as.bridge.core.model.PopClientConfiguration;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.net.*;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+@Getter
+@NoArgsConstructor
+public class DefaultBridgeConfigManager implements BridgeConfigManager {
+
+    /**
+     * 网桥设备所属产品的ProductKey。
+     */
+    private String bridgeProductKey;
+
+
+    /**
+     * 	网桥设备的DeviceName。
+     */
+    private String bridgeDeviceName;
+
+
+    /**
+     * 网桥设备的DeviceSecret。
+     */
+    private String bridgeDeviceSecret;
+
+    /**
+     * HTTP2网关服务地址。网桥设备和物联网平台通过HTTP2协议建立长连接通道。
+     */
+    private String http2Endpoint;
+
+    /**
+     * 	设备认证服务地址
+     */
+    private String authEndpoint;
+
+    private Map<String,Object> configMaps;
+
+    private PopClientConfiguration popConfiguration;
+
+    public static DefaultBridgeConfigManager of(String bridgeProductKey, String bridgeDeviceName, String bridgeDeviceSecret, String http2Endpoint,String authEndpoint, Map<String, Object> configMaps,PopClientConfiguration popConfiguration) {
+        return new DefaultBridgeConfigManager(bridgeProductKey,bridgeDeviceName,bridgeDeviceSecret,http2Endpoint,authEndpoint,configMaps,popConfiguration);
+    }
+
+    private DefaultBridgeConfigManager(String bridgeProductKey, String bridgeDeviceName, String bridgeDeviceSecret, String http2Endpoint,String authEndpoint,Map<String, Object> configMaps, PopClientConfiguration popConfiguration) {
+        this.bridgeProductKey = bridgeProductKey;
+        this.bridgeDeviceName = bridgeDeviceName;
+        this.bridgeDeviceSecret = bridgeDeviceSecret;
+        this.http2Endpoint = http2Endpoint;
+        this.configMaps = configMaps==null? new HashMap<>() :configMaps;
+        this.authEndpoint=authEndpoint;
+        this.popConfiguration = popConfiguration;
+    }
+
+    @Override
+    public String getBridgeInstanceId() {
+        return (String) configMaps.get(BridgeConfigConsts.BRIDGE_INSTANCEID_KEY);
+    }
+
+    @Override
+    public String getDeviceAuthEndpoint() {
+        return this.authEndpoint;
+    }
+
+    @Override
+    public String getBridgeNetId() {
+        return (String) configMaps.get(BridgeConfigConsts.NET_ID);
+    }
+
+    @Override
+    public void updateBridgeNetId(String netId) {
+        configMaps.put(BridgeConfigConsts.NET_ID,netId);
+    }
+
+    @Override
+    public String getBridgeNetworkKey() {
+        return (String) configMaps.get(BridgeConfigConsts.NETWORK_KEY);
+    }
+
+    @Override
+    public boolean isMultiGwMode() {
+        return Boolean.TRUE.equals(configMaps.get(BridgeConfigConsts.IS_MULTI_GW_MODE));
+    }
+
+    @Override
+    public String getSubDeviceConnectMode() {
+        return BridgeConfigConsts.SUBDEVICE_CONNECT_MODE_VALUE_3;
+    }
+
+    @Override
+    public String getBridgeProductSecret() {
+        return (String) configMaps.get(BridgeConfigConsts.BRIDGE_PRODUCT_SECRET);
+    }
+
+    @Override
+    public String getDeviceRegisterEndpoint() {
+        return (String) configMaps.get(BridgeConfigConsts.DEVICE_REGISTER_ENDPOINT);
+    }
+
+    @Override
+    public String getBridgeIotId() {
+        return (String) configMaps.get(BridgeConfigConsts.BRIDGE_DEVICE_ID);
+    }
+
+    @Override
+    public void updateBridgeIotId(String iotId) {
+        configMaps.put(BridgeConfigConsts.BRIDGE_DEVICE_ID,iotId);
+    }
+    @Override
+    public String getMacAddress() {
+        return getMac(getInetAddress());
+    }
+    @Override
+    public void updateBridgeDeviceSecret(String deviceSecret) {
+        this.bridgeDeviceSecret=deviceSecret;
+    }
+
+    @Override
+    public void updateBridgeDeviceName(String deviceName) {
+        this.bridgeDeviceName=deviceName;
+    }
+
+    private String getMac(InetAddress addr) {
+        try {
+            NetworkInterface net = NetworkInterface.getByInetAddress(addr);
+            byte[] macBytes = net.getHardwareAddress();
+            StringBuffer buffer = new StringBuffer();
+            for(int i = 0; i < macBytes.length; i++){
+                if(i != 0) { buffer.append("-"); }
+                int intMac = macBytes[i]&0xff;
+                String str = Integer.toHexString(intMac);
+                if(str.length() == 0){
+                    buffer.append("0");
+                }
+                buffer.append(str);
+            }
+            return buffer.toString().toUpperCase();
+        } catch (SocketException e) {
+            String msg = "Failed to resolve mac address, please check!";
+            throw new BootException(msg);
+        }
+    }
+
+    private InetAddress getInetAddress() {
+        try {
+            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+            while(interfaces.hasMoreElements()){
+                NetworkInterface netInterface= interfaces.nextElement();
+                if(netInterface.isLoopback() || !netInterface.isUp() || netInterface.getHardwareAddress() == null) {continue;}
+                Enumeration<InetAddress> inetAddresses = netInterface.getInetAddresses();
+                while(inetAddresses.hasMoreElements()) {
+                    InetAddress inetAdd = inetAddresses.nextElement();
+                    if (inetAdd instanceof Inet4Address) {
+                        return inetAdd;
+                    }
+                }
+            }
+        } catch (SocketException e) {
+            String msg = "Failed to resolve specified address, please check!";
+            throw new BootException(msg);
+        }
+        return getDefaultAddress();
+    }
+
+    private InetAddress getDefaultAddress() {
+        try {
+            return InetAddress.getLocalHost();
+        } catch (UnknownHostException e) {
+            String msg = "Failed to get ip address of localhost, please specify it manually!";
+            throw new BootException(msg);
+        }
+    }
+}

+ 61 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultDeviceConfigManager.java

@@ -0,0 +1,61 @@
+/**
+ * aliyun.com Inc.
+ * Copyright (c) 2004-2018 All Rights Reserved.
+ */
+package org.jetlinks.community.bridge.core;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.aliyun.iot.as.bridge.core.config.DeviceConfigConsts;
+import com.aliyun.iot.as.bridge.core.config.DeviceConfigManager;
+import com.aliyun.iot.as.bridge.core.config.impl.ConfigUtil;
+import com.aliyun.iot.as.bridge.core.model.DeviceIdentity;
+import com.aliyun.openservices.iot.api.util.StringUtil;
+import com.typesafe.config.Config;
+import com.typesafe.config.ConfigFactory;
+
+/**
+ * Default implementation of {@link DeviceConfigManager device configuration manager}. 
+ * It assumes the original identity of a device is of {@link String} type.
+ *
+ */
+public class DefaultDeviceConfigManager implements DeviceConfigManager{
+    private static DefaultDeviceConfigManager defaultDeviceConfigManager=new DefaultDeviceConfigManager();
+    private static ConcurrentMap<String, DeviceIdentity> configCache = new ConcurrentHashMap<>(500000);
+    private static ConcurrentMap<String, String> revertCache = new ConcurrentHashMap<>(500000);
+    private DefaultDeviceConfigManager() {
+    }
+
+    public static DefaultDeviceConfigManager getInstance(){
+        return defaultDeviceConfigManager;
+    }
+    public static void register(String originalIdentity,String productKey,String deviceName,String deviceSecret){
+        DeviceIdentity deviceIdentity = new DeviceIdentity(productKey, deviceName, deviceSecret);
+        configCache.putIfAbsent(originalIdentity,deviceIdentity);
+        revertCache.putIfAbsent(getMapKey(deviceIdentity), originalIdentity);
+    }
+
+    @Override
+    public DeviceIdentity getDeviceIdentity(String originalIdentity) {
+        // find in cache first
+        return  configCache.get(originalIdentity);
+    }
+
+    @Override
+    public String getOriginalIdentity(String productKey, String deviceName) {
+        if (StringUtil.isEmpty(productKey) || StringUtil.isEmpty(deviceName)) {
+            return null;
+        }
+        return revertCache.get(getMapKey(productKey, deviceName));
+    }
+
+    private static String getMapKey(DeviceIdentity identity) {
+        return getMapKey(identity.getProductKey(), identity.getDeviceName());
+    }
+
+    private static String getMapKey(String productKey, String deviceName) {
+        return productKey + deviceName;
+    }
+
+}

+ 179 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultDeviceServiceClient.java

@@ -0,0 +1,179 @@
+/**
+ * aliyun.com Inc.
+ * Copyright (c) 2004-2018 All Rights Reserved.
+ */
+package org.jetlinks.community.bridge.core;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import com.alibaba.fastjson.JSON;
+
+import com.aliyun.iot.as.bridge.core.config.BridgeConfigManager;
+import com.aliyun.iot.as.bridge.core.config.ConfigFactory;
+import com.aliyun.iot.as.bridge.core.exception.BridgeHttpException;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceAuthRequest;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceAuthResponse;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceRegisterRequest;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceRegisterResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultDeviceServiceClient {
+	private static final Logger logger = LoggerFactory.getLogger(DefaultDeviceServiceClient.class);
+	
+	private static final String HTTPS_PROTOCOL = "https";
+
+	private static final String PROTOCOL_SPLITER = "://";
+
+	private static final String METHOD = "POST";
+
+	private static final String CONTENT_TYPE_HEADER = "Content-Type";
+
+	private static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
+	
+	private static DefaultDeviceServiceClient instance;
+
+	private BridgeConfigManager bridgeConfigManager;
+
+    public static DefaultDeviceServiceClient getInstance(BridgeConfigManager bridgeConfigManager) {
+        if(instance != null) {
+            return instance;
+        }
+        instance = new DefaultDeviceServiceClient(bridgeConfigManager);
+        return instance;
+    }
+
+	private DefaultDeviceServiceClient() {
+		this.bridgeConfigManager = ConfigFactory.getBridgeConfigManager();
+	}
+
+    private DefaultDeviceServiceClient(BridgeConfigManager bridgeConfigManager) {
+        this.bridgeConfigManager = bridgeConfigManager==null?ConfigFactory.getBridgeConfigManager():bridgeConfigManager;
+    }
+	public DeviceAuthResponse auth(DeviceAuthRequest request, String deviceSecret) throws BridgeHttpException {
+		String requestUrl = bridgeConfigManager.getDeviceAuthEndpoint();
+		try {
+			request.setDeviceSecret(deviceSecret);
+			return doRequest(deviceSecret, requestUrl, request.buildRequestParam(), DeviceAuthResponse.class);
+		} catch (InvalidKeyException | NoSuchAlgorithmException e) {
+			logger.error("http sign exception " + request.getDeviceName(), e);
+		} catch (IOException e) {
+			logger.error("http IOException " + request.getProductKey() + ", " + request.getDeviceName(), e);
+			throw new BridgeHttpException(e);
+		}
+		return null;
+	}
+	
+	public DeviceRegisterResponse register(DeviceRegisterRequest request, String productSecret) {
+		String requestUrl = bridgeConfigManager.getDeviceRegisterEndpoint();
+		try {
+			return doRequest(productSecret, requestUrl, request.toRequest(productSecret), DeviceRegisterResponse.class);
+		} catch (InvalidKeyException | NoSuchAlgorithmException | IOException e) {
+			logger.error("Failed to register device {}", request.getDeviceName(), e);
+		}
+		
+		return null;
+	}
+
+	private <T> T doRequest(String secret, String requestUrl, String urlParams, Class<T> reponseClazz) throws InvalidKeyException, NoSuchAlgorithmException, IOException {
+		URL obj = new URL(requestUrl);
+		String protocol = requestUrl.split(PROTOCOL_SPLITER)[0];
+		if (HTTPS_PROTOCOL.equals(protocol.toLowerCase())) {
+			SSLContext ctx = null;
+			try {
+				ctx = SSLContext.getInstance("TLS");
+				ctx.init(new KeyManager[0], new TrustManager[] { new DefaultTrustManager() },
+					new SecureRandom());
+			} catch (Exception e) {
+				throw new IOException(e);
+			}
+			HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
+			con.setSSLSocketFactory(ctx.getSocketFactory());
+			con.setHostnameVerifier(new HostnameVerifier() {
+				public boolean verify(String hostname, SSLSession session) {
+					return true;
+				}
+			});
+			con.setRequestMethod(METHOD);
+			con.setRequestProperty(CONTENT_TYPE_HEADER, CONTENT_TYPE);
+			con.setDoOutput(true);
+			con.setConnectTimeout(3000);
+			con.setReadTimeout(5000);
+
+			// Send post request
+			logger.info("Sending 'POST' request to URL: {} " + requestUrl);
+			logger.info("Post parameters : " + urlParams);
+			DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+			wr.writeBytes(urlParams);
+			wr.flush();
+			wr.close();
+
+			int responseCode = con.getResponseCode();
+			logger.info("Response Code : " + responseCode);
+			BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+			String inputLine;
+			StringBuffer response = new StringBuffer();
+			while ((inputLine = in.readLine()) != null) {
+				response.append(inputLine);
+			}
+			in.close();
+			return JSON.parseObject(response.toString(), reponseClazz);
+		} else {
+			HttpURLConnection con = (HttpURLConnection) obj.openConnection();
+			con.setRequestMethod(METHOD);
+			con.setRequestProperty(CONTENT_TYPE_HEADER, CONTENT_TYPE);
+			con.setDoOutput(true);
+
+			// Send post request
+			logger.info("Sending 'POST' request to URL: {} " + requestUrl);
+			logger.info("Post parameters : " + urlParams);
+			DataOutputStream wr = new DataOutputStream(con.getOutputStream());
+			wr.writeBytes(urlParams);
+			wr.flush();
+			wr.close();
+
+			int responseCode = con.getResponseCode();
+			logger.info("Response Code : " + responseCode);
+			BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
+			String inputLine;
+			StringBuffer response = new StringBuffer();
+			while ((inputLine = in.readLine()) != null) {
+				response.append(inputLine);
+			}
+			in.close();
+			return JSON.parseObject(response.toString(), reponseClazz);
+		}
+	}
+
+	private class DefaultTrustManager implements X509TrustManager {
+		public X509Certificate[] getAcceptedIssuers() {
+			return null;
+		}
+
+		public void checkClientTrusted(X509Certificate[] chain, String authType)
+			throws CertificateException {
+		}
+
+		public void checkServerTrusted(X509Certificate[] chain, String authType)
+			throws CertificateException {
+		}
+	}
+}

+ 336 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/core/DefaultUplinkChannelHandler.java

@@ -0,0 +1,336 @@
+/**
+ * aliyun.com Inc.
+ * Copyright (c) 2004-2018 All Rights Reserved.
+ */
+package org.jetlinks.community.bridge.core;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson.JSON;
+
+import com.aliyun.iot.as.bridge.core.auth.AuthProvider;
+import com.aliyun.iot.as.bridge.core.auth.AuthProviderFactory;
+import com.aliyun.iot.as.bridge.core.client.DeviceServiceClient;
+import com.aliyun.iot.as.bridge.core.client.Http2MessageClientFactory;
+import com.aliyun.iot.as.bridge.core.client.IoTServiceClient;
+import com.aliyun.iot.as.bridge.core.config.BridgeConfigConsts;
+import com.aliyun.iot.as.bridge.core.config.BridgeConfigManager;
+import com.aliyun.iot.as.bridge.core.config.ConfigFactory;
+import com.aliyun.iot.as.bridge.core.config.DeviceConfigManager;
+import com.aliyun.iot.as.bridge.core.exception.BridgeHttpException;
+import com.aliyun.iot.as.bridge.core.model.BridgeMessagePayload;
+import com.aliyun.iot.as.bridge.core.model.BridgeMessagePayloadParams;
+import com.aliyun.iot.as.bridge.core.model.DeviceIdentity;
+import com.aliyun.iot.as.bridge.core.model.OfflineParams;
+import com.aliyun.iot.as.bridge.core.model.ProtocolMessage;
+import com.aliyun.iot.as.bridge.core.model.ServiceCallResponse;
+import com.aliyun.iot.as.bridge.core.model.Session;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceAuthRequest;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceAuthResponse;
+import com.aliyun.iot.as.bridge.core.model.device.DeviceOnlineResult;
+import com.aliyun.iot.as.bridge.core.session.SessionManager;
+import com.aliyun.iot.as.bridge.core.session.SessionManagerFactory;
+import com.aliyun.openservices.iot.api.message.entity.Message;
+import com.aliyun.openservices.iot.api.message.entity.MessageToken;
+import com.aliyun.openservices.iot.api.util.StringUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This handler will handle all the outgoing traffics to Aliyun IoT platform.
+ *
+ */
+public class DefaultUplinkChannelHandler {
+
+    public DefaultUplinkChannelHandler( BridgeConfigManager bridgeConfigManager,DeviceConfigManager deviceConfigManager) {
+        this.deviceConfigManager = deviceConfigManager;
+        this.bridgeConfigManager = bridgeConfigManager;
+        this.sessionManager = SessionManagerFactory.getInstance();
+        this.authProvider = AuthProviderFactory.getInstance();
+        this.servicClient = IoTServiceClient.getClient();
+        this.authClient = DeviceServiceClient.getInstance();
+    }
+
+    /**
+     * async publish message
+     * @param originalIdentity  original device identity
+     * @param protocolMsg msg to send
+     * @return result future
+     */
+    public CompletableFuture<ProtocolMessage> doPublishAsync(String originalIdentity, ProtocolMessage protocolMsg) {
+        return doPublishAsync(originalIdentity, protocolMsg.getTopic(), protocolMsg.getPayload(), protocolMsg.getQos());
+    }
+
+    /**
+     * async publish message
+     * @param originalIdentity original device identity
+     * @param topic message topic
+     * @param payload message payload bytes
+     * @param qos message qos (0 or 1)
+     * @return result future
+     */
+    public CompletableFuture<ProtocolMessage> doPublishAsync(String originalIdentity, String topic, byte[] payload,
+                                                             int qos) {
+        return doPublishAsyncImpl(originalIdentity, topic, payload, qos);
+    }
+
+    /**
+     * publish message
+     * @param originalIdentity original device identity
+     * @param protocolMsg message topic
+     * @param timeout timeout to wait for complete
+     * @return true if successfully publish in given timeout
+     */
+    public boolean doPublish(String originalIdentity, ProtocolMessage protocolMsg, int timeout) {
+        return doPublish(originalIdentity, protocolMsg.getTopic(), protocolMsg.getPayload(), protocolMsg.getQos(), timeout);
+    }
+
+    /**
+     * publish message
+     * @param originalIdentity original device identity
+     * @param topic message topic
+     * @param payload message payload
+     * @param qos message qos (0 or 1)
+     * @param timeout timeout to wait for complete
+     * @return true if successfully publish in given timeout
+     */
+    public boolean doPublish(String originalIdentity, String topic, byte[] payload, int qos, int timeout) {
+        return doPublishSyncImpl(originalIdentity, topic, payload, qos, timeout);
+    }
+
+    /**
+     * device online
+     * @param newSession device session
+     * @param originalIdentity device original identity
+     * @return true if successfully online
+     */
+    public boolean doOnline(Session newSession, String originalIdentity) {
+        DeviceOnlineResult result = doOnlineImpl(newSession, originalIdentity);
+        return result.hasSucceed();
+    }
+
+    /**
+     * device online with result
+     * @param newSession device session
+     * @param originalIdentity device original identity
+     * @return device online result detail
+     */
+    public DeviceOnlineResult doOnlineWithResult(Session newSession, String originalIdentity) {
+        return doOnlineImpl(newSession, originalIdentity);
+    }
+
+    @Deprecated
+    public boolean doOnline(String originalIdentity,
+                            String... credentials) {
+        return doOnline(null, originalIdentity, credentials);
+    }
+
+    @Deprecated
+    public boolean doOnline(Session newSession, String originalIdentity,
+                            String... credentials) {
+        DeviceOnlineResult result = doOnlineImpl(newSession, originalIdentity, credentials);
+        return result.hasSucceed();
+    }
+
+    @Deprecated
+    public DeviceOnlineResult doOnlineWithResult(Session newSession, String originalIdentity, String... credentials) {
+        return doOnlineImpl(newSession, originalIdentity);
+    }
+
+    /**
+     * device offline
+     * @param originalIdentity device original identity
+     * @return success or not
+     */
+    public boolean doOffline(String originalIdentity) {
+        return doOfflineImpl(originalIdentity);
+    }
+
+    @Deprecated
+    public boolean doOffline(String originalIdentity, int timeout) {
+        return doOfflineAndWaitForCloudResponse(originalIdentity, timeout);
+    }
+
+    private boolean doPublishSyncImpl(String originalIdentity, String topic, byte[] payload, int qos, int timeout) {
+        try {
+            CompletableFuture<ProtocolMessage> future = doPublishAsync(originalIdentity, topic, payload, qos);
+            future.get(timeout, TimeUnit.SECONDS);
+            return true;
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            logger.error(String.format(
+                "Failed to publish message for device[%s] of topic[%s] in [%ss] due to unexpected exception",
+                originalIdentity, topic, timeout));
+        }
+        return false;
+    }
+
+    private CompletableFuture<ProtocolMessage> doPublishAsyncImpl(String originalIdentity, String topic, byte[] payload,
+                                                                  int qos) {
+        DeviceIdentity identity = deviceConfigManager.getDeviceIdentity(originalIdentity);
+        BridgeMessagePayloadParams bridgePayloadParams = new BridgeMessagePayloadParams();
+        bridgePayloadParams.setDeviceName(identity.getDeviceName());
+        bridgePayloadParams.setProductKey(identity.getProductKey());
+        bridgePayloadParams.setPayload(payload);
+        bridgePayloadParams.setTopic(topic);
+        BridgeMessagePayload bridgePayload = new BridgeMessagePayload();
+        bridgePayload.setId(UUID.randomUUID().toString());
+        bridgePayload.setParams(bridgePayloadParams);
+        bridgePayload.setMethod(FAKE_METHOD);
+
+        String publishTopic = String.format(BridgeConfigConsts.PUB_TOPIC_TEMPLATE,
+            bridgeConfigManager.getBridgeProductKey(), bridgeConfigManager.getBridgeDeviceName());
+        Message message = new Message();
+        message.setPayload(JSON.toJSONString(bridgePayload).getBytes());
+        message.setQos(qos);
+
+        logger.info("Publish message : {}", message);
+        MessageToken token = Http2MessageClientFactory.getClient().publish(publishTopic, message);
+        CompletableFuture<ProtocolMessage> future = token.getPublishFuture().thenApply(msg -> {
+            String serizlizedPayload = new String(msg.getPayload(), StandardCharsets.UTF_8);
+            BridgeMessagePayload bridgePayloadSent = JSON.parseObject(serizlizedPayload, BridgeMessagePayload.class);
+            ProtocolMessage protocalMessageSent = new ProtocolMessage();
+            if (bridgePayloadSent != null && bridgePayloadSent.getParams() != null) {
+                protocalMessageSent.setPayload(bridgePayloadSent.getParams().getPayload());
+                protocalMessageSent.setQos(msg.getQos());
+                protocalMessageSent.setTopic(bridgePayloadSent.getParams().getTopic());
+            }
+            return protocalMessageSent;
+        });
+        return future;
+    }
+
+    private DeviceOnlineResult doOnlineImpl(Session newSession, String originalIdentity,
+                                            String... credentials) {
+        if (newSession == null) {
+            logger.error("[device-online-fail] param session is null for {}", originalIdentity);
+            return new DeviceOnlineResult(DeviceOnlineResult.PARAM_ERROR, "param session null");
+        }
+        boolean authorized = doAuth(credentials);
+        if (!authorized) {
+            logger.warn("[device-online-fail] Failed online {} with credential {}",
+                originalIdentity, String.join(",", credentials));
+            return new DeviceOnlineResult(DeviceOnlineResult.PARAM_ERROR, "local auth fail");
+        }
+
+        DeviceIdentity iotIdentity = deviceConfigManager.getDeviceIdentity(originalIdentity);
+        if (iotIdentity == null) {
+            logger.error("[device-online-fail] Failed to get device identity for device {}", originalIdentity);
+            return new DeviceOnlineResult(DeviceOnlineResult.PARAM_ERROR, "no originalIdentity");
+        }
+        String sessionId = sessionManager.createSession(newSession);
+        if (StringUtil.isEmpty(sessionId)) {
+            logger.error("[device-online-fail] Failed to create session for device {}", originalIdentity);
+            return new DeviceOnlineResult(DeviceOnlineResult.PARAM_ERROR, "fail to create local session");
+        }
+        newSession.setProductKey(iotIdentity.getProductKey());
+        newSession.setDeviceName(iotIdentity.getDeviceName());
+
+
+        DeviceAuthRequest request= new DeviceAuthRequest(iotIdentity.getProductKey(),iotIdentity.getDeviceName())
+            .setDeviceSecret(iotIdentity.getDeviceSecret())
+            .setSubDeviceConnectMode(bridgeConfigManager.getSubDeviceConnectMode())
+            .setGateway(false)
+            .setNetId(bridgeConfigManager.getBridgeNetId());
+        DeviceAuthResponse response = null;
+
+        if(StrUtil.isNullOrUndefined(bridgeConfigManager.getBridgeNetId())){
+            request.setGateway(true);
+        }
+        try {
+            response = DefaultDeviceServiceClient.getInstance(bridgeConfigManager).auth(request, iotIdentity.getDeviceSecret());
+        } catch (BridgeHttpException e) {
+            logger.error("[device-online-fail] network error for " + originalIdentity + ", "+ iotIdentity, e);
+            sessionManager.destroySession(originalIdentity);
+            return new DeviceOnlineResult(DeviceOnlineResult.NETWORK_ERROR, e.getMessage());
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+
+        if (response == null || !response.isSuccess() || response.getData() == null
+            || StringUtil.isEmpty(response.getData().getNetId())) {
+            sessionManager.destroySession(originalIdentity);
+            int code = Optional.ofNullable(response).map(DeviceAuthResponse::getCode).map(Integer::parseInt)
+                .orElse(DeviceOnlineResult.SERVER_RESULT_FAIL);
+            String message = Optional.ofNullable(response).map(JSON::toJSONString).orElse("server empty response");
+            logger.error("[device-online-fail] failed to pass authorization {}, {}, response: {}", originalIdentity,
+                iotIdentity, message);
+            return new DeviceOnlineResult(code, message);
+        } else {
+            logger.info("[device-online-success] device {} {} online success", originalIdentity, iotIdentity);
+        }
+        return new DeviceOnlineResult(DeviceOnlineResult.SUCCESS);
+    }
+
+    private boolean doAuth(String... credentials) {
+        if (credentials == null || credentials.length == 0) {
+            return true;
+        } else if (credentials.length == ONE_CREDENTIAL) {
+            return authProvider.auth(credentials[0]);
+        } else {
+            return authProvider.auth(credentials[0], credentials[1]);
+        }
+    }
+
+    private boolean doOfflineImpl(String originalIdentity) {
+        DeviceIdentity identity = deviceConfigManager.getDeviceIdentity(originalIdentity);
+        if (Objects.isNull(identity)
+            || StringUtil.isEmpty(identity.getProductKey())
+            || StringUtil.isEmpty(identity.getDeviceName())) {
+            logger.info("cannot find device config for {}", originalIdentity);
+            return false;
+        }
+        OfflineParams params = new OfflineParams();
+        params.setDeviceName(identity.getDeviceName());
+        params.setProductKey(identity.getProductKey());
+        params.setNetId(bridgeConfigManager.getBridgeNetId());
+        servicClient.callAsync(BridgeConfigConsts.OFFLINE_TOPIC_TEMPLATE, params)
+            .thenRun(() -> logger.info("device {} {} offline success", originalIdentity, identity));
+        return true;
+    }
+
+    private boolean doOfflineAndWaitForCloudResponse(String originalIdentity, int timeout) {
+        DeviceIdentity identity = deviceConfigManager.getDeviceIdentity(originalIdentity);
+        OfflineParams params = new OfflineParams();
+        params.setDeviceName(identity.getDeviceName());
+        params.setProductKey(identity.getProductKey());
+        params.setNetId(bridgeConfigManager.getBridgeNetId());
+        try {
+            ServiceCallResponse response = servicClient.call(BridgeConfigConsts.OFFLINE_TOPIC_TEMPLATE, timeout, params);
+            if(response.isSuccess()) {
+                sessionManager.destroySession(originalIdentity);
+                return true;
+            }
+            logger.error("Failed to offline device {} due to: {}", response.getMessage());
+        } catch (TimeoutException e) {
+            logger.warn("Failed to offline device {}, maybe the device has offline", originalIdentity);
+        }
+        return false;
+    }
+
+    private static final int ONE_CREDENTIAL = 1;
+
+    private static final String FAKE_METHOD = "fake";
+
+    protected DeviceConfigManager deviceConfigManager;
+
+    protected BridgeConfigManager bridgeConfigManager;
+
+    protected AuthProvider authProvider;
+
+    protected SessionManager<? extends Session> sessionManager;
+
+    protected IoTServiceClient servicClient;
+
+    protected DeviceServiceClient authClient;
+
+    protected static Logger logger = LoggerFactory.getLogger(DefaultUplinkChannelHandler.class);
+}
+

+ 75 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/entity/AliIotBridgeDeviceConfig.java

@@ -0,0 +1,75 @@
+package org.jetlinks.community.bridge.entity;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import org.hswebframework.ezorm.rdb.mapping.annotation.Comment;
+import org.hswebframework.web.api.crud.entity.GenericEntity;
+
+import javax.persistence.Column;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliIotBridgeDeviceConfig.java
+ * @Description 阿里云网桥配置
+ * @createTime 2021年11月26日 10:44:00
+ */
+@Data
+@Table(name = "ali_bridge_device", indexes = {
+    @Index(name = "deviceName", columnList = "device_name",unique = true),
+    @Index(name = "originalIdentity", columnList = "original_identity",unique = true),
+
+},
+schema = "阿里云设备网桥配置")
+@EqualsAndHashCode(callSuper = false)
+public class AliIotBridgeDeviceConfig extends GenericEntity<String> {
+    /**
+     * 网桥设备所属产品的ProductKey。
+     */
+    @NotNull
+    @Comment("设备所属产品的ProductKey")
+    @Column(name = "product_key",nullable = false)
+    @Schema(description = "设备所属产品的ProductKey")
+    private String productKey;
+    /**
+     * 网桥设备的DeviceName
+     */
+    @NotNull
+    @Comment("设备的DeviceName")
+    @Column(name = "device_name",nullable = false)
+    @Schema(description = "设备的DeviceName")
+    private String deviceName;
+    /**
+     * 网桥设备的DeviceSecret
+     */
+    @NotNull
+    @Comment("设备的DeviceSecret")
+    @Column(name = "device_secret",nullable = false)
+    @Schema(description = "设备的DeviceSecret")
+    private String deviceSecret;
+
+    /**
+     * 网桥id
+     * @see AliIotBridgeEntity
+     */
+    @NotNull
+    @Comment("设备挂载网桥id")
+    @Column(name = "bridge_id",nullable = false)
+    @Schema(description = "设备挂载网桥id")
+    private String bridgeId;
+
+    /**
+     * 网桥设备id
+     */
+    @NotNull
+    @Comment("设备id")
+    @Column(name = "original_identity",nullable = false)
+    @Schema(description = "设备id")
+    private String originalIdentity;
+}

+ 78 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/entity/AliIotBridgeEntity.java

@@ -0,0 +1,78 @@
+package org.jetlinks.community.bridge.entity;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import org.hswebframework.ezorm.rdb.mapping.annotation.ColumnType;
+import org.hswebframework.ezorm.rdb.mapping.annotation.Comment;
+import org.hswebframework.ezorm.rdb.mapping.annotation.JsonCodec;
+import org.hswebframework.web.api.crud.entity.GenericEntity;
+
+import javax.persistence.Column;
+import javax.persistence.Index;
+import javax.persistence.Table;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.sql.JDBCType;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliIotBridgeEntity.java
+ * @Description 阿里云与网桥的产品映射
+ * @createTime 2021年11月26日 09:35:00
+ */
+@Table(name = "ali_bridge", indexes = {
+    @Index(name = "product", columnList = "product_id")
+})
+@Getter
+@Setter
+public class AliIotBridgeEntity extends GenericEntity<String> {
+
+    @Comment("云云对接名称")
+    @Column(name = "name",nullable = false)
+    @Schema(description = "云云对接名称")
+    private String name;
+
+    @Comment("网桥产品id")
+    @Column(name = "product_id",nullable = false)
+    @Schema(description = "网桥产品id")
+    private String productId;
+
+    @Comment("说明")
+    @Column(name = "description",nullable = false)
+    @Schema(description = "说明")
+    private String description;
+
+    @Comment("认证配置")
+    @Column(name = "access_config",nullable = false)
+    @Schema(description = "认证配置")
+    @JsonCodec
+    @ColumnType(jdbcType = JDBCType.CLOB)
+    private AccessConfig accessConfig;
+
+    @Comment("云云协议")
+    @Column(name = "protocol",nullable = false)
+    @Schema(description = "云云协议")
+    private String protocol;
+
+
+    @Data
+    public static class AccessConfig implements Serializable {
+        @NotNull
+        private String productKey;
+        @NotNull
+        private String http2Endpoint;
+        @NotNull
+        private String authEndpoint;
+        @NotNull
+        private String regionId;
+        @NotNull
+        private String accessKey;
+        @NotNull
+        private String accessSecret;
+
+
+    }
+}

+ 538 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/exception/AliYunIotExceptionEnums.java

@@ -0,0 +1,538 @@
+package org.jetlinks.community.bridge.exception;
+
+import cn.hutool.core.util.EnumUtil;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliyunExceptionCode.java
+ * @Description TODO
+ * @createTime 2021年11月27日 08:56:00
+ */
+@AllArgsConstructor
+@Getter
+public enum AliYunIotExceptionEnums {
+    IotSystemSystemException("iot.system.SystemException","系统异常。请稍后重试。"),
+    IotSystemInstanceNotFound("iot.system.InstanceNotFound","实例ID不存在。请确保入参信息正确,然后重试。"),
+    IotSystemUidNotFound("iot.system.UidNotFound","阿里云账号不存在或未开通物联网服务。请确保阿里云账号正确并开通物联网服务,然后重试。"),
+    IotSystemInstanceIdSame("iot.system.InstanceIdSame","源实例ID和目标实例ID相同。请确保入参中的源实例ID和目标实例ID不同,然后重试。"),
+    IotSystemBillIsOverDue("Iot.System.BillIsOverDue","当前阿里云账号已欠费。请前往账号中心充值。"),
+    IotCommonInvalidPageParams("iot.common.InvalidPageParams","分页大小或者分页页号不合法。请参见具体API文档的分页相关参数描述,如PageSize。"),
+    IotCommonInvalidTenant("iot.common.InvalidTenant","不合法的租户。请确认阿里云账号信息和账号权限。"),
+    IotCommonQueryDeviceActionError("iot.common.QueryDeviceActionError","查询设备失败。请确保入参信息正确,然后重试。"),
+    IotCommonQueryDevicePropertyActionError("iot.common.QueryDevicePropertyActionError","查询设备属性失败。请确保入参信息正确,然后重试。"),
+    IotCommonQueryProductActionError("iot.common.QueryProductActionError","查询产品失败。请确保入参信息正确,然后重试。"),
+    IotCommonQueryProductCountActionError("iot.common.QueryProductCountActionError","查询产品总数失败。请确保入参信息正确,然后重试。"),
+    IotCommonRamActionPermissionDeny("iot.common.RamActionPermissionDeny","没有资源访问控制(RAM)权限。请参见RAM用户授权文档。"),
+    IotCommonAuthActionPermissionDeny("iot.common.AuthActionPermissionDeny","鉴权失败。原因可能是入参的设备信息不属于当前账号。请确认AccessKey信息和设备信息。"),
+    IotCheckParameterIsNotLessThanMax("iot.check.ParameterIsNotLessThanMax","参数A的值必须小于参数B的值。例如,参数startTime值必须小于参数endTime值。请确保入参信息符合上述规则,然后重试。"),
+    IotCommonAPINotSupportedInInstance("iot.common.APINotSupportedInInstance","当前实例不支持该API。公共实例尚未支持该功能。"),
+    IotCommonInvalidFormattedParameter("iot.common.InvalidFormattedParameter","参数格式错误。请确保入参信息正确,然后重试。"),
+    IotCommonIllegalFileBizCode("iot.common.IllegalFileBizCode","文件业务类型无效。请确保入参信息正确,然后重试。"),
+    IotCommonIllegalFileFormat("iot.common.IllegalFileFormat","文件格式无效。请确保入参信息正确,然后重试。"),
+    IotCommonFileNotExist("iot.common.FileNotExist","文件不存在。请确保入参信息正确,然后重试。"),
+    IotCommonUpdateThingModelOverLimit("iot.common.UpdateThingModelOverLimit","单个物模型TSL文件中,有效字符数量超出限制(256 KB,即256*1024个字符)。"),
+    IotProdAlreadyExistedProductName("iot.prod.AlreadyExistedProductName","已经存在相同的产品名称。一个阿里云账号下的产品名称不能重复。"),
+    IotProdCreateProductFailed("iot.prod.CreateProductFailed","创建产品失败。请确保入参信息正确,然后重试。"),
+    IotProdCreateProductTopicFailed("iot.prod.CreateProductTopicFailed","创建产品的Topic类失败。请确保入参信息正确,然后重试。"),
+    IotProdInvalidAliyunCommodityCode("iot.prod.InvalidAliyunCommodityCode","入参AliyunCommodityCode值错误。AliyunCommodityCode的可选值只有iothub_senior和iothub。"),
+    IotProdInvalidFormattedCatId("iot.prod.InvalidFormattedCatId","入参CategoryId(产品的设备类型)错误。"),
+    IotProdInvalidFormattedProductkey("iot.prod.InvalidFormattedProductkey","入参产品ProductKey格式错误。请核对输入的ProductKey值。"),
+    IotProdInvalidFormattedProductName("iot.prod.InvalidFormattedProductName","入参产品名称格式错误。产品名称格式请参见CreateProduct。"),
+    IotProdLongProductDesc("iot.prod.LongProductDesc","产品描述字符数超出限定值。描述信息应在100个字符以内。"),
+    IotProdInvalidNodeType("iot.prod.InvalidNodeType","产品的节点类型错误。节点类型支持的可选值请参见CreateProduct。"),
+    IotProdNotExistedProduct("iot.prod.NotExistedProduct","产品不存在。输入的ProductKey值在当前账号下不存在。"),
+    IotProdNotOpenID2Service("iot.prod.NotOpenID2Service","没有开通ID²服务。"),
+    IotProdNotSeniorProduct("iot.prod.NotSeniorProduct","产品不支持物模型。您创建产品时,定义为不使用物模型版的产品。"),
+    IotProdNullProductKey("iot.prod.NullProductKey","入参产品ProductKey不能为空。"),
+    IotProdNullProductName("iot.prod.NullProductName","入参产品名称不能为空。"),
+    IotProdProductCountExceedMax("iot.prod.ProductCountExceedMax","产品总数已超过最大限制数量。一个阿里云账号下最多可有1,000个产品。"),
+    IotProdQueryDeviceCountActionError("iot.prod.QueryDeviceCountActionError","查询产品下的设备总数失败。请确保入参信息正确,然后重试。"),
+    IotProdQueryProductAbilitiesFailed("iot.prod.QueryProductAbilitiesFailed","获取产品功能失败。请确保入参信息是否正确,如Identifier值等。"),
+    IotProdQueryProductAbilityFailed("iot.prod.QueryProductAbilityFailed","查询产品功能失败。请确保入参信息是否正确,如Identifier值等。"),
+    IotProdQueryProductListActionError("iot.prod.QueryProductListActionError","获取产品列表数据失败。请确保入参信息正确,然后重试。"),
+    IotProdUpdateProductFailed("iot.prod.UpdateProductFailed","更新产品信息失败。请确保入参信息正确,然后重试。"),
+    IotProdQueryExtendPropertyFailed("iot.prod.QueryExtendPropertyFailed","查询产品标签失败。请确保入参信息正确,然后重试。"),
+    IotProdInvalidScriptType("iot.prod.InvalidScriptType","数据解析脚本类型无效。"),
+    IotProdThingScriptExisted("iot.prod.ThingScriptExisted","产品下已存在创建或更新的数据解析脚本。"),
+    IotProdConcurrentScriptOperation("iot.prod.ConcurrentScriptOperation","操作数据解析脚本失败。请确保不要并发操作数据解析脚本。"),
+    IotProdThingScriptDataFormatError("iot.prod.ThingScriptDataFormatError","产品的数据解析脚本类型错误。请确保入参信息正确,然后重试。"),
+    IotProdBatchCreateThingModelFailed("iot.prod.BatchCreateThingModelFailed","创建物模型失败。请确保入参信息正确,然后重试。"),
+    IotProdThingModelNameDuplicated("iot.prod.ThingModelNameDuplicated","物模型中存在重复的功能定义名称。请更换功能定义名称,然后重试。"),
+    IotProdNotProductOwner("iot.Prod.NotProductOwner","当前操作者不是产品的拥有者。请确认访问的产品是否已有授权,然后重试。"),
+    IotProdProductDataFormatIsNull("iot.prod.ProductDataFormatIsNull","数据解析脚本提交失败。您创建产品时,请求参数DataFormat值为空。请确保创建产品时,已设置数据格式,请参见CreateProduct。"),
+    IotDeviceNotDeviceOwner("Iot.Device.NotDeviceOwner","当前操作的用户不是设备的拥有者。"),
+    IotDeviceAddTopoRelationFailed("iot.device.AddTopoRelationFailed","添加拓扑关系失败。请确保入参信息正确,然后重试。"),
+    IotDeviceAlreadyExistedDeviceName("iot.device.AlreadyExistedDeviceName","设备名称已经存在。设备名称需在产品维度唯一。"),
+    IotDeviceApplyManyDevicesFailed("iot.device.ApplyManyDevicesFailed","申请批量创建设备失败。请确保入参信息正确,然后重试。"),
+    IotDeviceCreateDeviceFailed("iot.device.CreateDeviceFailed","创建设备失败。请确保入参信息正确,然后重试。"),
+    IotDeviceCreateDeviceTaskIsRunning("iot.device.CreateDeviceTaskIsRunning","创建设备的申请任务还在执行中。"),
+    IotDeviceDeviceApplyIsNotFound("iot.device.DeviceApplyIsNotFound","申请设备的申请单不存在。请确保输入的ApplyId值,与您调用BatchCheckDeviceNames返回的ApplyId值一致。"),
+    IotDeviceDeviceCountExceeded("iot.device.DeviceCountExceeded","批量申请的设备数量超过最大值。单次调用,最多批量注册1,000个设备。"),
+    IotDeviceDeleteDeviceFailed("iot.device.DeleteDeviceFailed","删除设备失败。请确保入参信息正确,然后重试。"),
+    IotDeviceDeleteDevicePropertyFailed("iot.device.DeleteDevicePropertyFailed","删除设备属性失败。请确保入参信息正确,然后重试。"),
+    IotDeviceDisableDeviceFailed("iot.device.DisableDeviceFailed","禁用设备失败。请确保入参信息正确,然后重试。"),
+    IotDeviceEnableDeviceFailed("iot.device.EnableDeviceFailed","启用设备失败。请确保入参信息正确,然后重试。"),
+    IotDeviceInactiveDevice("iot.device.InactiveDevice","设备未激活,即物理设备从未连接物联网平台。"),
+    IotDeviceInvalidFormattedApplyId("iot.device.InvalidFormattedApplyId","创建设备的申请单(ApplyId)错误。其值需与您调用BatchCheckDeviceNames返回的ApplyId值一致。"),
+    IotDeviceIncorrentDeviceApplyInfo("iot.device.IncorrentDeviceApplyInfo","设备申请信息错误。请确认入参信息,如ApplyId等。"),
+    IotDeviceInvalidFormattedDeviceName("iot.device.InvalidFormattedDeviceName","设备名称格式错误。设备名称格式请参见RegisterDevice。"),
+    IotDeviceInvalidDeviceNickNameExisted("iot.device.InvalidDeviceNickNameExisted","设备名称列表中存在不合法的设备备注名称。具体错误请查看返回的不合法设备备注名称列表。"),
+    IotDeviceInvalidFormattedDevicePropertyKey("iot.device.InvalidFormattedDevicePropertyKey","设备属性标识符格式错误。请查看相关API文档中,关于入参属性格式的描述。"),
+    IotDeviceInvalidFormattedDevicePropertiesString("iot.device.InvalidFormattedDevicePropertiesString","入参设备属性格式错误。请查看相关API文档中,关于入参属性格式的描述。"),
+    IotDeviceInvalidIoTId("iot.device.InvalidIoTId","设备ID错误。请调用QueryDeviceDetail或QueryDevice查看正确的IotId值,或用ProductKey与DeviceName组合代替IotId。"),
+    IotDeviceInvalidTimeBucket("iot.device.InvalidTimeBucket","指定的时间区间不合法。请查看相关API文档的参数描述,输入正确的参数。"),
+    IotDeviceInvokeThingServiceFailed("iot.device.InvokeThingServiceFailed","调用设备服务失败。请检查输入参数是否正确,如Args的参数格式和取值等。"),
+    IotDeviceLongDevicePropertiesString("iot.device.LongDevicePropertiesString","入参设备属性长度超过最大值。请查看相关API文档的限制说明。"),
+    IotDeviceNoneDeviceNameElement("iot.device.NoneDeviceNameElement","设备名称列表为空。"),
+    IotDeviceNoneDeviceProperties("iot.device.NoneDeviceProperties","没有有效的设备属性。请核对传入的属性Identifier是否与TSL中定义的一致。"),
+    IotDeviceNotExistedDevice("iot.device.NotExistedDevice","设备不存在。传入的设备IotId、ProductKey或DeviceName值错误。请调用QueryDeviceDetail或QueryDevice查看正确值。"),
+    IotDeviceNullApplyId("iot.device.NullApplyId","创建设备的申请ID(ApplyId)不能为空。"),
+    IotDeviceNullDeviceName("iot.device.NullDeviceName","设备名称不能为空。"),
+    IotDeviceNullDevicePropertyKey("iot.device.NullDevicePropertyKey","设备属性名称不能为空。"),
+    IotDeviceNullDevicePropertiesString("iot.device.NullDevicePropertiesString","入参设备属性不能为空。"),
+    IotDeviceQueryDeviceApplyActionError("iot.device.QueryDeviceApplyActionError","查询设备申请单信息出错。请确保入参信息正确,然后重试。"),
+    IotDeviceQueryDeviceAttrDataHistoryFailed("iot.device.QueryDeviceAttrDataHistoryFailed","获取设备属性数据历史记录失败。请确保入参信息正确,然后重试。"),
+    IotDeviceQueryDeviceAttrStatusFailed("iot.device.QueryDeviceAttrStatusFailed","获取设备属性状态信息失败。请确保入参信息正确,然后重试。"),
+    IotDeviceQueryDeviceEventHistoryFailed("iot.device.QueryDeviceEventHistoryFailed","获取设备事件调用记录失败。请确保入参信息正确,然后重试。"),
+    IotDeviceQueryDeviceListActionError("iot.device.QueryDeviceListActionError","查询设备列表失败。请确保入参信息正确,然后重试。"),
+    IotDeviceQueryDeviceServiceHistoryFailed("iot.device.QueryDeviceServiceHistoryFailed","获取设备服务调用记录失败。请确保入参信息正确,然后重试。"),
+    IotDeviceQueryDeviceStatisticsFailed("iot.device.QueryDeviceStatisticsFailed","查询设备统计信息失败。请确保入参信息正确,然后重试。"),
+    IotDeviceQueryDeviceStatusFailed("iot.device.QueryDeviceStatusFailed","查询设备状态信息失败。请确保入参信息正确,然后重试。"),
+    IotDeviceQueryTopoRelationFailed("iot.device.QueryTopoRelationFailed","查询拓扑关系失败。请确保入参信息是否正确。如,传入的PageSize值大于限定值50会报此错误。"),
+    IotDeviceRemoveTopoRelationFailed("iot.device.RemoveTopoRelationFailed","移除拓扑关系失败。请确保入参信息正确,然后重试。"),
+    IotDeviceSaveOrUpdateDevicePropertiesFailed("iot.device.SaveOrUpdateDevicePropertiesFailed","新增或者修改设备属性失败。请确保入参信息正确,然后重试。"),
+    IotDeviceSetDevicePropertyFailed("iot.device.SetDevicePropertyFailed","设置设备属性失败。请检查入参Items的参数值和格式是否正确,指定的属性是否是读写型。"),
+    IotDeviceTooManyDevicePropertiesPerTime("iot.device.TooManyDevicePropertiesPerTime","传入的属性个数超过限定值。请参见相关API文档限制说明。"),
+    IotDeviceTopoRelationCountExceeded("iot.device.TopoRelationCountExceeded","拓扑关系数量过多。请参见使用限制中网关与子设备数量限制。"),
+    IotDeviceVerifyDeviceFailed("iot.device.VerifyDeviceFailed","验证设备失败。请确保入参信息正确,然后重试。"),
+    IotDevicesearchInvalidSQLError("iot.devicesearch.InvalidSQLError","不合法的SQL语句。"),
+    IotDevicesearchInvalidSQLOffsetError("iot.devicesearch.InvalidSQLOffsetError","不合法的SQL偏移量。"),
+    IotDevicesearchInvalidSQLFieldError("iot.devicesearch.InvalidSQLFieldError","SQL包含不合法的字段名。"),
+    IotDevicesearchInvalidSQLConditionError("iot.devicesearch.InvalidSQLConditionError","SQL的连接词个数超过限制。"),
+    IotDevicesearchInvalidSQLOrderError("iot.devicesearch.InvalidSQLOrderError","SQL的排序规则错误。"),
+    IotDevicesearchInvalidSQLOperatorTypeError("iot.devicesearch.InvalidSQLOperatorTypeError","SQL运算符和数据类型不匹配。"),
+    IotDevicesearchInvalidSQLParamError("iot.devicesearch.InvalidSQLParamError","不支持的SQL语法。"),
+    IotDeviceDeviceHasDistributed("iot.device.DeviceHasDistributed","同名设备已被分发。请更换设备名称,然后重试。"),
+    IotDeviceFuzzyRuntimeSearchMinLengthLimit("iot.device.FuzzyRuntimeSearchMinLengthLimit","模糊搜索的内容长度小于5个字符。请重新输入搜索内容,长度大于或等于5个字符,然后重试。"),
+    IotDeviceRegionNotSupportDynamicGroup("iot.device.RegionNotSupportDynamicGroup","当前地域不支持动态分组功能。仅华东2(上海)地域支持动态分组功能。"),
+    IotProvisioningRunningTaskExcceedLimit("iot.provisioning.RunningTaskExcceedLimit","当前阿里云账号的并行分发任务已超出限制。单个阿里云账号下,产品分发和设备分发的任务总数不能超过10个。"),
+    IotProvisioningTaskNotFinish("iot.provisioning.TaskNotFinish","分发任务未完成。"),
+    IotProvisioningTaskIsProcessing("iot.provisioning.TaskIsProcessing","分发任务还在进行中。"),
+    IotProvisioningDeviceIsInAnotherTask("iot.provisioning.DeviceIsInAnotherTask","当前设备在其它分发任务中。"),
+    IotProvisioningTaskNotExist("iot.provisioning.TaskNotExist","分发任务不存在。"),
+    IotProvisioningTaskAlreadyFinish("iot.provisioning.TaskAlreadyFinish","分发任务已完成。"),
+    IotProvisioningUidAndTaskNotMatch("iot.provisioning.UidAndTaskNotMatch","当前阿里云账号ID不是任务创建者的账号ID。"),
+    IotProvisioningDeviceIsDistributing("iot.provisioning.DeviceIsDistributing","设备分发中。"),
+    IotProvisioningProductIsDistributing("iot.provisioning.ProductIsDistributing","产品分发中。"),
+    IotProvisioningAccountNotFound("iot.provisioning.AccountNotFound","输入的阿里云账号不存在。"),
+    IotProvisioningTargetAccountParamsError("iot.provisioning.TargetAccountParamsError","账号信息不能为空。"),
+    IotProvisioningCrmServiceError("iot.provisioning.CrmServiceError","阿里云账号服务异常。"),
+    IotProvisioningInstanceOperationDeny("iot.provisioning.InstanceOperationDeny","目标实例不属于目标阿里云账号。"),
+    IotProvisioningSourceInstanceNotFound("iot.provisioning.SourceInstanceNotFound","源实例ID不存在。"),
+    IotProvisioningTargetInstanceNotFound("iot.provisioning.TargetInstanceNotFound","目标实例ID不存在。"),
+    IotProvisioningCaptchaNotNull("iot.provisioning.CaptchaNotNull","该操作需要填写授权码。"),
+    IotProvisioningCaptchaNotFound("iot.provisioning.CaptchaNotFound","授权码错误。"),
+    IotProvisioningSendCaptchaFlowControl("iot.provisioning.SendCaptchaFlowControl","操作频繁,1分钟后重试。"),
+    IotProvisioningLimitExceeded("iot.provisioning.LimitExceeded","分发设备数超出限制。"),
+    IotProvisioningFailGetLogLat("iot.provisioning.FailGetLogLat","获取经纬度信息失败。"),
+    IotProvisioningNotCrossTenantId("iot.provisioning.NotCrossTenantId","该操作不支持跨阿里云账号。"),
+    IotProvisioningErrorStrategy("iot.provisioning.ErrorStrategy","不支持该分发策略。"),
+    IotProvisioningInstanceRegionConflict("iot.provisioning.InstanceRegionConflict","每个地域只能选择一个实例ID。"),
+    IotProvisioningNotDeviceOwner("iot.provisioning.NotDeviceOwner","当前操作者不是设备的拥有者。"),
+    IotProvisioningNextTokenNotNull("iot.provisioning.NextTokenNotNull","请求参数NextToken不能为空。详细说明,请参见ListDeviceDistributeJob。"),
+    IotSecuretunnelInstanceIdMismatch("iot.securetunnel.InstanceIdMismatch","实例ID不匹配。请确保入参信息正确,然后重试。"),
+    IotSecuretunnelTunnelNotExist("iot.securetunnel.TunnelNotExist","设备安全隧道不存在。"),
+    IotSecuretunnelIllegalState("iot.securetunnel.IllegalState","设备安全隧道状态不正确。"),
+    IotSecuretunnelParamInvalid("iot.securetunnel.ParamInvalid","设备安全隧道API参数错误。请参见API参数说明,确保参数信息正确,然后重试。"),
+    IotSecuretunnelDescriptionTooLong("iot.securetunnel.DescriptionTooLong","创建设备安全隧道时,入参Description内容超出长度限制(1024个字符)。"),
+    IotSecuretunnelUdiTooLong("iot.securetunnel.UdiTooLong","创建设备安全隧道时,入参Udi内容超出长度限制(4096个字符)。"),
+    IotSecuretunnelTunnelAmountExceedByDevice("iot.securetunnel.TunnelAmountExceedByDevice","当前设备下安全隧道数量超过限制(10个)。"),
+    IotSecuretunnelTunnelAmountExceedByInstance("iot.securetunnel.TunnelAmountExceedByInstance","当前实例下,安全隧道数量超过限制(1,000个)。"),
+    IotSecuretunnelDeviceNotExist("iot.securetunnel.DeviceNotExist","设备不存在。"),
+    IotSecuretunnelDeviceNotOnline("iot.securetunnel.DeviceNotOnline","设备不在线。"),
+    IotSecuretunnelDeviceNotSpecified("iot.securetunnel.DeviceNotSpecified","设备未指定。"),
+    IotJobJobNotExist("iot.job.JobNotExist","任务不存在。"),
+    IotJobListTaskParamJobIdAndDeviceNull("iot.job.ListTaskParamJobIdAndDeviceNull","请求参数JobId和Device不能同时为空。"),
+    IotJobListTaskParamDeviceInfoInvalid("iot.job.ListTaskParamDeviceInfoInvalid","请求参数Device中设备信息不合法。productKey和deviceName不能同时为空。"),
+    IotJobGenerateFileUploadUrlFailed("iot.job.GenerateFileUploadUrlFailed","生成文件上传URL失败。"),
+    IotJobParamsError("iot.job.ParamsError","参数异常。请确保入参信息正确,然后重试。"),
+    IotJobJobInvokeServiceSidError("iot.job.JobInvokeServiceSidError","设备批量服务调用任务中,JobDocument中serviceIdentifier参数错误。"),
+    IotJobJobSetPropertyParamsError("iot.job.JobSetPropertyParamsError","设备批量属性设置任务中,JobDocument中params参数错误。"),
+    IotJobJobCountExceedLimit("iot.job.JobCountExceedLimit","任务数量超出限制。一个阿里云账号下,单个地域最多可添加10,000个任务。"),
+    IotJobIllegalTargetDevices("iot.job.IllegalTargetDevices","请求参数TargetConfig中的TargetDevices包含不合法的设备。"),
+    IotJobQueryJobFailed("iot.job.QueryJobFailed","查询任务信息失败。"),
+    IotJobNotJobOwner("iot.job.NotJobOwner","您不是该任务的拥有者。"),
+    IotJobUpdateJobFailed("iot.job.UpdateJobFailed","更新任务下作业配置失败。"),
+    IotJobJobActionForbidden("iot.job.JobActionForbidden","任务当前状态,禁止该类型的操作。"),
+    IotJobCancelJobFailed("iot.job.CancelJobFailed","取消任务失败。"),
+    IotJobDeleteJobFailed("iot.job.DeleteJobFailed","删除任务失败。"),
+    IotJobQueryJobStaticsFailed("iot.job.QueryJobStaticsFailed","查询任务信息失败。"),
+    IotJobQueryTaskFailed("iot.job.QueryTaskFailed","查询指定任务下作业失败。"),
+    IotJobTaskIdIsNull("iot.job.TaskIdIsNull","请求参数TaskId不能为空。"),
+    IotJobTaskNotExist("iot.job.TaskNotExist","查询的任务下作业不存在。"),
+    IotJobNotTaskOwne("iot.job.NotTaskOwne","您不是该设备作业的拥有者。"),
+    IotJobJobFileInSecure("iot.job.JobFileInSecure","创建任务中上传的文件内容不合法。请确保文件内容正确,然后重试。"),
+    IotJobReRunJobFailed("iot.job.ReRunJobFailed","任务重试失败。因并发操作,导致接口调用发生冲突,请稍后重试。"),
+    IotGroupNullGroupId("iot.group.NullGroupId","入参分组ID没有赋值。"),
+    IotGroupDeleteGroupFailed("iot.group.DeleteGroupFailed","删除分组失败。请确保入参信息正确,然后重试。"),
+    IotGroupSubGroupNotNull("iot.group.SubGroupNotNull","分组下有子分组。当分组下有子分组时,不能删除分组,需先删除子分组。"),
+    IotGroupInvalidGroupName("iot.group.InvalidGroupName","分组名称不合法。分组名称可包含中文汉字、英文字母、数字和下划线(_)。长度范围4~30个字符(一个中文汉字占二个字符)。"),
+    IotGroupGroupNameExisted("iot.group.GroupNameExisted","分组名称已存在。"),
+    IotGroupQueryGroupInfoFailed("iot.group.QueryGroupInfoFailed","查询分组详情失败。请确保入参信息正确,然后重试。"),
+    IotGroupNotExistedGroup("iot.group.NotExistedGroup","分组不存在。请确认GroupId值。"),
+    IotGroupQueryGroupCountFailed("iot.group.QueryGroupCountFailed","查询分组数量失败。请确保入参信息正确,然后重试。"),
+    IotGroupQueryGroupListFailed("iot.group.QueryGroupListFailed","查询分组列表失败。请确保入参信息正确,然后重试。"),
+    IotGroupBindGroupRelationFailed("iot.group.BindGroupRelationFailed","绑定分组关系失败。请确保入参信息正确,然后重试。"),
+    IotGroupUpdateGroupFailed("iot.group.UpdateGroupFailed","修改分组信息失败。请确保入参信息正确,然后重试。"),
+    IotGroupQueryGroupTreeFailed("iot.group.QueryGroupTreeFailed","获取分组关系结构失败。请确保入参信息正确,然后重试。"),
+    IotGroupCreateGroupFailed("iot.group.CreateGroupFailed","创建分组失败。请确保入参信息正确,然后重试。"),
+    IotGroupInvalidFormattedTagString("iot.group.InvalidFormattedTagString","标签格式不合法。标签数据为JSON格式。由标签的tagKey和tagValue组成,tagKey和tagValue均不能为空。"),
+    IotGroupTagCountExceedMax("iot.group.TagCountExceedMax","标签数量超过最大值。每个分组最多可有100个标签。"),
+    IotGroupGroupCountExceedMax("iot.group.GroupCountExceedMax","分组数量超过最大值。一个分组最多可包含100个子分组。一个设备最多可以被添加到10个分组中。"),
+    IotGroupSetGroupTagFailed("iot.group.SetGroupTagFailed","设置分组标签信息失败。请确保入参信息正确,然后重试。"),
+    IotGroupQueryGroupTagFailed("iot.group.QueryGroupTagFailed","查询分组标签信息失败。请确保入参信息正确,然后重试。"),
+    IotGroupLongGroupDescError("iot.group.LongGroupDescError","分组描述字段过长。分组描述长度限制为100个字符(一个中文汉字占一个字符)。"),
+    IotGroupQueryGroupRelationFailed("iot.group.QueryGroupRelationFailed","查询分组关系失败。请确保入参信息正确,然后重试。"),
+    IotGroupGroupLevelExceedingLimitError("iot.group.GroupLevelExceedingLimitError","分组层级超过限制。分组只支持三级嵌套,即分组>子分组>子子分组。"),
+    IotGroupSuperGroupUnsupport("iot.group.SuperGroupUnsupport","子分组不支持添加到当前父组。"),
+    IotMessagebrokerCreateTopicRouteFailed("iot.messagebroker.CreateTopicRouteFailed","创建Topic之间消息路由失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerCreateTopicTemplateException("iot.messagebroker.CreateTopicTemplateException","创建Topic类过程发生异常。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerCreateTopicTemplateFailed("iot.messagebroker.CreateTopicTemplateFailed","创建Topic类失败。请确保入参信息正确,然后重试。"),
+    IotMessageBrokerBatchSubTopicFailed("iot.MessageBroker.BatchSubTopicFailed","批量订阅Topic失败。请确保入参信息正确,然后重试。"),
+    IotMessageBrokerEmptySubTopicList("iot.MessageBroker.EmptySubTopicList","订阅的Topic列表为空。"),
+    IotMessagebrokerDeleteTopicTemplateException("iot.messagebroker.DeleteTopicTemplateException","删除Topic类过程发生异常。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerDeleteTopicTemplateFailed("iot.messagebroker.DeleteTopicTemplateFailed","删除Topic类失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerDestTopicNameArraySizeIsLarge("iot.messagebroker.DestTopicNameArraySizeIsLarge","同一消息源Topic配置的路由目标Topic数量超过最大限制数。一个源Topic最多可对应100个目标Topic。"),
+    IotMessagebrokerDeleteTopicRouteFailed("iot.messagebroker.DeleteTopicRouteFailed","删除指定Topic间的路由失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerDesireInfoInShadowMessageIsNotJson("iot.messagebroker.DesireInfoInShadowMessageIsNotJson","设备影子中的desire信息不是JSON格式。"),
+    IotMessagebrokerDesireValueIsNullInShadowMessage("iot.messagebroker.DesireValueIsNullInShadowMessage","设备影子中的desire信息值为空。"),
+    IotMessagebrokerElementKeyOrValueIsNullInDesire("iot.messagebroker.ElementKeyOrValueIsNullInDesire","desire信息包含有空的属性标识符或者属性值。"),
+    IotMessagebrokerElementKeyOrValueIsNullInReport("iot.messagebroker.ElementKeyOrValueIsNullInReport","report信息包含有空的属性标识符或者属性值。"),
+    IotMessagebrokerHALFCONN("iot.messagebroker.HALFCONN","由于设备为半连接状态导致失败。"),
+    IotMessagebrokerInvalidFormattedSrcTopicName("iot.messagebroker.InvalidFormattedSrcTopicName","消息源Topic名称格式错误。可在控制台设备详情页的Topic列表下查看设备的Topic。"),
+    IotMessagebrokerInvalidFormattedTopicName("iot.messagebroker.InvalidFormattedTopicName","Topic格式错误。可在控制台设备详情页的Topic列表下查看设备的Topic。"),
+    IotMessagebrokerInvalidFormattedTopicTemplateId("iot.messagebroker.InvalidFormattedTopicTemplateId","Topic类ID格式错误。可调用QueryProductTopic查看TopicId。"),
+    IotMessagebrokerInvalidTimeoutValue("iot.messagebroker.InvalidTimeoutValue","超时时间参数设置有误。请参见相关API文档查看时间设置方法。"),
+    IotMessagebrokerInvalidTopicTemplateOperationValue("iot.messagebroker.InvalidTopicTemplateOperationValue","Topic类的操作权限值错误。操作权限取值:SUB:订阅。PUB:发布。ALL:发布和订阅。"),
+    IotMessagebrokerInvalidVersionValueInShadowMessage("iot.messagebroker.InvalidVersionValueInShadowMessage","设备影子中的version值错误。"),
+    IotMessagebrokerMethodValueIsNotUpdate("iot.messagebroker.MethodValueIsNotUpdate","设备影子中的method信息值不是update。"),
+    IotMessagebrokerMessageContentIsNotBase64Encode("iot.messagebroker.MessageContentIsNotBase64Encode","消息内容没有经过base64编码。"),
+    IotMessagebrokerNoneElementInDesire("iot.messagebroker.NoneElementInDesire","desire信息中没有属性。"),
+    IotMessagebrokerNoneElementInReport("iot.messagebroker.NoneElementInReport","report信息中没有属性。"),
+    IotMessagebrokerNoneElementDestTopicNameInArray("iot.messagebroker.NoneElementDestTopicNameInArray","目标Topic列表中没有元素。"),
+    IotMessagebrokerNotFoundDesireInShadowMessage("iot.messagebroker.NotFoundDesireInShadowMessage","设备影子的state信息中没有desire信息。"),
+    IotMessagebrokerNotFoundMethodInShadowMessage("iot.messagebroker.NotFoundMethodInShadowMessage","设备影子没有method信息。"),
+    IotMessagebrokerNotFoundReportInShadowMessage("iot.messagebroker.NotFoundReportInShadowMessage","设备影子中没有report信息。"),
+    IotMessagebrokerNotFoundStateInShadowMessage("iot.messagebroker.NotFoundStateInShadowMessage","设备影子中没有state信息。"),
+    IotMessagebrokerNotFoundVersionOrNullVersionValue("iot.messagebroker.NotFoundVersionOrNullVersionValue","缺少version信息或者version值为空。"),
+    IotMessagebrokerNotMatchedProductKeyWithSrcTopicOwner("iot.messagebroker.NotMatchedProductKeyWithSrcTopicOwner","消息源Topic对应的产品ProductKey不属于当前用户。"),
+    IotMessagebrokerNullMessageContent("iot.messagebroker.NullMessageContent","消息内容不能为空。"),
+    IotMessagebrokerNullShadowMessage("iot.messagebroker.NullShadowMessage","设备影子内容不能为空。"),
+    IotMessagebrokerNullSrcTopicName("iot.messagebroker.NullSrcTopicName","消息源Topic名称不能为空。"),
+    IotMessagebrokerNullTopicName("iot.messagebroker.NullTopicName","Topic不能为空。"),
+    IotMessagebrokerNullTopicTemplateId("iot.messagebroker.NullTopicTemplateId","Topic类ID不能为空。"),
+    IotMessagebrokerNullTopicTemplateOperation("iot.messagebroker.NullTopicTemplateOperation","Topic类的操作权限不能为空。"),
+    IotMessagebrokerOFFLINE("iot.messagebroker.OFFLINE","由于设备离线导致失败。"),
+    IotMessagebrokerPublishMessageException("iot.messagebroker.PublishMessageException","发送消息过程出现异常。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerPublishMessageFailed("iot.messagebroker.PublishMessageFailed","发送消息失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerQueryDeviceShadowActionError("iot.messagebroker.QueryDeviceShadowActionError","查询设备影子失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerQueryProductTopicListActionError("iot.messagebroker.QueryProductTopicListActionError","获取Topic类列表失败。请确保入参信息正确,然后重试。"),
+    IotMessageborkerQueryTopicReverseRouteTableListActionError("iot.messageborker.QueryTopicReverseRouteTableListActionError","获取消息反向路由列表(即消息源Topic列表)失败。请确保入参信息正确,然后重试。"),
+    IotMessageborkerQueryTopicRouteTableListActionError("iot.messageborker.QueryTopicRouteTableListActionError","获取消息路由列表失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerQueryTopicTemplateActionError("iot.messagebroker.QueryTopicTemplateActionError","查询Topic类失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerQueryTopicTemplateException("iot.messagebroker.QueryTopicTemplateException","获取Topic类过程发生异常。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerRateLimit("iot.messagebroker.RateLimit","由于限流导致失败。请参见使用限制。"),
+    IotMessagebrokerReportInShadowMessageIsNotJson("iot.messagebroker.ReportInShadowMessageIsNotJson","设备影子中的state信息中的report信息不是JSON格式。"),
+    IotMessagebrokerRrpcException("iot.messagebroker.RrpcException","RRPC发送消息过程出现异常。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerRrpcFailed("iot.messagebroker.RrpcFailed","RRPC发送消息失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerShadowMessageIsNotJson("iot.messagebroker.ShadowMessageIsNotJson","设备影子不是JSON格式。"),
+    IotMessagebrokerShadowMessageLengthIsLarge("iot.messagebroker.ShadowMessageLengthIsLarge","设备影子的长度超过最大限制。设备影子文档的大小限制16 KB。"),
+    IotMessagebrokerTIMEOUT("iot.messagebroker.TIMEOUT","由于超时导致失败。"),
+    IotMessagebrokerTooManyElementInDesire("iot.messagebroker.TooManyElementInDesire","desire信息中包含的属性总数超过最大限定数。设备影子JSON文档的属性数量限制为128。"),
+    IotMessagebrokerTooManyElementInReport("iot.messagebroker.TooManyElementInReport","report信息包含的属性总数超过限定最大数。设备影子JSON文档的属性数量限制为128。"),
+    IotMessagebrokerTopicAlreadyFound("iot.messagebroker.TopicAlreadyFound","同一产品下Topic类名称重复。"),
+    IotMessagebrokerTopicTemplateCountExceedMax("iot.messagebroker.TopicTemplateCountExceedMax","产品的Topic类数量超过最大值。一个产品最多可以定义50个Topic类。"),
+    IotMessagebrokerTopicTemplateIsNotFound("iot.messagebroker.TopicTemplateIsNotFound","Topic类不存在。可调用QueryProductTopic查看产品的Topic类。"),
+    IotMessagebrokerUpdateDeviceShadowMessageFailed("iot.messagebroker.UpdateDeviceShadowMessageFailed","更新设备影子失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerUpdateTopicTemplateException("iot.messagebroker.UpdateTopicTemplateException","更新Topic类过程发生异常。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerUpdateTopicTemplateFailed("iot.messagebroker.UpdateTopicTemplateFailed","更新Topic类失败。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerTooManyUserProperties("iot.messagebroker.TooManyUserProperties","MQTT 5.0协议中,用户自定义属性个数超过最大限制(20个)。"),
+    IotMessagebrokerUserPropertyListExceedMaxLength("iot.messagebroker.UserPropertyListExceedMaxLength","MQTT 5.0协议中,用户自定义属性总长度超过最大限制(8192个字符)。"),
+    IotMessagebrokerInvalidUserProperty("iot.messagebroker.InvalidUserProperty","MQTT 5.0协议中,用户自定义属性格式不合法。请确保入参信息正确,然后重试。"),
+    IotMessagebrokerCorrelationDataNotBase64Encode("iot.messagebroker.CorrelationDataNotBase64Encode","MQTT 5.0协议中,相关数据(Correlation Data)未进行Base64编码转换。"),
+    IotMessagebrokerNullCorrelationData("iot.messagebroker.NullCorrelationData","MQTT 5.0协议中,相关数据(Correlation Data)为空。"),
+    IotMessagebrokerCorrelationDataExceedMaxLength("iot.messagebroker.CorrelationDataExceedMaxLength","MQTT 5.0协议中,相关数据长度超过最大限制(128个字符)。"),
+    IotMessagebrokerInvalidMqttResponseTopic("iot.messagebroker.InvalidMqttResponseTopic","MQTT 5.0协议中,响应主题(Response Topic)不合法。"),
+    IotRuleCreateRuleException("iot.rule.CreateRuleException","创建规则过程发生异常。请确保入参信息正确,然后重试。"),
+    IotRuleDeleteRuleFailed("iot.rule.DeleteRuleFailed","删除规则失败。请确保入参信息正确,然后重试。"),
+    IotRuleIncorrentRuleActionId("iot.rule.IncorrentRuleActionId","规则动作ID错误。可调用ListRuleActions查看规则动作ID。"),
+    IotRuleIncorrentRuleActionType("iot.rule.IncorrentRuleActionType","规则动作类型错误,具体要求请参见CreateRuleAction的Type请求参数说明。"),
+    IotRuleIncorrentRuleId("iot.rule.IncorrentRuleId","规则ID错误。"),
+    IotRuleNullForwardDestForRule("iot.rule.NullForwardDestForRule","转发数据目的地不能为空。Configuration中的具体配置方法,请参见CreateRuleAction。"),
+    IotRuleNullSqlForRule("iot.rule.NullSqlForRule","规则的SQL语句不能为空。"),
+    IotRuleNotFoundRule("iot.rule.NotFoundRule","规则不存在。请输入正确的规则ID (RuleId)。可调用ListRule查看账号下所有规则的RuleId。"),
+    IotRuleNotFoundRuleAction("iot.rule.NotFoundRuleAction","规则动作不存在。请输入正确的规则动作ID (ActionId)。可调用ListRuleActions查看某个规则下的所有ActionId。"),
+    IotRuleParseRuleActionConfigError("iot.rule.ParseRuleActionConfigError","无法正常解析规则动作的配置。请确保入参信息正确,然后重试。"),
+    IotRuleQueryRuleActionListError("iot.rule.QueryRuleActionListError","查询规则动作列表失败。请确保入参信息正确,然后重试。"),
+    IotRuleQueryRulePageActionError("iot.rule.QueryRulePageActionError","分页获取规则列表失败。请确保入参信息正确,然后重试。"),
+    IotRuleRuleActionIsAlreadyCreated("iot.rule.RuleActionIsAlreadyCreated","已存在相同的规则动作。"),
+    IotRuleRuleCountExceedMax("iot.rule.RuleCountExceedMax","规则总数超过最大限制数。单账号最多可以设置1,000条规则。"),
+    IotRuleRuleNameIsAlreadyExisted("iot.rule.RuleNameIsAlreadyExisted","规则名称已经存在。"),
+    IotRuleStartRuleFailed("iot.rule.StartRuleFailed","启动规则失败。请确保入参信息正确,然后重试。"),
+    IotRuleStopRuleFailed("iot.rule.StopRuleFailed","停止规则失败。请确保入参信息正确,然后重试。"),
+    IotRuleTooManyRuleAction("iot.rule.TooManyRuleAction","规则动作数量超过最大限制。一条规则中转发数据的操作不能超过10个。"),
+    IotRuleUpdateRuleFailed("iot.rule.UpdateRuleFailed","更新规则失败。请确保入参信息正确,然后重试。"),
+    IotRulengCreateRuleActionFailed("iot.ruleng.CreateRuleActionFailed","创建规则动作失败。请确保入参信息正确,然后重试。"),
+    IotRulengDeleteRuleActionFailed("iot.ruleng.DeleteRuleActionFailed","删除规则动作失败。请确保入参信息正确,然后重试。"),
+    IotRulengIncorrectActionTypeForError("iot.ruleng.IncorrectActionTypeForError","错误流转的规则不支持此云产品。"),
+    IotRulengIncorrectRegionName("iot.ruleng.IncorrectRegionName","regionName值错误,中国内地(大陆)节点只能流转到中国内地(大陆),海外节点只能流转到海外同一个节点。"),
+    IotRulengIncorrectSysTopic("iot.ruleng.IncorrectSysTopic","错误的基础通信Topic或物模型通信Topic。请参见CreateRule指定正确的Topic。"),
+    IotRulengIncorrectType("iot.ruleng.IncorrectType","应用规则的Topic类型错误。TopicType支持的可选值:0:基础通信Topic或物模型通信Topic。1:自定义Topic。2:设备状态消息Topic。"),
+    IotRulengInvalidEndpoint("iot.ruleng.InvalidEndpoint","非法的接入点。"),
+    IotRulengInvalidFormattedTagInConfiguration("iot.ruleng.InvalidFormattedTagInConfiguration","参数Configuration中的tag过长。"),
+    IotRulengInvalidRamRole("iot.ruleng.InvalidRamRole","非法的RAM角色。请登录RAM控制台查看角色信息。"),
+    IotRulengListInstancesError("iot.ruleng.ListInstancesError","获取实例列表失败。"),
+    IotRulengOnlyAllowOneErrorAction("iot.ruleng.OnlyAllowOneErrorAction","一个规则只能设置一个错误转发目的地。"),
+    IotRulengQueryInstanceError("iot.ruleng.QueryInstanceError","获取实例信息失败。"),
+    IotRulengQueryRuleActionFailed("iot.ruleng.QueryRuleActionFailed","获取规则动作失败。请确保入参信息正确,然后重试。"),
+    IotRulengRegionNotSupportRuleForwardCloudProduct("iot.ruleng.RegionNotSupportRuleForwardCloudProduct","此地域不支持将规则转发到该云产品。各地域支持转发的目标云产品请参见地域和可用区。"),
+    IotRulengRuleActionConfigurationIsNotJson("iot.ruleng.RuleActionConfigurationIsNotJson","规则动作配置不是JSON格式。参数Configuration的值必须是正确的JSON格式。具体请参见CreateRuleAction。"),
+    IotRulengRuleAlreadyIsStarted("iot.ruleng.RuleAlreadyIsStarted","规则是已启动状态。"),
+    IotRulengNullRamRoleArn("iot.ruleng.NullRamRoleArn","roleArn不能为空。"),
+    IotRulengNullRamRoleName("iot.ruleng.NullRamRoleName","roleName不能为空。"),
+    IotRulengNullRuleActionConfig("iot.ruleng.NullRuleActionConfig","规则动作配置(参数Configuration)不能为空。"),
+    IotRulengNullRuleActionType("iot.ruleng.NullRuleActionType","规则动作类型(参数Type)不能为空。"),
+    IotRulengUpdateRuleActionFailed("iot.ruleng.UpdateRuleActionFailed","更新规则动作失败。"),
+    IotMessagebrokerIncorrectRuleSql("iot.messagebroker.IncorrectRuleSql","规则的SQL配置错误。请根据CreateRule说明配置SQL。"),
+    IotMessagebrokerQueryRuleConfigActionException("iot.messagebroker.QueryRuleConfigActionException","获取规则配置信息过程出现异常。"),
+    IotRulengEmptySchemaNameOfTopic("iot.ruleng.EmptySchemaNameOfTopic","目标DataHub Topic的Schema的名称name值不能为空。"),
+    IotRulengEmptySchemaTypeOfTopic("iot.ruleng.EmptySchemaTypeOfTopic","目标DataHub Topic的Schema的类型type值不能为空。"),
+    IotRulengEmptySchemaValueOfTopic("iot.ruleng.EmptySchemaValueOfTopic","目标DataHub Topic的Schema值value不能为空。"),
+    IotRulengIncorrectSchemaValueOfTopic("iot.ruleng.IncorrectSchemaValueOfTopic","目标DataHub Topic的Schema值错误。"),
+    IotRulengNullOrEmptySchemaOfTopic("iot.ruleng.NullOrEmptySchemaOfTopic","目标DataHub Topic的Schema不能为空。"),
+    IotRulengNullProjectOfDatahub("iot.ruleng.NullProjectOfDatahub","DataHub的projectName不能为空。"),
+    IotRulengNullTopicInDatahubProject("iot.ruleng.NullTopicInDatahubProject","DataHub产品下的project中topicName不能为空。"),
+    IotRulengNotFoundProjectInDataHub("iot.ruleng.NotFoundProjectInDataHub","DataHub中不存在此项目(project)。"),
+    IotRulengNotFoundTopicInDataHubProject("iot.ruleng.NotFoundTopicInDataHubProject","DataHub的project中不存在此Topic。"),
+    IotRulengNullOtsInstanceName("iot.ruleng.NullOtsInstanceName","表格存储的实例名称不能为空。"),
+    IotRulengNullTableNameInOtsInstance("iot.ruleng.NullTableNameInOtsInstance","表格存储中实例的表名不能为空。"),
+    IotRulengNullPrimaryKeyInOtsTable("iot.ruleng.NullPrimaryKeyInOtsTable","表格存储中表的主键不能为空。"),
+    IotRulengNullPrimaryKeyNameInOts("iot.ruleng.NullPrimaryKeyNameInOts","主键的名称不能为空。"),
+    IotRulengNullPrimaryKeyTypeInOts("iot.ruleng.NullPrimaryKeyTypeInOts","主键的类型不能为空。"),
+    IotRulengNullPrimaryKeyValueInOts("iot.ruleng.NullPrimaryKeyValueInOts","主键的值不能为空。"),
+    IotRulengIncorrectPrimaryKeyValueInOtsTable("iot.ruleng.IncorrectPrimaryKeyValueInOtsTable","表格存储中主键值错误。请在表格存储中,查看您创建数据表时定义的主键。"),
+    IotRulengNullTopicNameInMns("iot.ruleng.NullTopicNameInMns","消息服务中的Topic不能为空。"),
+    IotRulengNotFoundTopicInMns("iot.ruleng.NotFoundTopicInMns","消息服务中不存在此Topic。请在消息服务中,确认主题(Topic)名称。"),
+    IotRulengQueryMnsTopicListActionError("iot.ruleng.QueryMnsTopicListActionError","获取消息服务Topic列表失败。请确保入参信息正确,然后重试。"),
+    IotRulengNullServiceNameInFc("iot.ruleng.NullServiceNameInFc","函数计算服务名称为空。"),
+    IotRulengNullFunctionNameInFc("iot.ruleng.NullFunctionNameInFc","函数计算函数名称为空。"),
+    IotRulengNotFoundServiceInFc("iot.ruleng.NotFoundServiceInFc","函数计算服务不存在。请在函数计算中,确认正确的服务名称。"),
+    IotRulengInstanceNotFound("iot.ruleng.InstanceNotFound","实例不存在。"),
+    IotRulengListMqTopicsError("iot.ruleng.ListMqTopicsError","获取消息队列Topic失败。"),
+    IotRulengTopicIsNotUnique("iot.ruleng.TopicIsNotUnique","Topic不唯一。"),
+    IotRulengTopicNotFound("iot.ruleng.TopicNotFound","Topic不存在。"),
+    IotRulengInvalidFormattedTagNameInHitsdb("iot.ruleng.InvalidFormattedTagNameInHitsdb","TSDB标签格式非法。"),
+    IotRulengInvalidFormattedTimestampInHitsdb("iot.ruleng.InvalidFormattedTimestampInHitsdb","时间戳格式非法。"),
+    IotRulengNotBindCustomerVpc("iot.ruleng.NotBindCustomerVpc","当前实例未绑定用户的VPC。"),
+    IotRulengNullInstanceNameInHitsdb("iot.ruleng.NullInstanceNameInHitsdb","TSDB实例名称为空。"),
+    IotRulengNullTagNameInHitsdb("iot.ruleng.NullTagNameInHitsdb","TSDB标签名称为空。"),
+    IotRulengNullTagValueInHitsdb("iot.ruleng.NullTagValueInHitsdb","TSDB标签值为空。"),
+    IotRulengNullTimestampInHitsdb("iot.ruleng.NullTimestampInHitsdb","时间戳标签为空。"),
+    IotRulengOnlySameRegionHitsdbSupport("iot.ruleng.OnlySameRegionHitsdbSupport","只支持转发到同节点的TSDB。"),
+    IotRulengGrantVpcInstanceFailed("iot.ruleng.GrantVpcInstanceFailed","VPC实例授权失败。"),
+    IotRulengInvalidFormattedConnectionIp("iot.ruleng.InvalidFormattedConnectionIp","连接IP格式错误。"),
+    IotRulengInvalidFormattedConnectionPort("iot.ruleng.InvalidFormattedConnectionPort","连接端口格式错误。"),
+    IotRulengInvalidFormattedRdsDatabaseName("iot.ruleng.InvalidFormattedRdsDatabaseName","RDS数据库名称包含非法字符。"),
+    IotRulengInvalidFormattedRdsTableName("iot.ruleng.InvalidFormattedRdsTableName","RDS表名称包含非法字符。"),
+    IotRulengListRdsAccountError("iot.ruleng.ListRdsAccountError","获取RDS数据库帐号失败。"),
+    IotRulengNotFoundTable("iot.ruleng.NotFoundTable","RDS数据库表不存在。"),
+    IotRulengNotFoundVpcInstance("iot.ruleng.NotFoundVpcInstance","VPC实例不存在。"),
+    IotRulengNotFoundVpcInstanceConnection("iot.ruleng.NotFoundVpcInstanceConnection","找不到RDS的VPC路径。"),
+    IotRulengNotMatchedField("iot.ruleng.NotMatchedField","RDS数据库表字段不匹配。"),
+    IotRulengNullConnectionIp("iot.ruleng.NullConnectionIp","连接IP为空。"),
+    IotRulengNullConnectionPort("iot.ruleng.NullConnectionPort","连接端口为空。"),
+    IotRulengNullRdsInstanceName("iot.ruleng.NullRdsInstanceName","RDS实例名称为空。"),
+    IotRulengNullRdsDatabaseName("iot.ruleng.NullRdsDatabaseName","RDS数据库名称为空。"),
+    IotRulengNullRdsAccountName("iot.ruleng.NullRdsAccountName","账号名称为空。"),
+    IotRulengNullRdsPassword("iot.ruleng.NullRdsPassword","账号密码为空。"),
+    IotRulengNullRdsTableName("iot.ruleng.NullRdsTableName","RDS表名称为空。"),
+    IotRulengNullRdsFieldName("iot.ruleng.NullRdsFieldName","RDS字段名称为空。"),
+    IotRulengNullRdsFieldValue("iot.ruleng.NullRdsFieldValue","RDS字段值为空。"),
+    IotRulengNullVpcInstance("iot.ruleng.NullVpcInstance","VPC实例不能为空。"),
+    IotRulengOnlySupportSameRegionRds("iot.ruleng.OnlySupportSameRegionRds","只支持转发到同节点的RDS。"),
+    IotRulengQueryServerMappingInfoError("iot.ruleng.QueryServerMappingInfoError","获取VPC实例的mapping信息失败。"),
+    IotConsumerGroupCallbackTypeEmpty("iot.consumer.group.CallbackTypeEmpty","CallbackType不能全为false。"),
+    IotConsumerGroupChangeDefaultConsumerGroupNotAllowed("iot.consumer.group.ChangeDefaultConsumerGroupNotAllowed","不能修改默认消费组。"),
+    IotConsumerGroupConsumerGroupExceedsLimit("iot.consumer.group.ConsumerGroupExceedsLimit","订阅关系下消费组数量超过限制。"),
+    IotConsumerGroupConsumerGroupExceedLimit("iot.consumer.group.ConsumerGroupExceedLimit","消费组数量超过最大限制。"),
+    IotConsumerGroupConsumerGroupNameExisted("iot.consumer.group.ConsumerGroupNameExisted","消费组名称已存在。"),
+    IotConsumerGroupConsumerGroupNotExist("iot.consumer.group.ConsumerGroupNotExist","消费组不存在。"),
+    IotConsumerGroupCreateConsumerGroupFailed("iot.consumer.group.CreateConsumerGroupFailed","添加消费组失败。"),
+    IotConsumerGroupDeleteConsumerGroupFailed("iot.consumer.group.DeleteConsumerGroupFailed","删除消费组失败。"),
+    IotConsumerGroupInvalidConsumerGroupName("iot.consumer.group.InvalidConsumerGroupName","非法的消费组名称。"),
+    IotConsumerGroupInvalidConsumerGroupId("iot.consumer.group.InvalidConsumerGroupId","非法的消费组ID。"),
+    IotConsumerGroupInvalidConsumerGroup("iot.consumer.group.InvalidConsumerGroup","消费组非法。"),
+    IotConsumerGroupInvalidGroupBy("iot.consumer.group.InvalidGroupBy","参数groupby非法。"),
+    IotConsumerGroupInvalidSubscribeType("iot.consumer.group.InvalidSubscribeType","订阅类型非法。"),
+    IotConsumerGroupMnsCheckFailed("iot.consumer.group.MnsCheckFailed","MNS鉴权失败。"),
+    IotConsumerGroupMnsNotSupport("iot.consumer.group.MnsNotSupport","设备转移的产品不支持MNS订阅。"),
+    IotConsumerGroupProductKeyExceedsLimit("iot.consumer.group.ProductKeyExceedsLimit","消费组订阅的产品超过限制。"),
+    IotConsumerGroupQueryConsumerGroupStatusFailed("iot.consumer.group.QueryConsumerGroupStatusFailed","查询消费组状态失败。"),
+    IotConsumerGroupUpdateConsumerGroupFailed("iot.consumer.group.UpdateConsumerGroupFailed","更新消费组失败。"),
+    IotFirmwareAlreadyExistedFirmwareName("iot.firmware.AlreadyExistedFirmwareName","同名的OTA升级包已经存在。"),
+    IotFirmwareAlreadyExistedFirmwareVersion("iot.firmware.AlreadyExistedFirmwareVersion","产品下已存在该OTA升级包版本。"),
+    IotFirmwareBadlyFormattedGrayPercent("iot.firmware.BadlyFormattedGrayPercent","灰度比例的格式不正确。"),
+    IotFirmwareBatchCancelDeviceUpgradeFailed("iot.firmware.BatchCancelDeviceUpgradeFailed","取消设备升级失败。"),
+    IotFirmwareBlankFirmwareName("iot.firmware.BlankFirmwareName","OTA升级包名称只包含空格。"),
+    IotFirmwareCancelOTAStrategyByJobFailed("iot.firmware.CancelOTAStrategyByJobFailed","取消动态批次所关联的动态升级策略失败。"),
+    IotFirmwareCancelOTATaskByJobFailed("iot.firmware.CancelOTATaskByJobFailed","取消批次下设备升级失败。"),
+    IotFirmwareCancelOptionIsMandatory("iot.firmware.CancelOptionIsMandatory","未指定取消参数。"),
+    IotFirmwareCancelUpgradeTaskFailed("iot.firmware.CancelUpgradeTaskFailed","取消升级批次失败"),
+    IotFirmwareCreateFirmwareFailure("iot.firmware.CreateFirmwareFailure","创建OTA升级包失败。"),
+    IotFirmwareCreateOTAUpgradeJobFailure("iot.firmware.CreateOTAUpgradeJobFailure","创建批量升级任务失败。"),
+    IotFirmwareDeleteFirmwareFailure("iot.firmware.DeleteFirmwareFailure","删除OTA升级包失败。"),
+    IotFirmwareDeviceUpgradeRecordNotFound("iot.firmware.DeviceUpgradeRecordNotFound","设备没有处在升级过程中。"),
+    IotFirmwareDeviceWithDestinationVersionInArray("iot.firmware.DeviceWithDestinationVersionInArray","设备版本号与目标版本号相同"),
+    IotFirmwareDynamicGrayNotSupported("iot.firmware.DynamicGrayNotSupported","不支持动态灰度升级。"),
+    IotFirmwareDynamicSpecificNotSupported("iot.firmware.DynamicSpecificNotSupported","不支持动态定向升级。"),
+    IotFirmwareEmptyFirmwareFile("iot.firmware.EmptyFirmwareFile","OTA升级包文件大小为0。"),
+    IotFirmwareFireTimeMustLargeThanCurrentTime("iot.firmware.FireTimeMustLargeThanCurrentTime","定时升级时间必须大于当前时间。"),
+    IotFirmwareFireTimeTooLate("iot.firmware.FireTimeTooLate","定时升级时间距当前时间不能大于7天。"),
+    IotFirmwareFireTimeTooShort("iot.firmware.FireTimeTooShort","定时升级时间距当前时间不能小于5分钟。"),
+    IotFirmwareFirmwareAndProductKeyMismatch("iot.firmware.FirmwareAndProductKeyMismatch","OTA升级包与产品ProductKey不匹配"),
+    IotFirmwareFirmwareCountExceedMax("iot.firmware.FirmwareCountExceedMax","OTA升级包数量超过最大值500。"),
+    IotFirmwareFirmwareDescriptionExceedMaxLength("iot.firmware.FirmwareDescriptionExceedMaxLength","OTA升级包描述字符个数超过100。"),
+    IotFirmwareFirmwareNotFound("iot.firmware.FirmwareNotFound","OTA升级包不存在。"),
+    IotFirmwareFirmwareNotVerified("iot.firmware.FirmwareNotVerified","未完成OTA升级包验证,无法发起升级。"),
+    IotFirmwareGenerateOTAUploadURLFailure("iot.firmware.GenerateOTAUploadURLFailure","生成升级包上传URL失败。"),
+    IotFirmwareGrayAppliesToStaticOnly("iot.firmware.GrayAppliesToStaticOnly","只有静态升级才允许灰度升级。"),
+    IotFirmwareInProgressDynamicJobExisted("iot.firmware.InProgressDynamicJobExisted","已存在执行中的动态批次。"),
+    IotFirmwareInProgressVerifyJobExisted("iot.firmware.InProgressVerifyJobExisted","存在未完成的验证OTA升级包任务。"),
+    IotFirmwareIncorrectDeviceUpgradeJobStatus("iot.firmware.IncorrectDeviceUpgradeJobStatus","待取消的设备升级记录状态错误。"),
+    IotFirmwareIncorrectFormattedFirmwareName("iot.firmware.IncorrectFormattedFirmwareName","OTA升级包名称格式错误。"),
+    IotFirmwareIncorrectFormattedFirmwareVersion("iot.firmware.IncorrectFormattedFirmwareVersion","OTA升级包版本格式非法。"),
+    IotFirmwareIncorrectFormattedModuleName("iot.firmware.IncorrectFormattedModuleName","模块名格式错误。"),
+    IotFirmwareIncorrectFormattedSrcVersion("iot.firmware.IncorrectFormattedSrcVersion","起始版本格式非法。"),
+    IotFirmwareInvalidDynamicMode("iot.firmware.InvalidDynamicMode","动态升级模式不正确。"),
+    IotFirmwareInvalidFirmware("iot.firmware.InvalidFirmware","无效的OTA升级包。"),
+    IotFirmwareInvalidFirmwareId("iot.firmware.InvalidFirmwareId","OTA升级包ID不正确。"),
+    IotFirmwareInvalidFirmwareSign("iot.firmware.InvalidFirmwareSign","无效的OTA升级包签名值。"),
+    IotFirmwareInvalidFirmwareSignMethod("iot.firmware.InvalidFirmwareSignMethod","无效的OTA升级包签名方法。"),
+    IotFirmwareInvalidFirmwareSize("iot.firmware.InvalidFirmwareSize","无效的OTA升级包大小。"),
+    IotFirmwareInvalidFirmwareType("iot.firmware.InvalidFirmwareType","无效的OTA升级包类型。"),
+    IotFirmwareInvalidFirmwareUrl("iot.firmware.InvalidFirmwareUrl","OTA升级包资源链接地址不是合法的物联网平台OTA升级包地址。"),
+    IotFirmwareInvalidGenerateOTAUploadURLParam("iot.firmware.InvalidGenerateOTAUploadURLParam","生成升级包上传URL的参数非法。"),
+    IotFirmwareInvalidGrayPercent("iot.firmware.InvalidGrayPercent","无效的灰度比例。"),
+    IotFirmwareInvalidMaximumPerMinute("iot.firmware.InvalidMaximumPerMinute","无效的推出速率。"),
+    IotFirmwareInvalidOTAJobId("iot.firmware.InvalidOTAJobId","批次ID不正确。"),
+    IotFirmwareInvalidOTATaskId("iot.firmware.InvalidOTATaskId","升级记录ID不正确。"),
+    IotFirmwareInvalidOverwriteMode("iot.firmware.InvalidOverwriteMode","覆盖模式不正确。"),
+    IotFirmwareInvalidProductKey("iot.firmware.InvalidProductKey","无效的产品ProductKey。"),
+    IotFirmwareInvalidRetryCount("iot.firmware.InvalidRetryCount","自动重试次数不正确。"),
+    IotFirmwareInvalidRetryInterval("iot.firmware.InvalidRetryInterval","自动重试时间间隔不正确。"),
+    IotFirmwareInvalidSelectionType("iot.firmware.InvalidSelectionType","选择类型不正确。"),
+    IotFirmwareInvalidTargetSelection("iot.firmware.InvalidTargetSelection","目标选择不正确。"),
+    IotFirmwareInvalidTaskStatus("iot.firmware.InvalidTaskStatus","升级记录状态不正确。"),
+    IotFirmwareInvalidTimeoutSetting("iot.firmware.InvalidTimeoutSetting","无效的超时设置"),
+    IotFirmwareJobAlreadyCanceled("iot.firmware.JobAlreadyCanceled","批次已经取消。"),
+    IotFirmwareJobHasNoScheduleTime("iot.firmware.JobHasNoScheduleTime","批次不是定时批次。"),
+    IotFirmwareListOTAFirmwareFailed("iot.firmware.ListOTAFirmwareFailed","获取OTA升级包列表失败。"),
+    IotFirmwareListOTAJobFailed("iot.firmware.ListOTAJobFailed","查询升级批次列表失败。"),
+    IotFirmwareListOTAModuleFailed("iot.firmware.ListOTAModuleFailed","查询模块列表失败。"),
+    IotFirmwareListOTAModuleNameByProductFailed("iot.firmware.ListOTAModuleNameByProductFailed","查询产品下模块列表失败。"),
+    IotFirmwareListOTATaskByDeviceFailed("iot.firmware.ListOTATaskByDeviceFailed","查询设备下的升级记录失败。"),
+    IotFirmwareListOTATaskByJobFailed("iot.firmware.ListOTATaskByJobFailed","查询批次下的升级记录失败。"),
+    IotFirmwareMalformedFirmwareUrl("iot.firmware.MalformedFirmwareUrl","OTA升级包资源链接地址不是合法的URL。"),
+    IotFirmwareModuleCountLimitExceeded("iot.firmware.ModuleCountLimitExceeded","模块数量超过了最大限制。"),
+    IotFirmwareModuleNameReserved("iot.firmware.ModuleNameReserved","使用了被保留的模块名,请选择其它模块名。"),
+    IotFirmwareMoreThanOneSrcVersion("iot.firmware.MoreThanOneSrcVersion","差分OTA升级包批量升级只允许指定一个源版本。"),
+    IotFirmwareNoDeviceCanUpgrade("iot.firmware.NoDeviceCanUpgrade","未找到可升级的设备。"),
+    IotFirmwareNonVersionedDeviceInArray("iot.firmware.NonVersionedDeviceInArray","设备数组参数中存在未上报或上报非法版本号的设备。"),
+    IotFirmwareNotDynamicJob("iot.firmware.NotDynamicJob","只允许取消动态批次所关联的动态升级策略。"),
+    IotFirmwareNullFirmwareId("iot.firmware.NullFirmwareId","OTA升级包ID为空。"),
+    IotFirmwareNullFirmwareName("iot.firmware.NullFirmwareName","OTA升级包名称为空。"),
+    IotFirmwareNullFirmwareUrl("iot.firmware.NullFirmwareUrl","OTA升级包资源链接地址为空。"),
+    IotFirmwareNullFirmwareVersion("iot.firmware.NullFirmwareVersion","OTA升级包版本为空。"),
+    IotFirmwareNullGrayPercent("iot.firmware.NullGrayPercent","灰度升级的灰度比例为空。"),
+    IotFirmwareNullOTAJobId("iot.firmware.NullOTAJobId","批次ID为空。"),
+    IotFirmwareNullSelectionType("iot.firmware.NullSelectionType","选择类型为空。"),
+    IotFirmwareNullSrcVersions("iot.firmware.NullSrcVersions","待升级版本号列表为空。"),
+    IotFirmwareNullTargetDeviceNames("iot.firmware.NullTargetDeviceNames","静态定向升级的设备范围为空。"),
+    IotFirmwareNullTargetSelection("iot.firmware.NullTargetSelection","目标选择为空。"),
+    IotFirmwareNullTaskId("iot.firmware.NullTaskId","批次ID为空。"),
+    IotFirmwareParameterSizeExceedMax("iot.firmware.ParameterSizeExceedMax","列表个数超过最大数量。"),
+    IotFirmwareReUpgradeFailedFirmwareNotVerified("iot.firmware.ReUpgradeFailedFirmwareNotVerified","未完成OTA升级包验证,无法发起重升级。"),
+    IotFirmwareScheduleFinishTimeEarlierThanStartTime("iot.firmware.ScheduleFinishTimeEarlierThanStartTime","指定定时结束时间必须晚于定时开始时间。"),
+    IotFirmwareScheduleFinishTimeTooEarly("iot.firmware.ScheduleFinishTimeTooEarly","定时升级结束时间距开始时间不能小于60分钟。"),
+    IotFirmwareScheduleFinishTimeTooLate("iot.firmware.ScheduleFinishTimeTooLate","定时升级结束时间距开始时间不能大于30天。"),
+    IotFirmwareScheduleFinishTimeWithoutStartTime("iot.firmware.ScheduleFinishTimeWithoutStartTime","指定定时结束时间必须同时指定定时开始时间。"),
+    IotFirmwareSrcDestVersionMatched("iot.firmware.SrcDestVersionMatched","起始版本与目标版本相同。"),
+    IotFirmwareSrcVersionIsMandatory("iot.firmware.SrcVersionIsMandatory","差分包未指定起始版本。"),
+    IotFirmwareSrcVersionMismatch("iot.firmware.SrcVersionMismatch","设备当前的版本与OTA升级包待升级版本不一致。"),
+    IotFirmwareSrcVersionsNotAllowed("iot.firmware.SrcVersionsNotAllowed","不允许指定源版本列表。"),
+    IotFirmwareSrcVersionsSizeExceedMax("iot.firmware.SrcVersionsSizeExceedMax","源版本列表大小超过最大数量。"),
+    IotFirmwareSystemBusy("iot.firmware.SystemBusy","系统繁忙,请稍后重试。"),
+    IotFirmwareSystemBusyForScheduleTime("iot.firmware.SystemBusyForScheduleTime","系统繁忙,请修改定时升级时间。"),
+    IotFirmwareTargetDeviceNamesNotAllowed("iot.firmware.TargetDeviceNamesNotAllowed","不允许指定静态定向升级的设备。"),
+    IotFirmwareTooManyDeviceUpgrade("iot.firmware.TooManyDeviceUpgrade","发起升级的设备数量过多,请稍后重试。"),
+    IotFirmwareTooManyDeviceUpgradeForScheduleTime("iot.firmware.TooManyDeviceUpgradeForScheduleTime","指定的定时升级时间发起升级的设备数量过多,请修改定时升级时间。"),
+    IotFirmwareUpgradeSrcVersionMismatch("iot.firmware.UpgradeSrcVersionMismatch","升级源版本与差分OTA升级包源版本不一致。"),
+    IotFirmwareVerifyFirmwareFailed("iot.firmware.VerifyFirmwareFailed","验证OTA升级包失败。"),
+    IotFirmwareJobTagIsTooLong("iot.firmware.JobTagIsTooLong","创建升级批次任务中,标签字符总数超出限制(4096个字符)。"),
+    IotFirmwareJobTagKeyInvalid("iot.firmware.JobTagKeyInvalid","创建升级批次任务中,标签Key不合法。"),
+    IotFirmwareJobTagValueInvalid("iot.firmware.JobTagValueInvalid","创建升级批次任务中,标签Value不合法。"),
+    IotFirmwareJobTagTooMany("iot.firmware.JobTagTooMany","创建升级批次任务中,标签Key:Value对个数超出限制(10个)。"),
+    IotOtaPackageFileAmountExceed("iot.ota.PackageFileAmountExceed","创建升级包请求中,升级包文件数量超过限制(20个)。"),
+    IotOtaPackageFileParamConflict("iot.ota.PackageFileParamConflict","创建升级包请求中,升级包文件相关参数有冲突。即单个文件和多文件相关参数不能同时设置。"),
+    IotOtaPackageFileNameDuplicated("iot.ota.PackageFileNameDuplicated","创建升级包请求中,多个升级包文件的名称重复。"),
+    IotOtaPackageFileUrlDuplicated("iot.ota.PackageFileUrlDuplicated","创建升级包请求中,多个升级包文件的URL重复。"),
+    IotOtaPackageFileSignDuplicated("iot.ota.PackageFileSignDuplicated","创建升级包请求中,多个升级包文件的签名值重复。"),
+    IotOtaTagKeyDuplicated("iot.ota.tag.KeyDuplicated","标签列表的Key重复。"),
+    IotOtaCreateOTAModuleFailure("iot.ota.CreateOTAModuleFailure","创建OTA模块的过程中产生异常。"),
+    IotOtaCreateOTAModuleParamError("iot.ota.CreateOTAModuleParamError","创建OTA模块的请求中参数错误。"),
+    IotOtaCreateOTAModuleExceedLimit("iot.ota.CreateOTAModuleExceedLimit","产品下的OTA模块数超过了限制。"),
+    IotOtaCreateOTAModuleNameExist("iot.ota.CreateOTAModuleNameExist","创建OTA模块的名称重复。"),
+    IotOtaUpdateOTAModuleFailure("iot.ota.UpdateOTAModuleFailure","更新OTA模块的过程中产生异常。"),
+    IotOtaUpdateOTAModuleParamError("iot.ota.UpdateOTAModuleParamError","更新OTA模块的请求中参数错误。"),
+    IotOtaDeleteOTAModuleParamError("iot.ota.DeleteOTAModuleParamError","删除OTA模块的请求中参数错误。"),
+    IotOtaDeleteOTAModuleFailure("iot.ota.DeleteOTAModuleFailure","删除OTA模块的过程中产生异常。"),
+    IotOtaDeleteOTAModulePackageExist("iot.ota.DeleteOTAModulePackageExist","有升级包的OTA模块不能被删除。"),
+    IotOtaDeleteOtaDefaultModule("iot.ota.DeleteOtaDefaultModule","默认模块不能被删除。"),
+    IotOtaListOTAModuleParamError("iot.ota.ListOTAModuleParamError","查询OTA模块列表的请求中参数错误。"),
+    IotOtaListOTAModuleFailure("iot.ota.ListOTAModuleFailure","查询OTA模块列表的过程中产生异常。"),
+    IotOtaOTAModuleNotExist("iot.ota.OTAModuleNotExist","删除或更新OTA模块时报错:产品下的OTA模块不存在。"),
+    IotOtaTooManyOtaTask("iot.ota.TooManyOtaTask","请求参数TaskId.N的TaskId个数,已超出最大限制。"),
+    IotOtaTaskIdIsNull("iot.ota.TaskIdIsNull","请求参数TaskId.N不能为空。"),
+    IotOtaConfirmNotAllowedNow("iot.ota.ConfirmNotAllowedNow","当前时间,设备不可升级,不能对待确认状态的设备升级作业,进行确认。"),
+    IotOtaEmptyDeviceInfoParams("iot.ota.EmptyDeviceInfoParams","请求参数中,设备信息不能为空。"),
+    IotOtaInvalidOtaModuleName("iot.ota.InvalidOtaModuleName","请求参数中,OTA模块名称的格式不合法。"),
+    IotOtaGroupNotExist("iot.ota.GroupNotExist","设备分组不存在。请确保入参信息正确,然后重试。"),
+    IotOtaDynamicGroupExpressionInvalid("iot.ota.DynamicGroupExpressionInvalid","发起OTA升级时,动态分组对应的规则中模块名称和模块版本必须同时存在。"),
+    IotOtaDynamicGroupTaskCountExceedLimit("iot.ota.DynamicGroupTaskCountExceedLimit","动态分组对应的升级批次数超过限制(5个)。"),
+    IotOtaUpgradeDeviceCountExceedLimit("iot.ota.UpgradeDeviceCountExceedLimit","发起的升级设备数超过限制,限制说明,请参见使用限制的OTA升级。"),
+    IotOtaGroupUpgradeTenantInvalid("iot.ota.GroupUpgradeTenantInvalid","当前阿里云账号发起的分组升级不合法。请确保入参信息正确,然后重试。"),
+    IotOtaGroupTypeInvalid("iot.ota.GroupTypeInvalid","分组升级中,传入的分组类型无效。请确保入参信息正确,然后重试。"),
+    IotFirmwareVerifyFirmwareNoNeed("iot.firmware.VerifyFirmwareNoNeed","当前升级包不需要验证。"),
+    IotOtaDownloadProtocolInvalid("iot.ota.DownloadProtocolInvalid","当前批次任务中的升级包下载协议不合法。"),
+    IotOtaDownloadProtocolMqttNotSuitable("iot.ota.DownloadProtocolMqttNotSuitable","当前批次任务不支持使用MQTT协议下载升级包。"),
+    IotDeviceprintProjectNotFound("iot.deviceprint.ProjectNotFound","项目不存在。"),
+    IotDeviceprintProjectHasStopped("iot.deviceprint.ProjectHasStopped","项目已停用。"),
+    IotDeviceprintInvalidDevice("iot.deviceprint.InvalidDevice","无效的打印设备。"),
+    IotDeviceprintDeviceNotActived("iot.deviceprint.DeviceNotActived","打印设备未激活。"),
+    IotDeviceprintDeviceOffline("iot.deviceprint.DeviceOffline","打印设备已离线。"),
+    IotDeviceprintRequestLimit("iot.deviceprint.RequestLimit","请求次数过多被限流,请稍后重试。"),
+    IotPrintserviceNotSigned("iot.printservice.NotSigned","寄雁传书服务未开通。"),
+    IotDeviceprintPrintingTemplateNotExists("iot.deviceprint.PrintingTemplateNotExists","打印模板不存在。"),
+    IotDeviceprintPrintingParamsError("iot.deviceprint.PrintingParamsError","打印参数错误。"),
+    IotDeviceprintUnknownResponse("iot.deviceprint.UnknownResponse","未知的打印设备响应。"),
+    IotDeviceprintMismatchingPrintId("iot.deviceprint.MismatchingPrintId","打印ID不匹配。");
+    private String code;
+    private String description;
+
+    public static AliYunIotExceptionEnums of(String code){
+        return EnumUtil.likeValueOf(AliYunIotExceptionEnums.class,code);
+    }
+}

+ 53 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/message/AliBridgeMessage.java

@@ -0,0 +1,53 @@
+package org.jetlinks.community.bridge.message;
+
+import com.aliyun.iot.as.bridge.core.model.Session;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliBridgeMessage.java
+ * @Description TODO
+ * @createTime 2021年11月30日 16:32:00
+ */
+@Data
+public class AliBridgeMessage {
+    public static AliBridgeMessage offline(String originalIdentity) {
+        return new AliBridgeMessage(originalIdentity,false,true,null,null,null);
+    }
+
+    public static AliBridgeMessage online(String originalIdentity) {
+        return new AliBridgeMessage(originalIdentity,true,false,null,null,null);
+    }
+
+    public static AliBridgeMessage of(String topic, byte[] payload) {
+        return new AliBridgeMessage(null,false,false,topic,payload,null);
+    }
+
+    public static AliBridgeMessage of(String originalIdentity,String topic, byte[] payload) {
+        return new AliBridgeMessage(originalIdentity,false,false,topic,payload,null);
+    }
+
+    public static AliBridgeMessage of(Session session,String topic, byte[] payload) {
+        return new AliBridgeMessage(null,false,false,topic,payload,session);
+    }
+    public AliBridgeMessage(String originalIdentity, boolean online, boolean offline, String topic, byte[] payload,Session session) {
+        this.originalIdentity = originalIdentity;
+        this.online = online;
+        this.offline = offline;
+        this.topic = topic;
+        this.payload = payload;
+        this.session=session;
+    }
+
+    private String productKey;
+    private String deviceName;
+    private String  originalIdentity;
+    private boolean online;
+    private boolean offline;
+    private String topic;
+    private Session session;
+
+    private byte[] payload;
+}

+ 73 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/BridgeServer.java

@@ -0,0 +1,73 @@
+package org.jetlinks.community.bridge.server;
+
+import org.jetlinks.community.bridge.message.AliBridgeMessage;
+import org.jetlinks.community.bridge.server.aliyun.AliBridgeServer;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import reactor.util.function.Tuple4;
+
+import javax.validation.constraints.NotNull;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName Server.java
+ * @Description 网桥服务器
+ * @see Channel 设备网桥
+ * @createTime 2021年11月26日 09:12:00
+ */
+public interface BridgeServer {
+    /**
+     * 校验网桥参数
+     * @param params 网桥构造参数
+     * @return
+     */
+    boolean verify(Map<String,Object> params);
+
+    /**
+     * 网桥组id
+     * @return
+     */
+    String productId();
+    /**
+     * 批量注册设备
+     * @param params
+     *  *************tp1 originalIdentity tp2 productId tp3 deviceName*************
+     * @return
+     */
+    Flux<Channel> registerBatch(Collection<Tuple4<String, String, String,String>> params);
+
+    /**
+     * 注册设备通道
+     * @param originalIdentity 设备原始id,即网桥设备id
+     * @param productKey 产品id
+     * @param deviceName 网桥配置
+     * @see   AliBridgeServer
+     * @return
+     */
+    Mono<Channel> register(@NotNull String originalIdentity, @NotNull String productKey,@NotNull  String deviceName,@NotNull  String deviceSecret);
+
+    /**
+     * 取消注册设备
+     * @param originalIdentity
+     */
+    Mono<Void>  unRegister(String originalIdentity);
+
+    /**
+     * 停止 网桥
+     */
+    Mono<Void> stopBridge();
+
+    /**
+     * 重新连接
+     */
+    Mono<Void> reconnect();
+
+    /**
+     * 处理网桥信息
+     * @return
+     */
+    Flux<AliBridgeMessage> handleReceive();
+}

+ 41 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/Channel.java

@@ -0,0 +1,41 @@
+package org.jetlinks.community.bridge.server;
+
+import org.jetlinks.community.bridge.message.AliBridgeMessage;
+import org.jetlinks.core.server.session.DeviceSession;
+import org.reactivestreams.Publisher;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+import java.util.function.Consumer;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName Channel.java
+ * @Description 网桥连接设备
+ * @createTime 2021年11月30日 09:02:00
+ */
+public interface Channel {
+
+
+    String getOriginalIdentity();
+
+    String getDeviceName();
+
+    String getProductKey();
+    /**
+     * 向云端推送设备
+     * 当设备连接到网桥服务器上时,设备向网桥服务器发送消息,则此时网桥服务器会通过网桥连接将消息推送至云端
+     * @param message 设备消息
+     * @return
+     * @see Mono <Void>
+     */
+    void upLink(AliBridgeMessage message);
+
+    /**
+     * 设备上线
+     * @return
+     */
+    void online();
+
+    void doOffline();
+}

+ 111 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/aliyun/AliBridgeGateway.java

@@ -0,0 +1,111 @@
+package org.jetlinks.community.bridge.server.aliyun;
+
+import lombok.extern.slf4j.Slf4j;
+import org.hswebframework.web.logger.ReactiveLogger;
+import org.jetlinks.community.bridge.core.AliBridgeCodec;
+import org.jetlinks.community.bridge.entity.AliIotBridgeEntity;
+import org.jetlinks.community.bridge.message.AliBridgeMessage;
+import org.jetlinks.core.device.DeviceRegistry;
+import org.jetlinks.core.event.EventBus;
+import org.jetlinks.core.message.CommonDeviceMessage;
+import org.jetlinks.core.message.CommonDeviceMessageReply;
+import org.jetlinks.core.message.Message;
+import org.jetlinks.supports.server.DecodedClientMessageHandler;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import reactor.core.publisher.EmitterProcessor;
+import reactor.core.publisher.FluxSink;
+import reactor.core.publisher.Mono;
+import reactor.core.scheduler.Schedulers;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliBridgeGateway.java
+ * @Description 阿里云网桥网关
+ * @createTime 2021年11月30日 08:35:00
+ */
+@Slf4j
+@Service
+public class AliBridgeGateway{
+
+    /**
+     * 阿里云网桥网关集合,每一个产品的设备归属于同一个网桥
+     */
+    private static final Map<String,AliBridgeServer> serverMap=new ConcurrentHashMap<>();
+
+    private final EmitterProcessor<Message> messageProcessor = EmitterProcessor.create(false);
+
+    private final FluxSink<Message> sink = messageProcessor.sink(FluxSink.OverflowStrategy.BUFFER);
+
+    private final DeviceRegistry registry;
+
+    private final EventBus eventBus;
+    private final DecodedClientMessageHandler messageHandler;
+
+    public AliBridgeGateway(DeviceRegistry registry,
+                            EventBus eventBus,
+                            DecodedClientMessageHandler messageHandler) {
+        this.registry = registry;
+        this.eventBus=eventBus;
+        this.messageHandler = messageHandler;
+    }
+
+    public Mono<Void> initBridge(AliIotBridgeEntity productMapping){
+       return Mono.fromRunnable(()->{
+           if (!serverMap.containsKey(productMapping.getProductId())) {
+               serverMap.computeIfAbsent(productMapping.getProductId(), k->AliBridgeServer.create(eventBus,productMapping))
+                   .handleReceive()
+                   .parallel()
+                   .runOn(Schedulers.parallel())
+                   .flatMap(this::decodeAndHandleMessage).subscribe();
+           }
+       });
+    }
+
+    public Mono<AliBridgeServer> getBridgeServer(String serverId){
+        return Mono.just(serverMap.get(serverId));
+    }
+
+
+    //解码消息并处理
+    private Mono<Void> decodeAndHandleMessage(AliBridgeMessage msg) {
+        return Mono.justOrEmpty(AliBridgeCodec.decode(msg,msg.getProductKey(),msg.getDeviceName()))
+            .filter(Objects::nonNull)
+            .flatMap(message -> {
+                if(messageProcessor.hasDownstreams()){
+                    sink.next(message);
+                }
+                if (message instanceof CommonDeviceMessage) {
+                    CommonDeviceMessage _msg = ((CommonDeviceMessage) message);
+                    if (StringUtils.isEmpty(_msg.getDeviceId())) {
+                        _msg.setDeviceId(msg.getOriginalIdentity());
+                    }
+                }
+                if (message instanceof CommonDeviceMessageReply) {
+                    CommonDeviceMessageReply<?> _msg = ((CommonDeviceMessageReply<?>) message);
+                    if (StringUtils.isEmpty(_msg.getDeviceId())) {
+                        _msg.setDeviceId(msg.getOriginalIdentity());
+                    }
+                }
+                return this.handleChannelMessage(message,msg.getOriginalIdentity());
+            })
+            .then()
+            .doOnEach(ReactiveLogger.onError(err -> log.error("处理阿里云云云对接[{}]消息失败:{}", msg.getOriginalIdentity(), msg, err)))
+            //发生错误不中断读
+            .onErrorResume((err)->Mono.empty());
+    }
+
+    private Mono<Void> handleChannelMessage(Message message,String deviceId) {
+            return registry.getDevice(deviceId)
+                .flatMap(operator ->
+                    messageHandler
+                        .handleMessage(operator, message)
+                        .then()
+                );
+    }
+}

+ 182 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/aliyun/AliBridgeServer.java

@@ -0,0 +1,182 @@
+package org.jetlinks.community.bridge.server.aliyun;
+
+import cn.hutool.core.lang.Assert;
+import com.aliyun.iot.as.bridge.core.handler.DownlinkChannelHandler;
+import com.aliyun.iot.as.bridge.core.model.PopClientConfiguration;
+import com.aliyun.iot.as.bridge.core.model.Session;
+import lombok.extern.slf4j.Slf4j;
+import org.hswebframework.web.logger.ReactiveLogger;
+import org.jetlinks.community.bridge.core.DefaultBridgeBootstrap;
+import org.jetlinks.community.bridge.core.DefaultBridgeConfigManager;
+import org.jetlinks.community.bridge.core.DefaultDeviceConfigManager;
+import org.jetlinks.community.bridge.core.DefaultUplinkChannelHandler;
+import org.jetlinks.community.bridge.entity.AliIotBridgeEntity;
+import org.jetlinks.community.bridge.message.AliBridgeMessage;
+import org.jetlinks.community.bridge.server.BridgeServer;
+import org.jetlinks.community.bridge.server.Channel;
+import org.jetlinks.core.event.EventBus;
+import reactor.core.publisher.*;
+import reactor.util.function.Tuple4;
+
+import javax.validation.constraints.NotNull;
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliBridgeServer.java
+ * @Description 产品网桥
+ *              管理统一产品下所有设备网桥
+ * @createTime 2021年11月30日 11:01:00
+ */
+@Slf4j
+public class AliBridgeServer implements BridgeServer {
+    private String productId;
+    private Map<String, Channel> channelMap=new ConcurrentHashMap<>();
+
+    private final EventBus eventBus;
+
+    /************ 下行数据处理 ***************/
+    private final UnicastProcessor<AliBridgeMessage> receive = UnicastProcessor.create();
+
+    private final FluxSink<AliBridgeMessage> receiveSink = receive.sink(FluxSink.OverflowStrategy.BUFFER);
+    /************ 下行数据处理 ***************/
+
+    private AtomicBoolean start=new AtomicBoolean(false);
+
+    private DefaultBridgeConfigManager bridgeConfigManager;
+
+    private  DefaultBridgeBootstrap bootstrap;
+    public AliBridgeServer(EventBus eventBus) {
+        this.eventBus = eventBus;
+    }
+
+    public static AliBridgeServer create(EventBus eventBus, AliIotBridgeEntity productMapping) {
+        return  new AliBridgeServer(eventBus).initBridge(productMapping);
+    }
+
+    public AliBridgeServer initBridge(AliIotBridgeEntity params){
+        if(start.get()){
+            return this;
+        }
+        String productId = params.getProductId();
+        Assert.notNull(productId,"创建网桥 productId 不能为空, mapping id {%s}",params.getId());
+        AliIotBridgeEntity.AccessConfig accessConfig = params.getAccessConfig();
+        String productKey = accessConfig.getProductKey();
+        Assert.notNull(productKey,"创建网桥 productKey 不能为空, mapping id {%s}",params.getId());
+        String accessKey = accessConfig.getAccessKey();
+        Assert.notNull(accessKey,"创建网桥 accessKey 不能为空, mapping id {%s}",params.getId());
+        String accessSecret = accessConfig.getAccessSecret();
+        Assert.notNull(accessSecret,"创建网桥 accessSecret 不能为空, mapping id {%s}",params.getId());
+        String authEndpoint = accessConfig.getAuthEndpoint();
+        Assert.notNull(authEndpoint,"创建网桥 authEndpoint 不能为空, mapping id {%s}",params.getId());
+        String http2Endpoint = accessConfig.getHttp2Endpoint();
+        Assert.notNull(http2Endpoint,"创建网桥 http2Endpoint 不能为空, mapping id {%s}",params.getId());
+        String regionId = accessConfig.getRegionId();
+        Assert.notNull(regionId,"创建网桥 regionId 不能为空, mapping id {%s}",params.getId());
+        bridgeConfigManager = DefaultBridgeConfigManager.of(productKey, null, null, http2Endpoint, authEndpoint, null, getPopClientProfile(accessKey, accessSecret, regionId));
+        bootstrap=new DefaultBridgeBootstrap(bridgeConfigManager);
+        if(start.get()){
+            return this;
+        }
+        if (start.compareAndSet(false, true)) {
+            this.productId=productId;
+            bootstrap.bootstrap(new DownlinkChannelHandler() {
+                @Override
+                public boolean pushToDevice(Session session, String topic, byte[] payload) {
+                    if(receive.hasDownstreams()){
+                        receiveSink.next(AliBridgeMessage.of(session,topic,payload));
+                    }
+                    return false;
+                }
+                @Override
+                public boolean broadcast(String topic, byte[] payload) {
+                    return false;
+                }
+            });
+        }
+        return this;
+
+    }
+
+    private PopClientConfiguration getPopClientProfile(@NotNull String accessKey,@NotNull String accessSecret,@NotNull String endpoint){
+        PopClientConfiguration popClientConfiguration = new PopClientConfiguration();
+        popClientConfiguration.setAccessKey(accessKey);
+        popClientConfiguration.setAccessSecret(accessSecret);
+        popClientConfiguration.setRegion(endpoint);
+        popClientConfiguration.setName(endpoint);
+        popClientConfiguration.setProduct("Iot");
+        popClientConfiguration.setEndpoint(String.format("iot.%s.aliyuncs.com",endpoint));
+        return popClientConfiguration;
+    }
+
+    @Override
+    public boolean verify(Map<String, Object> params) {
+        return true;
+    }
+
+    @Override
+    public String productId() {
+        return productId;
+    }
+
+    @Override
+    public Flux<Channel> registerBatch(Collection<Tuple4<String, String, String,String>> params) {
+        return Flux.fromStream(params.stream())
+            .flatMap(tp4->this.register(tp4.getT1(),tp4.getT2(),tp4.getT3(),tp4.getT4()))
+            .doOnEach(ReactiveLogger.onError(err -> log.error("注册阿里云云云对接网关失败:{}", err)))
+            //发生错误不中断读
+            .onErrorResume((err)->Mono.empty());
+    }
+
+
+    @Override
+    public Mono<Channel> register(@NotNull String originalIdentity,@NotNull String productKey,@NotNull String deviceName,@NotNull String deviceSecret) {
+        //注册设备信息
+        DefaultDeviceConfigManager.register(originalIdentity,productKey,deviceName,deviceSecret);
+        DefaultUplinkChannelHandler uplinkChannelHandler = new DefaultUplinkChannelHandler(bridgeConfigManager, DefaultDeviceConfigManager.getInstance());
+        channelMap.putIfAbsent(originalIdentity,new DefaultAliBridgeChannel(originalIdentity,productId,productKey,deviceName,uplinkChannelHandler,eventBus));
+        return Mono.just(channelMap.get(originalIdentity));
+    }
+
+    @Override
+    public Mono<Void> unRegister(String originalIdentity) {
+        return Mono.fromRunnable(()->{
+            Channel channel = channelMap.get(originalIdentity);
+            if(channel!=null){
+                channel.doOffline();
+                channelMap.remove(originalIdentity);
+            }
+        });
+    }
+
+    @Override
+    public Mono<Void> stopBridge() {
+        return Mono.fromRunnable(()->{
+            if(bootstrap!=null){
+              bootstrap.disconnectBridge();
+            }
+            log.info("产品网桥{}关闭",this.productId);
+        });
+    }
+
+    @Override
+    public Mono<Void> reconnect() {
+       return  Mono.fromRunnable(()->{
+            if(bootstrap!=null){
+                bootstrap.reconnectBridge();
+                channelMap.values().forEach(Channel::online);
+            }
+            log.info("产品网桥{}重启成功",this.productId);
+        });
+    }
+
+
+    @Override
+    public Flux<AliBridgeMessage> handleReceive() {
+        return receive;
+    }
+}

+ 42 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/aliyun/AliContanst.java

@@ -0,0 +1,42 @@
+package org.jetlinks.community.bridge.server.aliyun;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliContanst.java
+ * @Description TODO
+ * @createTime 2021年11月26日 10:33:00
+ */
+public class AliContanst {
+    /**
+     * 设备所在地域(与控制台上的地域对应),如cn-shanghai。
+     */
+    public static final String AUTH_REGION_ID="regionId";
+
+    /**
+     * 网桥设备所属产品的ProductKey。
+     */
+    public static final String AUTH_PRODUCT_KEY="productKey";
+
+    /**
+     * 登录key
+     */
+    public static final String AUTH_ACCESS_KEY="accessKey";
+
+    /**
+     * 登录秘钥
+     */
+    public static final String AUTH_ACCESS_SECRET="accessSecret";
+
+    /**
+     * 阿里云api认证服务地址
+     */
+    public static final String AUTH_AUTH_ENDPOINT="authEndpoint";
+
+    /**
+     * HTTP2网关服务地址。网桥设备和物联网平台通过HTTP2协议建立长连接通道。
+     */
+    public static final String AUTH_HTTP2_ENDPOINT="http2Endpoint";
+
+
+}

+ 120 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/server/aliyun/DefaultAliBridgeChannel.java

@@ -0,0 +1,120 @@
+package org.jetlinks.community.bridge.server.aliyun;
+
+import com.aliyun.iot.as.bridge.core.model.Session;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.hswebframework.web.logger.ReactiveLogger;
+import org.jetlinks.community.bridge.core.AliBridgeCodec;
+import org.jetlinks.community.bridge.core.DefaultBridgeBootstrap;
+import org.jetlinks.community.bridge.core.DefaultUplinkChannelHandler;
+import org.jetlinks.community.bridge.message.AliBridgeMessage;
+import org.jetlinks.community.bridge.server.Channel;
+import org.jetlinks.core.event.EventBus;
+import org.jetlinks.core.event.Subscription;
+import reactor.core.Disposable;
+import reactor.core.publisher.*;
+import reactor.core.scheduler.Schedulers;
+import java.util.Objects;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName DefaultAliBridgeChannel.java
+ * @Description 阿里云设备网桥通道,用于单一设备上报信息
+ * @createTime 2021年11月30日 16:06:00
+ */
+@Slf4j
+@Data
+public class DefaultAliBridgeChannel implements Channel {
+    private String originalIdentity;
+
+    /**
+     * 产品id
+     */
+    private String productId;
+
+    private String productKey;
+
+    private String deviceName;
+
+    private EventBus eventBus;
+
+    private  DefaultUplinkChannelHandler uplinkChannelHandler;
+
+    private Disposable disposable ;
+    /**
+     * @see DefaultBridgeBootstrap 网桥数据下行
+     * @see DefaultUplinkChannelHandler 网桥数据下行
+     */
+    public DefaultAliBridgeChannel(String originalIdentity,
+                                   String productId,
+                                   String productKey,
+                                   String deviceName,
+                                   DefaultUplinkChannelHandler uplinkChannelHandler,
+                                   EventBus eventBus) {
+        this.originalIdentity = originalIdentity;
+        this.productId=productId;
+        this.eventBus=eventBus;
+        this.productKey=productKey;
+        this.deviceName=deviceName;
+        this.uplinkChannelHandler = uplinkChannelHandler;
+
+        /**
+         *  监听 属性、事件、标签上报
+         *  属性设置,服务调用
+         *  上下线
+         */
+        Subscription subscription = Subscription.builder()
+            .subscriberId("alibridge-" + this.originalIdentity)
+            //上下线
+            .topics(String.format("/device/%s/%s/online",productId,originalIdentity),String.format("/device/%s/%s/offline",productId,originalIdentity),
+                String.format("/device/%s/%s/disconnect/reply",productId,originalIdentity))
+            //属性上报
+            .topics(String.format("/device/%s/%s/message/property/report",productId,originalIdentity))
+            //事件上报
+            .topics(String.format("/device/%s/%s/message/event/*",productId,originalIdentity))
+            //标签上报
+            .topics(String.format("/device/%s/%s/message/tags/update",productId,originalIdentity))
+            //坐标上报
+            .topics(String.format("/device/%s/%s/coordinate",productId,originalIdentity))
+            //属性设置成功上报
+            .topics(String.format("/device/%s/%s/message/property/write/reply",productId,originalIdentity))
+            //调用功能
+            .topics(String.format("/device/%s/%s/message/send/function",productId,originalIdentity))
+            .features( Subscription.Feature.local, Subscription.Feature.broker)
+            .build();
+        disposable = eventBus.subscribe(subscription)
+            .flatMap(payload -> Mono.just(payload.bodyToJson(true)))
+            .doOnEach(ReactiveLogger.onError(error -> log.error("aliBridge uplink message confront a error when convert to Json,", error)))
+            .parallel()
+            .runOn(Schedulers.parallel())
+            .flatMap(json -> Mono.justOrEmpty(AliBridgeCodec.encode(json, this.productKey, this.deviceName)))
+            .filter(Objects::nonNull)
+            .doOnNext(this::upLink).subscribe();
+    }
+
+    /**
+     * 处理上行数据   自有平台-》阿里云
+     */
+    @Override
+    public void upLink(AliBridgeMessage msg){
+        if(msg.isOnline()){
+            uplinkChannelHandler.doOnline(Session.newInstance(this.originalIdentity,new Object()),msg.getOriginalIdentity());
+        }else if(msg.isOffline()){
+            uplinkChannelHandler.doOffline(this.originalIdentity);
+        }else {
+            uplinkChannelHandler.doPublishAsync(this.originalIdentity, msg.getTopic(), msg.getPayload(), 1);
+        }
+    }
+
+    @Override
+    public void online() {
+        uplinkChannelHandler.doOnline(Session.newInstance(this.originalIdentity,new Object()),this.originalIdentity);
+    }
+
+    @Override
+    public void doOffline(){
+        uplinkChannelHandler.doOffline(this.originalIdentity);
+        log.info("设备网桥{}下线",this.originalIdentity);
+    }
+}

+ 17 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/service/AliBridgeDeviceService.java

@@ -0,0 +1,17 @@
+package org.jetlinks.community.bridge.service;
+
+import org.hswebframework.web.crud.service.GenericReactiveCacheSupportCrudService;
+import org.jetlinks.community.bridge.entity.AliIotBridgeDeviceConfig;
+import org.jetlinks.community.bridge.entity.AliIotBridgeEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliProductMappingService.java
+ * @Description TODO
+ * @createTime 2021年11月27日 09:59:00
+ */
+@Component
+public class AliBridgeDeviceService extends GenericReactiveCacheSupportCrudService<AliIotBridgeDeviceConfig, String> {
+}

+ 16 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/service/AliBridgeService.java

@@ -0,0 +1,16 @@
+package org.jetlinks.community.bridge.service;
+
+import org.hswebframework.web.crud.service.GenericReactiveCacheSupportCrudService;
+import org.jetlinks.community.bridge.entity.AliIotBridgeEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliProductMappingService.java
+ * @Description TODO
+ * @createTime 2021年11月27日 09:59:00
+ */
+@Component
+public class AliBridgeService extends GenericReactiveCacheSupportCrudService<AliIotBridgeEntity, String> {
+}

+ 68 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/service/AliIotService.java

@@ -0,0 +1,68 @@
+package org.jetlinks.community.bridge.service;
+
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.map.MapUtil;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.iot.model.v20180120.*;
+import com.aliyuncs.profile.DefaultProfile;
+import org.jetlinks.community.bridge.web.param.AliApiParam;
+import org.springframework.stereotype.Component;
+import reactor.core.publisher.Mono;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliYunIotService.java
+ * @Description 阿里云物联网服务接口
+ * @createTime 2021年11月26日 17:18:00
+ */
+@Component
+public class AliIotService {
+
+    /**
+     * 分页查询阿里云物联网平台产品信息
+     * @return 分页查询结果
+     */
+    public Mono<Map<String,Object>> queryIotProductPage(AliApiParam param,Integer currentPage,Integer pageSize) throws ClientException {
+        DefaultProfile profile = DefaultProfile.getProfile(param.getRegionId(), param.getAccessKeyId(), param.getAccessSecret());
+        DefaultAcsClient client = new DefaultAcsClient(profile);
+        QueryProductListRequest request = new QueryProductListRequest();
+        request.setPageSize(pageSize);
+        request.setCurrentPage(currentPage);
+        QueryProductListResponse response = client.getAcsResponse(request);
+        QueryProductListResponse.Data data = response.getData();
+        return Mono.just(MapUtil
+            .of(Pair.of("pageIndex",currentPage),Pair.of("pageSize",pageSize),Pair.of("total",data.getTotal()),Pair.of("data",data.getList())));
+
+    }
+
+    /**
+     * 分页查询阿里云物联网平台产品下设备列表
+     * @param param 阿里云api调用参数
+     * @param currentPage 当前页
+     * @param pageSize 页面大小
+     * @return
+     * @throws ClientException
+     */
+    public Mono<?> queryIotDevicePage(AliApiParam param, Integer currentPage, Integer pageSize) throws ClientException {
+        DefaultProfile profile = DefaultProfile.getProfile(param.getRegionId(), param.getAccessKeyId(), param.getAccessSecret());
+        DefaultAcsClient client = new DefaultAcsClient(profile);
+        QueryDeviceRequest request = new QueryDeviceRequest();
+        request.setPageSize(pageSize);
+        request.setCurrentPage(currentPage);
+        request.setProductKey(param.getProductKey());
+        request.setNextToken(param.getNextToken());
+        QueryDeviceResponse response = client.getAcsResponse(request);
+        List<QueryDeviceResponse.DeviceInfo> data = response.getData();
+        return Mono.just(MapUtil
+            .of(Pair.of("pageIndex",currentPage),
+                Pair.of("pageSize",pageSize),
+                Pair.of("total",response.getTotal()),
+                Pair.of("data",data),
+                Pair.of("nextToken",response.getNextToken())));
+    }
+
+}

+ 124 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/web/AliBridgeServerController.java

@@ -0,0 +1,124 @@
+package org.jetlinks.community.bridge.web;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.CreateAction;
+import org.hswebframework.web.authorization.annotation.DeleteAction;
+import org.hswebframework.web.authorization.annotation.Resource;
+import org.hswebframework.web.crud.service.ReactiveCrudService;
+import org.hswebframework.web.crud.web.reactive.ReactiveServiceCrudController;
+import org.jetlinks.community.bridge.entity.AliIotBridgeEntity;
+import org.jetlinks.community.bridge.entity.AliIotBridgeDeviceConfig;
+import org.jetlinks.community.bridge.server.aliyun.AliBridgeGateway;
+import org.jetlinks.community.bridge.server.aliyun.AliBridgeServer;
+import org.jetlinks.community.bridge.service.AliBridgeDeviceService;
+import org.jetlinks.community.bridge.service.AliBridgeService;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliYunBridgeController.java
+ * @Description 阿里云网桥
+ * @createTime 2021年11月26日 15:34:00
+ */
+@RestController
+@RequestMapping("/ali/bridge")
+@Slf4j
+@Authorize
+@Resource(id="ali-bridge",name = "阿里云网桥服务")
+@AllArgsConstructor
+@Tag(name = "阿里云网桥服务")
+public class AliBridgeServerController implements
+    ReactiveServiceCrudController<AliIotBridgeEntity, String> {
+
+    private final AliBridgeService bridgeService;
+
+    private final AliBridgeGateway bridgeGateway;
+
+    private final AliBridgeDeviceService  bridgeDeviceService;
+    @Override
+    public ReactiveCrudService<AliIotBridgeEntity, String> getService() {
+        return bridgeService;
+    }
+
+    @PostMapping("/delete/{id}")
+    @Operation(summary = "删除网桥")
+    @DeleteAction
+    public Mono<Void> deleteBridge(@PathVariable("id")String serverId){
+        //todo
+        return bridgeGateway.getBridgeServer(serverId)
+            .flatMap(AliBridgeServer::stopBridge)
+            .concatWith(bridgeService.deleteById(serverId).then())
+            .concatWith(Mono.empty())
+            .then();
+    }
+
+
+    @PostMapping("/register/{serverId}")
+    @Operation(summary = "注册网桥设备")
+    @CreateAction
+    public Mono<Void> register(@PathVariable("serverId") String id,@RequestBody Mono<AliIotBridgeDeviceConfig> config){
+        return Mono.zip(
+            //保存网桥设备信息
+            bridgeDeviceService.save(config),
+            //注册网桥设备
+            bridgeGateway.getBridgeServer(id)
+            .zipWith(config)
+            .flatMap(tp2-> tp2.getT1().register(tp2.getT2().getOriginalIdentity(),tp2.getT2().getProductKey(),tp2.getT2().getDeviceName(),tp2.getT2().getDeviceSecret())))
+            .then();
+    }
+
+    @PostMapping("/pause/{serverId}")
+    @Operation(summary = "暂停网桥")
+    @CreateAction
+    public Mono<Void> pause(@PathVariable("serverId") String id){
+        return bridgeGateway.getBridgeServer(id)
+            .flatMap(AliBridgeServer::stopBridge)
+            .then();
+    }
+
+    @PostMapping("/start/{serverId}")
+    @Operation(summary = "重启网桥")
+    @CreateAction
+    /**
+     * 判断是否是重启网桥,若不是重新,则重新注册网桥
+     */
+    public Mono<Void> startBridge(@PathVariable("serverId")String serverId){
+        return bridgeGateway.getBridgeServer(serverId)
+            .flatMap(AliBridgeServer::reconnect)
+            .then();
+    }
+
+    @PostMapping("/unregister/{serverId}")
+    @Operation(summary = "取消注册网桥设备")
+    @DeleteAction
+    public Mono<Void> unRegister(@PathVariable("id")String serverId,@RequestBody String originalIdentity){
+        return Mono.zip(
+            //删除网桥设备
+            bridgeDeviceService.createDelete().where(AliIotBridgeDeviceConfig::getOriginalIdentity,originalIdentity).execute(),
+            //取消注册
+            bridgeGateway.getBridgeServer(serverId)
+                .flatMap(server->server.unRegister(originalIdentity))).then();
+    }
+
+    @GetMapping("/lookUp/{serverId}")
+    @Operation(summary = "查看网桥设备")
+    //todo
+    public Mono<Void> lookUp(@PathVariable("id")Mono<String> serverId){
+        return Mono.empty();
+    }
+
+
+    @PutMapping("/update")
+    @Operation(summary = "更新网桥信息")
+    @CreateAction
+    public Mono<Void> update(@PathVariable("serverId") Mono<AliIotBridgeEntity> bridge){
+        //todo
+        return Mono.zip(bridgeService.save(bridge),bridge.flatMap(bridgeGateway::initBridge)).then();
+    }
+}

+ 82 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/web/AliIotApiController.java

@@ -0,0 +1,82 @@
+package org.jetlinks.community.bridge.web;
+
+import cn.hutool.core.util.StrUtil;
+import com.aliyuncs.exceptions.ClientException;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.authorization.annotation.Resource;
+import org.hswebframework.web.exception.BusinessException;
+import org.jetlinks.community.bridge.service.AliIotService;
+import org.jetlinks.community.bridge.web.param.AliApiParam;
+import org.springframework.web.bind.annotation.*;
+import reactor.core.publisher.Mono;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliYunBridgeController.java
+ * @Description 阿里云网桥
+ * @createTime 2021年11月26日 15:34:00
+ */
+@RestController
+@RequestMapping("/ali/api")
+@Slf4j
+@Authorize
+@Resource(id="ali-api",name = "阿里云api调用")
+@AllArgsConstructor
+@Tag(name = "阿里云api调用")
+public class AliIotApiController {
+
+
+    private final AliIotService aliIotService;
+
+    @PostMapping("/product/query/{currentPage}/{pageSize}")
+    @Operation(summary = "获取阿里云产品信息")
+    public Mono<?>  queryPageProducts(@PathVariable("currentPage") Integer currentPage,@PathVariable("pageSize") Integer pageSize,
+                                         @RequestBody AliApiParam param){
+        try {
+            verifyCommonParam(param);
+            return aliIotService.queryIotProductPage(param,currentPage,pageSize);
+        }catch (ClientException e){
+            return Mono.error(new BusinessException(e.getMessage()));
+        }
+    }
+
+    @PostMapping("/device/query/{currentPage}/{pageSize}")
+    @Operation(summary = "获取阿里云产品下设备信息")
+    public Mono<?>  queryPageDevices(@PathVariable("currentPage") Integer currentPage,@PathVariable("pageSize") Integer pageSize,
+                                      @RequestBody AliApiParam param){
+        try {
+            verifyDeviceParam(param);
+            return aliIotService.queryIotDevicePage(param,currentPage,pageSize);
+        }catch (ClientException e){
+            return Mono.error(new BusinessException(e.getMessage()));
+        }
+    }
+
+
+    private void verifyDeviceParam(AliApiParam param){
+        if(StrUtil.isEmpty(param.getProductKey())){
+            throw new BusinessException("productKey不能为空");
+        }
+        verifyCommonParam(param);
+    }
+    /**
+     * 验证阿里云公共参数
+     * @param param
+     */
+    private void verifyCommonParam(AliApiParam param){
+        if(StrUtil.isEmpty(param.getAccessKeyId())){
+            throw new BusinessException("accessKeyId不能为空");
+        }
+        if(StrUtil.isEmpty(param.getAccessSecret())){
+            throw new BusinessException("accessSecret不能为空");
+        }
+        if(StrUtil.isEmpty(param.getRegionId())){
+            throw new BusinessException("regionId不能为空");
+        }
+    }
+}

+ 49 - 0
jetlinks-manager/bridge-manager/src/main/java/org/jetlinks/community/bridge/web/param/AliApiParam.java

@@ -0,0 +1,49 @@
+package org.jetlinks.community.bridge.web.param;
+
+import io.swagger.v3.oas.annotations.Parameter;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliApiParam.java
+ * @Description 阿里云API参数参数
+ * @createTime 2021年11月27日 08:26:00
+ */
+@Data
+public class AliApiParam {
+    @Parameter(name = "区域id,如cn-shanghai")
+    @NotNull(message = "区域id不能为空")
+    private String regionId;
+
+    @Parameter(name = "物联网实例id")
+    private String iotInstanceId;
+
+    @Parameter(name = "accessKeyId")
+    @NotNull(message = "accessKeyId不能为空")
+    private String accessKeyId;
+    @Parameter(name = "通行秘钥")
+    @NotNull(message = "accessSecret不能为空")
+    private String accessSecret;
+    @Parameter(name = "api节点")
+    private String apiEndpoint;
+    @Parameter(name = "授权节点")
+    private String authEndpoint;
+    @Parameter(name = "资源组节点",allowEmptyValue = true)
+    private String resourceGroupId;
+
+
+    /**
+     *  要查询的设备所属产品的ProductKey。
+     */
+    private String productKey;
+
+    /**
+     * 下一页标识,首次查询无需传入。后续查询需使用的NextToken,要从上一次查询的返回结果中获取。
+     *
+     * 当PageSize×CurrentPage值大于10,000时,必须传入NextToken。否则,无法返回数据。
+     */
+    private String nextToken;
+}

+ 1 - 1
jetlinks-manager/pom.xml

@@ -19,7 +19,7 @@
         <module>logging-manager</module>
         <module>rule-engine-manager</module>
         <module>visualization-manager</module>
-        <!--<module>bridge-manager</module>-->
+        <module>bridge-manager</module>
     </modules>
 
 </project>

+ 7 - 6
jetlinks-standalone/pom.xml

@@ -71,7 +71,7 @@
         </dependency>
 
         <!--<dependency>-->
-            <!--<groupId>org.hswebframework.web</groupId>-->
+            <!--<productId>org.hswebframework.web</productId>-->
             <!--<artifactId>hsweb-system-dictionary</artifactId>-->
             <!--<version>${hsweb.framework.version}</version>-->
         <!--</dependency>-->
@@ -124,6 +124,12 @@
             <version>${project.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>bridge-manager</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
         <dependency>
             <groupId>${project.groupId}</groupId>
             <artifactId>notify-manager</artifactId>
@@ -159,11 +165,6 @@
             <artifactId>spring-boot-starter-data-redis</artifactId>
         </dependency>
 
-<!--        <dependency>-->
-<!--            <groupId>org.springframework.boot</groupId>-->
-<!--            <artifactId>spring-boot-starter-actuator</artifactId>-->
-<!--        </dependency>-->
-
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-data-r2dbc</artifactId>

+ 2 - 4
jetlinks-standalone/src/main/java/org/jetlinks/community/standalone/JetLinksApplication.java

@@ -16,10 +16,8 @@ import org.springframework.stereotype.Component;
 import javax.annotation.PostConstruct;
 
 
-@SpringBootApplication( scanBasePackages = "org.jetlinks.community", exclude = {
+@SpringBootApplication( scanBasePackages = {"org.jetlinks.community"}, exclude = {
     RedissonAutoConfiguration.class
-//    DataSourceAutoConfiguration.class,
-//    ElasticsearchRestClientAutoConfiguration.class
 })
 @EnableCaching
 @EnableEasyormRepository("org.jetlinks.community.**.entity")
@@ -28,7 +26,7 @@ import javax.annotation.PostConstruct;
 @Slf4j
 public class JetLinksApplication {
 
-    public static void main(String[] args) throws Exception {
+    public static void main(String[] args) {
         SpringApplication.run(JetLinksApplication.class, args);
     }
 

+ 5 - 0
jetlinks-standalone/src/main/resources/application.yml

@@ -176,6 +176,11 @@ springdoc:
         - /device-firmware/**
         - /firmware/upgrade/task/**
         - /firmware/upgrade/history/**
+    - group: 云云对接相关接口
+      packages-to-scan:
+        - org.jetlinks.community.bridge.web
+      paths-to-exclude:
+        - /ali/**
     - group: 设备管理相关接口
       packages-to-scan:
         - org.jetlinks.community.device

+ 78 - 0
jetlinks-standalone/src/test/java/org/jetlinks/community/BridgeTest.java

@@ -0,0 +1,78 @@
+package org.jetlinks.community;
+
+import cn.hutool.core.lang.Pair;
+import cn.hutool.core.map.MapUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.jetlinks.community.bridge.entity.AliIotBridgeDeviceConfig;
+import org.jetlinks.community.bridge.entity.AliIotBridgeEntity;
+import org.jetlinks.community.bridge.server.aliyun.AliBridgeGateway;
+import org.jetlinks.community.standalone.JetLinksApplication;
+import org.jetlinks.core.device.DeviceRegistry;
+import org.jetlinks.core.event.EventBus;
+import org.jetlinks.core.message.DeviceOnlineMessage;
+import org.jetlinks.core.message.property.ReportPropertyMessage;
+import org.jetlinks.supports.server.DecodedClientMessageHandler;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName bridge.java
+ * @Description TODO
+ * @createTime 2021年12月02日 08:13:00
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = JetLinksApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
+@Slf4j
+@EnableAspectJAutoProxy(proxyTargetClass = true,exposeProxy = true)
+public class BridgeTest {
+    @Autowired
+    private EventBus eventBus;
+    @Autowired
+    private DeviceRegistry registry;
+    @Autowired
+    private DecodedClientMessageHandler messageHandler;
+    @Test
+    public void init() throws InterruptedException {
+        AliIotBridgeDeviceConfig bridgeConfig = new AliIotBridgeDeviceConfig();
+        bridgeConfig.setDeviceName("ceshi_");
+        bridgeConfig.setDeviceSecret("81be3f5ef0b9d383e270218f8262c353");
+        bridgeConfig.setProductKey("a1UmTLN10zP");
+        bridgeConfig.setId("123");
+
+
+        AliIotBridgeEntity aliIotBridgeEntity = new AliIotBridgeEntity();
+        AliIotBridgeEntity.AccessConfig accessConfig = new AliIotBridgeEntity.AccessConfig();
+        accessConfig.setAccessKey("LTAIcCyEivml1zSX");
+        accessConfig.setAccessSecret("nFHWpY8rT1fP3vobiFz3jYnSoHm5fl");
+        accessConfig.setAuthEndpoint("https://iot-auth.cn-shanghai.aliyuncs.com/auth/bridge");
+        accessConfig.setHttp2Endpoint("https://a1UmTLN10zP.iot-as-http2.cn-shanghai.aliyuncs.com:443");
+        accessConfig.setProductKey("a1UmTLN10zP");
+        accessConfig.setRegionId("cn-shanghai");
+        aliIotBridgeEntity.setProductId("1");
+        aliIotBridgeEntity.setAccessConfig(accessConfig);
+        AliBridgeGateway aliBridgeGateway = new AliBridgeGateway(registry,eventBus,messageHandler);
+        ;
+        aliBridgeGateway.initBridge(aliIotBridgeEntity);
+
+        aliBridgeGateway.getBridgeServer("1").flatMap(server->server.register("819b4fd3896e67f74195df36920ae55a","a1UmTLN10zP","ceshi_","81be3f5ef0b9d383e270218f8262c353")).subscribe();
+
+        Thread.sleep(10000);
+        DeviceOnlineMessage onlineMessage = new DeviceOnlineMessage();
+        onlineMessage.setDeviceId("819b4fd3896e67f74195df36920ae55a");
+        eventBus.publish("/device/1/819b4fd3896e67f74195df36920ae55a/online",onlineMessage).subscribe();
+
+        ReportPropertyMessage propertyMessage = new ReportPropertyMessage();
+        propertyMessage.setDeviceId("819b4fd3896e67f74195df36920ae55a");
+        propertyMessage.setProperties(MapUtil.of(Pair.of("LightStatus",0)));
+        eventBus.publish("/device/1/819b4fd3896e67f74195df36920ae55a/message/property/report",propertyMessage).subscribe();
+        while (Thread.activeCount()>1){
+
+        }
+    }
+}

+ 1 - 0
pom.xml

@@ -34,6 +34,7 @@
         <jetlinks.version>1.1.7-SNAPSHOT</jetlinks.version>
         <aliyun.sdk.version>4.5.6</aliyun.sdk.version>
         <aliyun.iot.sdk.version>7.29.0</aliyun.iot.sdk.version>
+        <aliyun.bridge.sdk.version>2.4.1</aliyun.bridge.sdk.version>
     </properties>
 
     <build>