feat: 新功能开发,监测历史记录。微信和物联网平台,模块搭建。
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package com.intc.iot.config;
|
||||
|
||||
import com.aliyuncs.DefaultAcsClient;
|
||||
import com.aliyuncs.IAcsClient;
|
||||
import com.aliyuncs.profile.DefaultProfile;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 阿里云生活物联网平台(飞燕平台)配置
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(prefix = "aliyun.living-iot", name = "access-key-id")
|
||||
public class AliyunIotConfiguration {
|
||||
|
||||
private final AliyunIotProperties iotProperties;
|
||||
|
||||
/**
|
||||
* 创建阿里云飞燕平台客户端
|
||||
*/
|
||||
@Bean
|
||||
public IAcsClient livingIotClient() {
|
||||
DefaultProfile profile = DefaultProfile.getProfile(
|
||||
iotProperties.getRegionId(),
|
||||
iotProperties.getAccessKeyId(),
|
||||
iotProperties.getAccessKeySecret()
|
||||
);
|
||||
|
||||
IAcsClient client = new DefaultAcsClient(profile);
|
||||
log.info("阿里云生活物联网平台客户端初始化成功,RegionId: {}", iotProperties.getRegionId());
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.intc.iot.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 阿里云生活物联网平台(飞燕平台)配置属性
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Data
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "aliyun.living-iot")
|
||||
public class AliyunIotProperties {
|
||||
|
||||
/**
|
||||
* 阿里云AccessKey ID
|
||||
*/
|
||||
private String accessKeyId;
|
||||
|
||||
/**
|
||||
* 阿里云AccessKey Secret
|
||||
*/
|
||||
private String accessKeySecret;
|
||||
|
||||
/**
|
||||
* 地域节点(如:cn-shanghai)
|
||||
*/
|
||||
private String regionId;
|
||||
|
||||
/**
|
||||
* 飞燕平台项目ID(Project ID)
|
||||
*/
|
||||
private String projectId;
|
||||
|
||||
/**
|
||||
* App Key
|
||||
*/
|
||||
private String appKey;
|
||||
|
||||
/**
|
||||
* App Secret
|
||||
*/
|
||||
private String appSecret;
|
||||
|
||||
/**
|
||||
* 品类Key
|
||||
*/
|
||||
private String categoryKey;
|
||||
|
||||
/**
|
||||
* MQTT 配置
|
||||
*/
|
||||
private MqttConfig mqtt = new MqttConfig();
|
||||
|
||||
@Data
|
||||
public static class MqttConfig {
|
||||
/**
|
||||
* MQTT Broker 地址
|
||||
*/
|
||||
private String brokerUrl;
|
||||
|
||||
/**
|
||||
* 客户端ID
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 连接超时时间(秒)
|
||||
*/
|
||||
private Integer connectionTimeout = 30;
|
||||
|
||||
/**
|
||||
* 保活时间(秒)
|
||||
*/
|
||||
private Integer keepAliveInterval = 60;
|
||||
|
||||
/**
|
||||
* 自动重连
|
||||
*/
|
||||
private Boolean autoReconnect = true;
|
||||
|
||||
/**
|
||||
* 清除会话
|
||||
*/
|
||||
private Boolean cleanSession = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.intc.iot.config;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* MQTT 客户端配置
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnProperty(prefix = "aliyun.living-iot.mqtt", name = "broker-url")
|
||||
public class MqttConfiguration {
|
||||
|
||||
private final AliyunIotProperties iotProperties;
|
||||
|
||||
/**
|
||||
* 创建 MQTT 客户端
|
||||
*/
|
||||
@Bean
|
||||
public MqttClient mqttClient() throws Exception {
|
||||
AliyunIotProperties.MqttConfig mqtt = iotProperties.getMqtt();
|
||||
|
||||
MemoryPersistence persistence = new MemoryPersistence();
|
||||
MqttClient client = new MqttClient(mqtt.getBrokerUrl(), mqtt.getClientId(), persistence);
|
||||
|
||||
MqttConnectOptions options = new MqttConnectOptions();
|
||||
options.setUserName(mqtt.getUsername());
|
||||
options.setPassword(mqtt.getPassword().toCharArray());
|
||||
options.setConnectionTimeout(mqtt.getConnectionTimeout());
|
||||
options.setKeepAliveInterval(mqtt.getKeepAliveInterval());
|
||||
options.setAutomaticReconnect(mqtt.getAutoReconnect());
|
||||
options.setCleanSession(mqtt.getCleanSession());
|
||||
|
||||
client.connect(options);
|
||||
log.info("MQTT 客户端连接成功,Broker: {}", mqtt.getBrokerUrl());
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package com.intc.iot.controller;
|
||||
|
||||
import com.intc.common.core.domain.R;
|
||||
import com.intc.common.web.core.BaseController;
|
||||
import com.intc.iot.service.DeviceDataService;
|
||||
import com.intc.iot.service.IotDeviceService;
|
||||
import com.intc.iot.service.MqttService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 阿里云生活物联网平台(飞燕平台)控制器
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/iot")
|
||||
@Tag(name = "生活物联网平台管理", description = "阿里云飞燕平台对接接口")
|
||||
public class IotController extends BaseController {
|
||||
|
||||
@Autowired(required = false)
|
||||
private IotDeviceService iotDeviceService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private MqttService mqttService;
|
||||
|
||||
@Autowired(required = false)
|
||||
private DeviceDataService deviceDataService;
|
||||
|
||||
@Operation(summary = "测试接口")
|
||||
@GetMapping("/test")
|
||||
public R<String> test() {
|
||||
return R.ok("飞燕平台模块测试成功!");
|
||||
}
|
||||
|
||||
@Operation(summary = "查询设备列表")
|
||||
@GetMapping("/device/list")
|
||||
public R<Map<String, Object>> queryDeviceList(
|
||||
@Parameter(description = "页码") @RequestParam(defaultValue = "1") Integer pageNo,
|
||||
@Parameter(description = "每页大小") @RequestParam(defaultValue = "20") Integer pageSize) {
|
||||
try {
|
||||
if (iotDeviceService == null) {
|
||||
return R.fail("飞燕平台配置未启用");
|
||||
}
|
||||
Map<String, Object> response = iotDeviceService.queryDeviceList(pageNo, pageSize);
|
||||
return R.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("查询设备列表失败", e);
|
||||
return R.fail("查询设备列表失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "查询设备详情")
|
||||
@GetMapping("/device/info")
|
||||
public R<Map<String, Object>> queryDeviceInfo(
|
||||
@Parameter(description = "设备ID") @RequestParam String iotId) {
|
||||
try {
|
||||
if (iotDeviceService == null) {
|
||||
return R.fail("飞燕平台配置未启用");
|
||||
}
|
||||
Map<String, Object> response = iotDeviceService.queryDeviceInfo(iotId);
|
||||
return R.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("查询设备详情失败", e);
|
||||
return R.fail("查询设备详情失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "查询设备属性")
|
||||
@GetMapping("/device/properties")
|
||||
public R<Map<String, Object>> queryDeviceProperties(
|
||||
@Parameter(description = "设备ID") @RequestParam String iotId) {
|
||||
try {
|
||||
if (iotDeviceService == null) {
|
||||
return R.fail("飞燕平台配置未启用");
|
||||
}
|
||||
Map<String, Object> response = iotDeviceService.queryDeviceProperties(iotId);
|
||||
return R.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("查询设备属性失败", e);
|
||||
return R.fail("查询设备属性失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "设置设备属性")
|
||||
@PostMapping("/device/property/set")
|
||||
public R<Map<String, Object>> setDeviceProperty(
|
||||
@Parameter(description = "设备ID") @RequestParam String iotId,
|
||||
@Parameter(description = "属性JSON") @RequestParam String properties) {
|
||||
try {
|
||||
if (iotDeviceService == null) {
|
||||
return R.fail("飞燕平台配置未启用");
|
||||
}
|
||||
Map<String, Object> response = iotDeviceService.setDeviceProperty(iotId, properties);
|
||||
return R.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("设置设备属性失败", e);
|
||||
return R.fail("设置设备属性失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "调用设备服务")
|
||||
@PostMapping("/device/invoke")
|
||||
public R<Map<String, Object>> invokeService(
|
||||
@Parameter(description = "设备ID") @RequestParam String iotId,
|
||||
@Parameter(description = "服务标识符") @RequestParam String identifier,
|
||||
@Parameter(description = "参数JSON") @RequestParam String args) {
|
||||
try {
|
||||
if (iotDeviceService == null) {
|
||||
return R.fail("飞燕平台配置未启用");
|
||||
}
|
||||
Map<String, Object> response = iotDeviceService.invokeService(iotId, identifier, args);
|
||||
return R.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("调用设备服务失败", e);
|
||||
return R.fail("调用设备服务失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "解绑设备")
|
||||
@DeleteMapping("/device/unbind")
|
||||
public R<Map<String, Object>> unbindDevice(
|
||||
@Parameter(description = "设备ID") @RequestParam String iotId) {
|
||||
try {
|
||||
if (iotDeviceService == null) {
|
||||
return R.fail("飞燕平台配置未启用");
|
||||
}
|
||||
Map<String, Object> response = iotDeviceService.unbindDevice(iotId);
|
||||
return R.ok(response);
|
||||
} catch (Exception e) {
|
||||
log.error("解绑设备失败", e);
|
||||
return R.fail("解绑设备失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "发布MQTT消息")
|
||||
@PostMapping("/mqtt/publish")
|
||||
public R<String> publishMqtt(
|
||||
@Parameter(description = "主题") @RequestParam String topic,
|
||||
@Parameter(description = "消息内容") @RequestParam String payload,
|
||||
@Parameter(description = "QoS等级") @RequestParam(defaultValue = "1") int qos) {
|
||||
try {
|
||||
if (mqttService == null) {
|
||||
return R.fail("MQTT配置未启用");
|
||||
}
|
||||
mqttService.publish(topic, payload, qos);
|
||||
return R.ok("消息发布成功");
|
||||
} catch (Exception e) {
|
||||
log.error("发布MQTT消息失败", e);
|
||||
return R.fail("发布MQTT消息失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "订阅MQTT主题")
|
||||
@PostMapping("/mqtt/subscribe")
|
||||
public R<String> subscribeMqtt(
|
||||
@Parameter(description = "主题") @RequestParam String topic,
|
||||
@Parameter(description = "QoS等级") @RequestParam(defaultValue = "1") int qos) {
|
||||
try {
|
||||
if (mqttService == null) {
|
||||
return R.fail("MQTT配置未启用");
|
||||
}
|
||||
mqttService.subscribe(topic, qos);
|
||||
return R.ok("订阅成功");
|
||||
} catch (Exception e) {
|
||||
log.error("订阅MQTT主题失败", e);
|
||||
return R.fail("订阅MQTT主题失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "取消订阅MQTT主题")
|
||||
@PostMapping("/mqtt/unsubscribe")
|
||||
public R<String> unsubscribeMqtt(
|
||||
@Parameter(description = "主题") @RequestParam String topic) {
|
||||
try {
|
||||
if (mqttService == null) {
|
||||
return R.fail("MQTT配置未启用");
|
||||
}
|
||||
mqttService.unsubscribe(topic);
|
||||
return R.ok("取消订阅成功");
|
||||
} catch (Exception e) {
|
||||
log.error("取消订阅MQTT主题失败", e);
|
||||
return R.fail("取消订阅MQTT主题失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "订阅设备实时数据(按产品)")
|
||||
@PostMapping("/device/data/subscribe")
|
||||
public R<String> subscribeDeviceData(
|
||||
@Parameter(description = "产品Key") @RequestParam String productKey) {
|
||||
try {
|
||||
if (deviceDataService == null) {
|
||||
return R.fail("设备数据服务未启用");
|
||||
}
|
||||
deviceDataService.subscribeAllDevices(productKey);
|
||||
return R.ok("订阅成功,设备数据将实时推送");
|
||||
} catch (Exception e) {
|
||||
log.error("订阅设备数据失败", e);
|
||||
return R.fail("订阅设备数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.intc.iot.domain;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 物联网设备信息
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Data
|
||||
@TableName("iot_device")
|
||||
public class IotDevice implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 设备名称
|
||||
*/
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 产品Key
|
||||
*/
|
||||
private String productKey;
|
||||
|
||||
/**
|
||||
* 设备密钥
|
||||
*/
|
||||
private String deviceSecret;
|
||||
|
||||
/**
|
||||
* 设备状态:0-未激活 1-在线 2-离线 3-已禁用
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 设备备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
/**
|
||||
* 最后上线时间
|
||||
*/
|
||||
private LocalDateTime lastOnlineTime;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.intc.iot.handler;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 设备数据处理器
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class DeviceDataHandler {
|
||||
|
||||
/**
|
||||
* 处理设备属性上报数据
|
||||
*
|
||||
* @param topic 主题
|
||||
* @param payload 消息内容
|
||||
*/
|
||||
public void handlePropertyPost(String topic, String payload) {
|
||||
log.info("收到设备属性上报,Topic: {}", topic);
|
||||
|
||||
try {
|
||||
JSONObject data = JSONUtil.parseObj(payload);
|
||||
|
||||
// 解析飞燕平台消息格式
|
||||
String method = data.getStr("method");
|
||||
String id = data.getStr("id");
|
||||
JSONObject params = data.getJSONObject("params");
|
||||
|
||||
log.info("设备属性数据 - Method: {}, ID: {}, Params: {}", method, id, params);
|
||||
|
||||
// TODO: 这里添加您的业务逻辑
|
||||
// 1. 存储到数据库
|
||||
// 2. 触发告警
|
||||
// 3. 推送到前端(通过 WebSocket/SSE)
|
||||
// 4. 数据分析处理
|
||||
|
||||
if (params != null) {
|
||||
params.forEach((key, value) -> {
|
||||
log.info("属性: {} = {}", key, value);
|
||||
// 根据不同属性进行不同处理
|
||||
handleProperty(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理设备属性数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理设备事件上报数据
|
||||
*
|
||||
* @param topic 主题
|
||||
* @param payload 消息内容
|
||||
*/
|
||||
public void handleEventPost(String topic, String payload) {
|
||||
log.info("收到设备事件上报,Topic: {}", topic);
|
||||
|
||||
try {
|
||||
JSONObject data = JSONUtil.parseObj(payload);
|
||||
|
||||
String method = data.getStr("method");
|
||||
String id = data.getStr("id");
|
||||
JSONObject params = data.getJSONObject("params");
|
||||
|
||||
log.info("设备事件数据 - Method: {}, ID: {}, Params: {}", method, id, params);
|
||||
|
||||
// TODO: 处理设备事件
|
||||
// 例如:故障告警、状态变化等
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理设备事件数据失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理单个属性
|
||||
*
|
||||
* @param propertyName 属性名称
|
||||
* @param propertyValue 属性值
|
||||
*/
|
||||
private void handleProperty(String propertyName, Object propertyValue) {
|
||||
// 根据属性名称进行不同的业务处理
|
||||
switch (propertyName) {
|
||||
case "temperature":
|
||||
handleTemperature(propertyValue);
|
||||
break;
|
||||
case "humidity":
|
||||
handleHumidity(propertyValue);
|
||||
break;
|
||||
case "status":
|
||||
handleStatus(propertyValue);
|
||||
break;
|
||||
default:
|
||||
log.debug("未处理的属性: {} = {}", propertyName, propertyValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理温度数据
|
||||
*/
|
||||
private void handleTemperature(Object value) {
|
||||
// 示例:温度告警
|
||||
if (value instanceof Number) {
|
||||
double temp = ((Number) value).doubleValue();
|
||||
if (temp > 80) {
|
||||
log.warn("温度过高告警: {}°C", temp);
|
||||
// TODO: 发送告警通知
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理湿度数据
|
||||
*/
|
||||
private void handleHumidity(Object value) {
|
||||
// 示例:湿度处理逻辑
|
||||
log.debug("湿度数据: {}", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理状态数据
|
||||
*/
|
||||
private void handleStatus(Object value) {
|
||||
// 示例:设备状态变化
|
||||
log.info("设备状态变化: {}", value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.intc.iot.service;
|
||||
|
||||
/**
|
||||
* 设备数据服务接口
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
public interface DeviceDataService {
|
||||
|
||||
/**
|
||||
* 订阅设备属性上报
|
||||
*
|
||||
* @param iotId 设备ID
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void subscribeDeviceProperties(String iotId) throws Exception;
|
||||
|
||||
/**
|
||||
* 订阅设备事件上报
|
||||
*
|
||||
* @param iotId 设备ID
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void subscribeDeviceEvents(String iotId) throws Exception;
|
||||
|
||||
/**
|
||||
* 订阅所有设备数据
|
||||
*
|
||||
* @param productKey 产品Key
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void subscribeAllDevices(String productKey) throws Exception;
|
||||
|
||||
/**
|
||||
* 取消订阅设备数据
|
||||
*
|
||||
* @param iotId 设备ID
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void unsubscribeDevice(String iotId) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.intc.iot.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 阿里云生活物联网平台(飞燕平台)设备管理服务
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
public interface IotDeviceService {
|
||||
|
||||
/**
|
||||
* 查询设备列表
|
||||
*
|
||||
* @param pageNo 页码
|
||||
* @param pageSize 每页大小
|
||||
* @return 设备列表
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
Map<String, Object> queryDeviceList(Integer pageNo, Integer pageSize) throws Exception;
|
||||
|
||||
/**
|
||||
* 查询设备详情
|
||||
*
|
||||
* @param iotId 设备ID
|
||||
* @return 设备详情
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
Map<String, Object> queryDeviceInfo(String iotId) throws Exception;
|
||||
|
||||
/**
|
||||
* 查询设备属性
|
||||
*
|
||||
* @param iotId 设备ID
|
||||
* @return 设备属性
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
Map<String, Object> queryDeviceProperties(String iotId) throws Exception;
|
||||
|
||||
/**
|
||||
* 设置设备属性
|
||||
*
|
||||
* @param iotId 设备ID
|
||||
* @param properties 属性JSON
|
||||
* @return 设置结果
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
Map<String, Object> setDeviceProperty(String iotId, String properties) throws Exception;
|
||||
|
||||
/**
|
||||
* 调用设备服务
|
||||
*
|
||||
* @param iotId 设备ID
|
||||
* @param identifier 服务标识符
|
||||
* @param args 参数
|
||||
* @return 调用结果
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
Map<String, Object> invokeService(String iotId, String identifier, String args) throws Exception;
|
||||
|
||||
/**
|
||||
* 解绑设备
|
||||
*
|
||||
* @param iotId 设备ID
|
||||
* @return 解绑结果
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
Map<String, Object> unbindDevice(String iotId) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.intc.iot.service;
|
||||
|
||||
/**
|
||||
* MQTT 消息服务
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
public interface MqttService {
|
||||
|
||||
/**
|
||||
* 发布消息
|
||||
*
|
||||
* @param topic 主题
|
||||
* @param payload 消息内容
|
||||
* @param qos 服务质量等级(0/1/2)
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void publish(String topic, String payload, int qos) throws Exception;
|
||||
|
||||
/**
|
||||
* 订阅主题
|
||||
*
|
||||
* @param topic 主题
|
||||
* @param qos 服务质量等级(0/1/2)
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void subscribe(String topic, int qos) throws Exception;
|
||||
|
||||
/**
|
||||
* 取消订阅
|
||||
*
|
||||
* @param topic 主题
|
||||
* @throws Exception 异常
|
||||
*/
|
||||
void unsubscribe(String topic) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.intc.iot.service.impl;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.intc.iot.service.DeviceDataService;
|
||||
import com.intc.iot.service.MqttService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 设备数据服务实现
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnBean(MqttClient.class)
|
||||
public class DeviceDataServiceImpl implements DeviceDataService {
|
||||
|
||||
private final MqttService mqttService;
|
||||
|
||||
/**
|
||||
* 飞燕平台设备属性上报 Topic 格式
|
||||
* /sys/{productKey}/{deviceName}/thing/event/property/post
|
||||
*/
|
||||
private static final String PROPERTY_POST_TOPIC = "/sys/%s/%s/thing/event/property/post";
|
||||
|
||||
/**
|
||||
* 飞燕平台设备事件上报 Topic 格式
|
||||
* /sys/{productKey}/{deviceName}/thing/event/{eventIdentifier}/post
|
||||
*/
|
||||
private static final String EVENT_POST_TOPIC = "/sys/%s/%s/thing/event/+/post";
|
||||
|
||||
/**
|
||||
* 所有设备属性上报 Topic(使用通配符)
|
||||
* /sys/{productKey}/+/thing/event/property/post
|
||||
*/
|
||||
private static final String ALL_DEVICES_PROPERTY_TOPIC = "/sys/%s/+/thing/event/property/post";
|
||||
|
||||
@Override
|
||||
public void subscribeDeviceProperties(String iotId) throws Exception {
|
||||
// 注意:需要根据 iotId 获取 productKey 和 deviceName
|
||||
// 这里简化处理,实际应该从数据库或缓存中查询
|
||||
log.info("订阅设备属性上报,IotId: {}", iotId);
|
||||
|
||||
// 示例:假设从设备信息中获取
|
||||
// String productKey = getProductKeyByIotId(iotId);
|
||||
// String deviceName = getDeviceNameByIotId(iotId);
|
||||
// String topic = String.format(PROPERTY_POST_TOPIC, productKey, deviceName);
|
||||
|
||||
// mqttService.subscribe(topic, 1);
|
||||
|
||||
log.warn("请先实现 iotId 到 productKey/deviceName 的映射逻辑");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeDeviceEvents(String iotId) throws Exception {
|
||||
log.info("订阅设备事件上报,IotId: {}", iotId);
|
||||
|
||||
// 类似属性订阅,需要映射关系
|
||||
log.warn("请先实现 iotId 到 productKey/deviceName 的映射逻辑");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribeAllDevices(String productKey) throws Exception {
|
||||
log.info("订阅产品下所有设备属性上报,ProductKey: {}", productKey);
|
||||
|
||||
String topic = String.format(ALL_DEVICES_PROPERTY_TOPIC, productKey);
|
||||
mqttService.subscribe(topic, 1);
|
||||
|
||||
log.info("成功订阅 Topic: {}", topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribeDevice(String iotId) throws Exception {
|
||||
log.info("取消订阅设备数据,IotId: {}", iotId);
|
||||
|
||||
// 需要取消对应的 Topic 订阅
|
||||
log.warn("请先实现 iotId 到 productKey/deviceName 的映射逻辑");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.intc.iot.service.impl;
|
||||
|
||||
import com.aliyuncs.IAcsClient;
|
||||
import com.aliyuncs.iot.model.v20180120.*;
|
||||
import com.intc.iot.config.AliyunIotProperties;
|
||||
import com.intc.iot.service.IotDeviceService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 阿里云生活物联网平台(飞燕平台)设备管理服务实现
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnBean(IAcsClient.class)
|
||||
public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
|
||||
private final IAcsClient acsClient;
|
||||
private final AliyunIotProperties iotProperties;
|
||||
|
||||
@Override
|
||||
public Map<String, Object> queryDeviceList(Integer pageNo, Integer pageSize) throws Exception {
|
||||
log.info("查询设备列表,页码: {}, 每页大小: {}", pageNo, pageSize);
|
||||
|
||||
QueryDeviceRequest request = new QueryDeviceRequest();
|
||||
request.setCurrentPage(pageNo);
|
||||
request.setPageSize(pageSize);
|
||||
|
||||
QueryDeviceResponse response = acsClient.getAcsResponse(request);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", response.getSuccess());
|
||||
result.put("data", response.getData());
|
||||
result.put("total", response.getTotal());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> queryDeviceInfo(String iotId) throws Exception {
|
||||
log.info("查询设备详情,IotId: {}", iotId);
|
||||
|
||||
QueryDeviceDetailRequest request = new QueryDeviceDetailRequest();
|
||||
request.setIotId(iotId);
|
||||
|
||||
QueryDeviceDetailResponse response = acsClient.getAcsResponse(request);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", response.getSuccess());
|
||||
result.put("data", response.getData());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> queryDeviceProperties(String iotId) throws Exception {
|
||||
log.info("查询设备属性,IotId: {}", iotId);
|
||||
|
||||
QueryDevicePropertyStatusRequest request = new QueryDevicePropertyStatusRequest();
|
||||
request.setIotId(iotId);
|
||||
|
||||
QueryDevicePropertyStatusResponse response = acsClient.getAcsResponse(request);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", response.getSuccess());
|
||||
result.put("data", response.getData());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> setDeviceProperty(String iotId, String properties) throws Exception {
|
||||
log.info("设置设备属性,IotId: {}, Properties: {}", iotId, properties);
|
||||
|
||||
SetDevicePropertyRequest request = new SetDevicePropertyRequest();
|
||||
request.setIotId(iotId);
|
||||
request.setItems(properties);
|
||||
|
||||
SetDevicePropertyResponse response = acsClient.getAcsResponse(request);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", response.getSuccess());
|
||||
result.put("data", response.getData());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> invokeService(String iotId, String identifier, String args) throws Exception {
|
||||
log.info("调用设备服务,IotId: {}, Identifier: {}", iotId, identifier);
|
||||
|
||||
InvokeThingServiceRequest request = new InvokeThingServiceRequest();
|
||||
request.setIotId(iotId);
|
||||
request.setIdentifier(identifier);
|
||||
request.setArgs(args);
|
||||
|
||||
InvokeThingServiceResponse response = acsClient.getAcsResponse(request);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", response.getSuccess());
|
||||
result.put("data", response.getData());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> unbindDevice(String iotId) throws Exception {
|
||||
log.info("解绑设备,IotId: {}", iotId);
|
||||
|
||||
// 注: 通用IoT SDK没有直接的解绑接口,这里使用删除设备
|
||||
DeleteDeviceRequest request = new DeleteDeviceRequest();
|
||||
request.setIotId(iotId);
|
||||
|
||||
DeleteDeviceResponse response = acsClient.getAcsResponse(request);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", response.getSuccess());
|
||||
result.put("errorMessage", response.getErrorMessage());
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.intc.iot.service.impl;
|
||||
|
||||
import com.intc.iot.handler.DeviceDataHandler;
|
||||
import com.intc.iot.service.MqttService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.paho.client.mqttv3.IMqttMessageListener;
|
||||
import org.eclipse.paho.client.mqttv3.MqttClient;
|
||||
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* MQTT 消息服务实现
|
||||
*
|
||||
* @author intc
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@ConditionalOnBean(MqttClient.class)
|
||||
public class MqttServiceImpl implements MqttService {
|
||||
|
||||
private final MqttClient mqttClient;
|
||||
|
||||
@Autowired(required = false)
|
||||
private DeviceDataHandler deviceDataHandler;
|
||||
|
||||
@Override
|
||||
public void publish(String topic, String payload, int qos) throws Exception {
|
||||
if (!mqttClient.isConnected()) {
|
||||
log.warn("MQTT 客户端未连接,尝试重连...");
|
||||
mqttClient.reconnect();
|
||||
}
|
||||
|
||||
MqttMessage message = new MqttMessage(payload.getBytes());
|
||||
message.setQos(qos);
|
||||
message.setRetained(false);
|
||||
|
||||
mqttClient.publish(topic, message);
|
||||
log.info("MQTT 消息发布成功,Topic: {}, Payload: {}", topic, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(String topic, int qos) throws Exception {
|
||||
if (!mqttClient.isConnected()) {
|
||||
log.warn("MQTT 客户端未连接,尝试重连...");
|
||||
mqttClient.reconnect();
|
||||
}
|
||||
|
||||
mqttClient.subscribe(topic, qos, new IMqttMessageListener() {
|
||||
@Override
|
||||
public void messageArrived(String topic, MqttMessage message) throws Exception {
|
||||
String payload = new String(message.getPayload());
|
||||
log.info("收到 MQTT 消息,Topic: {}, Payload: {}", topic, payload);
|
||||
|
||||
// 根据 Topic 类型分发处理
|
||||
if (deviceDataHandler != null) {
|
||||
if (topic.contains("/thing/event/property/post")) {
|
||||
// 设备属性上报
|
||||
deviceDataHandler.handlePropertyPost(topic, payload);
|
||||
} else if (topic.contains("/thing/event/") && topic.endsWith("/post")) {
|
||||
// 设备事件上报
|
||||
deviceDataHandler.handleEventPost(topic, payload);
|
||||
} else {
|
||||
log.debug("未匹配的 Topic 类型: {}", topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
log.info("MQTT 主题订阅成功,Topic: {}", topic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsubscribe(String topic) throws Exception {
|
||||
if (!mqttClient.isConnected()) {
|
||||
log.warn("MQTT 客户端未连接");
|
||||
return;
|
||||
}
|
||||
|
||||
mqttClient.unsubscribe(topic);
|
||||
log.info("MQTT 主题取消订阅成功,Topic: {}", topic);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user