Commit b4013e9a6c057f18dd42cc5ef867af3db730ffa4
1 parent
4b1dfad0
feat(iot):【网关设备:30%】增加网关设备绑定能力(优化代码),基于 optimized-pondering-dragon.md 规划
Showing
9 changed files
with
73 additions
and
91 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
| ... | ... | @@ -96,35 +96,20 @@ public class IotDeviceController { |
| 96 | 96 | })); |
| 97 | 97 | } |
| 98 | 98 | |
| 99 | - // TODO @AI:希望改成“未绑定的”。需要剔除已经绑定,包括自己的; | |
| 100 | - // TODO @AI:不需要传递 gatewayId; | |
| 101 | - // TODO @AI:需要分页; | |
| 102 | - @GetMapping("/bindable-sub-device-list") | |
| 103 | - @Operation(summary = "获取可绑定到网关的子设备列表") | |
| 104 | - @Parameter(name = "gatewayId", description = "网关设备编号(可选)", example = "1") | |
| 99 | + @GetMapping("/unbound-sub-device-page") | |
| 100 | + @Operation(summary = "获取未绑定网关的子设备分页") | |
| 105 | 101 | @PreAuthorize("@ss.hasPermission('iot:device:query')") |
| 106 | - public CommonResult<List<IotDeviceRespVO>> getBindableSubDeviceList( | |
| 107 | - @RequestParam(value = "gatewayId", required = false) Long gatewayId) { | |
| 108 | - List<IotDeviceDO> list = deviceService.getBindableSubDeviceList(gatewayId); | |
| 109 | - if (CollUtil.isEmpty(list)) { | |
| 110 | - return success(Collections.emptyList()); | |
| 102 | + public CommonResult<PageResult<IotDeviceRespVO>> getUnboundSubDevicePage(@Valid IotDevicePageReqVO pageReqVO) { | |
| 103 | + PageResult<IotDeviceDO> pageResult = deviceService.getUnboundSubDevicePage(pageReqVO); | |
| 104 | + if (CollUtil.isEmpty(pageResult.getList())) { | |
| 105 | + return success(PageResult.empty()); | |
| 111 | 106 | } |
| 112 | 107 | |
| 113 | 108 | // 补充产品名称 |
| 114 | 109 | Map<Long, IotProductDO> productMap = convertMap(productService.getProductList(), IotProductDO::getId); |
| 115 | - return success(convertList(list, device -> { | |
| 116 | - // TODO @AI:可以 beanutils 转换么? | |
| 117 | - IotDeviceRespVO respVO = new IotDeviceRespVO() | |
| 118 | - .setId(device.getId()) | |
| 119 | - .setDeviceName(device.getDeviceName()) | |
| 120 | - .setNickname(device.getNickname()) | |
| 121 | - .setProductId(device.getProductId()) | |
| 122 | - .setState(device.getState()) | |
| 123 | - .setGatewayId(device.getGatewayId()); | |
| 124 | - MapUtils.findAndThen(productMap, device.getProductId(), | |
| 125 | - product -> respVO.setProductName(product.getName())); | |
| 126 | - return respVO; | |
| 127 | - })); | |
| 110 | + PageResult<IotDeviceRespVO> result = BeanUtils.toBean(pageResult, IotDeviceRespVO.class, device -> | |
| 111 | + MapUtils.findAndThen(productMap, device.getProductId(), product -> device.setProductName(product.getName()))); | |
| 112 | + return success(result); | |
| 128 | 113 | } |
| 129 | 114 | |
| 130 | 115 | @PutMapping("/update-group") | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/product/IotProductController.java
| ... | ... | @@ -143,11 +143,13 @@ public class IotProductController { |
| 143 | 143 | |
| 144 | 144 | @GetMapping("/simple-list") |
| 145 | 145 | @Operation(summary = "获取产品的精简信息列表", description = "主要用于前端的下拉选项") |
| 146 | - public CommonResult<List<IotProductRespVO>> getProductSimpleList() { | |
| 147 | - List<IotProductDO> list = productService.getProductList(); | |
| 148 | - return success(convertList(list, product -> // 只返回 id、name 字段 | |
| 146 | + @Parameter(name = "deviceType", description = "设备类型", example = "1") | |
| 147 | + public CommonResult<List<IotProductRespVO>> getProductSimpleList( | |
| 148 | + @RequestParam(value = "deviceType", required = false) Integer deviceType) { | |
| 149 | + List<IotProductDO> list = productService.getProductList(deviceType); | |
| 150 | + return success(convertList(list, product -> // 只返回 id、name、productKey 字段 | |
| 149 | 151 | new IotProductRespVO().setId(product.getId()).setName(product.getName()).setStatus(product.getStatus()) |
| 150 | - .setDeviceType(product.getDeviceType()))); | |
| 152 | + .setDeviceType(product.getDeviceType()).setProductKey(product.getProductKey()))); | |
| 151 | 153 | } |
| 152 | 154 | |
| 153 | 155 | } |
| 154 | 156 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java
| ... | ... | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 6 | 6 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 7 | 7 | import cn.iocoder.yudao.module.iot.controller.admin.device.vo.device.IotDevicePageReqVO; |
| 8 | 8 | import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; |
| 9 | +import cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum; | |
| 9 | 10 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| 10 | 11 | import jakarta.annotation.Nullable; |
| 11 | 12 | import org.apache.ibatis.annotations.Mapper; |
| ... | ... | @@ -142,20 +143,20 @@ public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> { |
| 142 | 143 | } |
| 143 | 144 | |
| 144 | 145 | /** |
| 145 | - * 查询可绑定到网关的子设备列表 | |
| 146 | - * <p> | |
| 147 | - * 条件:设备类型为 GATEWAY_SUB 且未绑定任何网关,或已绑定到指定网关 | |
| 146 | + * 分页查询未绑定网关的子设备 | |
| 148 | 147 | * |
| 149 | - * @param gatewayId 网关设备编号(可选,用于包含已绑定到该网关的设备) | |
| 150 | - * @return 子设备列表 | |
| 148 | + * @param reqVO 分页查询参数 | |
| 149 | + * @return 子设备分页 | |
| 151 | 150 | */ |
| 152 | - default List<IotDeviceDO> selectBindableSubDeviceList(@Nullable Long gatewayId) { | |
| 153 | - return selectList(new LambdaQueryWrapperX<IotDeviceDO>() | |
| 154 | - .eq(IotDeviceDO::getDeviceType, cn.iocoder.yudao.module.iot.enums.product.IotProductDeviceTypeEnum.GATEWAY_SUB.getType()) | |
| 155 | - .and(wrapper -> wrapper | |
| 156 | - .isNull(IotDeviceDO::getGatewayId))); | |
| 157 | -// .or() | |
| 158 | -// .eqIfPresent(IotDeviceDO::getGatewayId, gatewayId))) | |
| 151 | + default PageResult<IotDeviceDO> selectUnboundSubDevicePage(IotDevicePageReqVO reqVO) { | |
| 152 | + return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>() | |
| 153 | + .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName()) | |
| 154 | + .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname()) | |
| 155 | + .eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId()) | |
| 156 | + // 仅查询子设备 + 未绑定网关 | |
| 157 | + .eq(IotDeviceDO::getDeviceType, IotProductDeviceTypeEnum.GATEWAY_SUB.getType()) | |
| 158 | + .isNull(IotDeviceDO::getGatewayId) | |
| 159 | + .orderByDesc(IotDeviceDO::getId)); | |
| 159 | 160 | } |
| 160 | 161 | |
| 161 | 162 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/product/IotProductMapper.java
| ... | ... | @@ -27,6 +27,12 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> { |
| 27 | 27 | .orderByDesc(IotProductDO::getId)); |
| 28 | 28 | } |
| 29 | 29 | |
| 30 | + default List<IotProductDO> selectList(Integer deviceType) { | |
| 31 | + return selectList(new LambdaQueryWrapperX<IotProductDO>() | |
| 32 | + .eqIfPresent(IotProductDO::getDeviceType, deviceType) | |
| 33 | + .orderByDesc(IotProductDO::getId)); | |
| 34 | + } | |
| 35 | + | |
| 30 | 36 | default IotProductDO selectByProductKey(String productKey) { |
| 31 | 37 | return selectOne(new LambdaQueryWrapper<IotProductDO>() |
| 32 | 38 | .apply("LOWER(product_key) = {0}", productKey.toLowerCase())); |
| ... | ... | @@ -37,5 +43,4 @@ public interface IotProductMapper extends BaseMapperX<IotProductDO> { |
| 37 | 43 | .geIfPresent(IotProductDO::getCreateTime, createTime)); |
| 38 | 44 | } |
| 39 | 45 | |
| 40 | - | |
| 41 | 46 | } |
| 42 | 47 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
| ... | ... | @@ -26,19 +26,15 @@ public interface ErrorCodeConstants { |
| 26 | 26 | // ========== 设备 1-050-003-000 ============ |
| 27 | 27 | ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在"); |
| 28 | 28 | ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一"); |
| 29 | - ErrorCode DEVICE_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除"); | |
| 29 | + ErrorCode DEVICE_GATEWAY_HAS_SUB = new ErrorCode(1_050_003_002, "网关设备存在已绑定的子设备,不允许删除"); | |
| 30 | 30 | ErrorCode DEVICE_KEY_EXISTS = new ErrorCode(1_050_003_003, "设备标识已经存在"); |
| 31 | 31 | ErrorCode DEVICE_GATEWAY_NOT_EXISTS = new ErrorCode(1_050_003_004, "网关设备不存在"); |
| 32 | 32 | ErrorCode DEVICE_NOT_GATEWAY = new ErrorCode(1_050_003_005, "设备不是网关设备"); |
| 33 | 33 | ErrorCode DEVICE_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_050_003_006, "导入设备数据不能为空!"); |
| 34 | 34 | ErrorCode DEVICE_DOWNSTREAM_FAILED_SERVER_ID_NULL = new ErrorCode(1_050_003_007, "下行设备消息失败,原因:设备未连接网关"); |
| 35 | 35 | ErrorCode DEVICE_SERIAL_NUMBER_EXISTS = new ErrorCode(1_050_003_008, "设备序列号已存在,序列号必须全局唯一"); |
| 36 | - // TODO @AI:1_050_003_009 需要提示具体的哪个设备。产品/设备,标识下 | |
| 37 | - ErrorCode DEVICE_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_009, "设备不是网关子设备类型,无法绑定到网关"); | |
| 38 | - // TODO @AI:1_050_003_009 需要提示具体的哪个设备。产品/设备,标识下 | |
| 39 | - ErrorCode DEVICE_GATEWAY_BINDTO_EXISTS = new ErrorCode(1_050_003_010, "设备已绑定到其他网关,请先解绑"); | |
| 40 | - // TODO @AI:是不是可以删除,DEVICE_GATEWAY_BINDTO_NOT_EXISTS | |
| 41 | - ErrorCode DEVICE_GATEWAY_BINDTO_NOT_EXISTS = new ErrorCode(1_050_003_011, "设备未绑定到任何网关"); | |
| 36 | + ErrorCode DEVICE_NOT_GATEWAY_SUB = new ErrorCode(1_050_003_009, "设备【{}/{}】不是网关子设备类型,无法绑定到网关"); | |
| 37 | + ErrorCode DEVICE_GATEWAY_BINDTO_EXISTS = new ErrorCode(1_050_003_010, "设备【{}/{}】已绑定到其他网关,请先解绑"); | |
| 42 | 38 | |
| 43 | 39 | // ========== 产品分类 1-050-004-000 ========== |
| 44 | 40 | ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_050_004_000, "产品分类不存在"); | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
| ... | ... | @@ -73,7 +73,6 @@ public interface IotDeviceService { |
| 73 | 73 | */ |
| 74 | 74 | void updateDeviceGroup(@Valid IotDeviceUpdateGroupReqVO updateReqVO); |
| 75 | 75 | |
| 76 | - // TODO @AI:网关设备被删除时,需要查看是否有子设备绑定。如果有,则不允许删除。 | |
| 77 | 76 | /** |
| 78 | 77 | * 删除单个设备 |
| 79 | 78 | * |
| ... | ... | @@ -307,14 +306,12 @@ public interface IotDeviceService { |
| 307 | 306 | void unbindDeviceGateway(Collection<Long> ids); |
| 308 | 307 | |
| 309 | 308 | /** |
| 310 | - * 获取可绑定到网关的子设备列表 | |
| 311 | - * <p> | |
| 312 | - * 条件:设备类型为 GATEWAY_SUB 且未绑定任何网关 | |
| 309 | + * 获取未绑定网关的子设备分页 | |
| 313 | 310 | * |
| 314 | - * @param gatewayId 网关设备编号(可选,用于包含已绑定到该网关的设备) | |
| 315 | - * @return 子设备列表 | |
| 311 | + * @param pageReqVO 分页查询参数(仅使用 productId、deviceName、nickname) | |
| 312 | + * @return 子设备分页 | |
| 316 | 313 | */ |
| 317 | - List<IotDeviceDO> getBindableSubDeviceList(@Nullable Long gatewayId); | |
| 314 | + PageResult<IotDeviceDO> getUnboundSubDevicePage(IotDevicePageReqVO pageReqVO); | |
| 318 | 315 | |
| 319 | 316 | /** |
| 320 | 317 | * 根据网关编号获取子设备列表 |
| ... | ... | @@ -324,13 +321,4 @@ public interface IotDeviceService { |
| 324 | 321 | */ |
| 325 | 322 | List<IotDeviceDO> getDeviceListByGatewayId(Long gatewayId); |
| 326 | 323 | |
| 327 | - // TODO @AI:暂时用不到,可以删除。 | |
| 328 | - /** | |
| 329 | - * 根据网关编号获取子设备数量 | |
| 330 | - * | |
| 331 | - * @param gatewayId 网关设备编号 | |
| 332 | - * @return 子设备数量 | |
| 333 | - */ | |
| 334 | - Long getDeviceCountByGatewayId(Long gatewayId); | |
| 335 | - | |
| 336 | 324 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceServiceImpl.java
| ... | ... | @@ -170,9 +170,10 @@ public class IotDeviceServiceImpl implements IotDeviceService { |
| 170 | 170 | public void deleteDevice(Long id) { |
| 171 | 171 | // 1.1 校验存在 |
| 172 | 172 | IotDeviceDO device = validateDeviceExists(id); |
| 173 | - // 1.2 如果是网关设备,检查是否有子设备 | |
| 174 | - if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(id) > 0) { | |
| 175 | - throw exception(DEVICE_HAS_CHILDREN); | |
| 173 | + // 1.2 如果是网关设备,检查是否有子设备绑定 | |
| 174 | + if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType()) | |
| 175 | + && deviceMapper.selectCountByGatewayId(id) > 0) { | |
| 176 | + throw exception(DEVICE_GATEWAY_HAS_SUB); | |
| 176 | 177 | } |
| 177 | 178 | |
| 178 | 179 | // 2. 删除设备 |
| ... | ... | @@ -193,10 +194,11 @@ public class IotDeviceServiceImpl implements IotDeviceService { |
| 193 | 194 | if (CollUtil.isEmpty(devices)) { |
| 194 | 195 | return; |
| 195 | 196 | } |
| 196 | - // 1.2 校验网关设备是否存在 | |
| 197 | + // 1.2 如果是网关设备,检查是否有子设备绑定 | |
| 197 | 198 | for (IotDeviceDO device : devices) { |
| 198 | - if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(device.getId()) > 0) { | |
| 199 | - throw exception(DEVICE_HAS_CHILDREN); | |
| 199 | + if (IotProductDeviceTypeEnum.isGateway(device.getDeviceType()) | |
| 200 | + && deviceMapper.selectCountByGatewayId(device.getId()) > 0) { | |
| 201 | + throw exception(DEVICE_GATEWAY_HAS_SUB); | |
| 200 | 202 | } |
| 201 | 203 | } |
| 202 | 204 | |
| ... | ... | @@ -522,34 +524,29 @@ public class IotDeviceServiceImpl implements IotDeviceService { |
| 522 | 524 | if (CollUtil.isEmpty(ids)) { |
| 523 | 525 | return; |
| 524 | 526 | } |
| 525 | - // TODO @AI:校验应该是 1.1、1.2 统一风格; | |
| 526 | - // 1. 校验网关设备存在且类型正确 | |
| 527 | + // 1.1 校验网关设备存在且类型正确 | |
| 527 | 528 | validateGatewayDeviceExists(gatewayId); |
| 528 | - | |
| 529 | - // 2. 校验并绑定每个子设备 | |
| 529 | + // 1.2 校验子设备存在 | |
| 530 | 530 | List<IotDeviceDO> devices = deviceMapper.selectByIds(ids); |
| 531 | 531 | if (devices.size() != ids.size()) { |
| 532 | 532 | throw exception(DEVICE_NOT_EXISTS); |
| 533 | 533 | } |
| 534 | - | |
| 535 | - List<IotDeviceDO> updateList = new ArrayList<>(); | |
| 534 | + // 1.3 校验每个设备是否可绑定 | |
| 536 | 535 | for (IotDeviceDO device : devices) { |
| 537 | - // 2.1 校验是否为子设备类型 | |
| 538 | 536 | if (!IotProductDeviceTypeEnum.isGatewaySub(device.getDeviceType())) { |
| 539 | - throw exception(DEVICE_NOT_GATEWAY_SUB); | |
| 537 | + throw exception(DEVICE_NOT_GATEWAY_SUB, device.getProductKey(), device.getDeviceName()); | |
| 540 | 538 | } |
| 541 | - // 2.2 校验是否已绑定其他网关 | |
| 542 | 539 | if (device.getGatewayId() != null && !device.getGatewayId().equals(gatewayId)) { |
| 543 | - throw exception(DEVICE_GATEWAY_BINDTO_EXISTS); | |
| 540 | + throw exception(DEVICE_GATEWAY_BINDTO_EXISTS, device.getProductKey(), device.getDeviceName()); | |
| 544 | 541 | } |
| 545 | - updateList.add(new IotDeviceDO().setId(device.getId()).setGatewayId(gatewayId)); | |
| 546 | 542 | } |
| 547 | 543 | |
| 548 | - // 3. 批量更新数据库 | |
| 549 | - // TODO @AI:List<IotDeviceDO> updateList 直接 convertList,不用上面 for 里面搞;校验是校验,插入是插入; | |
| 544 | + // 2. 批量更新数据库 | |
| 545 | + List<IotDeviceDO> updateList = convertList(devices, device -> | |
| 546 | + new IotDeviceDO().setId(device.getId()).setGatewayId(gatewayId)); | |
| 550 | 547 | deviceMapper.updateBatch(updateList); |
| 551 | 548 | |
| 552 | - // 4. 清空对应缓存 | |
| 549 | + // 3. 清空对应缓存 | |
| 553 | 550 | deleteDeviceCache(devices); |
| 554 | 551 | } |
| 555 | 552 | |
| ... | ... | @@ -579,8 +576,8 @@ public class IotDeviceServiceImpl implements IotDeviceService { |
| 579 | 576 | } |
| 580 | 577 | |
| 581 | 578 | @Override |
| 582 | - public List<IotDeviceDO> getBindableSubDeviceList(@Nullable Long gatewayId) { | |
| 583 | - return deviceMapper.selectBindableSubDeviceList(gatewayId); | |
| 579 | + public PageResult<IotDeviceDO> getUnboundSubDevicePage(IotDevicePageReqVO pageReqVO) { | |
| 580 | + return deviceMapper.selectUnboundSubDevicePage(pageReqVO); | |
| 584 | 581 | } |
| 585 | 582 | |
| 586 | 583 | @Override |
| ... | ... | @@ -588,11 +585,6 @@ public class IotDeviceServiceImpl implements IotDeviceService { |
| 588 | 585 | return deviceMapper.selectListByGatewayId(gatewayId); |
| 589 | 586 | } |
| 590 | 587 | |
| 591 | - @Override | |
| 592 | - public Long getDeviceCountByGatewayId(Long gatewayId) { | |
| 593 | - return deviceMapper.selectCountByGatewayId(gatewayId); | |
| 594 | - } | |
| 595 | - | |
| 596 | 588 | private IotDeviceServiceImpl getSelf() { |
| 597 | 589 | return SpringUtil.getBean(getClass()); |
| 598 | 590 | } | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductService.java
| ... | ... | @@ -106,6 +106,14 @@ public interface IotProductService { |
| 106 | 106 | List<IotProductDO> getProductList(); |
| 107 | 107 | |
| 108 | 108 | /** |
| 109 | + * 根据设备类型获得产品列表 | |
| 110 | + * | |
| 111 | + * @param deviceType 设备类型(可选) | |
| 112 | + * @return 产品列表 | |
| 113 | + */ | |
| 114 | + List<IotProductDO> getProductList(@Nullable Integer deviceType); | |
| 115 | + | |
| 116 | + /** | |
| 109 | 117 | * 获得产品数量 |
| 110 | 118 | * |
| 111 | 119 | * @param createTime 创建时间,如果为空,则统计所有产品数量 | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
| ... | ... | @@ -158,6 +158,11 @@ public class IotProductServiceImpl implements IotProductService { |
| 158 | 158 | } |
| 159 | 159 | |
| 160 | 160 | @Override |
| 161 | + public List<IotProductDO> getProductList(Integer deviceType) { | |
| 162 | + return productMapper.selectList(deviceType); | |
| 163 | + } | |
| 164 | + | |
| 165 | + @Override | |
| 161 | 166 | public Long getProductCount(LocalDateTime createTime) { |
| 162 | 167 | return productMapper.selectCountByCreateTime(createTime); |
| 163 | 168 | } | ... | ... |