From 9536ae19b1233c73b2b53b97255243af2f907bb5 Mon Sep 17 00:00:00 2001 From: George Date: Mon, 9 Mar 2026 23:18:35 +0800 Subject: [PATCH] =?UTF-8?q?20260309:=E8=AE=BE=E5=A4=87=E6=89=B9=E9=87=8F?= =?UTF-8?q?=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/device/DeviceController.java | 12 + .../http/map/BaiduILocationService.java | 3 +- .../gateWay/SubDeviceCreateByProductVO.java | 36 ++ .../iot/model/gateWay/SubDeviceListVO.java | 6 + .../fastbee/iot/service/IDeviceService.java | 9 + .../iot/service/impl/DeviceServiceImpl.java | 104 +++++ .../resources/mapper/iot/SubGatewayMapper.xml | 11 +- yack-web/src/api/iot/gateway.js | 9 + yack-web/src/lang/en-US/device.json | 20 +- yack-web/src/lang/zh-CN/device.json | 20 +- yack-web/src/views/iot/device/device-sub.vue | 80 +++- .../views/iot/device/sub-device-create.vue | 355 ++++++++++++++++++ 12 files changed, 652 insertions(+), 13 deletions(-) create mode 100644 fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/gateWay/SubDeviceCreateByProductVO.java create mode 100644 yack-web/src/views/iot/device/sub-device-create.vue diff --git a/fastbee-open-api/src/main/java/com/fastbee/controller/device/DeviceController.java b/fastbee-open-api/src/main/java/com/fastbee/controller/device/DeviceController.java index adbb89d0..7861d593 100644 --- a/fastbee-open-api/src/main/java/com/fastbee/controller/device/DeviceController.java +++ b/fastbee-open-api/src/main/java/com/fastbee/controller/device/DeviceController.java @@ -21,6 +21,7 @@ import com.fastbee.iot.model.DeviceImportVO; import com.fastbee.iot.model.DeviceRelateUserInput; import com.fastbee.iot.model.SerialNumberVO; import com.fastbee.iot.model.dto.ThingsModelDTO; +import com.fastbee.iot.model.gateWay.SubDeviceCreateByProductVO; import com.fastbee.iot.service.IDeviceService; import com.github.pagehelper.PageInfo; import io.swagger.annotations.Api; @@ -493,4 +494,15 @@ public class DeviceController extends BaseController { } } + /** + * 根据子产品批量新增子设备 + */ + @PreAuthorize("@ss.hasPermi('iot:device:add')") + @Log(title = "批量新增子设备", businessType = BusinessType.INSERT) + @PostMapping("/addBatchByProduct") + @ApiOperation("根据子产品批量新增子设备") + public AjaxResult addBatchByProduct(@RequestBody SubDeviceCreateByProductVO vo) { + int count = deviceService.addSubDevicesByProducts(vo); + return AjaxResult.success("成功新增 " + count + " 个子设备"); + } } diff --git a/fastbee-plugs/fastbee-http/src/main/java/com/fastbee/http/map/BaiduILocationService.java b/fastbee-plugs/fastbee-http/src/main/java/com/fastbee/http/map/BaiduILocationService.java index fcf27f02..33d0e62f 100644 --- a/fastbee-plugs/fastbee-http/src/main/java/com/fastbee/http/map/BaiduILocationService.java +++ b/fastbee-plugs/fastbee-http/src/main/java/com/fastbee/http/map/BaiduILocationService.java @@ -16,6 +16,7 @@ import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.Objects; import static com.fastbee.common.exception.ServiceExceptionUtil.exception; @@ -43,7 +44,7 @@ public class BaiduILocationService implements ILocationService { log.info("结构化地址:{}", address); String baseUrl = FastBeeConstant.MAP.BAIDU_GEOCODING; - address = StringUtils.isEmpty(address) ? "湖南省长沙市岳麓区" : address; + address = StringUtils.isEmpty(address) || Objects.equals("内网IP", address) ? "湖南省长沙市岳麓区" : address; String encodedAddress; try { encodedAddress = URLEncoder.encode(address, StandardCharsets.UTF_8.toString()); diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/gateWay/SubDeviceCreateByProductVO.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/gateWay/SubDeviceCreateByProductVO.java new file mode 100644 index 00000000..d10c5f7d --- /dev/null +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/gateWay/SubDeviceCreateByProductVO.java @@ -0,0 +1,36 @@ +package com.fastbee.iot.model.gateWay; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; + +/** + * 根据子产品批量新增子设备VO + */ +@Data +public class SubDeviceCreateByProductVO { + @ApiModelProperty("网关设备id") + private Long gwDeviceId; + + @ApiModelProperty("子产品设备创建项") + private List items; + + @Data + public static class SubProductDeviceItem { + @ApiModelProperty("子产品id") + private Long subProductId; + + @ApiModelProperty("子设备详情列表") + private List devices; + } + + @Data + public static class DeviceDetail { + @ApiModelProperty("自定义设备名称") + private String deviceName; + + @ApiModelProperty("自定义从机地址") + private Integer slaveId; + } +} diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/gateWay/SubDeviceListVO.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/gateWay/SubDeviceListVO.java index 7c39e7ff..2ba82068 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/gateWay/SubDeviceListVO.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/model/gateWay/SubDeviceListVO.java @@ -51,4 +51,10 @@ public class SubDeviceListVO { private Date createTime; private Long subProductId; + + @ApiModelProperty("子产品名称") + private String subProductName; + + @ApiModelProperty("在线状态") + private Integer status; } diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceService.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceService.java index 66476543..0f3c7ff3 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceService.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/IDeviceService.java @@ -10,6 +10,7 @@ import com.fastbee.iot.model.*; import com.fastbee.iot.model.ThingsModels.ThingsModelShadow; import com.fastbee.common.core.thingsModel.ThingsModelValuesInput; import com.fastbee.iot.model.dto.ThingsModelDTO; +import com.fastbee.iot.model.gateWay.SubDeviceCreateByProductVO; import org.quartz.SchedulerException; import java.util.List; @@ -146,6 +147,14 @@ public interface IDeviceService { */ public Device insertDevice(Device device); + /** + * 根据子产品批量新增子设备并绑定到网关 + * + * @param vo 包含网关设备id和子产品设备创建项列表 + * @return 新增的子设备数量 + */ + public int addSubDevicesByProducts(SubDeviceCreateByProductVO vo); + /** * 设备关联用户 * diff --git a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceServiceImpl.java b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceServiceImpl.java index 54f73518..db55871e 100644 --- a/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceServiceImpl.java +++ b/fastbee-service/fastbee-iot-service/src/main/java/com/fastbee/iot/service/impl/DeviceServiceImpl.java @@ -43,6 +43,7 @@ import com.fastbee.iot.model.ThingsModels.ThingsModelValueItem; import com.fastbee.iot.model.ThingsModels.ValueItem; import com.fastbee.iot.model.dto.ThingsModelDTO; import com.fastbee.iot.model.gateWay.ProductSubGatewayVO; +import com.fastbee.iot.model.gateWay.SubDeviceCreateByProductVO; import com.fastbee.iot.model.gateWay.SubDeviceListVO; import com.fastbee.iot.service.*; import com.fastbee.iot.tdengine.service.ILogService; @@ -1189,6 +1190,109 @@ public class DeviceServiceImpl implements IDeviceService { } } + /** + * 根据子产品批量新增子设备并绑定到网关(支持自定义每个设备的名称和从机地址) + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int addSubDevicesByProducts(SubDeviceCreateByProductVO vo) { + Long gwDeviceId = vo.getGwDeviceId(); + List items = vo.getItems(); + if (items == null || items.isEmpty()) { + throw new ServiceException("子产品创建项不能为空"); + } + Device gwDevice = deviceMapper.selectDeviceByDeviceId(gwDeviceId); + if (gwDevice == null) { + throw new ServiceException("网关设备不存在"); + } + Product product = productService.selectProductByProductId(gwDevice.getProductId()); + if (product == null) { + throw new ServiceException("网关产品不存在"); + } + List subProductIdList = items.stream() + .map(SubDeviceCreateByProductVO.SubProductDeviceItem::getSubProductId) + .collect(Collectors.toList()); + Map> deviceDetailMap = items.stream() + .collect(Collectors.toMap( + SubDeviceCreateByProductVO.SubProductDeviceItem::getSubProductId, + i -> i.getDevices() != null ? i.getDevices() : Collections.emptyList())); + ProductSubGateway query = new ProductSubGateway(); + query.setGwProductId(product.getProductId()); + List allSubProducts = productSubGatewayMapper.selectListVO(query); + List selectedSubProducts = allSubProducts.stream() + .filter(p -> subProductIdList.contains(p.getSubProductId())) + .sorted(Comparator.comparing(ProductSubGatewayVO::getSlaveId)) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(selectedSubProducts)) { + throw new ServiceException("未找到匹配的子产品"); + } + List productIds = selectedSubProducts.stream() + .map(ProductSubGatewayVO::getSubProductId).collect(Collectors.toList()); + LambdaQueryWrapper jobQuery = new LambdaQueryWrapper<>(); + jobQuery.in(ProductModbusJob::getProductId, productIds); + List productModbusJobList = productModbusJobMapper.selectList(jobQuery); + Map> productModbusJobMap = productModbusJobList.stream() + .collect(Collectors.groupingBy(ProductModbusJob::getProductId)); + int totalCount = 0; + for (ProductSubGatewayVO subProduct : selectedSubProducts) { + List details = + deviceDetailMap.getOrDefault(subProduct.getSubProductId(), Collections.emptyList()); + if (details.isEmpty()) { + continue; + } + for (int i = 0; i < details.size(); i++) { + SubDeviceCreateByProductVO.DeviceDetail detail = details.get(i); + String deviceName = StringUtils.isNotEmpty(detail.getDeviceName()) + ? detail.getDeviceName() + : subProduct.getSubProductName() + "_" + subProduct.getSlaveId() + "_" + (i + 1); + Integer slaveId = detail.getSlaveId() != null + ? detail.getSlaveId() + : subProduct.getSlaveId(); + Device addDevice = new Device(); + addDevice.setDeviceName(deviceName); + addDevice.setProductId(subProduct.getSubProductId()); + addDevice.setProductName(subProduct.getSubProductName()); + addDevice.setSerialNumber(this.generationDeviceNum(1)); + addDevice.setTenantId(gwDevice.getTenantId()); + addDevice.setTenantName(gwDevice.getTenantName()); + addDevice.setCreateBy(SecurityUtils.getUsername()); + addDevice.setFirmwareVersion(new BigDecimal(1)); + addDevice.setRssi(0); + addDevice.setIsShadow(0); + addDevice.setLocationWay(subProduct.getSubLocationWay()); + addDevice.setImgUrl(product.getImgUrl()); + addDevice.setCreateTime(new Date()); + deviceMapper.insertDevice(addDevice); + SubGateway subGateway = new SubGateway(); + subGateway.setGwDeviceId(gwDeviceId); + subGateway.setSubDeviceId(addDevice.getDeviceId()); + subGateway.setSlaveId(slaveId); + subGateway.setCreateBy(SecurityUtils.getUsername()); + subGateway.setCreateTime(new Date()); + subGatewayMapper.insertGateway(subGateway); + List modbusJobs = productModbusJobMap.get(subProduct.getSubProductId()); + if (!CollectionUtils.isEmpty(modbusJobs)) { + for (ProductModbusJob pmj : modbusJobs) { + ModbusJob modbusJob = new ModbusJob(); + modbusJob.setJobName(pmj.getJobName()); + modbusJob.setSubDeviceId(addDevice.getDeviceId()); + modbusJob.setSubSerialNumber(addDevice.getSerialNumber()); + modbusJob.setCommand(pmj.getCommand()); + modbusJob.setStatus(pmj.getStatus()); + modbusJob.setRemark(pmj.getRemark()); + modbusJobService.insertModbusJob(modbusJob); + if (FastBeeConstant.PROTOCOL.ModbusTcp.equals(product.getProtocolCode())) { + redisCache.getCacheModbusTcpId(gwDevice.getSerialNumber()); + } + } + } + itslValueCache.addCacheDeviceStatus(addDevice.getProductId(), addDevice.getSerialNumber()); + totalCount++; + } + } + return totalCount; + } + /** * 终端-设备关联用户 * diff --git a/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/SubGatewayMapper.xml b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/SubGatewayMapper.xml index abca9ce2..2f3db55f 100644 --- a/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/SubGatewayMapper.xml +++ b/fastbee-service/fastbee-iot-service/src/main/resources/mapper/iot/SubGatewayMapper.xml @@ -30,6 +30,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" + + @@ -47,8 +49,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"