【功能完善】IoT:设备新增、修改支持更多字段
This commit is contained in:
parent
db9c485285
commit
afaf98c44f
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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, "产品分类不存在");
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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 -> // 只返回 id、name 字段
|
||||||
|
new IotDeviceRespVO().setId(device.getId()).setDeviceName(device.getDeviceName())));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
@ -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 -> // 只返回 id、name 字段
|
return success(convertList(list, product -> // 只返回 id、name 字段
|
||||||
new IotProductRespVO().setId(product.getId()).setName(product.getName())));
|
new IotProductRespVO().setId(product.getId()).setName(product.getName())
|
||||||
|
.setDeviceType(product.getDeviceType())));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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 = "你猜")
|
||||||
|
@ -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 = "描述")
|
||||||
|
@ -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") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
@KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备状态最后更新时间
|
* 设备状态最后更新时间
|
||||||
*/
|
*/
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user