feat: 新功能开发,监测历史记录。微信和物联网平台,模块搭建。

This commit is contained in:
tianyongbao
2025-11-05 00:35:23 +08:00
parent 4d10d291a3
commit 8fd28e727e
38 changed files with 2099 additions and 49 deletions

View File

@@ -0,0 +1,91 @@
<?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">
<parent>
<groupId>com.intc</groupId>
<artifactId>intc-modules</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>intc-iot</artifactId>
<description>
阿里云生活物联网平台(飞燕平台)对接模块
</description>
<dependencies>
<!-- 阿里云生活物联网平台 SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.6.4</version>
</dependency>
<!-- 阿里云 IoT SDK -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-iot</artifactId>
<version>7.46.0</version>
</dependency>
<!-- MQTT 客户端 -->
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
<!-- HTTP 客户端 -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<!-- 通用工具-->
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-core</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-doc</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-redis</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-log</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-security</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-web</artifactId>
</dependency>
<dependency>
<groupId>com.intc</groupId>
<artifactId>intc-common-tenant</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -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;
}
}

View File

@@ -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;
/**
* 飞燕平台项目IDProject 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 的映射逻辑");
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,36 @@
# 阿里云生活物联网平台(飞燕平台)配置
aliyun:
living-iot:
# 阿里云 AccessKey ID必填
access-key-id: LTAI5txxxxxxxxxxxxxxxxxx
# 阿里云 AccessKey Secret必填
access-key-secret: your_access_key_secret_here_32_chars
# 地域节点必填cn-shanghai
region-id: cn-shanghai
# 飞燕平台项目IDProject ID必填
project-id: a1xxxxxx
# App Key必填
app-key: your_app_key_here
# App Secret必填
app-secret: your_app_secret_here_32_characters
# 品类Key选填
category-key:
# MQTT 配置(可选)
mqtt:
# MQTT Broker 地址格式ssl://实例ID.iot-as-mqtt.cn-shanghai.aliyuncs.com:1883
broker-url: ssl://a1xxxxxx.iot-as-mqtt.cn-shanghai.aliyuncs.com:1883
# 客户端ID格式{ClientID}|securemode=2,signmethod=hmacsha1|
client-id: your_client_id|securemode=2,signmethod=hmacsha1|
# 用户名(设备名称&产品Key
username: DeviceName&ProductKey
# 密码通过MQTT密码工具生成
password: your_mqtt_password_here
# 连接超时时间(秒)
connection-timeout: 30
# 保活时间(秒)
keep-alive-interval: 60
# 自动重连
auto-reconnect: true
# 清除会话
clean-session: true