【功能完善】IoT:设备新增、修改支持更多字段

This commit is contained in:
YunaiV 2024-12-14 16:12:24 +08:00
parent db9c485285
commit afaf98c44f
13 changed files with 249 additions and 147 deletions

View File

@ -0,0 +1,58 @@
package cn.iocoder.yudao.framework.mybatis.core.type;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;
/**
* Set<Long> 的类型转换器实现类对应数据库的 varchar 类型
*
* @author 芋道源码
*/
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class LongSetTypeHandler implements TypeHandler<Set<Long>> {
private static final String COMMA = ",";
@Override
public void setParameter(PreparedStatement ps, int i, Set<Long> strings, JdbcType jdbcType) throws SQLException {
// 设置占位符
ps.setString(i, CollUtil.join(strings, COMMA));
}
@Override
public Set<Long> getResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
return getResult(value);
}
@Override
public Set<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
return getResult(value);
}
@Override
public Set<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
return getResult(value);
}
private Set<Long> getResult(String value) {
if (value == null) {
return null;
}
return StrUtils.splitToLongSet(value, COMMA);
}
}

View File

@ -26,9 +26,9 @@ public interface ErrorCodeConstants {
ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在"); ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在");
ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一"); ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一");
ErrorCode DEVICE_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除"); ErrorCode DEVICE_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除");
ErrorCode DEVICE_NAME_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_003, "设备名称不能修改"); ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, "设备标识已经存在");
ErrorCode DEVICE_PRODUCT_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_004, "产品不能修改"); ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在");
ErrorCode DEVICE_INVALID_DEVICE_STATUS = new ErrorCode(1_050_003_005, "无效的设备状态"); ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备");
// ========== 产品分类 1-050-004-000 ========== // ========== 产品分类 1-050-004-000 ==========
ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在");

View File

@ -36,4 +36,14 @@ public enum IotProductDeviceTypeEnum implements IntArrayValuable {
return ARRAYS; return ARRAYS;
} }
/**
* 判断是否是网关
*
* @param type 类型
* @return 是否是网关
*/
public static boolean isGateway(Integer type) {
return GATEWAY.getType().equals(type);
}
} }

View File

@ -18,7 +18,10 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "管理后台 - IoT 设备") @Tag(name = "管理后台 - IoT 设备")
@RestController @RestController
@ -86,4 +89,14 @@ public class IotDeviceController {
return success(deviceService.getDeviceCountByProductId(productId)); return success(deviceService.getDeviceCountByProductId(productId));
} }
@GetMapping("/simple-list")
@Operation(summary = "获取设备的精简信息列表", description = "主要用于前端的下拉选项")
@Parameter(name = "deviceType", description = "设备类型", example = "1")
public CommonResult<List<IotDeviceRespVO>> getSimpleDeviceList(
@RequestParam(value = "deviceType", required = false) Integer deviceType) {
List<IotDeviceDO> list = deviceService.getDeviceList(deviceType);
return success(convertList(list, device -> // 只返回 idname 字段
new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName())));
}
} }

View File

@ -10,13 +10,25 @@ public class IotDeviceSaveReqVO {
@Schema(description = "设备编号", example = "177") @Schema(description = "设备编号", example = "177")
private Long id; private Long id;
@Schema(description = "设备编号", requiredMode = Schema.RequiredMode.AUTO, example = "177")
private String deviceKey;
@Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
private String deviceName; private String deviceName;
@Schema(description = "备注名称", example = "张三") @Schema(description = "备注名称", example = "张三")
private String nickname; private String nickname;
@Schema(description = "设备序列号", example = "123456")
private String serialNumber;
@Schema(description = "设备图片", example = "https://iocoder.cn/1.png")
private String picUrl;
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202") @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
private Long productId; private Long productId;
@Schema(description = "网关设备 ID", example = "16380")
private Long gatewayId;
} }

View File

