微信小程序支付前,使用 MQTT 检测设备是否在线

文章目录

    需求场景

    例如,用户要出停车场,进行扫码支付,但是需要在支付之前判断一下起杆设备是否在线。

    如果不在线需要给出提示,防止收了钱,但是没有起杆。

    TODO

    • [x] 腾讯云 MQTT 接口
    • [x] golang api
    • [x] controller 更名为 mqtt.go, 把 mqtt 相关的操作都放在这个 controller 里,方便共用一些配置
    • [x] 封装 DescribeDevice。参数为 deviceId,获取 device,从而得到设备名,然后 mqtt 查询在线状态
    • [x] 线上测试
    • [x] 小程序端:预支付这里调用了两个 http 微信接口,可能等待时间有点长,导致用户点两次按钮,所以加个防止重复点击的功能

    腾讯云 MQTT 接口

    找到一个接口:查看设备详情

    https://cloud.tencent.com/document/product/634/31583

    可以通过产品名、加设备名,查询设备是否在线。
    返回值里包含 Online 字段,设备是否在线,0不在线,1在线。

    golang api

    https://github.com/TencentCloud/tencentcloud-sdk-go/blob/master/tencentcloud/iotcloud/v20180614/client.go

    // DescribeDevice
    // 本接口(DescribeDevice)用于查看设备信息
    //
    // 可能返回的错误码:
    //  INTERNALERROR = "InternalError"
    //  INVALIDPARAMETERVALUE = "InvalidParameterValue"
    //  RESOURCENOTFOUND_DEVICENOTEXIST = "ResourceNotFound.DeviceNotExist"
    //  RESOURCENOTFOUND_PRODUCTNOTEXIST = "ResourceNotFound.ProductNotExist"
    func (c *Client) DescribeDevice(request *DescribeDeviceRequest) (response *DescribeDeviceResponse, err error) {
        if request == nil {
            request = NewDescribeDeviceRequest()
        }
        response = NewDescribeDeviceResponse()
        err = c.Send(request, response)
        return
    }
    

    参数 DescribeDeviceRequest 通过 NewDescribeDeviceRequest 初始化获得,这样就不需要自己手动填写 Version 和 Action 了。

    func NewDescribeDeviceRequest() (request *DescribeDeviceRequest) {
        request = &DescribeDeviceRequest{
            BaseRequest: &tchttp.BaseRequest{},
        }
        request.Init().WithApiInfo("iotcloud", APIVersion, "DescribeDevice")
        
        return
    }
    

    腾讯云的 golang SDK 代码质量非常差,代码没有规范 ProductId ProductID 混着写,类型瞎定义。
    浪费时间。

    设备是否在线接口封装

    // 通过 MQTT 接口,判断设备是否在线
    // 逻辑:
    // 参数为 deviceId,获取 device,从而得到设备名,然后 mqtt 查询在线状态
    func isDeviceOnline(deviceId int) bool {
    	var device models.Device
    	err := models.DB.Where("`id` = ?", deviceId).First(&device).Error
    	if err != nil {
    		// record not found
    		log.Println(err)
    		return false
    	}
    
    	cpf := profile.NewClientProfile()
    	cpf.HttpProfile.ReqMethod = "POST"
    	cpf.HttpProfile.ReqTimeout = 10
    	cpf.HttpProfile.Endpoint = "iotcloud.tencentcloudapi.com"
    	client, _ := iotcloud.NewClient(credential, "", cpf)
    
    	request := iotcloud.NewDescribeDeviceRequest()
    	request.ProductID = common.StringPtr(mqttProductId)
    	request.DeviceName = common.StringPtr(device.DeviceName)
    
    	response, err := client.DescribeDevice(request)
    
    	// 处理异常
    	if _, ok := err.(*errors.TencentCloudSDKError); ok {
    		fmt.Printf("An API error has returned: %s", err)
    		return false
    	}
    	// 非SDK异常,直接失败。实际代码中可以加入其他的处理。
    	if err != nil {
    		panic(err)
    		return false
    	}
    	// 打印返回的json字符串
    	fmt.Printf("%s\n", response.ToJsonString())
    
    	if *response.Response.Online == uint64(1) {
    		return true
    	} else {
    		return false
    	}
    }
    

    极端情况

    如果设备断线,但是恰好在两次心跳之间,进行 MQTT 在线状态查询,那么还是显示在线,但是此时支付,并不能通过下发指令使设备启动。

    关于作者 🌱

    我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊,或者关注我的个人公众号“大象工具”, 查看更多联系方式