Commit 6b9cca0b79612fc3ca26f04b73b23d9f973dd873

Authored by YunaiV
1 parent bd18e730

【代码评审】IOT:设备的 review

Showing 14 changed files with 143 additions and 438 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceStatusEnum.java
1 1 package cn.iocoder.yudao.module.iot.enums.device;
2 2  
  3 +import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
3 4 import lombok.Getter;
4 5  
  6 +import java.util.Arrays;
  7 +
5 8 /**
6 9 * IoT 设备状态枚举
7   - * 设备状态:0 - 未激活,1 - 在线,2 - 离线,3 - 已禁用
  10 + *
  11 + * @author haohao
8 12 */
9 13 @Getter
10   -public enum IotDeviceStatusEnum {
  14 +public enum IotDeviceStatusEnum implements IntArrayValuable {
11 15  
12 16 INACTIVE(0, "未激活"),
13 17 ONLINE(1, "在线"),
14 18 OFFLINE(2, "离线"),
15 19 DISABLED(3, "已禁用");
16 20  
  21 + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(IotDeviceStatusEnum::getStatus).toArray();
  22 +
17 23 /**
18 24 * 状态
19 25 */
... ... @@ -40,4 +46,10 @@ public enum IotDeviceStatusEnum {
40 46 public static boolean isValidStatus(Integer status) {
41 47 return fromStatus(status) != null;
42 48 }
  49 +
  50 + @Override
  51 + public int[] array() {
  52 + return ARRAYS;
  53 + }
  54 +
43 55 }
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
... ... @@ -37,14 +37,14 @@ public class IotDeviceController {
37 37 private IotDeviceService deviceService;
38 38  
39 39 @PostMapping("/create")
40   - @Operation(summary = "创建IoT 设备")
  40 + @Operation(summary = "创建设备")
41 41 @PreAuthorize("@ss.hasPermission('iot:device:create')")
42 42 public CommonResult<Long> createDevice(@Valid @RequestBody IotDeviceSaveReqVO createReqVO) {
43 43 return success(deviceService.createDevice(createReqVO));
44 44 }
45 45  
46 46 @PutMapping("/update-status")
47   - @Operation(summary = "更新IoT 设备状态")
  47 + @Operation(summary = "更新设备状态")
48 48 @Parameter(name = "id", description = "编号", required = true)
49 49 @Parameter(name = "status", description = "状态", required = true, example = "1")
50 50 @PreAuthorize("@ss.hasPermission('iot:device:update')")
... ... @@ -55,7 +55,7 @@ public class IotDeviceController {
55 55 }
56 56  
57 57 @PutMapping("/update")
58   - @Operation(summary = "更新IoT 设备")
  58 + @Operation(summary = "更新设备")
59 59 @PreAuthorize("@ss.hasPermission('iot:device:update')")
60 60 public CommonResult<Boolean> updateDevice(@Valid @RequestBody IotDeviceSaveReqVO updateReqVO) {
61 61 deviceService.updateDevice(updateReqVO);
... ... @@ -63,7 +63,7 @@ public class IotDeviceController {
63 63 }
64 64  
65 65 @DeleteMapping("/delete")
66   - @Operation(summary = "删除IoT 设备")
  66 + @Operation(summary = "删除设备")
67 67 @Parameter(name = "id", description = "编号", required = true)
68 68 @PreAuthorize("@ss.hasPermission('iot:device:delete')")
69 69 public CommonResult<Boolean> deleteDevice(@RequestParam("id") Long id) {
... ... @@ -72,7 +72,7 @@ public class IotDeviceController {
72 72 }
73 73  
74 74 @GetMapping("/get")
75   - @Operation(summary = "获得IoT 设备")
  75 + @Operation(summary = "获得设备")
76 76 @Parameter(name = "id", description = "编号", required = true, example = "1024")
77 77 @PreAuthorize("@ss.hasPermission('iot:device:query')")
78 78 public CommonResult<IotDeviceRespVO> getDevice(@RequestParam("id") Long id) {
... ... @@ -81,7 +81,7 @@ public class IotDeviceController {
81 81 }
82 82  
83 83 @GetMapping("/page")
84   - @Operation(summary = "获得IoT 设备分页")
  84 + @Operation(summary = "获得设备分页")
85 85 @PreAuthorize("@ss.hasPermission('iot:device:query')")
86 86 public CommonResult<PageResult<IotDeviceRespVO>> getDevicePage(@Valid IotDevicePageReqVO pageReqVO) {
87 87 PageResult<IotDeviceDO> pageResult = deviceService.getDevicePage(pageReqVO);
... ... @@ -89,7 +89,7 @@ public class IotDeviceController {
89 89 }
90 90  
91 91 @GetMapping("/export-excel")
92   - @Operation(summary = "导出IoT 设备 Excel")
  92 + @Operation(summary = "导出设备 Excel")
93 93 @PreAuthorize("@ss.hasPermission('iot:device:export')")
94 94 @ApiAccessLog(operateType = EXPORT)
95 95 public void exportDeviceExcel(@Valid IotDevicePageReqVO pageReqVO,
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java
1 1 package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
2 2  
3   -import lombok.*;
4   -import io.swagger.v3.oas.annotations.media.Schema;
5 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
6   -import java.math.BigDecimal;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +import lombok.ToString;
7 10 import org.springframework.format.annotation.DateTimeFormat;
  11 +
  12 +import java.math.BigDecimal;
8 13 import java.time.LocalDateTime;
9 14  
10 15 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
... ... @@ -15,28 +20,32 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
15 20 @ToString(callSuper = true)
16 21 public class IotDevicePageReqVO extends PageParam {
17 22  
18   - @Schema(description = "设备唯一标识符,全局唯一,用于识别设备")
  23 + // TODO @芋艿:需要去掉一些多余的字段;
  24 +
  25 + @Schema(description = "设备唯一标识符", example = "24602")
19 26 private String deviceKey;
20 27  
21   - @Schema(description = "设备名称,在产品内唯一,用于标识设备", example = "王五")
  28 + @Schema(description = "设备名称", example = "王五")
22 29 private String deviceName;
23 30  
24   - @Schema(description = "产品 ID,关联 iot_product 表的 id", example = "26202")
  31 + @Schema(description = "产品编号", example = "26202")
25 32 private Long productId;
26 33  
27   - @Schema(description = "产品 Key,关联 iot_product 表的 product_key")
  34 + @Schema(description = "产品标识")
28 35 private String productKey;
29 36  
30   - @Schema(description = "设备类型:0 - 直连设备,1 - 网关子设备,2 - 网关设备", example = "1")
  37 + @Schema(description = "设备类型", example = "1")
  38 + // TODO @haohao:需要有个设备类型的枚举
31 39 private Integer deviceType;
32 40  
33   - @Schema(description = "设备备注名称,供用户自定义备注", example = "张三")
  41 + @Schema(description = "备注名称", example = "张三")
34 42 private String nickname;
35 43  
36   - @Schema(description = "网关设备 ID,子设备需要关联的网关设备 ID", example = "16380")
  44 + @Schema(description = "网关设备 ID", example = "16380")
37 45 private Long gatewayId;
38 46  
39   - @Schema(description = "设备状态:0 - 未激活,1 - 在线,2 - 离线,3 - 已禁用", example = "1")
  47 + @Schema(description = "设备状态", example = "1")
  48 + @InEnum(IotDeviceStatusEnum.class)
40 49 private Integer status;
41 50  
42 51 @Schema(description = "设备状态最后更新时间")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java
... ... @@ -13,7 +13,7 @@ import java.time.LocalDateTime;
13 13 @ExcelIgnoreUnannotated
14 14 public class IotDeviceRespVO {
15 15  
16   - @Schema(description = "设备 ID,主键,自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
  16 + @Schema(description = "设备编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
17 17 private Long id;
18 18  
19 19 @Schema(description = "设备唯一标识符", requiredMode = Schema.RequiredMode.REQUIRED)
... ... @@ -24,11 +24,11 @@ public class IotDeviceRespVO {
24 24 @ExcelProperty("设备名称备")
25 25 private String deviceName;
26 26  
27   - @Schema(description = "产品 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
  27 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
28 28 @ExcelProperty("产品 ID")
29 29 private Long productId;
30 30  
31   - @Schema(description = "产品 Key", requiredMode = Schema.RequiredMode.REQUIRED)
  31 + @Schema(description = "产品标识", requiredMode = Schema.RequiredMode.REQUIRED)
32 32 @ExcelProperty("产品 Key")
33 33 private String productKey;
34 34  
... ... @@ -72,7 +72,7 @@ public class IotDeviceRespVO {
72 72 @ExcelProperty("设备的固件版本")
73 73 private String firmwareVersion;
74 74  
75   - @Schema(description = "设备密钥,用于设备认证,需安全存储")
  75 + @Schema(description = "设备密钥,用于设备认证")
76 76 @ExcelProperty("设备密钥")
77 77 private String deviceSecret;
78 78  
... ... @@ -92,16 +92,17 @@ public class IotDeviceRespVO {
92 92 @ExcelProperty("认证类型(如一机一密、动态注册)")
93 93 private String authType;
94 94  
95   - @Schema(description = "设备位置的纬度,范围 -90.000000 ~ 90.000000")
96   - @ExcelProperty("设备位置的纬度,范围 -90.000000 ~ 90.000000")
  95 + // TODO @haohao:经纬度:可能 double 就够啦
  96 + @Schema(description = "设备位置的纬度,范围")
  97 + @ExcelProperty("设备位置的纬度")
97 98 private BigDecimal latitude;
98 99  
99   - @Schema(description = "设备位置的经度,范围 -180.000000 ~ 180.000000")
100   - @ExcelProperty("设备位置的经度,范围 -180.000000 ~ 180.000000")
  100 + @Schema(description = "设备位置的经度")
  101 + @ExcelProperty("设备位置的经度")
101 102 private BigDecimal longitude;
102 103  
103   - @Schema(description = "地区编码,符合国家地区编码标准,关联地区表", example = "16995")
104   - @ExcelProperty("地区编码,符合国家地区编码标准,关联地区表")
  104 + @Schema(description = "地区编码", example = "16995")
  105 + @ExcelProperty("地区编码")
105 106 private Integer areaId;
106 107  
107 108 @Schema(description = "设备详细地址")
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceSaveReqVO.java
... ... @@ -7,16 +7,16 @@ import lombok.Data;
7 7 @Data
8 8 public class IotDeviceSaveReqVO {
9 9  
10   - @Schema(description = "设备 ID,主键,自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "177")
  10 + @Schema(description = "设备编号", example = "177")
11 11 private Long id;
12 12  
13   - @Schema(description = "设备名称,在产品内唯一,用于标识设备", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
  13 + @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五")
14 14 private String deviceName;
15 15  
16   - @Schema(description = "设备备注名称,供用户自定义备注", example = "张三")
  16 + @Schema(description = "备注名称", example = "张三")
17 17 private String nickname;
18 18  
19   - @Schema(description = "产品 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
  19 + @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202")
20 20 private Long productId;
21 21  
22 22 }
23 23 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.iot.dal.dataobject.device;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
4 4 import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
  5 +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum;
5 6 import com.baomidou.mybatisplus.annotation.KeySequence;
6 7 import com.baomidou.mybatisplus.annotation.TableId;
7 8 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -13,7 +14,7 @@ import java.time.LocalDateTime;
13 14 /**
14 15 * IoT 设备 DO
15 16 *
16   - * @author 芋道源码
  17 + * @author haohao
17 18 */
18 19 @TableName("iot_device")
19 20 @KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
... ... @@ -39,33 +40,48 @@ public class IotDeviceDO extends BaseDO {
39 40 */
40 41 private String deviceName;
41 42 /**
42   - * 产品 ID,关联 iot_product 表的 id
  43 + * 设备备注名称
  44 + */
  45 + private String nickname;
  46 + /**
  47 + * 设备序列号
  48 + */
  49 + private String serialNumber;
  50 +
  51 + /**
  52 + * 产品编号
  53 + *
43 54 * 关联 {@link IotProductDO#getId()}
44 55 */
45 56 private Long productId;
46 57 /**
47   - * 产品 Key,关联 iot_product 表的 product_key
48   - * 关联 {@link IotProductDO#getProductKey()}
  58 + * 产品标识
  59 + *
  60 + * 冗余 {@link IotProductDO#getProductKey()}
49 61 */
50 62 private String productKey;
51 63 /**
52   - * 设备类型:0 - 直连设备,1 - 网关子设备,2 - 网关设备
53   - * 关联 {@link IotProductDO#getDeviceType()}
  64 + * 设备类型
  65 + *
  66 + * 冗余 {@link IotProductDO#getDeviceType()}
54 67 */
55 68 private Integer deviceType;
  69 +
56 70 /**
57   - * 设备备注名称,供用户自定义备注
  71 + * 设备状态
  72 + *
  73 + * 枚举 {@link IotDeviceStatusEnum}
58 74 */
59   - private String nickname;
  75 + private Integer status;
60 76 /**
61   - * 网关设备 ID,子设备需要关联的网关设备 ID
  77 + * 网关设备编号
  78 + *
  79 + * 子设备需要关联的网关设备 ID
  80 + *
  81 + * 关联 {@link IotDeviceDO#getId()}
62 82 */
63 83 private Long gatewayId;
64   - /**
65   - * 设备状态:0 - 未激活,1 - 在线,2 - 离线,3 - 已禁用
66   - * 关联 {@link cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum}
67   - */
68   - private Integer status;
  84 +
69 85 /**
70 86 * 设备状态最后更新时间
71 87 */
... ... @@ -82,6 +98,7 @@ public class IotDeviceDO extends BaseDO {
82 98 * 设备激活时间
83 99 */
84 100 private LocalDateTime activeTime;
  101 +
85 102 /**
86 103 * 设备的 IP 地址
87 104 */
... ... @@ -90,6 +107,7 @@ public class IotDeviceDO extends BaseDO {
90 107 * 设备的固件版本
91 108 */
92 109 private String firmwareVersion;
  110 +
93 111 /**
94 112 * 设备密钥,用于设备认证,需安全存储
95 113 */
... ... @@ -109,26 +127,26 @@ public class IotDeviceDO extends BaseDO {
109 127 /**
110 128 * 认证类型(如一机一密、动态注册)
111 129 */
  130 + // TODO @haohao:是不是要枚举哈
112 131 private String authType;
  132 +
113 133 /**
114   - * 设备位置的纬度,范围 -90.000000 ~ 90.000000
  134 + * 设备位置的纬度
115 135 */
116 136 private BigDecimal latitude;
117 137 /**
118   - * 设备位置的经度,范围 -180.000000 ~ 180.000000
  138 + * 设备位置的经度
119 139 */
120 140 private BigDecimal longitude;
121 141 /**
122   - * 地区编码,符合国家地区编码标准,关联地区表
  142 + * 地区编码
  143 + *
  144 + * 关联 Area 的 id
123 145 */
124 146 private Integer areaId;
125 147 /**
126 148 * 设备详细地址
127 149 */
128 150 private String address;
129   - /**
130   - * 设备序列号
131   - */
132   - private String serialNumber;
133 151  
134 152 }
135 153 \ 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
... ... @@ -53,4 +53,5 @@ public interface IotDeviceMapper extends BaseMapperX&lt;IotDeviceDO&gt; {
53 53 default long selectCountByGatewayId(Long id) {
54 54 return selectCount(IotDeviceDO::getGatewayId, id);
55 55 }
  56 +
56 57 }
57 58 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImpl.java
1 1 package cn.iocoder.yudao.module.iot.service.device;
2 2  
  3 +import cn.hutool.core.util.IdUtil;
3 4 import cn.hutool.core.util.StrUtil;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 6 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
... ... @@ -34,6 +35,7 @@ public class DeviceServiceImpl implements IotDeviceService {
34 35  
35 36 @Resource
36 37 private IotDeviceMapper deviceMapper;
  38 + // TODO @haohao:不直接调用 productmapper,通过 productservice;每一个模型,不直接使用对方的
37 39 @Resource
38 40 private IotProductMapper productMapper;
39 41  
... ... @@ -46,40 +48,33 @@ public class DeviceServiceImpl implements IotDeviceService {
46 48 @Override
47 49 @Transactional(rollbackFor = Exception.class)
48 50 public Long createDevice(IotDeviceSaveReqVO createReqVO) {
49   - // 1. 转换 VO 为 DO
50   - IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class);
51   -
52   - // 2. 根据产品 ID 查询产品信息
  51 + // 1.1 校验产品是否存在
53 52 IotProductDO product = productMapper.selectById(createReqVO.getProductId());
54 53 if (product == null) {
55 54 throw exception(PRODUCT_NOT_EXISTS);
56 55 }
57   - device.setProductKey(product.getProductKey());
58   - device.setDeviceType(product.getDeviceType());
59   -
60   - // 3. DeviceName 可以为空,当为空时,自动生成产品下的唯一标识符作为 DeviceName
61   - if (StrUtil.isBlank(device.getDeviceName())) {
62   - device.setDeviceName(generateUniqueDeviceName(createReqVO.getProductId()));
  56 + // 1.2 校验设备名称在同一产品下是否唯一
  57 + if (StrUtil.isBlank(createReqVO.getDeviceName())) {
  58 + createReqVO.setDeviceName(generateUniqueDeviceName(product.getProductKey()));
  59 + } else {
  60 + validateDeviceNameUnique(product.getProductKey(), createReqVO.getDeviceName());
63 61 }
64 62  
65   - // 4. 校验设备名称在同一产品下是否唯一
66   - validateDeviceNameUnique(device.getProductKey(), device.getDeviceName());
67   -
68   - // 5. 生成并设置必要的字段
  63 + // 2.1 转换 VO 为 DO
  64 + IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class)
  65 + .setProductKey(product.getProductKey())
  66 + .setDeviceType(product.getDeviceType());
  67 + // 2.2 生成并设置必要的字段
69 68 device.setDeviceKey(generateUniqueDeviceKey());
70 69 device.setDeviceSecret(generateDeviceSecret());
71 70 device.setMqttClientId(generateMqttClientId());
72 71 device.setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey()));
73 72 device.setMqttPassword(generateMqttPassword());
74   -
75   - // 6. 设置设备状态为未激活
  73 + // 2.3 设置设备状态为未激活
76 74 device.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus());
77 75 device.setStatusLastUpdateTime(LocalDateTime.now());
78   -
79   - // 7. 插入到数据库
  76 + // 2.4 插入到数据库
80 77 deviceMapper.insert(device);
81   -
82   - // 8. 返回生成的设备 ID
83 78 return device.getId();
84 79 }
85 80  
... ... @@ -111,7 +106,7 @@ public class DeviceServiceImpl implements IotDeviceService {
111 106 * @return 生成的 deviceSecret
112 107 */
113 108 private String generateDeviceSecret() {
114   - // 32 位随机字符串
  109 + // TODO @haohao:return IdUtil.fastSimpleUUID()
115 110 return UUID.randomUUID().toString().replace("-", "");
116 111 }
117 112  
... ... @@ -141,39 +136,25 @@ public class DeviceServiceImpl implements IotDeviceService {
141 136 * @return 生成的 MQTT Password
142 137 */
143 138 private String generateMqttPassword() {
144   - // 在实际应用中,建议使用更安全的方法生成 MQTT Password,如加密或哈希
  139 + // TODO @haohao:【后续优化】在实际应用中,建议使用更安全的方法生成 MQTT Password,如加密或哈希
145 140 return UUID.randomUUID().toString();
146 141 }
147 142  
148 143 /**
149 144 * 生成唯一的 DeviceName
150 145 *
151   - * @param productId 产品 ID
  146 + * @param productKey 产品标识
152 147 * @return 生成的唯一 DeviceName
153 148 */
154   - private String generateUniqueDeviceName(Long productId) {
155   - // 实现逻辑以在产品下生成唯一的设备名称
156   - String deviceName;
157   - String productKey = getProductKey(productId);
158   - do {
159   - // 20 位随机字符串
160   - deviceName = UUID.randomUUID().toString().replace("-", "").substring(0, 20);
161   - } while (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null);
162   - return deviceName;
163   - }
164   -
165   - /**
166   - * 获取产品 Key
167   - *
168   - * @param productId 产品 ID
169   - * @return 产品 Key
170   - */
171   - private String getProductKey(Long productId) {
172   - IotProductDO product = productMapper.selectById(productId);
173   - if (product == null) {
174   - throw exception(PRODUCT_NOT_EXISTS);
  149 + private String generateUniqueDeviceName(String productKey) {
  150 + // TODO @haohao:业务逻辑里,尽量避免 while true。万一 bug = =;虽然这个不会哈。我先改了下
  151 + for (int i = 0; i < Short.MAX_VALUE; i++) {
  152 + String deviceName = IdUtil.fastSimpleUUID().substring(0, 20);
  153 + if (deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName) != null) {
  154 + return deviceName;
  155 + }
175 156 }
176   - return product.getProductKey();
  157 + throw new IllegalArgumentException("生成 DeviceName 失败");
177 158 }
178 159  
179 160 @Override
... ... @@ -183,6 +164,7 @@ public class DeviceServiceImpl implements IotDeviceService {
183 164 IotDeviceDO existingDevice = validateDeviceExists(updateReqVO.getId());
184 165  
185 166 // 设备名称 和 产品 ID 不能修改
  167 + // TODO @haohao:这种,直接设置为 null 就不会更新了。忽略前端的传参
186 168 if (updateReqVO.getDeviceName() != null && !updateReqVO.getDeviceName().equals(existingDevice.getDeviceName())) {
187 169 throw exception(DEVICE_NAME_CANNOT_BE_MODIFIED);
188 170 }
... ... @@ -200,18 +182,14 @@ public class DeviceServiceImpl implements IotDeviceService {
200 182 @Override
201 183 @Transactional(rollbackFor = Exception.class)
202 184 public void deleteDevice(Long id) {
203   - // 校验存在
204   - IotDeviceDO iotDeviceDO = validateDeviceExists(id);
205   -
206   - // 如果是网关设备,检查是否有子设备
207   - if (iotDeviceDO.getGatewayId() != null) {
208   - long childCount = deviceMapper.selectCountByGatewayId(id);
209   - if (childCount > 0) {
210   - throw exception(DEVICE_HAS_CHILDREN);
211   - }
  185 + // 1.1 校验存在
  186 + IotDeviceDO device = validateDeviceExists(id);
  187 + // 1.2 如果是网关设备,检查是否有子设备
  188 + if (device.getGatewayId() != null && deviceMapper.selectCountByGatewayId(id) > 0) {
  189 + throw exception(DEVICE_HAS_CHILDREN);
212 190 }
213 191  
214   - // 删除设备
  192 + // 2. 删除设备
215 193 deviceMapper.deleteById(id);
216 194 }
217 195  
... ... @@ -222,11 +200,11 @@ public class DeviceServiceImpl implements IotDeviceService {
222 200 * @return 设备对象
223 201 */
224 202 private IotDeviceDO validateDeviceExists(Long id) {
225   - IotDeviceDO iotDeviceDO = deviceMapper.selectById(id);
226   - if (iotDeviceDO == null) {
  203 + IotDeviceDO device = deviceMapper.selectById(id);
  204 + if (device == null) {
227 205 throw exception(DEVICE_NOT_EXISTS);
228 206 }
229   - return iotDeviceDO;
  207 + return device;
230 208 }
231 209  
232 210 @Override
... ... @@ -244,21 +222,20 @@ public class DeviceServiceImpl implements IotDeviceService {
244 222 }
245 223  
246 224 @Override
247   - @Transactional(rollbackFor = Exception.class)
248 225 public void updateDeviceStatus(Long id, Integer status) {
249 226 // 校验存在
250 227 validateDeviceExists(id);
251 228  
  229 + // TODO @haohao:这个可以直接用 swagger 注解哈
252 230 // 校验状态是否合法
253 231 if (!IotDeviceStatusEnum.isValidStatus(status)) {
254 232 throw exception(DEVICE_INVALID_DEVICE_STATUS);
255 233 }
256 234  
257 235 // 更新状态和更新时间
258   - IotDeviceDO updateObj = new IotDeviceDO()
259   - .setId(id)
260   - .setStatus(status)
  236 + IotDeviceDO updateObj = new IotDeviceDO().setId(id).setStatus(status)
261 237 .setStatusLastUpdateTime(LocalDateTime.now());
262 238 deviceMapper.updateById(updateObj);
263 239 }
  240 +
264 241 }
265 242 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
... ... @@ -13,7 +13,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
13 13 public interface IotDeviceService {
14 14  
15 15 /**
16   - * 创建IoT 设备
  16 + * 创建设备
17 17 *
18 18 * @param createReqVO 创建信息
19 19 * @return 编号
... ... @@ -21,21 +21,21 @@ public interface IotDeviceService {
21 21 Long createDevice(@Valid IotDeviceSaveReqVO createReqVO);
22 22  
23 23 /**
24   - * 更新IoT 设备
  24 + * 更新设备
25 25 *
26 26 * @param updateReqVO 更新信息
27 27 */
28 28 void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO);
29 29  
30 30 /**
31   - * 删除IoT 设备
  31 + * 删除设备
32 32 *
33 33 * @param id 编号
34 34 */
35 35 void deleteDevice(Long id);
36 36  
37 37 /**
38   - * 获得IoT 设备
  38 + * 获得设备
39 39 *
40 40 * @param id 编号
41 41 * @return IoT 设备
... ... @@ -43,7 +43,7 @@ public interface IotDeviceService {
43 43 IotDeviceDO getDevice(Long id);
44 44  
45 45 /**
46   - * 获得IoT 设备分页
  46 + * 获得设备分页
47 47 *
48 48 * @param pageReqVO 分页查询
49 49 * @return IoT 设备分页
... ... @@ -51,10 +51,11 @@ public interface IotDeviceService {
51 51 PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO);
52 52  
53 53 /**
54   - * 更新IoT 设备状态
  54 + * 更新设备状态
55 55 *
56 56 * @param id 编号
57 57 * @param status 状态
58 58 */
59 59 void updateDeviceStatus(Long id, Integer status);
  60 +
60 61 }
61 62 \ No newline at end of file
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImpl.java
... ... @@ -170,7 +170,6 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
170 170  
171 171 // 3.1 使用 diffList 方法比较新旧列表
172 172 List<List<IotThinkModelFunctionDO>> diffResult = diffList(oldFunctionList, newFunctionList,
173   - // TODO @haohao:是不是用 id 比较相同就 ok 哈。如果可以的化,下面的 update 可以更简单
174 173 // 继续使用 identifier 和 type 进行比较:这样可以准确地匹配对应的功能对象。
175 174 (oldFunc, newFunc) -> Objects.equals(oldFunc.getIdentifier(), newFunc.getIdentifier())
176 175 && Objects.equals(oldFunc.getType(), newFunc.getType()));
... ... @@ -191,6 +190,7 @@ public class IotThinkModelFunctionServiceImpl implements IotThinkModelFunctionSe
191 190 thinkModelFunctionMapper.updateById(updateFunc);
192 191 }
193 192 }
  193 + // TODO @haohao:seckillProductMapper.updateBatch(diffList.get(1)); 可以直接类似这么操作哇?
194 194 }
195 195 if (CollUtil.isNotEmpty(deleteList)) {
196 196 Set<Long> idsToDelete = CollectionUtils.convertSet(deleteList, IotThinkModelFunctionDO::getId);
... ...
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3   -<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper">
4   -
5   - <!--
6   - 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
7   - 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
8   - 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
9   - 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
10   - -->
11   -
12   -</mapper>
13 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/thinkmodelfunction/IotThinkModelFunctionMapper.xml deleted
1   -<?xml version="1.0" encoding="UTF-8"?>
2   -<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3   -<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotThinkModelFunctionMapper">
4   -
5   - <!--
6   - 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
7   - 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
8   - 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
9   - 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
10   - -->
11   -
12   -</mapper>
13 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/device/DeviceServiceImplTest.java deleted
1   -package cn.iocoder.yudao.module.iot.service.device;
2   -
3   -import org.junit.jupiter.api.Disabled;
4   -import org.junit.jupiter.api.Test;
5   -
6   -import jakarta.annotation.Resource;
7   -
8   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
9   -
10   -import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*;
11   -import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
12   -import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper;
13   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
14   -
15   -import org.springframework.context.annotation.Import;
16   -
17   -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
18   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
19   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
20   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
21   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
22   -import static org.junit.jupiter.api.Assertions.*;
23   -
24   -/**
25   - * {@link DeviceServiceImpl} 的单元测试类
26   - *
27   - * @author 芋道源码
28   - */
29   -@Import(DeviceServiceImpl.class)
30   -public class DeviceServiceImplTest extends BaseDbUnitTest {
31   -
32   - @Resource
33   - private DeviceServiceImpl deviceService;
34   -
35   - @Resource
36   - private IotDeviceMapper deviceMapper;
37   -
38   - @Test
39   - public void testCreateDevice_success() {
40   - // 准备参数
41   - IotDeviceSaveReqVO createReqVO = randomPojo(IotDeviceSaveReqVO.class).setId(null);
42   -
43   - // 调用
44   - Long deviceId = deviceService.createDevice(createReqVO);
45   - // 断言
46   - assertNotNull(deviceId);
47   - // 校验记录的属性是否正确
48   - IotDeviceDO device = deviceMapper.selectById(deviceId);
49   - assertPojoEquals(createReqVO, device, "id");
50   - }
51   -
52   - @Test
53   - public void testUpdateDevice_success() {
54   - // mock 数据
55   - IotDeviceDO dbDevice = randomPojo(IotDeviceDO.class);
56   - deviceMapper.insert(dbDevice);// @Sql: 先插入出一条存在的数据
57   - // 准备参数
58   - IotDeviceSaveReqVO updateReqVO = randomPojo(IotDeviceSaveReqVO.class, o -> {
59   - o.setId(dbDevice.getId()); // 设置更新的 ID
60   - });
61   -
62   - // 调用
63   - deviceService.updateDevice(updateReqVO);
64   - // 校验是否更新正确
65   - IotDeviceDO device = deviceMapper.selectById(updateReqVO.getId()); // 获取最新的
66   - assertPojoEquals(updateReqVO, device);
67   - }
68   -
69   - @Test
70   - public void testUpdateDevice_notExists() {
71   - // 准备参数
72   - IotDeviceSaveReqVO updateReqVO = randomPojo(IotDeviceSaveReqVO.class);
73   -
74   - // 调用, 并断言异常
75   - assertServiceException(() -> deviceService.updateDevice(updateReqVO), DEVICE_NOT_EXISTS);
76   - }
77   -
78   - @Test
79   - public void testDeleteDevice_success() {
80   - // mock 数据
81   - IotDeviceDO dbDevice = randomPojo(IotDeviceDO.class);
82   - deviceMapper.insert(dbDevice);// @Sql: 先插入出一条存在的数据
83   - // 准备参数
84   - Long id = dbDevice.getId();
85   -
86   - // 调用
87   - deviceService.deleteDevice(id);
88   - // 校验数据不存在了
89   - assertNull(deviceMapper.selectById(id));
90   - }
91   -
92   - @Test
93   - public void testDeleteDevice_notExists() {
94   - // 准备参数
95   - Long id = randomLongId();
96   -
97   - // 调用, 并断言异常
98   - assertServiceException(() -> deviceService.deleteDevice(id), DEVICE_NOT_EXISTS);
99   - }
100   -
101   - @Test
102   - @Disabled // TODO 请修改 null 为需要的值,然后删除 @Disabled 注解
103   - public void testGetDevicePage() {
104   - // mock 数据
105   - IotDeviceDO dbDevice = randomPojo(IotDeviceDO.class, o -> { // 等会查询到
106   - o.setDeviceKey(null);
107   - o.setDeviceName(null);
108   - o.setProductId(null);
109   - o.setProductKey(null);
110   - o.setDeviceType(null);
111   - o.setNickname(null);
112   - o.setGatewayId(null);
113   - o.setStatus(null);
114   - o.setStatusLastUpdateTime(null);
115   - o.setLastOnlineTime(null);
116   - o.setLastOfflineTime(null);
117   - o.setActiveTime(null);
118   - o.setIp(null);
119   - o.setFirmwareVersion(null);
120   - o.setDeviceSecret(null);
121   - o.setMqttClientId(null);
122   - o.setMqttUsername(null);
123   - o.setMqttPassword(null);
124   - o.setAuthType(null);
125   - o.setLatitude(null);
126   - o.setLongitude(null);
127   - o.setAreaId(null);
128   - o.setAddress(null);
129   - o.setSerialNumber(null);
130   - o.setCreateTime(null);
131   - });
132   - deviceMapper.insert(dbDevice);
133   - // 测试 deviceKey 不匹配
134   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setDeviceKey(null)));
135   - // 测试 deviceName 不匹配
136   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setDeviceName(null)));
137   - // 测试 productId 不匹配
138   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setProductId(null)));
139   - // 测试 productKey 不匹配
140   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setProductKey(null)));
141   - // 测试 deviceType 不匹配
142   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setDeviceType(null)));
143   - // 测试 nickname 不匹配
144   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setNickname(null)));
145   - // 测试 gatewayId 不匹配
146   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setGatewayId(null)));
147   - // 测试 status 不匹配
148   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setStatus(null)));
149   - // 测试 statusLastUpdateTime 不匹配
150   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setStatusLastUpdateTime(null)));
151   - // 测试 lastOnlineTime 不匹配
152   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setLastOnlineTime(null)));
153   - // 测试 lastOfflineTime 不匹配
154   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setLastOfflineTime(null)));
155   - // 测试 activeTime 不匹配
156   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setActiveTime(null)));
157   - // 测试 ip 不匹配
158   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setIp(null)));
159   - // 测试 firmwareVersion 不匹配
160   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setFirmwareVersion(null)));
161   - // 测试 deviceSecret 不匹配
162   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setDeviceSecret(null)));
163   - // 测试 mqttClientId 不匹配
164   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setMqttClientId(null)));
165   - // 测试 mqttUsername 不匹配
166   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setMqttUsername(null)));
167   - // 测试 mqttPassword 不匹配
168   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setMqttPassword(null)));
169   - // 测试 authType 不匹配
170   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setAuthType(null)));
171   - // 测试 latitude 不匹配
172   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setLatitude(null)));
173   - // 测试 longitude 不匹配
174   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setLongitude(null)));
175   - // 测试 areaId 不匹配
176   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setAreaId(null)));
177   - // 测试 address 不匹配
178   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setAddress(null)));
179   - // 测试 serialNumber 不匹配
180   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setSerialNumber(null)));
181   - // 测试 createTime 不匹配
182   - deviceMapper.insert(cloneIgnoreId(dbDevice, o -> o.setCreateTime(null)));
183   - // 准备参数
184   - IotDevicePageReqVO reqVO = new IotDevicePageReqVO();
185   - reqVO.setDeviceKey(null);
186   - reqVO.setDeviceName(null);
187   - reqVO.setProductId(null);
188   - reqVO.setProductKey(null);
189   - reqVO.setDeviceType(null);
190   - reqVO.setNickname(null);
191   - reqVO.setGatewayId(null);
192   - reqVO.setStatus(null);
193   - reqVO.setStatusLastUpdateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
194   - reqVO.setLastOnlineTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
195   - reqVO.setLastOfflineTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
196   - reqVO.setActiveTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
197   - reqVO.setIp(null);
198   - reqVO.setFirmwareVersion(null);
199   - reqVO.setDeviceSecret(null);
200   - reqVO.setMqttClientId(null);
201   - reqVO.setMqttUsername(null);
202   - reqVO.setMqttPassword(null);
203   - reqVO.setAuthType(null);
204   - reqVO.setLatitude(null);
205   - reqVO.setLongitude(null);
206   - reqVO.setAreaId(null);
207   - reqVO.setAddress(null);
208   - reqVO.setSerialNumber(null);
209   - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));
210   -
211   - // 调用
212   - PageResult<IotDeviceDO> pageResult = deviceService.getDevicePage(reqVO);
213   - // 断言
214   - assertEquals(1, pageResult.getTotal());
215   - assertEquals(1, pageResult.getList().size());
216   - assertPojoEquals(dbDevice, pageResult.getList().get(0));
217   - }
218   -
219   -}
220 0 \ No newline at end of file
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/thinkmodelfunction/IotThinkModelFunctionServiceImplTest.java deleted
1   -package cn.iocoder.yudao.module.iot.service.thinkmodelfunction;
2   -
3   -import org.junit.jupiter.api.Test;
4   -
5   -import jakarta.annotation.Resource;
6   -
7   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
8   -
9   -import cn.iocoder.yudao.module.iot.controller.admin.thinkmodelfunction.vo.*;
10   -import cn.iocoder.yudao.module.iot.dal.dataobject.thinkmodelfunction.IotThinkModelFunctionDO;
11   -import cn.iocoder.yudao.module.iot.dal.mysql.thinkmodelfunction.IotThinkModelFunctionMapper;
12   -
13   -import org.springframework.context.annotation.Import;
14   -
15   -import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
16   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
17   -import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
18   -import static org.junit.jupiter.api.Assertions.*;
19   -
20   -/**
21   - * {@link IotThinkModelFunctionServiceImpl} 的单元测试类
22   - *
23   - * @author 芋道源码
24   - */
25   -@Import(IotThinkModelFunctionServiceImpl.class)
26   -public class IotThinkModelFunctionServiceImplTest extends BaseDbUnitTest {
27   -
28   - @Resource
29   - private IotThinkModelFunctionServiceImpl thinkModelFunctionService;
30   -
31   - @Resource
32   - private IotThinkModelFunctionMapper thinkModelFunctionMapper;
33   -
34   - @Test
35   - public void testCreateThinkModelFunction_success() {
36   - // 准备参数
37   - IotThinkModelFunctionSaveReqVO createReqVO = randomPojo(IotThinkModelFunctionSaveReqVO.class);
38   -
39   - // 调用
40   - Long thinkModelFunctionId = thinkModelFunctionService.createThinkModelFunction(createReqVO);
41   - // 断言
42   - assertNotNull(thinkModelFunctionId);
43   - // 校验记录的属性是否正确
44   - IotThinkModelFunctionDO thinkModelFunction = thinkModelFunctionMapper.selectById(thinkModelFunctionId);
45   - assertPojoEquals(createReqVO, thinkModelFunction, "id");
46   - }
47   -
48   - @Test
49   - public void testDeleteThinkModelFunction_success() {
50   - // mock 数据
51   - IotThinkModelFunctionDO dbThinkModelFunction = randomPojo(IotThinkModelFunctionDO.class);
52   - thinkModelFunctionMapper.insert(dbThinkModelFunction);// @Sql: 先插入出一条存在的数据
53   - // 准备参数
54   - Long id = dbThinkModelFunction.getId();
55   -
56   - // 调用
57   - thinkModelFunctionService.deleteThinkModelFunction(id);
58   - // 校验数据不存在了
59   - assertNull(thinkModelFunctionMapper.selectById(id));
60   - }
61   -
62   - @Test
63   - public void testDeleteThinkModelFunction_notExists() {
64   - // 准备参数
65   - Long id = randomLongId();
66   -
67   - // 调用, 并断言异常
68   - assertServiceException(() -> thinkModelFunctionService.deleteThinkModelFunction(id), THINK_MODEL_FUNCTION_NOT_EXISTS);
69   - }
70   -
71   -}
72 0 \ No newline at end of file