@ -121,12 +121,12 @@ public class IotProductController {
} }
@GetMapping("/simple-list") @GetMapping("/simple-list")
@Operation(summary = "获得所有产品列表") @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项")
@PreAuthorize("@ss.hasPermission('iot:product:query')")
public CommonResult<List<IotProductRespVO>> getSimpleProductList() { public CommonResult<List<IotProductRespVO>> getSimpleProductList() {
List<IotProductDO> list = productService.getProductList(); List<IotProductDO> list = productService.getProductList();
return success(convertList(list, product -> // 只返回 idname 字段 return success(convertList(list, product -> // 只返回 idname 字段
new IotProductRespVO().setId(product.getId()).setName(product.getName()))); new IotProductRespVO().setId(product.getId()).setName(product.getName())
.setDeviceType(product.getDeviceType())));
} }
} }

View File

@ -38,8 +38,8 @@ public class IotProductRespVO {
@ExcelProperty("产品图标") @ExcelProperty("产品图标")
private String icon; private String icon;
@Schema(description = "产品图", example = "https://iocoder.cn/1.png") @Schema(description = "产品图", example = "https://iocoder.cn/1.png")
@ExcelProperty("产品图") @ExcelProperty("产品图")
private String picUrl; private String picUrl;
@Schema(description = "产品描述", example = "你猜") @Schema(description = "产品描述", example = "你猜")

View File

@ -28,7 +28,7 @@ public class IotProductSaveReqVO {
@Schema(description = "产品图标", example = "https://iocoder.cn/1.svg") @Schema(description = "产品图标", example = "https://iocoder.cn/1.svg")
private String icon; private String icon;
@Schema(description = "产品图", example = "https://iocoder.cn/1.png") @Schema(description = "产品图", example = "https://iocoder.cn/1.png")
private String picUrl; private String picUrl;
@Schema(description = "产品描述", example = "描述") @Schema(description = "产品描述", example = "描述")

View File

@ -1,22 +1,25 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.device; package cn.iocoder.yudao.module.iot.dal.dataobject.device;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongSetTypeHandler;
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*; import lombok.*;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Set;
/** /**
* IoT 设备 DO * IoT 设备 DO
* *
* @author haohao * @author haohao
*/ */
@TableName("iot_device") @TableName(value = "iot_device", autoResultMap = true)
@KeySequence("iot_device_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写 @KeySequence("iot_device_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -47,6 +50,17 @@ public class IotDeviceDO extends BaseDO {
* 设备序列号 * 设备序列号
*/ */
private String serialNumber; private String serialNumber;
/**
* 设备图片
*/
private String picUrl;
/**
* 设备分组编号集合
*
* 关联 TODO 芋艿
*/
@TableField(typeHandler = LongSetTypeHandler.class)
private Set<Long> groupIds;
/** /**
* 产品编号 * 产品编号
@ -66,13 +80,6 @@ public class IotDeviceDO extends BaseDO {
* 冗余 {@link IotProductDO#getDeviceType()} * 冗余 {@link IotProductDO#getDeviceType()}
*/ */
private Integer deviceType; private Integer deviceType;
/**
* 设备状态
* <p>
* 枚举 {@link IotDeviceStatusEnum}
*/
private Integer status;
/** /**
* 网关设备编号 * 网关设备编号
* <p> * <p>
@ -82,6 +89,13 @@ public class IotDeviceDO extends BaseDO {
*/ */
private Long gatewayId; private Long gatewayId;
/**
* 设备状态
* <p>
* 枚举 {@link IotDeviceStatusEnum}
*/
private Integer status;
/** /**
* 设备状态最后更新时间 * 设备状态最后更新时间
*/ */

View File

@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePa
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/** /**
* IoT 设备 Mapper * IoT 设备 Mapper
* *
@ -51,4 +53,13 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
default Long selectCountByProductId(Long productId) { default Long selectCountByProductId(Long productId) {
return selectCount(IotDeviceDO::getProductId, productId); return selectCount(IotDeviceDO::getProductId, productId);
} }
default IotDeviceDO selectByDeviceKey(String deviceKey) {
return selectOne(IotDeviceDO::getDeviceKey, deviceKey);
}
default List<IotDeviceDO> selectList(Integer deviceType) {
return selectList(IotDeviceDO::getDeviceType, deviceType);
}
} }

View File

@ -21,7 +21,7 @@ public interface IotProductCategoryMapper extends BaseMapperX<IotProductCategory
return selectPage(reqVO, new LambdaQueryWrapperX<IotProductCategoryDO>() return selectPage(reqVO, new LambdaQueryWrapperX<IotProductCategoryDO>()
.likeIfPresent(IotProductCategoryDO::getName, reqVO.getName()) .likeIfPresent(IotProductCategoryDO::getName, reqVO.getName())
.betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(IotProductCategoryDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(IotProductCategoryDO::getId)); .orderByAsc(IotProductCategoryDO::getSort));
} }
default List<IotProductCategoryDO> selectListByStatus(Integer status) { default List<IotProductCategoryDO> selectListByStatus(Integer status) {

View File

@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.iot.service.device; package cn.iocoder.yudao.module.iot.service.device;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceSaveReqVO;
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO; import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDeviceStatusUpdateReqVO;
import jakarta.validation.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import jakarta.validation.Valid;
import javax.annotation.Nullable;
import java.util.List;
/** /**
* IoT 设备 Service 接口 * IoT 设备 Service 接口
@ -52,6 +55,14 @@ public interface IotDeviceService {
*/ */
PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO); PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO);
/**
* 获得设备列表
*
* @param deviceType 设备类型
* @return 设备列表
*/
List<IotDeviceDO> getDeviceList(@Nullable Integer deviceType);
/** /**
* 更新设备状态 * 更新设备状态
* *
@ -75,4 +86,5 @@ public interface IotDeviceService {
* @return 设备信息 * @return 设备信息
*/ */
IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName); IotDeviceDO getDeviceByProductKeyAndDeviceName(String productKey, String deviceName);
} }

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.iot.service.device; package cn.iocoder.yudao.module.iot.service.device;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
@ -12,18 +12,17 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;
import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
import cn.iocoder.yudao.module.iot.service.product.IotProductService; import cn.iocoder.yudao.module.iot.service.product.IotProductService;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import java.security.SecureRandom; import javax.annotation.Nullable;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Base64; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.UUID;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
@ -40,142 +39,64 @@ public class IotDeviceServiceImpl implements IotDeviceService {
@Resource @Resource
private IotDeviceMapper deviceMapper; private IotDeviceMapper deviceMapper;
@Resource @Resource
private IotProductService productService; private IotProductService productService;
/**
* 创建 IoT 设备
*
* @param createReqVO 创建请求 VO
* @return 设备 ID
*/
@Override @Override
@Transactional(rollbackFor = Exception.class)
public Long createDevice(IotDeviceSaveReqVO createReqVO) { public Long createDevice(IotDeviceSaveReqVO createReqVO) {
// 1.1 校验产品是否存在 // 1.1 校验产品是否存在
IotProductDO product = productService.getProduct(createReqVO.getProductId()); IotProductDO product = productService.getProduct(createReqVO.getProductId());
if (product == null) { if (product == null) {
throw exception(PRODUCT_NOT_EXISTS); throw exception(PRODUCT_NOT_EXISTS);
} }
// 1.2 校验设备名称在同一产品下是否唯一 // 1.2 校验设备标识是否唯一
if (StrUtil.isBlank(createReqVO.getDeviceName())) { if (deviceMapper.selectByDeviceKey(createReqVO.getDeviceKey()) != null) {
createReqVO.setDeviceName(generateUniqueDeviceName(product.getProductKey())); throw exception(DEVICE_KEY_EXISTS);
} else { }
validateDeviceNameUnique(product.getProductKey(), createReqVO.getDeviceName()); // 1.3 校验设备名称在同一产品下是否唯一
if (deviceMapper.selectByProductKeyAndDeviceName(product.getProductKey(), createReqVO.getDeviceKey()) != null) {
throw exception(DEVICE_NAME_EXISTS);
}
// 1.4 校验父设备是否为合法网关
if (IotProductDeviceTypeEnum.isGateway(product.getDeviceType())
&& createReqVO.getGatewayId() != null) {
validateGatewayDeviceExists(createReqVO.getGatewayId());
} }
// 2.1 转换 VO DO // 2.1 转换 VO DO
IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class) IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class, o -> {
.setProductKey(product.getProductKey()) o.setProductKey(product.getProductKey()).setDeviceType(product.getDeviceType());
.setDeviceType(product.getDeviceType()); // 生成并设置必要的字段
// 2.2 生成并设置必要的字段 o.setDeviceSecret(generateDeviceSecret())
device.setDeviceKey(generateUniqueDeviceKey()); .setMqttClientId(generateMqttClientId())
device.setDeviceSecret(generateDeviceSecret()); .setMqttUsername(generateMqttUsername(o.getDeviceName(), o.getProductKey()))
device.setMqttClientId(generateMqttClientId()); .setMqttPassword(generateMqttPassword());
device.setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey())); // 设置设备状态为未激活
device.setMqttPassword(generateMqttPassword()); o.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus()).setStatusLastUpdateTime(LocalDateTime.now());
// 2.3 设置设备状态为未激活 });
device.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus()); // 2.2 插入到数据库
device.setStatusLastUpdateTime(LocalDateTime.now());
// 2.4 插入到数据库
deviceMapper.insert(device); deviceMapper.insert(device);
return device.getId(); return device.getId();
} }
/**
* 校验设备名称在同一产品下是否唯一
*
* @param productKey 产品 Key
* @param deviceName 设备名称
*/
private void validateDeviceNameUnique(String productKey, String deviceName) {
IotDeviceDO existingDevice = deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
if (existingDevice != null) {
throw exception(DEVICE_NAME_EXISTS);
}
}
/**
* 生成唯一的 deviceKey
*
* @return 生成的 deviceKey
*/
private String generateUniqueDeviceKey() {
return UUID.randomUUID().toString();
}
/**
* 生成 deviceSecret
*
* @return 生成的 deviceSecret
*/
private String generateDeviceSecret() {
return IdUtil.fastSimpleUUID();
}
/**
* 生成 MQTT Client ID
*
* @return 生成的 MQTT Client ID
*/
private String generateMqttClientId() {
return UUID.randomUUID().toString();
}
/**
* 生成 MQTT Username
*
* @param deviceName 设备名称
* @param productKey 产品 Key
* @return 生成的 MQTT Username
*/
private String generateMqttUsername(String deviceName, String productKey) {
return deviceName + "&" + productKey;
}
/**
* 生成 MQTT Password
*
* @return 生成的 MQTT Password
*/
private String generateMqttPassword() {
// TODO @浩浩这里的 StrUtil 随机字符串
SecureRandom secureRandom = new SecureRandom();
byte[] passwordBytes = new byte[32]; // 256 位的随机数
secureRandom.nextBytes(passwordBytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(passwordBytes);
}
/**
* 生成唯一的 DeviceName
*
* @param productKey 产品标识
* @return 生成的唯一 DeviceName
*/
private String generateUniqueDeviceName(String productKey) {
for (int i = 0; i < Short.MAX_VALUE; i++) {
String deviceName = IdUtil.fastSimpleUUID().substring(0, 20);
if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {
return deviceName;
}
}
throw new IllegalArgumentException("生成 DeviceName 失败");
}
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void updateDevice(IotDeviceSaveReqVO updateReqVO) { public void updateDevice(IotDeviceSaveReqVO updateReqVO) {
// 1. 校验存在 updateReqVO.setDeviceKey(null).setDeviceName(null).setProductId(null); // 不允许更新
validateDeviceExists(updateReqVO.getId()); // 1.1 校验存在
IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
// 1.2 校验父设备是否为合法网关
if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())
&& updateReqVO.getGatewayId() != null) {
validateGatewayDeviceExists(updateReqVO.getGatewayId());
}
// 2. 更新到数据库 // 2. 更新到数据库
IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class) IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class);
.setDeviceName(null).setProductId(null); // 设备名称 产品 ID 不能修改
deviceMapper.updateById(updateObj); deviceMapper.updateById(updateObj);
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public void deleteDevice(Long id) { public void deleteDevice(Long id) {
// 1.1 校验存在 // 1.1 校验存在
IotDeviceDO device = validateDeviceExists(id); IotDeviceDO device = validateDeviceExists(id);
@ -202,13 +123,24 @@ public class IotDeviceServiceImpl implements IotDeviceService {
return device; return device;
} }
@Override /**
public IotDeviceDO getDevice(Long id) { * 校验网关设备是否存在
*
* @param id 设备 ID
*/
private void validateGatewayDeviceExists(Long id) {
IotDeviceDO device = deviceMapper.selectById(id); IotDeviceDO device = deviceMapper.selectById(id);
if (device == null) { if (device == null) {
throw exception(DEVICE_NOT_EXISTS); throw exception(DEVICE_GATEWAY_NOT_EXISTS);
} }
return device; if (!IotProductDeviceTypeEnum.isGateway(device.getDeviceType())) {
throw exception(DEVICE_NOT_GATEWAY);
}
}
@Override
public IotDeviceDO getDevice(Long id) {
return deviceMapper.selectById(id);
} }
@Override @Override
@ -216,19 +148,24 @@ public class IotDeviceServiceImpl implements IotDeviceService {
return deviceMapper.selectPage(pageReqVO); return deviceMapper.selectPage(pageReqVO);
} }
@Override
public List<IotDeviceDO> getDeviceList(@Nullable Integer deviceType) {
return deviceMapper.selectList(deviceType);
}
@Override @Override
public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) { public void updateDeviceStatus(IotDeviceStatusUpdateReqVO updateReqVO) {
// 1. 校验存在 // 1. 校验存在
IotDeviceDO device = validateDeviceExists(updateReqVO.getId()); IotDeviceDO device = validateDeviceExists(updateReqVO.getId());
// 2.1 更新状态和更新时间 // 2.1 更新状态和更新时间
IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); IotDeviceDO updateDevice = BeanUtils.toBean(updateReqVO, IotDeviceDO.class)
.setStatusLastUpdateTime(LocalDateTime.now());
// 2.2 更新状态相关时间 // 2.2 更新状态相关时间
if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus()) if (Objects.equals(device.getStatus(), IotDeviceStatusEnum.INACTIVE.getStatus())
&& Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { && Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
// 从未激活到在线设置激活时间和最后上线时间 // 从未激活到在线设置激活时间和最后上线时间
updateDevice.setActiveTime(LocalDateTime.now()); updateDevice.setActiveTime(LocalDateTime.now()).setLastOnlineTime(LocalDateTime.now());
updateDevice.setLastOnlineTime(LocalDateTime.now());
} else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) { } else if (Objects.equals(updateDevice.getStatus(), IotDeviceStatusEnum.ONLINE.getStatus())) {
// 如果是上线设置最后上线时间 // 如果是上线设置最后上线时间
updateDevice.setLastOnlineTime(LocalDateTime.now()); updateDevice.setLastOnlineTime(LocalDateTime.now());
@ -236,10 +173,7 @@ public class IotDeviceServiceImpl implements IotDeviceService {
// 如果是离线设置最后离线时间 // 如果是离线设置最后离线时间
updateDevice.setLastOfflineTime(LocalDateTime.now()); updateDevice.setLastOfflineTime(LocalDateTime.now());
} }
// 2.3 更新到数据库
// 2.3 设置状态更新时间
updateDevice.setStatusLastUpdateTime(LocalDateTime.now());
// 2.4 更新到数据库
deviceMapper.updateById(updateDevice); deviceMapper.updateById(updateDevice);
} }
@ -254,4 +188,42 @@ public class IotDeviceServiceImpl implements IotDeviceService {
return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); return deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName);
} }
/**
* 生成 deviceSecret
*
* @return 生成的 deviceSecret
*/
private String generateDeviceSecret() {
return IdUtil.fastSimpleUUID();
}
/**
* 生成 MQTT Client ID
*
* @return 生成的 MQTT Client ID
*/
private String generateMqttClientId() {
return IdUtil.fastSimpleUUID();
}
/**
* 生成 MQTT Username
*
* @param deviceName 设备名称
* @param productKey 产品 Key
* @return 生成的 MQTT Username
*/
private String generateMqttUsername(String deviceName, String productKey) {
return deviceName + "&" + productKey;
}
/**
* 生成 MQTT Password
*
* @return 生成的 MQTT Password
*/
private String generateMqttPassword() {
return RandomUtil.randomString(32);
}
} }