chore(release): 版本更新汇总 (2025-08-10 至 2025-09-23)

核心功能优化:
- 新增UDP模块并默认开启,TCP基础上增加UDP适配
- 支持网关绑定多产品子设备,优化设备重连机制
- 新增Ezviz(萤石)接入模块,调整imoulife支持自有平台账号

性能与稳定性:
- 优化设备影子热数据存储,增加强制刷盘功能
- 产品列表及MQTT查询增加多级缓存策略
- 强化TCP安全机制,新增异常攻击封禁策略

功能完善:
- 集成rulego可视化规则引擎基础框架
- Web-IDE新增20+ modbus/hex封装方法
- 优化乐城播放器界面及TCP连接信息展示

问题修复:
- 解决系统间强依赖及注入相关问题
- 修复时间格式化、设备创建时间显示问题
- 完善日志处理及应用推送相关异常

其他改进:
- 重构TCP粘包处理,删除冗余代码
- 增加Docker部署配置
- 细化权限控制策略
This commit is contained in:
奈科斯(NexIoT)
2025-09-23 10:56:07 +08:00
parent 4db7a1bedd
commit 7aef1ba8e3
16 changed files with 807 additions and 378 deletions
+6 -1
View File
@@ -114,7 +114,12 @@
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cn.universal.iot</groupId>
<artifactId>cn-universal-notice</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -1,82 +0,0 @@
/*
*
* Copyright (c) 2025, IoT-Universal. All Rights Reserved.
*
* @Description: 本文件由 Aleo 开发并拥有版权,未经授权严禁擅自商用、复制或传播。
* @Author: Aleo
* @Email: wo8335224@gmail.com
* @Wechat: outlookFil
*
*
*/
package cn.universal.admin.network.doc;
/**
* 网络组件API文档
*
* @version 1.0 @Author Aleo
* @since 2025/1/20
*/
public class NetworkApiDoc {
/**
* 网络组件管理API文档
*
* <p>基础路径: /admin/network
*
* <p>1. 查询网络组件列表 GET /admin/network/list 参数: - page: 页码 (可选,默认1) - size: 每页大小 (可选,默认10) - type:
* 网络类型 (可选,单个类型,如 TCP_CLIENT) - types: 网络类型列表 (可选,多个类型,如 ["MQTT_CLIENT", "MQTT_SERVER"]) - name:
* 网络组件名称 (可选,模糊查询) - productKey: 产品Key (可选) - state: 状态 (可选,true/false) - unionId: 唯一标识 (可选)
*
* <p>2. 查询网络组件列表(支持多个类型) GET /admin/network/list/multi-type 参数: - page: 页码 (可选,默认1) - size: 每页大小
* (可选,默认10) - type: 网络类型 (可选,支持逗号分隔多个类型,如 "MQTT_CLIENT,MQTT_SERVER") - name: 网络组件名称 (可选,模糊查询) -
* productKey: 产品Key (可选) - state: 状态 (可选,true/false) - unionId: 唯一标识 (可选)
*
* <p>3. 根据ID查询网络组件 GET /admin/network/{id} 参数: - id: 网络组件ID (路径参数)
*
* <p>4. 新增网络组件 POST /admin/network 请求体: { "type": "TCP_SERVER", "unionId": "unique_id",
* "productKey": "product_key", "name": "网络组件名称", "description": "详细描述", "state": false,
* "configuration": "{\"host\":\"0.0.0.0\",\"port\":6372,\"ssl\":false}", "createUser": "admin" }
*
* <p>5. 修改网络组件 PUT /admin/network 请求体: { "id": 1, "type": "TCP_SERVER", "unionId": "unique_id",
* "productKey": "product_key", "name": "网络组件名称", "description": "详细描述", "state": false,
* "configuration": "{\"host\":\"0.0.0.0\",\"port\":6372,\"ssl\":false}" }
*
* <p>6. 删除网络组件 DELETE /admin/network/{id} 参数: - id: 网络组件ID (路径参数)
*
* <p>7. 批量删除网络组件 DELETE /admin/network/batch/{ids} 参数: - ids: 网络组件ID数组 (路径参数,逗号分隔)
*
* <p>8. 启动网络组件 POST /admin/network/start/{id} 参数: - id: 网络组件ID (路径参数)
*
* <p>9. 停止网络组件 POST /admin/network/stop/{id} 参数: - id: 网络组件ID (路径参数)
*
* <p>10. 重启网络组件 POST /admin/network/restart/{id} 参数: - id: 网络组件ID (路径参数)
*
* <p>11. 获取网络类型列表 GET /admin/network/types
*
* <p>12. 验证网络组件配置 POST /admin/network/validate 请求体: { "type": "TCP_SERVER", "unionId":
* "unique_id", "name": "网络组件名称", "configuration":
* "{\"host\":\"0.0.0.0\",\"port\":6372,\"ssl\":false}" }
*
* <p>响应格式: { "code": 200, "msg": "操作成功", "data": { // 具体数据 } }
*
* <p>网络类型说明: - TCP_CLIENT: TCP客户端 - TCP_SERVER: TCP服务端 - MQTT_CLIENT: MQTT客户端 - MQTT_SERVER:
* MQTT服务端
*
* <p>配置示例:
*
* <p>TCP_SERVER配置: { "allIdleTime": 0, "allowInsert": false, "alwaysPreDecode": false,
* "decoderType": "STRING", "host": "0.0.0.0", "idleInterval": 0, "onlyCache": false,
* "parserConfiguration": { "byteOrderLittle": true, "delimited": "]", "delimitedMaxlength": 1024,
* "failFast": true }, "parserType": "DELIMITED", "port": 6372, "productKey":
* "630477f1cd68445d90b04653", "readerIdleTime": 360, "readTimeout": 0, "sendTimeout": 0, "ssl":
* false, "writerIdleTime": 0 }
*
* <p>MQTT_SERVER配置: { "autoReconnect": true, "cleanSession": true, "clientIdPrefix": "univ_cli_",
* "defaultQos": 1, "enabled": true, "host": "tcp://125.210.48.96:1883", "id":
* "6863896d75386b5c1e7e908c", "keepAliveInterval": 60, "password": "univiot@2025!", "productKey":
* "local", "ssl": false, "subscribeTopics": "$univ_cli/up/property/+/+", "username": "univ_cli",
* "connectTimeout": 30 }
*/
}
@@ -29,26 +29,16 @@ import java.util.List;
*/
public interface INetworkService {
boolean start(String productKey);
boolean stop(String productKey);
Object get(String productKey);
boolean del(String productKey);
List<NetworkVO> selectNetworkList(NetworkBO bo);
NetworkVO getDetail(Long id);
List<IoTDevice> queryMileSightList(NetworkBO bo);
R insertDevInstance(IoTDeviceBO devInstancebo);
R deleteDevInstanceByIds(String[] ids);
void reloadTcpClient(String applicationId);
/**
* 查询网络组件列表
*
@@ -12,6 +12,7 @@
package cn.universal.admin.network.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
@@ -25,12 +26,15 @@ import cn.universal.common.domain.R;
import cn.universal.common.enums.NetworkType;
import cn.universal.common.exception.IoTException;
import cn.universal.core.service.IoTDownlFactory;
import cn.universal.dm.device.service.protocol.ProtocolClusterService;
import cn.universal.dm.device.service.protocol.ProtocolServerManager;
import cn.universal.mqtt.protocol.third.ThirdMQTTServerManager;
import cn.universal.persistence.entity.IoTDevice;
import cn.universal.persistence.entity.IoTProduct;
import cn.universal.persistence.entity.IoTUser;
import cn.universal.persistence.entity.Network;
import cn.universal.persistence.entity.bo.IoTDeviceBO;
import cn.universal.persistence.entity.bo.IoTProductBO;
import cn.universal.persistence.entity.bo.NetworkBO;
import cn.universal.persistence.entity.vo.IoTProductVO;
import cn.universal.persistence.entity.vo.NetworkVO;
@@ -49,6 +53,7 @@ import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
@@ -63,11 +68,8 @@ import tk.mybatis.mapper.entity.Example;
@Service
public class NetworkServiceImpl implements INetworkService {
@Autowired
private NetworkMapper networkMapper;
@Resource
private NetworkMapper netWorkMapper;
private NetworkMapper networkMapper;
@Resource
private IoTDeviceMapper ioTDeviceMapper;
@Resource
@@ -75,42 +77,31 @@ public class NetworkServiceImpl implements INetworkService {
@Resource
private IoTDeviceFenceRelMapper ioTDeviceFenceRelMapper;
// 直接注入 TCP 和 MQTT 服务
@Qualifier("tcpClusterService")
@Autowired(required = false)
private ProtocolClusterService tcpClusterService;
@Autowired(required = false)
private ProtocolServerManager tcpServerManager;
@Qualifier("udpClusterService")
@Autowired(required = false)
private ProtocolClusterService udpClusterService;
@Autowired(required = false)
private ThirdMQTTServerManager mqttServerManager;
@Override
public boolean start(String productKey) {
return true;
}
@Override
public boolean stop(String productKey) {
return true;
}
@Override
public Object get(String productKey) {
return null;
}
@Override
public boolean del(String productKey) {
Example ex = new Example(Network.class);
ex.createCriteria().andEqualTo("productKey", productKey);
return netWorkMapper.deleteByExample(ex) > 0;
return networkMapper.deleteByExample(ex) > 0;
}
@Override
public List<NetworkVO> selectNetworkList(NetworkBO bo) {
return netWorkMapper.selectNetworkListV1(bo);
}
@Override
public NetworkVO getDetail(Long id) {
return netWorkMapper.selectById(id);
return networkMapper.selectNetworkListV1(bo);
}
@Override
@@ -182,10 +173,6 @@ public class NetworkServiceImpl implements INetworkService {
return R.ok("删除成功");
}
@Override
public void reloadTcpClient(String applicationId) {
}
@Override
public List<NetworkBO> selectNetworkList(NetworkQuery query) {
// 验证类型参数
@@ -214,9 +201,32 @@ public class NetworkServiceImpl implements INetworkService {
String unionId = bo.getUnionId();
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId()
.equals(type)) {
if (tcpClusterService != null && tcpServerManager != null) {
Object serverInstance = tcpServerManager.getServerInstance(productKey);
if (serverInstance != null && tcpServerManager.isAlive(serverInstance)) {
bo.setStateName("已启动");
bo.setRunning(Boolean.TRUE);
} else {
bo.setStateName("未启动");
bo.setRunning(Boolean.FALSE);
}
} else {
bo.setStateName("未启动");
bo.setRunning(Boolean.FALSE);
}
// 查出哪些TCP-Server绑定了产品
if (NetworkType.TCP_SERVER.getId().equals(type)) {
IoTProductBO ioTProductBO = ioTProductMapper.selectTcpProductsUseNetwork(
bo.getProductKey());
bo.setBindTcpServerProductCount(ioTProductBO == null ? 0 : 1);
bo.setBindTcpServerProducts(ioTProductBO);
}
} else if (NetworkType.MQTT_CLIENT.getId().equals(type) || NetworkType.MQTT_SERVER.getId()
.equals(type)) {
List<IoTProductBO> ioTProductBOS = ioTProductMapper.selectMqttProductsUseNetwork(unionId);
bo.setBindMqttServerProductCount(CollUtil.size(ioTProductBOS));
bo.setBindMqttServerProducts(ioTProductBOS);
if (mqttServerManager != null && mqttServerManager.isConnected(unionId)) {
bo.setStateName("已启动");
bo.setRunning(Boolean.TRUE);
@@ -224,6 +234,14 @@ public class NetworkServiceImpl implements INetworkService {
bo.setStateName("未启动");
bo.setRunning(Boolean.FALSE);
}
} else if (NetworkType.UDP.getId().equals(type)) {
if (udpClusterService != null && udpClusterService.isProductServerAlive(productKey)) {
bo.setStateName("已启动");
bo.setRunning(Boolean.TRUE);
} else {
bo.setStateName("未启动");
bo.setRunning(Boolean.FALSE);
}
} else {
bo.setStateName("未知");
bo.setRunning(Boolean.FALSE);
@@ -264,6 +282,9 @@ public class NetworkServiceImpl implements INetworkService {
case NetworkTypeConstants.MQTT_SERVER:
vo.setTypeName(NetworkType.MQTT_SERVER.getDescription());
break;
case NetworkTypeConstants.UDP:
vo.setTypeName(NetworkType.UDP.getDescription());
break;
default:
vo.setTypeName(network.getType());
}
@@ -275,7 +296,19 @@ public class NetworkServiceImpl implements INetworkService {
String unionId = network.getUnionId();
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId()
.equals(type)) {
//TODO 开源版本无
if (tcpClusterService != null && tcpServerManager != null) {
Object serverInstance = tcpServerManager.getServerInstance(productKey);
if (serverInstance != null && tcpServerManager.isAlive(serverInstance)) {
vo.setStateName("已启动");
vo.setRunning(Boolean.TRUE);
} else {
vo.setStateName("未启动");
vo.setRunning(Boolean.FALSE);
}
} else {
vo.setStateName("未启动");
vo.setRunning(Boolean.FALSE);
}
} else if (NetworkType.MQTT_CLIENT.getId().equals(type) || NetworkType.MQTT_SERVER.getId()
.equals(type)) {
@@ -286,6 +319,14 @@ public class NetworkServiceImpl implements INetworkService {
vo.setStateName("未启动");
vo.setRunning(Boolean.FALSE);
}
} else if (NetworkType.UDP.getId().equals(type)) {
if (udpClusterService != null && udpClusterService.isProductServerAlive(productKey)) {
vo.setStateName("已启动");
vo.setRunning(Boolean.TRUE);
} else {
vo.setStateName("未启动");
vo.setRunning(Boolean.FALSE);
}
} else {
vo.setStateName("未知");
vo.setRunning(Boolean.FALSE);
@@ -347,11 +388,11 @@ public class NetworkServiceImpl implements INetworkService {
network.setUnionId(IdUtil.objectId());
network.setState(false);
// 新增TCP_SERVER时,必须指定productKey,并且不能重复绑定
// 新增TCP_SERVER/UDP时,必须指定productKey,并且不能重复绑定
if (NetworkType.TCP_SERVER.getId().equals(network.getType()) || NetworkType.TCP_CLIENT.getId()
.equals(network.getType())) {
.equals(network.getType()) || NetworkType.UDP.getId().equals(network.getType())) {
if (StrUtil.isBlank(network.getProductKey())) {
throw new RuntimeException("TCP服务组件必须选择产品");
throw new RuntimeException("TCP/UDP服务组件必须选择产品");
}
network.setUnionId(network.getProductKey());
// 检查是否已被绑定
@@ -391,8 +432,8 @@ public class NetworkServiceImpl implements INetworkService {
}
String type = existNetwork.getType();
if ((NetworkType.TCP_SERVER.getId().equals(type) || NetworkType.TCP_CLIENT.getId()
.equals(type))) {
if ((NetworkType.TCP_SERVER.getId().equals(type) || NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.UDP.getId().equals(type))) {
// 只有当unionId发生变化时,才做"已被产品关联且产品下有设备时禁止修改"的校验
String oldProductKey = existNetwork.getProductKey();
String networkProductKey = network.getProductKey();
@@ -423,9 +464,9 @@ public class NetworkServiceImpl implements INetworkService {
throw new RuntimeException("网络组件不存在");
}
String type = network.getType();
// 只对TCP_SERVER和TCP_CLIENT做判断
if (NetworkType.TCP_SERVER.getId().equals(type) || NetworkType.TCP_CLIENT.getId()
.equals(type)) {
// 只对TCP_SERVER、TCP_CLIENT和UDP做判断
if (NetworkType.TCP_SERVER.getId().equals(type) || NetworkType.TCP_CLIENT.getId().equals(type)
|| NetworkType.UDP.getId().equals(type)) {
// 查找是否有关联产品
String productKey = ioTProductMapper.findProductKeyByNetworkUnionId(network.getUnionId());
if (productKey != null) {
@@ -466,6 +507,11 @@ public class NetworkServiceImpl implements INetworkService {
if (StrUtil.isBlank(host)) {
throw new RuntimeException("MQTT网络组件启动/重启时host不能为空");
}
} else if (NetworkType.UDP.getId().equals(type)) {
Integer port = config.getInt("port");
if (port == null) {
throw new RuntimeException("UDP网络组件启动/重启时端口不能为空");
}
}
try {
// 根据网络类型调用相应的启动逻辑
@@ -527,6 +573,11 @@ public class NetworkServiceImpl implements INetworkService {
if (StrUtil.isBlank(host)) {
throw new RuntimeException("MQTT网络组件启动/重启时host不能为空");
}
} else if (NetworkType.UDP.getId().equals(type)) {
Integer port = config.getInt("port");
if (port == null) {
throw new RuntimeException("UDP网络组件启动/重启时端口不能为空");
}
}
try {
// 根据网络类型调用相应的重启逻辑
@@ -548,7 +599,7 @@ public class NetworkServiceImpl implements INetworkService {
@Override
public List<String> getNetworkTypes() {
return Arrays.asList(NetworkType.TCP_CLIENT.getId(), NetworkType.TCP_SERVER.getId(),
NetworkType.MQTT_CLIENT.getId(), NetworkType.MQTT_SERVER.getId());
NetworkType.MQTT_CLIENT.getId(), NetworkType.MQTT_SERVER.getId(), NetworkType.UDP.getId());
}
/**
@@ -608,9 +659,9 @@ public class NetworkServiceImpl implements INetworkService {
try {
JSONObject config = JSONUtil.parseObj(network.getConfiguration());
String type = network.getType();
// TCP类型检查端口唯一性
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId()
.equals(type)) {
// TCP/UDP类型检查端口唯一性
if (NetworkType.TCP_CLIENT.getId().equals(type) || NetworkType.TCP_SERVER.getId().equals(type)
|| NetworkType.UDP.getId().equals(type)) {
Integer port = config.getInt("port");
if (port != null) {
List<Network> existingNetworks = networkMapper.selectTcpNetworkByPort(port, excludeId);
@@ -663,6 +714,8 @@ public class NetworkServiceImpl implements INetworkService {
case NetworkTypeConstants.MQTT_CLIENT:
case NetworkTypeConstants.MQTT_SERVER:
return startMqttNetwork(network);
case NetworkTypeConstants.UDP:
return startUdpNetwork(network);
default:
log.warn("不支持的网络类型: {}", type);
return false;
@@ -690,6 +743,8 @@ public class NetworkServiceImpl implements INetworkService {
case NetworkTypeConstants.MQTT_CLIENT:
case NetworkTypeConstants.MQTT_SERVER:
return stopMqttNetwork(network);
case NetworkTypeConstants.UDP:
return stopUdpNetwork(network);
default:
log.warn("不支持的网络类型: {}", type);
return false;
@@ -708,7 +763,6 @@ public class NetworkServiceImpl implements INetworkService {
*/
private boolean restartNetworkByType(Network network) {
String type = network.getType();
try {
switch (type) {
case NetworkTypeConstants.TCP_CLIENT:
@@ -717,6 +771,8 @@ public class NetworkServiceImpl implements INetworkService {
case NetworkTypeConstants.MQTT_CLIENT:
case NetworkTypeConstants.MQTT_SERVER:
return restartMqttNetwork(network);
case NetworkTypeConstants.UDP:
return restartUdpNetwork(network);
default:
log.warn("不支持的网络类型: {}", type);
return false;
@@ -734,11 +790,28 @@ public class NetworkServiceImpl implements INetworkService {
* @return 是否成功
*/
private boolean startTcpNetwork(Network network) {
if (tcpClusterService == null) {
log.error("TCP服务管理器未注入,无法启动TCP网络组件: {}", network.getName());
return false;
}
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("TCP网络组件缺少productKey: {}", network.getName());
return false;
}
try {
//TODO 开源版本无
return true;
// 使用新的方法,从数据库加载配置并启动
boolean success = tcpClusterService.start(productKey);
if (success) {
log.info("TCP网络组件启动成功: productKey={}", productKey);
} else {
log.error("TCP网络组件启动失败: productKey={}", productKey);
}
return success;
} catch (Exception e) {
log.error("启动TCP网络组件失败: productKey={}", "", e);
log.error("启动TCP网络组件失败: productKey={}", productKey, e);
return false;
}
}
@@ -750,17 +823,27 @@ public class NetworkServiceImpl implements INetworkService {
* @return 是否成功
*/
private boolean stopTcpNetwork(Network network) {
if (tcpClusterService == null) {
log.error("TCP服务管理器未注入,无法停止TCP网络组件: {}", network.getName());
return false;
}
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("TCP网络组件缺少productKey: {}", network.getName());
return false;
}
try {
return true;
try {
boolean success = tcpClusterService.stop(productKey);
if (success) {
log.info("TCP网络组件停止成功: productKey={}", productKey);
} else {
log.error("TCP网络组件停止失败: productKey={}", productKey);
}
return success;
} catch (Exception e) {
log.error("停止TCP网络组件失败: productKey={}", "", e);
log.error("停止TCP网络组件失败: productKey={}", productKey, e);
return false;
}
}
@@ -772,11 +855,27 @@ public class NetworkServiceImpl implements INetworkService {
* @return 是否成功
*/
private boolean restartTcpNetwork(Network network) {
if (tcpClusterService == null) {
log.error("TCP服务管理器未注入,无法重启TCP网络组件: {}", network.getName());
return false;
}
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("TCP网络组件缺少productKey: {}", network.getName());
return false;
}
try {
return true;
boolean success = tcpClusterService.restart(productKey);
if (success) {
log.info("TCP网络组件重启成功: productKey={}", productKey);
} else {
log.error("TCP网络组件重启失败: productKey={}", productKey);
}
return success;
} catch (Exception e) {
log.error("重启TCP网络组件失败: productKey={}", "", e);
log.error("重启TCP网络组件失败: productKey={}", productKey, e);
return false;
}
}
@@ -881,4 +980,94 @@ public class NetworkServiceImpl implements INetworkService {
public IoTDevice getDeviceById(String id) {
return ioTDeviceMapper.selectDevInstanceById(id);
}
/**
* 启动UDP网络组件
*
* @param network 网络组件
* @return 是否成功
*/
private boolean startUdpNetwork(Network network) {
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("UDP网络组件缺少productKey: {}", network.getName());
return false;
}
try {
boolean success;
if (udpClusterService != null) {
// 使用集群服务启动(会广播到其他节点)
success = udpClusterService.start(productKey);
log.info("UDP网络组件集群启动: productKey={}, success={}", productKey, success);
} else {
log.error("UDP集群服务未注入,无法启动UDP网络组件: {}", network.getName());
return false;
}
return success;
} catch (Exception e) {
log.error("启动UDP网络组件失败: productKey={}", productKey, e);
return false;
}
}
/**
* 停止UDP网络组件
*
* @param network 网络组件
* @return 是否成功
*/
private boolean stopUdpNetwork(Network network) {
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("UDP网络组件缺少productKey: {}", network.getName());
return false;
}
try {
boolean success;
if (udpClusterService != null) {
// 使用集群服务停止(会广播到其他节点)
success = udpClusterService.stop(productKey);
log.info("UDP网络组件集群停止: productKey={}, success={}", productKey, success);
} else {
log.error("UDP集群服务未注入,无法停止UDP网络组件: {}", network.getName());
return false;
}
return success;
} catch (Exception e) {
log.error("停止UDP网络组件失败: productKey={}", productKey, e);
return false;
}
}
/**
* 重启UDP网络组件
*
* @param network 网络组件
* @return 是否成功
*/
private boolean restartUdpNetwork(Network network) {
String productKey = network.getProductKey();
if (StrUtil.isBlank(productKey)) {
log.error("UDP网络组件缺少productKey: {}", network.getName());
return false;
}
try {
boolean success;
if (udpClusterService != null) {
// 使用集群服务重启(会广播到其他节点)
success = udpClusterService.restart(productKey);
log.info("UDP网络组件集群重启: productKey={}, success={}", productKey, success);
} else {
log.error("UDP集群服务未注入,无法重启UDP网络组件: {}", network.getName());
return false;
}
return success;
} catch (Exception e) {
log.error("重启UDP网络组件失败: productKey={}", productKey, e);
return false;
}
}
}
@@ -29,7 +29,7 @@ public class NetworkTypeUtil {
/** 支持的网络类型列表 */
public static final List<String> SUPPORTED_TYPES =
Arrays.asList("TCP_CLIENT", "TCP_SERVER", "MQTT_CLIENT", "MQTT_SERVER");
Arrays.asList("TCP_CLIENT", "TCP_SERVER", "MQTT_CLIENT", "MQTT_SERVER", "UDP");
/**
* 解析网络类型字符串,支持逗号分隔的多个类型
@@ -329,7 +329,7 @@ public class NetworkController extends BaseController {
}
/** 查询绑定指定星纵网关的设备 */
@GetMapping("/milesight/list")
@GetMapping("/xz/gateway/list")
public TableDataInfo queryMileSightList(NetworkBO bo) {
startPage();
// 设置用户权限过滤
@@ -339,7 +339,7 @@ public class NetworkController extends BaseController {
}
/** 新增设备 */
@PostMapping("/milesight/add")
@PostMapping("/xz/gateway/add")
@Log(title = "新增设备", businessType = BusinessType.INSERT)
public R add(@RequestBody IoTDeviceBO devInstancebo) {
// 设置创建用户
@@ -356,7 +356,7 @@ public class NetworkController extends BaseController {
}
/** 删除设备 */
@DeleteMapping("/milesight/{ids}")
@DeleteMapping("/xz/gateway/{ids}")
@Log(title = "删除设备", businessType = BusinessType.DELETE)
public R remove(@PathVariable String[] ids) {
// 校验用户权限
@@ -385,7 +385,6 @@ public class NetworkController extends BaseController {
public R reloadTcpClient(@PathVariable("applicationId") String applicationId) {
// 校验用户权限 - 需要根据applicationId获取相关的信息进行权限校验
// 这里可能需要额外的业务逻辑来实现权限校验
iNetworkService.reloadTcpClient(applicationId);
return R.ok();
}
@@ -27,6 +27,7 @@ import cn.universal.persistence.entity.vo.IoTDeviceModelVO;
import cn.universal.persistence.entity.vo.IoTDeviceVO;
import com.github.pagehelper.Page;
import java.util.List;
import java.util.Map;
/**
* 设备Service接口
@@ -147,9 +148,15 @@ public interface IIoTDeviceService {
List<GatewayDeviceVo> selectGwIotDevices(IoTGwDeviceBO bo);
IoTDevice selectDeviceByDeviceId(String deviceId);
IoTDevice selectIoTDevice(String productKey, String deviceId);
List<IoTDevice> selectDevInstanceListWithTags(IoTDeviceBO ioTDeviceBO, IoTUser iotUser);
List<IoTProduct> selectProductListInBatchFunction(Long applicationId, IoTUser iotUser);
/** 新:按第三方平台过滤设备列表(不影响旧接口) */
List<IoTDevice> selectDevInstanceListByPlatform(
IoTDevice ioTDevice, IoTUser iotUser, String thirdPlatform);
}
@@ -178,7 +178,9 @@ public interface IIoTProductService {
Map<String, Integer> countDevNumberByProductKey(String unionId);
AjaxResult<IoTProduct> selectDevProductByKey(String key);
AjaxResult<IoTProduct> selectIoTProductByKey(String key);
AjaxResult<List<IoTProduct>> selectGatewaySubProductsByKey(String gwProductKey);
int updateDevProductOtherConfig(String otherConfig);
@@ -34,6 +34,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -84,7 +85,7 @@ public class IoTDeviceGroupServiceImpl extends BaseServiceImpl implements IIoTDe
// 生成设备分组树
List<IoTDeviceGroupVO> roots = collect.get(0L);
ioTDeviceGroupVO.setChildren(roots);
buildGroupTree(roots, collect);
groupTree(roots, collect);
return groupVos;
}
@@ -192,12 +193,23 @@ public class IoTDeviceGroupServiceImpl extends BaseServiceImpl implements IIoTDe
}
/**
* 绑定设备到分组
* 绑定设备到分组 iot_dev_action
*
* @param devGroup
* @return
*/
@Override
@CacheEvict(
cacheNames = {
"iot_dev_instance_bo",
"iot_dev_metadata_bo",
"iot_dev_shadow_bo",
"iot_dev_action",
"selectDevCount",
"iot_dev_product_list",
"iot_product_device"
},
allEntries = true)
public AjaxResult<Void> bindDevToGroup(IoTDeviceGroupBO devGroup) {
// 判断分组是否存在
Long groupId = devGroup.getId();
@@ -292,6 +304,17 @@ public class IoTDeviceGroupServiceImpl extends BaseServiceImpl implements IIoTDe
*/
@Override
@Transactional
@CacheEvict(
cacheNames = {
"iot_dev_instance_bo",
"iot_dev_metadata_bo",
"iot_dev_shadow_bo",
"iot_dev_action",
"selectDevCount",
"iot_dev_product_list",
"iot_product_device"
},
allEntries = true)
public AjaxResult<Void> unBindDevByGroupId(String groupId, String[] devId) {
for (int i = 0; i < devId.length; i++) {
ioTDeviceTagsMapper.delete(IoTDeviceTags.builder().iotId(devId[i]).value(groupId).build());
@@ -299,14 +322,8 @@ public class IoTDeviceGroupServiceImpl extends BaseServiceImpl implements IIoTDe
return AjaxResult.success();
}
/**
* 生成设备分组树
*
* @param ioTDeviceGroupVO
* @param groups
* @return
*/
private void buildGroupTree(
/** 生成设备分组列表 */
private void groupTree(
List<IoTDeviceGroupVO> ioTDeviceGroupVO, Map<Long, List<IoTDeviceGroupVO>> groups) {
if (ioTDeviceGroupVO == null) {
return;
@@ -315,7 +332,7 @@ public class IoTDeviceGroupServiceImpl extends BaseServiceImpl implements IIoTDe
if (ioTDeviceGroupVO.get(i).getHasChild() == 1) {
List<IoTDeviceGroupVO> devGroups = groups.get(ioTDeviceGroupVO.get(i).getId());
ioTDeviceGroupVO.get(i).setChildren(devGroups);
buildGroupTree(devGroups, groups);
groupTree(devGroups, groups);
}
}
return;
@@ -45,7 +45,6 @@ import cn.universal.persistence.mapper.IoTDeviceProtocolMapper;
import cn.universal.persistence.mapper.IoTDeviceShadowMapper;
import cn.universal.persistence.mapper.IoTDeviceTagsMapper;
import cn.universal.persistence.mapper.IoTProductMapper;
import cn.universal.persistence.query.IoTDeviceQuery;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import jakarta.annotation.Resource;
@@ -149,6 +148,13 @@ public class IoTDeviceServiceImpl extends BaseServiceImpl implements IIoTDeviceS
ioTDevice, iotUser.isAdmin() ? null : iotUser.getUnionId());
}
@Override
public List<IoTDevice> selectDevInstanceListByPlatform(
IoTDevice ioTDevice, IoTUser iotUser, String thirdPlatform) {
return ioTDeviceMapper.selectDevInstanceListByPlatform(
ioTDevice, thirdPlatform, iotUser.isAdmin() ? null : iotUser.getUnionId());
}
/**
* 查询设备列表
*
@@ -229,8 +235,8 @@ public class IoTDeviceServiceImpl extends BaseServiceImpl implements IIoTDeviceS
}
@Override
public IoTDevice selectDeviceByDeviceId(String deviceId) {
return ioTDeviceMapper.getOneByDeviceId(IoTDeviceQuery.builder().deviceId(deviceId).build());
public IoTDevice selectIoTDevice(String productKey, String deviceId) {
return ioTDeviceMapper.selectIoTDevice(productKey, deviceId);
}
@Override
@@ -270,11 +276,12 @@ public class IoTDeviceServiceImpl extends BaseServiceImpl implements IIoTDeviceS
if (ioTDeviceBO.getOtherParams().containsKey("deviceKey")) {
ioTDeviceBO.setSecretKey(ioTDeviceBO.getOtherParams().get("deviceKey").toString());
}
if (cn.universal.common.utils.StringUtils.isEmpty(ioTDeviceBO.getLatitude())) {
ioTDeviceBO.setLatitude("30.4484801");
ioTDeviceBO.setLatitude("44.61081");
}
if (cn.universal.common.utils.StringUtils.isEmpty(ioTDeviceBO.getLongitude())) {
ioTDeviceBO.setLongitude("120.2922654");
ioTDeviceBO.setLongitude("80.990372");
}
if (StrUtil.isNotBlank(deviceId)) {
ioTDeviceBO.setGwProductKey(deviceId);
@@ -333,6 +340,17 @@ public class IoTDeviceServiceImpl extends BaseServiceImpl implements IIoTDeviceS
}
@Override
@CacheEvict(
cacheNames = {
"iot_dev_instance_bo",
"iot_dev_metadata_bo",
"iot_dev_shadow_bo",
"iot_dev_action",
"selectDevCount",
"iot_dev_product_list",
"iot_product_device"
},
allEntries = true)
public int bindApp(String ids, String appUniqueId) {
String[] a = ids.split(",");
return ioTDeviceMapper.bindApp(a, appUniqueId);
@@ -362,7 +380,7 @@ public class IoTDeviceServiceImpl extends BaseServiceImpl implements IIoTDeviceS
downRequest.set("detail", devInstancebo.getDetail());
// 网关产品ProductKey
downRequest.set("gwProductKey", devInstancebo.getGwProductKey());
downRequest.set("gwDeviceId", devInstancebo.getExtDeviceId());
downRequest.set("gwDeviceId", devInstancebo.getGwDeviceId());
downRequest.set("extDeviceId", devInstancebo.getExtDeviceId());
JSONObject ob = new JSONObject();
// 设备实例名称
@@ -506,9 +524,7 @@ public class IoTDeviceServiceImpl extends BaseServiceImpl implements IIoTDeviceS
public R iotFunctionsDown(String productKey, String downRequest) {
JSONObject jsonObject = JSONUtil.parseObj(downRequest);
String deviceId = jsonObject.getStr("deviceId");
IoTDevice ioTDeviceBo =
ioTDeviceMapper.getOneByDeviceId(
IoTDeviceQuery.builder().deviceId(deviceId).productKey(productKey).build());
IoTDevice ioTDeviceBo = ioTDeviceMapper.selectIoTDevice(productKey, deviceId);
if (ioTDeviceBo == null) {
return R.error("设备不存在");
}
@@ -25,10 +25,12 @@ import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.universal.dm.device.service.protocol.ProtocolClusterService;
import cn.universal.admin.common.service.BaseServiceImpl;
import cn.universal.admin.common.utils.SecurityUtils;
import cn.universal.admin.platform.service.IIoTProductService;
import cn.universal.admin.system.service.IoTDeviceProtocolService;
import cn.universal.cache.annotation.MultiLevelCacheable;
import cn.universal.common.constant.IoTConstant;
import cn.universal.common.constant.IoTConstant.DeviceNode;
import cn.universal.common.constant.IoTConstant.ProductFlushType;
@@ -192,31 +194,54 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
}
@Override
// @Cacheable(cacheNames = "iot_dev_product_list", unless = "#result == null", keyGenerator =
// "redisKeyGenerate")
// 分页查询不使用缓存,因为分页参数会导致缓存键不同,效率低下
public List<IoTProductVO> selectDevProductV4List(IoTProductQuery ioTProductQuery) {
Page<IoTProductVO> page =
PageHelper.startPage(ioTProductQuery.getPageNum(), ioTProductQuery.getPageSize());
// 设置分页参数
PageHelper.startPage(ioTProductQuery.getPageNum(), ioTProductQuery.getPageSize());
// 优化:一次SQL查询获取产品列表和设备数量,避免N+1查询
List<IoTProductVO> devProductVOS = ioTProductMapper.selectDevProductV3List(ioTProductQuery);
List<IoTProductVO> results =
ioTProductMapper.countDevNumberByProductKey(ioTProductQuery.getCreatorId());
if (CollUtil.isNotEmpty(results)) {
final Map<String, Integer> collect =
results.stream()
.collect(Collectors.toMap(IoTProductVO::getProductKey, IoTProductVO::getDevNum));
// 处理图片URL
if (CollUtil.isNotEmpty(devProductVOS)) {
for (IoTProductVO devProduct : devProductVOS) {
devProduct.setDevNum(collect.getOrDefault(devProduct.getProductKey(), 0));
if (JSONUtil.isTypeJSON(devProduct.getPhotoUrl())) {
JSONObject photo = JSONUtil.parseObj(devProduct.getPhotoUrl());
devProduct.setImage(photo.getStr("img", ""));
}
}
}
// 按设备数量倒序排序
// if (CollUtil.isNotEmpty(devProductVOS)) {
// devProductVOS.sort(Comparator.comparingInt(IoTProductVO::getDevNum).reversed());
// }
return page;
return devProductVOS;
}
/** 获取不分页的产品列表(用于缓存) 缓存基础产品数据,不包含分页信息 */
@MultiLevelCacheable(
cacheNames = "selectDevProductV4ListNoPage",
keyGenerator = "redisKeyGenerate",
unless = "#result == null",
l1Expire = 30)
public List<IoTProductVO> selectDevProductV4ListNoPage(IoTProductQuery ioTProductQuery) {
// 创建不分页的查询条件
IoTProductQuery noPageQuery = new IoTProductQuery();
BeanUtil.copyProperties(ioTProductQuery, noPageQuery);
noPageQuery.setPageNum(1);
noPageQuery.setPageSize(Integer.MAX_VALUE); // 获取所有数据
// 优化:一次SQL查询获取产品列表和设备数量,避免N+1查询
List<IoTProductVO> devProductVOS = ioTProductMapper.selectDevProductV3List(noPageQuery);
// 处理图片URL
if (CollUtil.isNotEmpty(devProductVOS)) {
for (IoTProductVO devProduct : devProductVOS) {
if (JSONUtil.isTypeJSON(devProduct.getPhotoUrl())) {
JSONObject photo = JSONUtil.parseObj(devProduct.getPhotoUrl());
devProduct.setImage(photo.getStr("img", ""));
}
}
}
return devProductVOS;
}
@Override
@@ -237,7 +262,9 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
cacheNames = {
"iot_all_dev_product_list",
"iot_dev_product_list",
"selectAllEnableNetworkProductKey"
"selectAllEnableNetworkProductKey",
"selectDevProductV4ListNoPage",
"countDevNumberByProductKey"
},
allEntries = true)
public int insertDevProduct(IoTProduct ioTProduct) {
@@ -257,6 +284,9 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
if (StrUtil.isBlank(ioTProduct.getProductSecret())) {
ioTProduct.setProductSecret(IdUtil.fastSimpleUUID());
}
if (StrUtil.isBlank(ioTProduct.getConfiguration())) {
ioTProduct.setConfiguration(buildProductCfg(ioTProduct));
}
ioTProduct.setProductKey(productKey);
ioTProduct.setMetadata(defaultMetadata);
ioTProduct.setThirdConfiguration(thirdConfig);
@@ -268,9 +298,30 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
return i;
}
/** 默认创建mqtt设置自动注册 */
private String buildProductCfg(IoTProduct ioTProduct) {
JSONObject cfg = new JSONObject();
boolean isMqtt = false;
String thirdPlatform = ioTProduct == null ? null : ioTProduct.getThirdPlatform();
if (thirdPlatform == null) {
return JSONUtil.toJsonStr(cfg);
}
if (ProtocolModule.tcp.name().equalsIgnoreCase(thirdPlatform)
|| ProtocolModule.mqtt.name().equalsIgnoreCase(thirdPlatform)
|| ProtocolModule.udp.name().equalsIgnoreCase(thirdPlatform)) {
cfg.set(IoTConstant.ALLOW_INSERT, true);
}
return JSONUtil.toJsonStr(cfg);
}
@Override
@CacheEvict(
cacheNames = {"iot_all_dev_product_list", "iot_dev_product_list"},
cacheNames = {
"iot_all_dev_product_list",
"iot_dev_product_list",
"selectDevProductV4ListNoPage",
"countDevNumberByProductKey"
},
allEntries = true)
public int insertList(List<IoTProduct> ioTProductList) {
int i = ioTProductMapper.insertList(ioTProductList);
@@ -280,7 +331,13 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
/** 产品协议导入 */
@Override
@CacheEvict(
cacheNames = {"iot_all_dev_product_list", "iot_dev_product_list", "supportMQTTNetwork"},
cacheNames = {
"iot_all_dev_product_list",
"iot_dev_product_list",
"supportMQTTNetwork",
"selectDevProductV4ListNoPage",
"countDevNumberByProductKey"
},
allEntries = true)
public String importProduct(List<IoTProductImportBO> productImportBos, String unionId) {
Long timeMillis = System.currentTimeMillis();
@@ -357,7 +414,9 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
devProduct -> {
R r =
IoTDownlFactory.safeInvokeDown(
ProtocolModule.ctaiot.name(), "downPro", devProduct.getThirdDownRequest());
ProtocolModule.ctaiot.name(),
IoTConstant.DOWN_TO_THIRD_PLATFORM,
devProduct.getThirdDownRequest());
if (!R.SUCCESS.equals(r.getCode())) {
failedList.add(devProduct);
} else {
@@ -395,7 +454,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
ioTProductAction.create(null, unionId);
String result =
String.format(
"导入普通产品总数:%s 个,成功:%s个;导入电信产品总数:%s 个,成功:%s 个;导入协议总数:%s 个,成功:%s 个。",
"导入普通产品总数:%s 个,成功:%s个;导入电信ctwing产品总数:%s 个,成功:%s 个;导入协议总数:%s 个,成功:%s 个。",
customProductList.size() + duplicateNum.get(),
customSuccess,
ctwingProductList.size(),
@@ -413,7 +472,13 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
*/
@Override
@CacheEvict(
cacheNames = {"iot_all_dev_product_list", "iot_dev_product_list", "supportMQTTNetwork"},
cacheNames = {
"iot_all_dev_product_list",
"iot_dev_product_list",
"supportMQTTNetwork",
"selectDevProductV4ListNoPage",
"countDevNumberByProductKey"
},
allEntries = true)
public int updateDevProduct(IoTProduct ioTProduct) {
IoTUser iotUser = queryIotUser(SecurityUtils.getUnionId());
@@ -423,7 +488,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
String appUnionId = iotUser.getUnionId();
IoTProduct pro = ioTProductMapper.selectByPrimaryKey(ioTProduct.getId());
if (!appUnionId.equals(pro.getCreatorId()) && !iotUser.isAdmin()) {
throw new IoTException("您没有权限操作此产品!");
throw new IoTException("没有产品权限");
}
if (Objects.nonNull(ioTProduct.getClassifiedId())) {
IoTProductSort ioTProductSort =
@@ -432,7 +497,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
}
int count = ioTProductMapper.updateDevProduct(ioTProduct);
log.info("修改产品记录数={},修改人={}", count, appUnionId);
log.info("updateDevProduct,操作成功={},userId={}", count, appUnionId);
ioTProductAction.update(ioTProduct);
return count;
}
@@ -445,7 +510,13 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
*/
@Override
@CacheEvict(
cacheNames = {"iot_all_dev_product_list", "iot_dev_product_list", "supportMQTTNetwork"},
cacheNames = {
"iot_all_dev_product_list",
"iot_dev_product_list",
"supportMQTTNetwork",
"selectDevProductV4ListNoPage",
"countDevNumberByProductKey"
},
allEntries = true)
public int deleteDevProductByIds(String[] ids) {
String appUnionId = queryIotUser(SecurityUtils.getUnionId()).getUnionId();
@@ -453,7 +524,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
for (String id : ids) {
IoTProduct ioTProduct = ioTProductMapper.selectDevProductById(id);
if (!appUnionId.equals(ioTProduct.getCreatorId())) {
throw new IoTException("您没有权限操作此产品!");
throw new IoTException("无产品权限");
}
count = +deleteDevProductById(id);
}
@@ -468,7 +539,13 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
*/
@Override
@CacheEvict(
cacheNames = {"iot_all_dev_product_list", "iot_dev_product_list", "supportMQTTNetwork"},
cacheNames = {
"iot_all_dev_product_list",
"iot_dev_product_list",
"supportMQTTNetwork",
"selectDevProductV4ListNoPage",
"countDevNumberByProductKey"
},
allEntries = true)
public int deleteDevProductById(String id) {
String appUnionId = queryIotUser(SecurityUtils.getUnionId()).getUnionId();
@@ -477,7 +554,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
throw new IoTException("产品不存在");
}
if (!appUnionId.equals(ioTProduct.getCreatorId())) {
throw new IoTException("您没有权限操作此产品!");
throw new IoTException("无产品权限");
}
IoTDevice ioTDevice = IoTDevice.builder().productKey(ioTProduct.getProductKey()).build();
int count = ioTDeviceMapper.selectCount(ioTDevice);
@@ -507,7 +584,8 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
"iot_dev_action",
"selectDevCount",
"supportMQTTNetwork",
"iot_dev_product_list"
"iot_dev_product_list",
"getProductConfiguration"
},
allEntries = true)
public int updateDevProductConfig(IoTProductVO devProduct) {
@@ -518,7 +596,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
throw new IoTException("产品不存在");
}
if (!appUnionId.equals(product.getCreatorId())) {
throw new IoTException("您没有权限操作此产品!");
throw new IoTException("无产品权限");
}
// 获取新的产品配置信息
JSONObject newConfig = JSONUtil.parseObj(devProduct.getConfiguration());
@@ -802,7 +880,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
String appUnionId = queryIotUser(SecurityUtils.getUnionId()).getUnionId();
IoTProduct product = ioTProductMapper.getProductByProductKey(ioTProductBO.getProductKey());
if (!appUnionId.equals(product.getCreatorId())) {
throw new IoTException("您没有权限操作此产品!");
throw new IoTException("无产品权限");
}
ioTProductBO.setPath(getPath(ioTProductBO));
int delCount = ioTProductMapper.deleteMetadata(ioTProductBO);
@@ -840,7 +918,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
String appUnionId = queryIotUser(SecurityUtils.getUnionId()).getUnionId();
IoTProduct product = ioTProductMapper.getProductByProductKey(ioTProductBO.getProductKey());
if (!appUnionId.equals(product.getCreatorId())) {
throw new IoTException("您没有权限操作此产品!");
throw new IoTException("无产品权限");
}
ioTProductBO.beanToJson();
// if(getMetadata(ioTProductBO)!=null){
@@ -884,19 +962,41 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
}
@Override
@MultiLevelCacheable(
cacheNames = "countDevNumberByProductKey",
keyGenerator = "redisKeyGenerate",
unless = "#result == null",
l1Expire = 30)
public Map<String, Integer> countDevNumberByProductKey(String unionId) {
List<IoTProductVO> results = ioTProductMapper.countDevNumberByProductKey(unionId);
return results.stream()
.collect(Collectors.toMap(IoTProductVO::getProductKey, IoTProductVO::getDevNum));
}
/** 设备数量变化时清除相关缓存 当设备新增、删除、修改时调用此方法 */
@CacheEvict(
cacheNames = {"countDevNumberByProductKey", "selectDevProductV4ListNoPage"},
allEntries = true)
public void evictDeviceCountCache() {
// 此方法用于手动清除设备数量相关缓存
// 当设备数据发生变化时调用
}
@Override
public AjaxResult<IoTProduct> selectDevProductByKey(String key) {
public AjaxResult<IoTProduct> selectIoTProductByKey(String key) {
IoTProduct ioTProduct = new IoTProduct();
ioTProduct.setProductKey(key);
return AjaxResult.success(ioTProductMapper.selectOne(ioTProduct));
}
@Override
public AjaxResult<List<IoTProduct>> selectGatewaySubProductsByKey(String gwProductKey) {
IoTProduct ioTProduct = new IoTProduct();
ioTProduct.setGwProductKey(gwProductKey);
ioTProduct.setState((byte) 0);
return AjaxResult.success(ioTProductMapper.select(ioTProduct));
}
/** 修改产品其他配置信息 */
@Override
@CacheEvict(
@@ -917,7 +1017,7 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
IoTProduct product = ioTProductMapper.selectDevProductById(newConfig.getStr("productId"));
IoTUser iotUser = queryIotUser(SecurityUtils.getUnionId());
if (!product.getCreatorId().equals(iotUser.getUnionId())) {
throw new IoTException("您没有权限操作此产品!");
throw new IoTException("无产品权限");
}
// 获取第三方配置
String thirdConfiguration = product.getThirdConfiguration();
@@ -1042,7 +1142,20 @@ public class IoTProductServiceImpl extends BaseServiceImpl implements IIoTProduc
@Override
public void flushNettyServer(String config, String productKey, TcpFlushType type) {
JSONObject object = new JSONObject();
object.set("type", ProductFlushType.server.name());
object.set("productKey", productKey);
object.set("customType", type.name());
eventPublisher.publishEvent(EventTopics.PRODUCT_CONFIG_UPDATED, object);
// 使用ProtocolClusterService进行集群操作
ProtocolClusterService tcpClusterService = SpringUtil.getBean(ProtocolClusterService.class);
if (tcpClusterService != null) {
switch (type) {
case start -> tcpClusterService.start(productKey);
case reload -> tcpClusterService.restart(productKey);
case close -> tcpClusterService.stop(productKey);
}
}
}
@Override
@@ -90,6 +90,18 @@ public class IoTDeviceController extends BaseController {
return getDataTable(list);
}
/** 新:按第三方平台过滤的设备列表(用于萤石/乐橙) */
@GetMapping("/listByPlatform")
public TableDataInfo listByPlatform(
IoTDevice ioTDevice,
@RequestParam(value = "thirdPlatform", required = false) String thirdPlatform) {
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
startPage();
List<IoTDevice> list =
iIotDeviceService.selectDevInstanceListByPlatform(ioTDevice, iotUser, thirdPlatform);
return getDataTable(list);
}
/** 查询分组未绑定设备列表 */
@GetMapping("/list/{groupId}")
public TableDataInfo list(@PathVariable("groupId") String groupId, IoTDevice ioTDevice) {
@@ -152,27 +164,18 @@ public class IoTDeviceController extends BaseController {
/** 获取设备详细信息 */
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id) {
public AjaxResult getIotDeviceInfo(@PathVariable("id") String id) {
IoTDevice ioTDevice = iIotDeviceService.selectDevInstanceById(id);
// if (ObjectUtil.isEmpty(ioTDevice)) {
// throw new IoTException("设备不存在");
// }
// IoTUser iotUser = checkParent(SecurityUtils.getUnionId());
// if (!iotUser.isAdmin() && !ioTDevice.getCreatorId()
// .equals(iotUser.getUnionId())) {
// throw new IoTException("你无权操作");
// }
// 是否有编解码插件
int count = ioTDeviceProtocolService.countProtocol(ioTDevice.getProductKey());
IoTDeviceBO ioTDeviceBO = new IoTDeviceBO();
BeanUtil.copyProperties(ioTDevice, ioTDeviceBO);
// 查询网关产品信息
if (StrUtil.isNotBlank(ioTDeviceBO.getGwProductKey())) {
IoTDevice parentDevice =
iIotDeviceService.selectDeviceByDeviceId(ioTDevice.getGwProductKey());
iIotDeviceService.selectIoTDevice(
ioTDeviceBO.getGwProductKey(), ioTDevice.getExtDeviceId());
if (ObjectUtil.isNotNull(parentDevice)) {
ioTDeviceBO.setParentName(
ioTDeviceBO.setGwDeviceInfo(
parentDevice.getDeviceId() + "(" + parentDevice.getDeviceName() + ")");
}
}
@@ -20,16 +20,22 @@ import cn.universal.admin.common.annotation.Log;
import cn.universal.admin.common.enums.BusinessType;
import cn.universal.admin.common.utils.SecurityUtils;
import cn.universal.admin.network.service.INetworkService;
import cn.universal.admin.platform.dto.ConnectionInfoDTO;
import cn.universal.admin.platform.service.ConnectionInfoService;
import cn.universal.admin.platform.service.IIoTDeviceService;
import cn.universal.admin.platform.service.IIoTProductService;
import cn.universal.admin.platform.service.impl.IoTProductServiceImpl;
import cn.universal.admin.system.service.ISysDictTypeService;
import cn.universal.admin.system.service.IoTDeviceProtocolService;
import cn.universal.admin.system.web.BaseController;
import cn.universal.common.constant.IoTConstant;
import cn.universal.common.constant.IoTConstant.ProtocolModule;
import cn.universal.common.constant.IoTConstant.TcpFlushType;
import cn.universal.common.event.EventTopics;
import cn.universal.common.event.processer.EventPublisher;
import cn.universal.common.exception.IoTException;
import cn.universal.core.service.IoTDownlFactory;
import cn.universal.ossm.service.ISysOssService;
import cn.universal.persistence.entity.IoTDevice;
import cn.universal.persistence.entity.IoTDeviceEvents;
import cn.universal.persistence.entity.IoTDeviceFunction;
@@ -55,7 +61,6 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
@@ -77,29 +82,20 @@ import org.springframework.web.multipart.MultipartFile;
@RequestMapping("/admin/v1/product")
public class IoTProductController extends BaseController {
@Autowired
private IIoTProductService devProductService;
@Autowired
private IIoTDeviceService iIotDeviceService;
@Autowired
private ISysDictTypeService dictTypeService;
@Resource
private INetworkService iNetworkService;
@Resource
private IoTDeviceProtocolService ioTDeviceProtocolService;
@Resource
private IoTProductMapper ioTProductMapper;
@Autowired
private IoTProductServiceImpl ioTProductServiceImpl;
@Autowired
private ApplicationEventPublisher eventPublisher;
@Autowired private IIoTProductService devProductService;
@Autowired private IIoTDeviceService iIotDeviceService;
@Autowired private ISysDictTypeService dictTypeService;
@Resource private INetworkService iNetworkService;
@Resource private IoTDeviceProtocolService ioTDeviceProtocolService;
@Resource private IoTProductMapper ioTProductMapper;
@Autowired private IoTProductServiceImpl ioTProductServiceImpl;
@Autowired private EventPublisher eventPublisher;
@Autowired private ISysOssService iSysOssService;
@Autowired private ConnectionInfoService connectionInfoService;
/**
* 查询设备产品列表
*/
/** 查询设备产品列表 */
@GetMapping("/list")
public TableDataInfo list(IoTProductQuery query) {
// 只能看自己设备,除非是特殊设置和超管
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
if (!iotUser.viewAllProduct()) {
query.setSelf(true);
@@ -118,20 +114,21 @@ public class IoTProductController extends BaseController {
return getDataTable(list);
}
/**
* 获取产品详情通过 ProductKey
*/
/** 获取产品详情通过 ProductKey */
@GetMapping("/get/pro/{key}")
public AjaxResult<IoTProduct> getProByKey(@PathVariable("key") String key) {
return devProductService.selectDevProductByKey(key);
return devProductService.selectIoTProductByKey(key);
}
/**
* 查询设备产品列表
*/
/** 获取网关绑定的子产品列表 */
@GetMapping("/gateway/subProducts/{key}")
public AjaxResult<List<IoTProduct>> gatewaySubProducts(@PathVariable("key") String gwProductKey) {
return devProductService.selectGatewaySubProductsByKey(gwProductKey);
}
/** 查询设备产品列表 */
@GetMapping("/v2/list")
public TableDataInfo v2List(IoTProductQuery query) {
// 只能看自己设备,除非是特殊设置和超管
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
if (!iotUser.viewAllProduct()) {
query.setSelf(true);
@@ -150,12 +147,10 @@ public class IoTProductController extends BaseController {
return getDataTable(list);
}
/**
* 查询设备产品列表
*/
/** 查询设备产品列表 */
@GetMapping("/v3/list")
public TableDataInfo v3List(IoTProductQuery query) {
// 只能看自己设备,除非是特殊设置和超管
//
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
if (!iotUser.viewAllProduct()) {
query.setSelf(true);
@@ -175,12 +170,10 @@ public class IoTProductController extends BaseController {
return getDataTable(list);
}
/**
* 查询设备产品列表
*/
/** 查询设备产品列表 */
@GetMapping("/v4/list")
public TableDataInfo v4List(IoTProductQuery query) {
// 只能看自己设备,除非是特殊设置和超管
//
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
if (!iotUser.viewAllProduct()) {
query.setSelf(true);
@@ -200,70 +193,47 @@ public class IoTProductController extends BaseController {
return getDataTable(list);
}
/**
* 查询所有设备产品列表
*/
/** 查询所有设备产品列表 */
@GetMapping("/all/list")
public TableDataInfo v3List() {
List<IoTProductVO> list = devProductService.selectDevProductAllList();
return getDataTable(list);
}
/**
* 物模型新增内容
*/
/** 物模型新增内容 */
@PostMapping("/metadata/add")
@Log(title = "物模型新增内容", businessType = BusinessType.INSERT)
public AjaxResult<Void> metadataAdd(@RequestBody IoTProductBO ioTProductBO) {
return toAjax(devProductService.insertMetadata(ioTProductBO));
}
/**
* 物模型修改内容
*/
/** 物模型修改内容 */
@PostMapping("/metadata/update")
@Log(title = "物模型修改内容", businessType = BusinessType.UPDATE)
public AjaxResult<Void> metadataUpdate(@RequestBody IoTProductBO ioTProductBO) {
return toAjax(devProductService.updateMetadata(ioTProductBO));
}
/**
* 物模型删除内容
*/
/** 物模型删除内容 */
@PostMapping("/metadata/delete")
@Log(title = "物模型删除内容", businessType = BusinessType.DELETE)
public AjaxResult<Void> metadataDelete(@RequestBody IoTProductBO ioTProductBO) {
return toAjax(devProductService.deleteMetadata(ioTProductBO));
}
/**
* 物模型查询内容
*/
/** 物模型查询内容 */
@PostMapping("/metadata/get")
public AjaxResult<IoTProductBO> metadataGet(@RequestBody IoTProductBO ioTProductBO) {
return AjaxResult.success(devProductService.getMetadata(ioTProductBO));
}
/** 导出设备产品列表 */
// @PostMapping("/export")
// public void export(HttpServletResponse response, IoTProduct ioTProduct)
// {
// List<IoTProduct> list = devProductService.selectDevProductList(ioTProduct);
// ExcelUtil<IoTProduct> util = new ExcelUtil<IoTProduct>(IoTProduct.class);
// util.exportExcel(response, list, "设备产品数据");
// }
/**
* 获取设备产品详细信息
*/
/** 获取设备产品详细信息 */
@GetMapping(value = "/{id}")
public AjaxResult getInfo(@PathVariable("id") String id) {
return AjaxResult.success(devProductService.selectDevProductById(id));
}
/**
* 查询可绑定网关列表
*/
/** 查询可绑定网关列表 */
@Operation(summary = "查询可绑定网关产品列表")
@GetMapping("/gateway/list")
public AjaxResult<List<IoTProductVO>> getGatewayList() {
@@ -271,20 +241,16 @@ public class IoTProductController extends BaseController {
return AjaxResult.success(list);
}
/**
* 查询可绑定网关子设备产品列表
*/
/** 查询可绑定网关子设备产品列表 */
@Operation(summary = "查询可绑定网关子设备产品列表")
@GetMapping("/subdevice/{productKey}")
@GetMapping("/sub/device/{productKey}")
public AjaxResult<List<IoTProductVO>> getGatewaySubDeviceList(
@PathVariable("productKey") String productKey) {
List<IoTProductVO> list = devProductService.getGatewaySubDeviceList(productKey);
return AjaxResult.success(list);
}
/**
* 查询电信公共产品列表
*/
/** 查询电信公共产品列表 */
@PostMapping(value = "/ctwing/pubpro")
public AjaxResult getCtwingPubPro(@RequestBody String downRequest) {
String creatorId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
@@ -299,23 +265,27 @@ public class IoTProductController extends BaseController {
downProRequest.set("data", data);
return AjaxResult.success(
IoTDownlFactory.safeInvokeDown(
ProtocolModule.ctaiot.name(), "downPro", downProRequest.toString()));
ProtocolModule.ctaiot.name(),
IoTConstant.DOWN_TO_THIRD_PLATFORM,
downProRequest.toString()));
}
/**
* 新增设备产品
*/
/** 新增设备产品 */
@PostMapping
@Log(title = "新增产品", businessType = BusinessType.INSERT)
public AjaxResult add(@RequestBody IoTProduct ioTProduct) {
String unionId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
ioTProduct.setCreatorId(unionId);
// 发布产品配置更新事件
Map<String, Object> eventData = new HashMap<>();
eventData.put("type", "product");
eventData.put("productKey", ioTProduct.getProductKey());
eventData.put("operation", "ADD");
eventPublisher.publishEvent(EventTopics.PRODUCT_CONFIG_UPDATED, eventData);
return toAjax(devProductService.insertDevProduct(ioTProduct));
}
/**
* 新增设备产品NB
*/
/** 新增设备产品NB */
@PostMapping(value = "/nb")
@Log(title = "新增产品NB", businessType = BusinessType.INSERT)
public AjaxResult addNb(@RequestBody String downRequest) {
@@ -343,12 +313,12 @@ public class IoTProductController extends BaseController {
downProRequest.set("data", data);
return AjaxResult.success(
IoTDownlFactory.safeInvokeDown(
ProtocolModule.ctaiot.name(), "downPro", downProRequest.toString()));
ProtocolModule.ctaiot.name(),
IoTConstant.DOWN_TO_THIRD_PLATFORM,
downProRequest.toString()));
}
/**
* 新增电信公共产品
*/
/** 新增电信公共产品 */
@PostMapping(value = "/ctwing/pubproadd")
@Log(title = "新增电信公共产品", businessType = BusinessType.INSERT)
public AjaxResult addCtwingPubPro(@RequestBody String downRequest) {
@@ -367,12 +337,12 @@ public class IoTProductController extends BaseController {
downProRequest.set("data", data);
return AjaxResult.success(
IoTDownlFactory.safeInvokeDown(
ProtocolModule.ctaiot.name(), "downPro", downProRequest.toString()));
ProtocolModule.ctaiot.name(),
IoTConstant.DOWN_TO_THIRD_PLATFORM,
downProRequest.toString()));
}
/**
* 修改设备产品
*/
/** 修改设备产品 */
@PostMapping(value = "/updateNetworkUnionId")
@Log(title = "修改网络组件", businessType = BusinessType.UPDATE)
public AjaxResult editNetwork(@RequestBody IoTProductBO ioTProductBO) {
@@ -387,9 +357,7 @@ public class IoTProductController extends BaseController {
ioTProductBO.getId(), ioTProductBO.getNetworkUnionId()));
}
/**
* 子设备绑定网关
*/
/** 子设备绑定网关 */
@Operation(summary = "子设备绑定网关")
@PutMapping("/gateway")
@Log(title = "子设备绑定网关", businessType = BusinessType.UPDATE)
@@ -407,9 +375,7 @@ public class IoTProductController extends BaseController {
return toAjax(devProductService.updateDevProduct(productVO));
}
/**
* 修改设备产品
*/
/** 修改设备产品 */
@PutMapping
@Log(title = "修改设备产品", businessType = BusinessType.UPDATE)
public AjaxResult edit(@RequestBody IoTProductBO ioTProductBO) {
@@ -434,42 +400,45 @@ public class IoTProductController extends BaseController {
downProRequest.set("productKey", ioTProductBO.getProductKey());
return AjaxResult.success(
IoTDownlFactory.safeInvokeDown(
ProtocolModule.ctaiot.name(), "downPro", downProRequest.toString()));
ProtocolModule.ctaiot.name(),
IoTConstant.DOWN_TO_THIRD_PLATFORM,
downProRequest.toString()));
}
IoTProduct ioTProduct = BeanUtil.toBean(ioTProductBO, IoTProduct.class);
return toAjax(devProductService.updateDevProduct(ioTProduct));
int result = devProductService.updateDevProduct(ioTProduct);
if (result > 0) {
// 发布产品配置更新事件
Map<String, Object> eventData = new HashMap<>();
eventData.put("type", "product");
eventData.put("productKey", ioTProduct.getProductKey());
eventData.put("operation", "UPDATE");
eventPublisher.publishEvent(EventTopics.PRODUCT_CONFIG_UPDATED, eventData);
}
return toAjax(result);
}
/**
* 修改产品配置信息
*/
/** 修改产品配置信息 */
@PutMapping("/config")
@Log(title = "修改产品配置信息", businessType = BusinessType.UPDATE)
public AjaxResult editConfig(@RequestBody IoTProductVO devProduct) {
return toAjax(devProductService.updateDevProductConfig(devProduct));
}
/**
* 修改产品其他配置信息
*/
/** 修改产品其他配置信息 */
@PutMapping("/otherConfig")
@Log(title = "修改产品其他配置信息", businessType = BusinessType.UPDATE)
public AjaxResult editOtherConfig(@RequestBody String otherConfig) {
return toAjax(devProductService.updateDevProductOtherConfig(otherConfig));
}
/**
* 修改产品存储策略
*/
/** 修改产品存储策略 */
@PutMapping("/storeConfig")
@Log(title = "修改产品存储策略", businessType = BusinessType.UPDATE)
public AjaxResult editOtherConfig(@RequestBody IoTProductVO devProduct) {
return toAjax(devProductService.updateDevProductStoreConfig(devProduct));
}
/**
* 删除设备产品
*/
/** 删除设备产品 */
@DeleteMapping("/{ids}")
@Log(title = "删除设备产品", businessType = BusinessType.DELETE)
public AjaxResult remove(@PathVariable String[] ids) {
@@ -480,7 +449,7 @@ public class IoTProductController extends BaseController {
throw new IoTException("没有该产品");
}
if (!iotUser.isAdmin() && !ioTProduct.getCreatorId().equals(iotUser.getUnionId())) {
throw new IoTException("您没有权限操作此产品!");
throw new IoTException("没有产品权限");
}
IoTDevice ioTDevice = IoTDevice.builder().productKey(ioTProduct.getProductKey()).build();
List<IoTDevice> ioTDeviceList =
@@ -501,7 +470,9 @@ public class IoTProductController extends BaseController {
return AjaxResult.success(
IoTDownlFactory.safeInvokeDown(
ProtocolModule.ctaiot.name(), "downPro", downProRequest.toString()));
ProtocolModule.ctaiot.name(),
IoTConstant.DOWN_TO_THIRD_PLATFORM,
downProRequest.toString()));
}
// tcp产品删除时同时删除network表
if (ProtocolModule.tcp.name().equals(ioTProduct.getThirdPlatform())) {
@@ -512,22 +483,25 @@ public class IoTProductController extends BaseController {
}
// 删除协议
ioTDeviceProtocolService.deleteDevProtocolById(ioTProduct.getProductKey());
// 发布产品删除事件
Map<String, Object> eventData = new HashMap<>();
eventData.put("type", "product");
eventData.put("productKey", ioTProduct.getProductKey());
eventData.put("operation", "DELETE");
eventPublisher.publishEvent(EventTopics.PRODUCT_CONFIG_UPDATED, eventData);
}
return toAjax(devProductService.deleteDevProductByIds(ids));
}
/**
* 查询设备产品物模型属性列表
*/
/** 查询设备产品物模型属性列表 */
@GetMapping("/properties/{id}")
public AjaxResult getPropertiesList(@PathVariable("id") String id) {
List<IoTDeviceProperties> list = devProductService.selectDevProperties(id);
return AjaxResult.success(list);
}
/**
* 查询设备产品物模型属性列表(功能下发属性)
*/
/** 查询设备产品物模型属性列表(功能下发属性) */
@GetMapping("/functionProperties/{id}")
public AjaxResult getFunctionPropertiesList(@PathVariable("id") String id) {
List<IoTDeviceProperties> list = devProductService.selectDevProperties(id);
@@ -535,7 +509,8 @@ public class IoTProductController extends BaseController {
List<IoTDeviceProperties> dataRw =
list.stream().filter((item) -> "rw".equals(item.getMode())).collect(Collectors.toList());
// 获取设备公共字段
List<SysDictData> data = dictTypeService.selectDictDataByType("device_common_property");
List<SysDictData> data =
dictTypeService.selectDictDataByType(IoTConstant.DEVICE_SHADOW_CUSTOMIZED_PROPERTY);
// 根据公共字段过滤
if (Validator.isNotNull(data)) {
for (SysDictData datum : data) {
@@ -558,27 +533,21 @@ public class IoTProductController extends BaseController {
return AjaxResult.success(dataRw);
}
/**
* 查询设备产品物模型事件列表
*/
/** 查询设备产品物模型事件列表 */
@GetMapping("/events/{id}")
public AjaxResult getEventsList(@PathVariable("id") String id) {
List<IoTDeviceEvents> list = devProductService.selectDevEvents(id);
return AjaxResult.success(list);
}
/**
* 查询设备产品物模型方法列表
*/
/** 查询设备产品物模型方法列表 */
@GetMapping("/functions/{id}")
public AjaxResult getFunctionsList(@PathVariable("id") String id) {
List<IoTDeviceFunction> list = devProductService.selectDevFunctions(id);
return AjaxResult.success(list);
}
/**
* 根据产品key查model配置
*/
/** 根据产品key查model配置 */
@GetMapping("/model/{id}")
public AjaxResult getmodel(@PathVariable("id") String id) {
IoTDeviceModelVO ioTDeviceModelVO = devProductService.getModelByProductKey(id);
@@ -590,7 +559,7 @@ public class IoTProductController extends BaseController {
return AjaxResult.success(devProductService.selectMetadataByDevId(devId));
}
@GetMapping("/devnumber")
@GetMapping("/device/count")
public AjaxResult countDevNumberByProductKey() {
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
Map<String, Integer> products =
@@ -599,13 +568,11 @@ public class IoTProductController extends BaseController {
return AjaxResult.success(products);
}
/**
* 导出设备产品列表
*/
/** 导出设备产品列表 */
@PostMapping("/export")
@Log(title = "产品导出", businessType = BusinessType.EXPORT)
public void export(HttpServletResponse response, IoTProductQuery query) {
// 只能看自己设备,除非是特殊设置和超管
//
IoTUser iotUser = loginIoTUnionUser(SecurityUtils.getUnionId());
query.setSelf(!iotUser.isAdmin());
if (query.isSelf()) {
@@ -646,15 +613,13 @@ public class IoTProductController extends BaseController {
}
}
/**
* 批量导入产品
*/
/** 批量导入产品 */
@PostMapping("/import")
@Log(title = "产品批量导入", businessType = BusinessType.IMPORT)
@Transactional(rollbackFor = Exception.class)
public AjaxResult<Void> importProduct(MultipartFile file) throws Exception {
// 模板
// 只能看自己设备,除非是特殊设置和超管
//
String unionId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
try {
// 读取JSON文件内容
@@ -675,9 +640,41 @@ public class IoTProductController extends BaseController {
}
}
/**
* 下载导入模版
*/
/** 上传产品图片并保存到产品信息 */
@Operation(summary = "上传产品图片")
@PostMapping("/uploadImage")
@Log(title = "上传产品图片", businessType = BusinessType.UPDATE)
public AjaxResult<Map<String, String>> uploadProductImage(
@RequestParam("id") String id, @RequestParam("file") MultipartFile file) {
if (file == null || file.isEmpty()) {
throw new IoTException("上传文件不能为空");
}
IoTProductVO product = devProductService.selectDevProductById(id);
if (product == null) {
throw new IoTException("产品不存在");
}
String unionId = loginIoTUnionUser(SecurityUtils.getUnionId()).getUnionId();
// 上传到 OSS,沿用系统统一上传逻辑
var oss = iSysOssService.upload(file, unionId);
String url = oss.getUrl();
// 更新产品 photoUrl 字段(保持前端期望结构)
IoTProduct update = new IoTProduct();
update.setId(product.getId());
JSONObject photo = new JSONObject();
photo.set("img", url);
photo.set("detail", "");
update.setPhotoUrl(photo.toString());
devProductService.updateDevProduct(update);
Map<String, String> data = new HashMap<>(2);
data.put("url", url);
String name = oss.getOriginalName();
data.put("fileName", name.substring(name.lastIndexOf('/') + 1, name.lastIndexOf(".")));
return AjaxResult.success(data);
}
/** 下载导入模版 */
@PostMapping("/import/template")
public void downloadTemplate(HttpServletResponse response) {
List<IoTProductImportBO> list = new ArrayList<>();
@@ -723,9 +720,7 @@ public class IoTProductController extends BaseController {
return getDataTable(maps);
}
/**
* 绑定证书到产品
*/
/** 绑定证书到产品 */
@PostMapping("/bindCertificate")
@Log(title = "绑定证书", businessType = BusinessType.UPDATE)
public AjaxResult bindCertificate(@RequestParam String productKey, @RequestParam String sslKey) {
@@ -733,13 +728,35 @@ public class IoTProductController extends BaseController {
return toAjax(result);
}
/**
* 解绑证书
*/
/** 解绑证书 */
@PostMapping("/unbindCertificate")
@Log(title = "解绑证书", businessType = BusinessType.UPDATE)
public AjaxResult unbindCertificate(@RequestParam String productKey) {
int result = devProductService.unbindCertificate(productKey);
return toAjax(result);
}
/** 查看连接信息 */
@PostMapping("/connect/info")
@Log(title = "查看连接信息", businessType = BusinessType.OTHER)
public AjaxResult connectInfo(@RequestParam String productKey) {
try {
ConnectionInfoDTO connectionInfo = connectionInfoService.getConnectionInfo(productKey);
return AjaxResult.success(connectionInfo);
} catch (Exception e) {
return AjaxResult.error("获取连接信息失败:" + e.getMessage());
}
}
/** 查看MQTT密码 */
@PostMapping("/connect/mqtt/password")
@Log(title = "查看连接信息密码", businessType = BusinessType.GRANT)
public AjaxResult connectInfoPassword(@RequestParam String productKey) {
try {
ConnectionInfoDTO passwordInfo = connectionInfoService.getMqttPasswordInfo(productKey);
return AjaxResult.success(passwordInfo);
} catch (Exception e) {
return AjaxResult.error("获取密码信息失败:" + e.getMessage());
}
}
}
@@ -16,11 +16,12 @@ import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import cn.universal.admin.common.annotation.Log;
import cn.universal.admin.common.enums.BusinessType;
@@ -137,6 +138,28 @@ public class IoTUserAppController extends BaseController {
util.exportExcel(response, list, "用户应用信息数据");
}
/** 获取用户应用信息详细信息 */
@Operation(summary = "启用推送")
@PostMapping(value = "/{appUniqueId}/{pushType}/{enable}")
public AjaxResult<Void> enableOrDisablePush(
@PathVariable("appUniqueId") String appUniqueId,
@PathVariable("pushType") String pushType,
@PathVariable("enable") boolean enable) {
IoTUserApplication application =
iotUserApplicationService.selectIotUserApplicationById(appUniqueId);
IoTUser user = loginIoTUnionUser(SecurityUtils.getUnionId());
if (!user.isAdmin()
&& ObjectUtil.isNotEmpty(application)
&& !application.getUnionId().equals(user.getUnionId())) {
throw new IoTException("你无权操作");
}
boolean flag = iotUserApplicationService.enableOrDisablePushCfg(appUniqueId, pushType, enable);
if (flag) {
return AjaxResult.success();
}
return AjaxResult.error("失败");
}
/** 获取用户应用信息详细信息 */
@Operation(summary = "获取用户应用信息详细信息")
@GetMapping(value = "/{appUniqueId}")
@@ -198,21 +221,21 @@ public class IoTUserAppController extends BaseController {
@Transactional
@Log(title = "重置密钥", businessType = BusinessType.UPDATE)
public AjaxResult<Void> resetSecret(@RequestBody IoTUserApplication iotUserApplication) {
IoTUserApplication oldapplication =
iotUserApplicationService.selectIotUserApplicationById(iotUserApplication.getAppUniqueId());
IoTUser user = loginIoTUnionUser(SecurityUtils.getUnionId());
// 权限检查
IoTUserApplication oldApplication =
iotUserApplicationService.selectIotUserApplicationById(iotUserApplication.getAppUniqueId());
if (!user.isAdmin()
&& ObjectUtil.isNotEmpty(oldapplication)
&& !oldapplication.getUnionId().equals(user.getUnionId())) {
&& ObjectUtil.isNotEmpty(oldApplication)
&& !oldApplication.getUnionId().equals(user.getUnionId())) {
throw new IoTException("你无权操作");
}
// iotUserApplication.setAppId(RandomUtil.randomString(16));
iotUserApplication.setAppSecret(RandomUtil.randomString(32));
iotUserApplication.setUnionId(user.getUnionId());
if (iotUserApplicationService.updateIotUserApplication(iotUserApplication) == 0) {
// 调用service层方法完成密钥重置和MQTT配置同步
if (!iotUserApplicationService.resetAppSecretAndSyncMqtt(
iotUserApplication.getAppUniqueId(), user.getUnionId())) {
throw new IoTException("应用密钥重置失败");
}
return AjaxResult.success();
}
@@ -238,7 +261,6 @@ public class IoTUserAppController extends BaseController {
Map<String, String> map = new HashMap<>();
map.put("message", "success");
iotUserApplication.setUnionId(app.getUnionId());
iotUserApplication.setUpTopic(app.getUnionId() + StrUtil.C_SLASH + app.getAppId());
iotUserApplicationService.updateIotUserApplication(iotUserApplication);
return AjaxResult.success(map);
}
@@ -67,4 +67,15 @@ public interface IIoTUserApplicationService {
int deleteIotUserApplicationById(String appUniqueId);
List<IoTUserApplicationVO> selectApplicationList(IoTUserApplication application, IoTUser iotUser);
boolean enableOrDisablePushCfg(String appUniqueId, String pushType, boolean isEnable);
/**
* 重置应用密钥并同步更新MQTT配置
*
* @param appUniqueId 应用唯一标识
* @param unionId 用户唯一标识
* @return 是否成功
*/
boolean resetAppSecretAndSyncMqtt(String appUniqueId, String unionId);
}
@@ -13,6 +13,9 @@
package cn.universal.admin.system.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.universal.admin.common.service.BaseServiceImpl;
import cn.universal.admin.common.utils.SecurityUtils;
import cn.universal.admin.system.service.IIoTUserApplicationService;
@@ -28,9 +31,12 @@ import cn.universal.persistence.mapper.OauthClientDetailsMapper;
import jakarta.annotation.Resource;
import java.util.List;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;
/**
@@ -39,6 +45,7 @@ import tk.mybatis.mapper.entity.Example;
* @since 2025-12-30
*/
@Service
@Slf4j
public class IoTUserApplicationServiceImpl extends BaseServiceImpl
implements IIoTUserApplicationService {
@@ -47,6 +54,12 @@ public class IoTUserApplicationServiceImpl extends BaseServiceImpl
@Resource private OauthClientDetailsMapper oauthClientDetailsMapper;
@Value("${mqtt.cfg.enable:true}")
private boolean sysMqttEnabled;
@Value("${mqtt.cfg.host:tcp://localhost:1883}")
private String sysMqttHost;
@Override
public boolean EnDisableIoTUser(String unionId, boolean isEnable) {
List<IoTUserApplication> select =
@@ -212,4 +225,111 @@ public class IoTUserApplicationServiceImpl extends BaseServiceImpl
application.setUnionId(iotUser.isAdmin() ? null : iotUser.getUnionId());
return iotUserApplicationMapper.selectApplicationList(application);
}
@Override
public boolean enableOrDisablePushCfg(String appUniqueId, String pushType, boolean isEnable) {
IoTUserApplication application =
iotUserApplicationMapper.selectOne(
IoTUserApplication.builder().appUniqueId(appUniqueId).build());
if (application == null) {
return false;
}
// 获取当前推送配置
String pushCfgStr = application.getCfg();
JSONObject pushCfg;
if (StrUtil.isBlank(pushCfgStr)) {
pushCfg = new JSONObject();
} else {
pushCfg = JSONUtil.parseObj(pushCfgStr);
}
switch (pushType) {
case "mqtt":
if (isEnable) {
// MQTT启用:设置url为sysMqttHost、support为true、enable为true
// password为app_secret、username为app_id
JSONObject mqttConfig = new JSONObject();
mqttConfig.set("enable", true);
mqttConfig.set("support", true);
mqttConfig.set("url", sysMqttHost);
mqttConfig.set("password", application.getAppSecret());
mqttConfig.set("username", application.getAppId());
mqttConfig.set("clientId", application.getAppId());
pushCfg.set("mqtt", mqttConfig);
} else {
// MQTT禁用:只设置enable为false
if (pushCfg.containsKey("mqtt")) {
JSONObject mqttConfig = pushCfg.getJSONObject("mqtt");
mqttConfig.set("enable", false);
pushCfg.set("mqtt", mqttConfig);
}
}
break;
case "http":
if (isEnable) {
// HTTP启用:设置enable和support为true
JSONObject httpConfig = pushCfg.getJSONObject("http");
httpConfig.set("enable", true);
httpConfig.set("support", true);
pushCfg.set("http", httpConfig);
} else {
// HTTP禁用:设置enable为false
if (pushCfg.containsKey("http")) {
JSONObject httpConfig = pushCfg.getJSONObject("http");
httpConfig.set("enable", false);
pushCfg.set("http", httpConfig);
}
}
break;
default:
return false;
}
// 更新推送配置
application.setCfg(pushCfg.toString());
int result = iotUserApplicationMapper.updateIotUserApplication(application);
return result > 0;
}
@Override
@Transactional
public boolean resetAppSecretAndSyncMqtt(String appUniqueId, String unionId) {
// 查询应用信息
IoTUserApplication application =
iotUserApplicationMapper.selectOne(
IoTUserApplication.builder().appUniqueId(appUniqueId).build());
if (application == null) {
return false;
}
// 生成新的应用密钥
String newAppSecret = cn.hutool.core.util.RandomUtil.randomString(32);
application.setAppSecret(newAppSecret);
// 更新应用信息
if (iotUserApplicationMapper.updateByPrimaryKeySelective(application) == 0) {
return false;
}
// 检查MQTT是否开启,如果开启则同步更新MQTT配置中的密码
String oldCfg = application.getCfg();
if (StrUtil.isNotBlank(oldCfg)) {
JSONObject cfg = JSONUtil.parseObj(oldCfg);
if (cfg.containsKey("mqtt")) {
JSONObject mqttConfig = cfg.getJSONObject("mqtt");
if (mqttConfig.getBool("enable", false)) {
// MQTT已启用,更新密码
mqttConfig.set("password", newAppSecret);
cfg.set("mqtt", mqttConfig);
// 更新配置
IoTUserApplication updateCfg = new IoTUserApplication();
updateCfg.setAppUniqueId(appUniqueId);
updateCfg.setCfg(cfg.toString());
iotUserApplicationMapper.updateByPrimaryKeySelective(updateCfg);
log.info("应用 {} 密钥重置后,MQTT配置密码已同步更新", appUniqueId);
}
}
}
return true;
}
}