mirror of
https://github.com/YunaiV/ruoyi-vue-pro.git
synced 2026-03-30 04:49:43 +00:00
feat(iot):【网关设备:30%】增加网关设备绑定能力(优化代码),基于 optimized-pondering-dragon.md 规划
This commit is contained in:
@@ -96,35 +96,20 @@ public class IotDeviceController {
|
||||
}));
|
||||
}
|
||||
|
||||
// TODO @AI:希望改成“未绑定的”。需要剔除已经绑定,包括自己的;
|
||||
// TODO @AI:不需要传递 gatewayId;
|
||||
// TODO @AI:需要分页;
|
||||
@GetMapping("/bindable-sub-device-list")
|
||||
@Operation(summary = "获取可绑定到网关的子设备列表")
|
||||
@Parameter(name = "gatewayId", description = "网关设备编号(可选)", example = "1")
|
||||
@GetMapping("/unbound-sub-device-page")
|
||||
@Operation(summary = "获取未绑定网关的子设备分页")
|
||||
@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());
|
||||
public CommonResult<PageResult<IotDeviceRespVO>> getUnboundSubDevicePage(@Valid IotDevicePageReqVO pageReqVO) {
|
||||
PageResult<IotDeviceDO> pageResult = deviceService.getUnboundSubDevicePage(pageReqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
|
||||
// 补充产品名称
|
||||
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;
|
||||
}));
|
||||
PageResult<IotDeviceRespVO> result = BeanUtils.toBean(pageResult, IotDeviceRespVO.class, device ->
|
||||
MapUtils.findAndThen(productMap, device.getProductId(), product -> device.setProductName(product.getName())));
|
||||
return success(result);
|
||||
}
|
||||
|
||||
@PutMapping("/update-group")
|
||||
|
||||
@@ -143,11 +143,13 @@ public class IotProductController {
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项")
|
||||
public CommonResult<List<IotProductRespVO>> getProductSimpleList() {
|
||||
List<IotProductDO> list = productService.getProductList();
|
||||
return success(convertList(list, product -> // 只返回 id、name 字段
|
||||
@Parameter(name = "deviceType", description = "设备类型", example = "1")
|
||||
public CommonResult<List<IotProductRespVO>> getProductSimpleList(
|
||||
@RequestParam(value = "deviceType", required = false) Integer deviceType) {
|
||||
List<IotProductDO> list = productService.getProductList(deviceType);
|
||||
return success(convertList(list, product -> // 只返回 id、name、productKey 字段
|
||||
new IotProductRespVO().setId(product.getId()).setName(product.getName()).setStatus(product.getStatus())
|
||||
.setDeviceType(product.getDeviceType())));
|
||||
.setDeviceType(product.getDeviceType()).setProductKey(product.getProductKey())));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import jakarta.annotation.Nullable;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
@@ -142,20 +143,20 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询可绑定到网关的子设备列表
|
||||
* <p>
|
||||
* 条件:设备类型为 GATEWAY_SUB 且未绑定任何网关,或已绑定到指定网关
|
||||
* 分页查询未绑定网关的子设备
|
||||
*
|
||||
* @param gatewayId 网关设备编号(可选,用于包含已绑定到该网关的设备)
|
||||
* @return 子设备列表
|
||||
* @param reqVO 分页查询参数
|
||||
* @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)))
|
||||
default PageResult<IotDeviceDO> selectUnboundSubDevicePage(IotDevicePageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>()
|
||||
.likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName())
|
||||
.likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname())
|
||||
.eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId())
|
||||
// 仅查询子设备 + 未绑定网关
|
||||
.eq(IotDeviceDO::getDeviceType, IotProductDeviceTypeEnum.GATEWAY_SUB.getType())
|
||||
.isNull(IotDeviceDO::getGatewayId)
|
||||
.orderByDesc(IotDeviceDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,12 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> {
|
||||
.orderByDesc(IotProductDO::getId));
|
||||
}
|
||||
|
||||
default List<IotProductDO> selectList(Integer deviceType) {
|
||||
return selectList(new LambdaQueryWrapperX<IotProductDO>()
|
||||
.eqIfPresent(IotProductDO::getDeviceType, deviceType)
|
||||
.orderByDesc(IotProductDO::getId));
|
||||
}
|
||||
|
||||
default IotProductDO selectByProductKey(String productKey) {
|
||||
return selectOne(new LambdaQueryWrapper<IotProductDO>()
|
||||
.apply("LOWER(product_key) = {0}", productKey.toLowerCase()));
|
||||
@@ -37,5 +43,4 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> {
|
||||
.geIfPresent(IotProductDO::getCreateTime, createTime));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -26,19 +26,15 @@ public interface ErrorCodeConstants {
|
||||
// ========== 设备 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_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除");
|
||||
ErrorCode DEVICE_GATEWAY_HAS_SUB = new ErrorCode(1_050_003_002, "网关设备存在已绑定的子设备,不允许删除");
|
||||
ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, "设备标识已经存在");
|
||||
ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在");
|
||||
ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备");
|
||||
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_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, "设备未绑定到任何网关");
|
||||
ErrorCode DEVICE_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_009, "设备【{}/{}】不是网关子设备类型,无法绑定到网关");
|
||||
ErrorCode DEVICE_GATEWAY_BINDTO_EXISTS = new ErrorCode(1_050_003_010, "设备【{}/{}】已绑定到其他网关,请先解绑");
|
||||
|
||||
// ========== 产品分类 1-050-004-000 ==========
|
||||
ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在");
|
||||
|
||||
@@ -73,7 +73,6 @@ public interface IotDeviceService {
|
||||
*/
|
||||
void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO);
|
||||
|
||||
// TODO @AI:网关设备被删除时,需要查看是否有子设备绑定。如果有,则不允许删除。
|
||||
/**
|
||||
* 删除单个设备
|
||||
*
|
||||
@@ -307,14 +306,12 @@ public interface IotDeviceService {
|
||||
void unbindDeviceGateway(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 获取可绑定到网关的子设备列表
|
||||
* <p>
|
||||
* 条件:设备类型为 GATEWAY_SUB 且未绑定任何网关
|
||||
* 获取未绑定网关的子设备分页
|
||||
*
|
||||
* @param gatewayId 网关设备编号(可选,用于包含已绑定到该网关的设备)
|
||||
* @return 子设备列表
|
||||
* @param pageReqVO 分页查询参数(仅使用 productId、deviceName、nickname)
|
||||
* @return 子设备分页
|
||||
*/
|
||||
List<IotDeviceDO> getBindableSubDeviceList(@Nullable Long gatewayId);
|
||||
PageResult<IotDeviceDO> getUnboundSubDevicePage(IotDevicePageReqVO pageReqVO);
|
||||
|
||||
/**
|
||||
* 根据网关编号获取子设备列表
|
||||
@@ -324,13 +321,4 @@ public interface IotDeviceService {
|
||||
*/
|
||||
List<IotDeviceDO> getDeviceListByGatewayId(Long gatewayId);
|
||||
|
||||
// TODO @AI:暂时用不到,可以删除。
|
||||
/**
|
||||
* 根据网关编号获取子设备数量
|
||||
*
|
||||
* @param gatewayId 网关设备编号
|
||||
* @return 子设备数量
|
||||
*/
|
||||
Long getDeviceCountByGatewayId(Long gatewayId);
|
||||
|
||||
}
|
||||
|
||||
@@ -170,9 +170,10 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
public void deleteDevice(Long id) {
|
||||
// 1.1 校验存在
|
||||
IotDeviceDO device = validateDeviceExists(id);
|
||||
// 1.2 如果是网关设备,检查是否有子设备
|
||||
if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(id) > 0) {
|
||||
throw exception(DEVICE_HAS_CHILDREN);
|
||||
// 1.2 如果是网关设备,检查是否有子设备绑定
|
||||
if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())
|
||||
&& deviceMapper.selectCountByGatewayId(id) > 0) {
|
||||
throw exception(DEVICE_GATEWAY_HAS_SUB);
|
||||
}
|
||||
|
||||
// 2. 删除设备
|
||||
@@ -193,10 +194,11 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
if (CollUtil.isEmpty(devices)) {
|
||||
return;
|
||||
}
|
||||
// 1.2 校验网关设备是否存在
|
||||
// 1.2 如果是网关设备,检查是否有子设备绑定
|
||||
for (IotDeviceDO device : devices) {
|
||||
if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(device.getId()) > 0) {
|
||||
throw exception(DEVICE_HAS_CHILDREN);
|
||||
if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType())
|
||||
&& deviceMapper.selectCountByGatewayId(device.getId()) > 0) {
|
||||
throw exception(DEVICE_GATEWAY_HAS_SUB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,34 +524,29 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return;
|
||||
}
|
||||
// TODO @AI:校验应该是 1.1、1.2 统一风格;
|
||||
// 1. 校验网关设备存在且类型正确
|
||||
// 1.1 校验网关设备存在且类型正确
|
||||
validateGatewayDeviceExists(gatewayId);
|
||||
|
||||
// 2. 校验并绑定每个子设备
|
||||
// 1.2 校验子设备存在
|
||||
List<IotDeviceDO> devices = deviceMapper.selectByIds(ids);
|
||||
if (devices.size() != ids.size()) {
|
||||
throw exception(DEVICE_NOT_EXISTS);
|
||||
}
|
||||
|
||||
List<IotDeviceDO> updateList = new ArrayList<>();
|
||||
// 1.3 校验每个设备是否可绑定
|
||||
for (IotDeviceDO device : devices) {
|
||||
// 2.1 校验是否为子设备类型
|
||||
if (!IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType())) {
|
||||
throw exception(DEVICE_NOT_GATEWAY_SUB);
|
||||
throw exception(DEVICE_NOT_GATEWAY_SUB, device.getProductKey(), device.getDeviceName());
|
||||
}
|
||||
// 2.2 校验是否已绑定其他网关
|
||||
if (device.getGatewayId() != null && !device.getGatewayId().equals(gatewayId)) {
|
||||
throw exception(DEVICE_GATEWAY_BINDTO_EXISTS);
|
||||
throw exception(DEVICE_GATEWAY_BINDTO_EXISTS, device.getProductKey(), device.getDeviceName());
|
||||
}
|
||||
updateList.add(new IotDeviceDO().setId(device.getId()).setGatewayId(gatewayId));
|
||||
}
|
||||
|
||||
// 3. 批量更新数据库
|
||||
// TODO @AI:List<IotDeviceDO> updateList 直接 convertList,不用上面 for 里面搞;校验是校验,插入是插入;
|
||||
// 2. 批量更新数据库
|
||||
List<IotDeviceDO> updateList = convertList(devices, device ->
|
||||
new IotDeviceDO().setId(device.getId()).setGatewayId(gatewayId));
|
||||
deviceMapper.updateBatch(updateList);
|
||||
|
||||
// 4. 清空对应缓存
|
||||
// 3. 清空对应缓存
|
||||
deleteDeviceCache(devices);
|
||||
}
|
||||
|
||||
@@ -579,8 +576,8 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IotDeviceDO> getBindableSubDeviceList(@Nullable Long gatewayId) {
|
||||
return deviceMapper.selectBindableSubDeviceList(gatewayId);
|
||||
public PageResult<IotDeviceDO> getUnboundSubDevicePage(IotDevicePageReqVO pageReqVO) {
|
||||
return deviceMapper.selectUnboundSubDevicePage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -588,11 +585,6 @@ public class IotDeviceServiceImpl implements IotDeviceService {
|
||||
return deviceMapper.selectListByGatewayId(gatewayId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getDeviceCountByGatewayId(Long gatewayId) {
|
||||
return deviceMapper.selectCountByGatewayId(gatewayId);
|
||||
}
|
||||
|
||||
private IotDeviceServiceImpl getSelf() {
|
||||
return SpringUtil.getBean(getClass());
|
||||
}
|
||||
|
||||
@@ -105,6 +105,14 @@ public interface IotProductService {
|
||||
*/
|
||||
List<IotProductDO> getProductList();
|
||||
|
||||
/**
|
||||
* 根据设备类型获得产品列表
|
||||
*
|
||||
* @param deviceType 设备类型(可选)
|
||||
* @return 产品列表
|
||||
*/
|
||||
List<IotProductDO> getProductList(@Nullable Integer deviceType);
|
||||
|
||||
/**
|
||||
* 获得产品数量
|
||||
*
|
||||
|
||||
@@ -157,6 +157,11 @@ public class IotProductServiceImpl implements IotProductService {
|
||||
return productMapper.selectList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IotProductDO> getProductList(Integer deviceType) {
|
||||
return productMapper.selectList(deviceType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getProductCount(LocalDateTime createTime) {
|
||||
return productMapper.selectCountByCreateTime(createTime);
|
||||
|
||||
Reference in New Issue
Block a user