mirror of
https://gitee.com/zhijiantianya/ruoyi-vue-pro.git
synced 2026-03-22 05:07:17 +08:00
feat(iot):【网关设备:20%】增加网关设备绑定能力(未完成),基于 breezy-doodling-starlight.md 规划
This commit is contained in:
@@ -51,6 +51,7 @@ public class IotDeviceController {
|
|||||||
return success(deviceService.createDevice(createReqVO));
|
return success(deviceService.createDevice(createReqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@PutMapping("/update")
|
@PutMapping("/update")
|
||||||
@Operation(summary = "更新设备")
|
@Operation(summary = "更新设备")
|
||||||
@PreAuthorize("@ss.hasPermission('iot:device:update')")
|
@PreAuthorize("@ss.hasPermission('iot:device:update')")
|
||||||
@@ -59,7 +60,72 @@ public class IotDeviceController {
|
|||||||
return success(true);
|
return success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @芋艿:参考阿里云:1)绑定网关;2)解绑网关
|
@PutMapping("/bind-gateway")
|
||||||
|
@Operation(summary = "绑定子设备到网关")
|
||||||
|
@PreAuthorize("@ss.hasPermission('iot:device:update')")
|
||||||
|
public CommonResult<Boolean> bindDeviceGateway(@Valid @RequestBody IotDeviceBindGatewayReqVO reqVO) {
|
||||||
|
deviceService.bindDeviceGateway(reqVO.getIds(), reqVO.getGatewayId());
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/unbind-gateway")
|
||||||
|
@Operation(summary = "解绑子设备与网关")
|
||||||
|
@PreAuthorize("@ss.hasPermission('iot:device:update')")
|
||||||
|
public CommonResult<Boolean> unbindDeviceGateway(@Valid @RequestBody IotDeviceUnbindGatewayReqVO reqVO) {
|
||||||
|
deviceService.unbindDeviceGateway(reqVO.getIds());
|
||||||
|
return success(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/sub-device-list")
|
||||||
|
@Operation(summary = "获取网关的子设备列表")
|
||||||
|
@Parameter(name = "gatewayId", description = "网关设备编号", required = true, example = "1")
|
||||||
|
@PreAuthorize("@ss.hasPermission('iot:device:query')")
|
||||||
|
public CommonResult<List<IotDeviceRespVO>> getSubDeviceList(@RequestParam("gatewayId") Long gatewayId) {
|
||||||
|
List<IotDeviceDO> list = deviceService.getDeviceListByGatewayId(gatewayId);
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
return success(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补充产品名称
|
||||||
|
Map<Long, IotProductDO> productMap = convertMap(productService.getProductList(), IotProductDO::getId);
|
||||||
|
return success(convertList(list, device -> {
|
||||||
|
IotDeviceRespVO respVO = BeanUtils.toBean(device, IotDeviceRespVO.class);
|
||||||
|
MapUtils.findAndThen(productMap, device.getProductId(),
|
||||||
|
product -> respVO.setProductName(product.getName()));
|
||||||
|
return respVO;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO @AI:希望改成“未绑定的”。需要剔除已经绑定,包括自己的;
|
||||||
|
// TODO @AI:不需要传递 gatewayId;
|
||||||
|
// TODO @AI:需要分页;
|
||||||
|
@GetMapping("/bindable-sub-device-list")
|
||||||
|
@Operation(summary = "获取可绑定到网关的子设备列表")
|
||||||
|
@Parameter(name = "gatewayId", description = "网关设备编号(可选)", example = "1")
|
||||||
|
@PreAuthorize("@ss.hasPermission('iot:device:query')")
|
||||||
|
public CommonResult<List<IotDeviceRespVO>> getBindableSubDeviceList(
|
||||||
|
@RequestParam(value = "gatewayId", required = false) Long gatewayId) {
|
||||||
|
List<IotDeviceDO> list = deviceService.getBindableSubDeviceList(gatewayId);
|
||||||
|
if (CollUtil.isEmpty(list)) {
|
||||||
|
return success(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补充产品名称
|
||||||
|
Map<Long, IotProductDO> productMap = convertMap(productService.getProductList(), IotProductDO::getId);
|
||||||
|
return success(convertList(list, device -> {
|
||||||
|
// TODO @AI:可以 beanutils 转换么?
|
||||||
|
IotDeviceRespVO respVO = new IotDeviceRespVO()
|
||||||
|
.setId(device.getId())
|
||||||
|
.setDeviceName(device.getDeviceName())
|
||||||
|
.setNickname(device.getNickname())
|
||||||
|
.setProductId(device.getProductId())
|
||||||
|
.setState(device.getState())
|
||||||
|
.setGatewayId(device.getGatewayId());
|
||||||
|
MapUtils.findAndThen(productMap, device.getProductId(),
|
||||||
|
product -> respVO.setProductName(product.getName()));
|
||||||
|
return respVO;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
@PutMapping("/update-group")
|
@PutMapping("/update-group")
|
||||||
@Operation(summary = "更新设备分组")
|
@Operation(summary = "更新设备分组")
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - IoT 设备绑定网关 Request VO")
|
||||||
|
@Data
|
||||||
|
public class IotDeviceBindGatewayReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "子设备编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
|
||||||
|
@NotEmpty(message = "子设备编号列表不能为空")
|
||||||
|
private Set<Long> ids;
|
||||||
|
|
||||||
|
@Schema(description = "网关设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||||
|
@NotNull(message = "网关设备编号不能为空")
|
||||||
|
private Long gatewayId;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package cn.iocoder.yudao.module.iot.controller.admin.device.vo.device;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@Schema(description = "管理后台 - IoT 设备解绑网关 Request VO")
|
||||||
|
@Data
|
||||||
|
public class IotDeviceUnbindGatewayReqVO {
|
||||||
|
|
||||||
|
@Schema(description = "子设备编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
|
||||||
|
@NotEmpty(message = "子设备编号列表不能为空")
|
||||||
|
private Set<Long> ids;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -129,4 +129,33 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
|
|||||||
.isNotNull(IotDeviceDO::getLongitude));
|
.isNotNull(IotDeviceDO::getLongitude));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== 网关-子设备绑定相关 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网关编号查询子设备列表
|
||||||
|
*
|
||||||
|
* @param gatewayId 网关设备编号
|
||||||
|
* @return 子设备列表
|
||||||
|
*/
|
||||||
|
default List<IotDeviceDO> selectListByGatewayId(Long gatewayId) {
|
||||||
|
return selectList(IotDeviceDO::getGatewayId, gatewayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询可绑定到网关的子设备列表
|
||||||
|
* <p>
|
||||||
|
* 条件:设备类型为 GATEWAY_SUB 且未绑定任何网关,或已绑定到指定网关
|
||||||
|
*
|
||||||
|
* @param gatewayId 网关设备编号(可选,用于包含已绑定到该网关的设备)
|
||||||
|
* @return 子设备列表
|
||||||
|
*/
|
||||||
|
default List<IotDeviceDO> selectBindableSubDeviceList(@Nullable Long gatewayId) {
|
||||||
|
return selectList(new LambdaQueryWrapperX<IotDeviceDO>()
|
||||||
|
.eq(IotDeviceDO::getDeviceType, cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum.GATEWAY_SUB.getType())
|
||||||
|
.and(wrapper -> wrapper
|
||||||
|
.isNull(IotDeviceDO::getGatewayId)));
|
||||||
|
// .or()
|
||||||
|
// .eqIfPresent(IotDeviceDO::getGatewayId, gatewayId)))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,12 @@ public interface ErrorCodeConstants {
|
|||||||
ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!");
|
ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!");
|
||||||
ErrorCode DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL = new ErrorCode(1_050_003_007, "下行设备消息失败,原因:设备未连接网关");
|
ErrorCode DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL = new ErrorCode(1_050_003_007, "下行设备消息失败,原因:设备未连接网关");
|
||||||
ErrorCode DEVICE_SERIAL_NUMBER_EXISTS = new ErrorCode(1_050_003_008, "设备序列号已存在,序列号必须全局唯一");
|
ErrorCode DEVICE_SERIAL_NUMBER_EXISTS = new ErrorCode(1_050_003_008, "设备序列号已存在,序列号必须全局唯一");
|
||||||
|
// TODO @AI:1_050_003_009 需要提示具体的哪个设备。产品/设备,标识下
|
||||||
|
ErrorCode DEVICE_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_009, "设备不是网关子设备类型,无法绑定到网关");
|
||||||
|
// TODO @AI:1_050_003_009 需要提示具体的哪个设备。产品/设备,标识下
|
||||||
|
ErrorCode DEVICE_GATEWAY_BINDTO_EXISTS = new ErrorCode(1_050_003_010, "设备已绑定到其他网关,请先解绑");
|
||||||
|
// TODO @AI:是不是可以删除,DEVICE_GATEWAY_BINDTO_NOT_EXISTS
|
||||||
|
ErrorCode DEVICE_GATEWAY_BINDTO_NOT_EXISTS = new ErrorCode(1_050_003_011, "设备未绑定到任何网关");
|
||||||
|
|
||||||
// ========== 产品分类 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, "产品分类不存在");
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ public interface IotDeviceService {
|
|||||||
*/
|
*/
|
||||||
void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO);
|
void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO);
|
||||||
|
|
||||||
|
// TODO @AI:网关设备被删除时,需要查看是否有子设备绑定。如果有,则不允许删除。
|
||||||
/**
|
/**
|
||||||
* 删除单个设备
|
* 删除单个设备
|
||||||
*
|
*
|
||||||
@@ -288,4 +289,48 @@ public interface IotDeviceService {
|
|||||||
*/
|
*/
|
||||||
List<IotDeviceDO> getDeviceListByHasLocation();
|
List<IotDeviceDO> getDeviceListByHasLocation();
|
||||||
|
|
||||||
|
// ========== 网关-子设备绑定相关 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定子设备到网关
|
||||||
|
*
|
||||||
|
* @param ids 子设备编号列表
|
||||||
|
* @param gatewayId 网关设备编号
|
||||||
|
*/
|
||||||
|
void bindDeviceGateway(Collection<Long> ids, Long gatewayId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解绑子设备与网关
|
||||||
|
*
|
||||||
|
* @param ids 子设备编号列表
|
||||||
|
*/
|
||||||
|
void unbindDeviceGateway(Collection<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取可绑定到网关的子设备列表
|
||||||
|
* <p>
|
||||||
|
* 条件:设备类型为 GATEWAY_SUB 且未绑定任何网关
|
||||||
|
*
|
||||||
|
* @param gatewayId 网关设备编号(可选,用于包含已绑定到该网关的设备)
|
||||||
|
* @return 子设备列表
|
||||||
|
*/
|
||||||
|
List<IotDeviceDO> getBindableSubDeviceList(@Nullable Long gatewayId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据网关编号获取子设备列表
|
||||||
|
*
|
||||||
|
* @param gatewayId 网关设备编号
|
||||||
|
* @return 子设备列表
|
||||||
|
*/
|
||||||
|
List<IotDeviceDO> getDeviceListByGatewayId(Long gatewayId);
|
||||||
|
|
||||||
|
// TODO @AI:暂时用不到,可以删除。
|
||||||
|
/**
|
||||||
|
* 根据网关编号获取子设备数量
|
||||||
|
*
|
||||||
|
* @param gatewayId 网关设备编号
|
||||||
|
* @return 子设备数量
|
||||||
|
*/
|
||||||
|
Long getDeviceCountByGatewayId(Long gatewayId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -514,6 +514,85 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
|||||||
return deviceMapper.selectListByHasLocation();
|
return deviceMapper.selectListByHasLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== 网关-子设备绑定相关 ==========
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void bindDeviceGateway(Collection<Long> ids, Long gatewayId) {
|
||||||
|
if (CollUtil.isEmpty(ids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO @AI:校验应该是 1.1、1.2 统一风格;
|
||||||
|
// 1. 校验网关设备存在且类型正确
|
||||||
|
validateGatewayDeviceExists(gatewayId);
|
||||||
|
|
||||||
|
// 2. 校验并绑定每个子设备
|
||||||
|
List<IotDeviceDO> devices = deviceMapper.selectByIds(ids);
|
||||||
|
if (devices.size() != ids.size()) {
|
||||||
|
throw exception(DEVICE_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IotDeviceDO> updateList = new ArrayList<>();
|
||||||
|
for (IotDeviceDO device : devices) {
|
||||||
|
// 2.1 校验是否为子设备类型
|
||||||
|
if (!IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType())) {
|
||||||
|
throw exception(DEVICE_NOT_GATEWAY_SUB);
|
||||||
|
}
|
||||||
|
// 2.2 校验是否已绑定其他网关
|
||||||
|
if (device.getGatewayId() != null && !device.getGatewayId().equals(gatewayId)) {
|
||||||
|
throw exception(DEVICE_GATEWAY_BINDTO_EXISTS);
|
||||||
|
}
|
||||||
|
updateList.add(new IotDeviceDO().setId(device.getId()).setGatewayId(gatewayId));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 批量更新数据库
|
||||||
|
// TODO @AI:List<IotDeviceDO> updateList 直接 convertList,不用上面 for 里面搞;校验是校验,插入是插入;
|
||||||
|
deviceMapper.updateBatch(updateList);
|
||||||
|
|
||||||
|
// 4. 清空对应缓存
|
||||||
|
deleteDeviceCache(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public void unbindDeviceGateway(Collection<Long> ids) {
|
||||||
|
if (CollUtil.isEmpty(ids)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 1. 校验设备存在
|
||||||
|
List<IotDeviceDO> devices = deviceMapper.selectByIds(ids);
|
||||||
|
if (devices.size() != ids.size()) {
|
||||||
|
throw exception(DEVICE_NOT_EXISTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 批量更新数据库(将 gatewayId 设置为 null)
|
||||||
|
List<IotDeviceDO> updateList = devices.stream()
|
||||||
|
.filter(device -> device.getGatewayId() != null)
|
||||||
|
.map(device -> new IotDeviceDO().setId(device.getId()).setGatewayId(null))
|
||||||
|
.toList();
|
||||||
|
if (CollUtil.isNotEmpty(updateList)) {
|
||||||
|
deviceMapper.updateBatch(updateList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 清空对应缓存
|
||||||
|
deleteDeviceCache(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IotDeviceDO> getBindableSubDeviceList(@Nullable Long gatewayId) {
|
||||||
|
return deviceMapper.selectBindableSubDeviceList(gatewayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IotDeviceDO> getDeviceListByGatewayId(Long gatewayId) {
|
||||||
|
return deviceMapper.selectListByGatewayId(gatewayId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getDeviceCountByGatewayId(Long gatewayId) {
|
||||||
|
return deviceMapper.selectCountByGatewayId(gatewayId);
|
||||||
|
}
|
||||||
|
|
||||||
private IotDeviceServiceImpl getSelf() {
|
private IotDeviceServiceImpl getSelf() {
|
||||||
return SpringUtil.getBean(getClass());
|
return SpringUtil.getBean(getClass());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user