Commit bd18e730525671e1286a063d17604db483d41412
1 parent
d8d37d1b
【新增】IOT 设备管理
Showing
13 changed files
with
1137 additions
and
0 deletions
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java
| ... | ... | @@ -21,5 +21,10 @@ public interface ErrorCodeConstants { |
| 21 | 21 | |
| 22 | 22 | // ========== IoT 设备 1-050-003-000 ============ |
| 23 | 23 | ErrorCode DEVICE_NOT_EXISTS = new ErrorCode(1_050_003_000, "设备不存在"); |
| 24 | + ErrorCode DEVICE_NAME_EXISTS = new ErrorCode(1_050_003_001, "设备名称在同一产品下必须唯一"); | |
| 25 | + ErrorCode DEVICE_HAS_CHILDREN = new ErrorCode(1_050_003_002, "有子设备,不允许删除"); | |
| 26 | + ErrorCode DEVICE_NAME_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_003, "设备名称不能修改"); | |
| 27 | + ErrorCode DEVICE_PRODUCT_CANNOT_BE_MODIFIED = new ErrorCode(1_050_003_004, "产品不能修改"); | |
| 28 | + ErrorCode DEVICE_INVALID_DEVICE_STATUS = new ErrorCode(1_050_003_005, "无效的设备状态"); | |
| 24 | 29 | |
| 25 | 30 | } | ... | ... |
yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/device/IotDeviceStatusEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.enums.device; | |
| 2 | + | |
| 3 | +import lombok.Getter; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * IoT 设备状态枚举 | |
| 7 | + * 设备状态:0 - 未激活,1 - 在线,2 - 离线,3 - 已禁用 | |
| 8 | + */ | |
| 9 | +@Getter | |
| 10 | +public enum IotDeviceStatusEnum { | |
| 11 | + | |
| 12 | + INACTIVE(0, "未激活"), | |
| 13 | + ONLINE(1, "在线"), | |
| 14 | + OFFLINE(2, "离线"), | |
| 15 | + DISABLED(3, "已禁用"); | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 状态 | |
| 19 | + */ | |
| 20 | + private final Integer status; | |
| 21 | + /** | |
| 22 | + * 状态名 | |
| 23 | + */ | |
| 24 | + private final String name; | |
| 25 | + | |
| 26 | + IotDeviceStatusEnum(Integer status, String name) { | |
| 27 | + this.status = status; | |
| 28 | + this.name = name; | |
| 29 | + } | |
| 30 | + | |
| 31 | + public static IotDeviceStatusEnum fromStatus(Integer status) { | |
| 32 | + for (IotDeviceStatusEnum value : values()) { | |
| 33 | + if (value.getStatus().equals(status)) { | |
| 34 | + return value; | |
| 35 | + } | |
| 36 | + } | |
| 37 | + return null; | |
| 38 | + } | |
| 39 | + | |
| 40 | + public static boolean isValidStatus(Integer status) { | |
| 41 | + return fromStatus(status) != null; | |
| 42 | + } | |
| 43 | +} | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/IotDeviceController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 7 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 8 | +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; | |
| 9 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; | |
| 10 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceRespVO; | |
| 11 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO; | |
| 12 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | |
| 13 | +import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | |
| 14 | +import io.swagger.v3.oas.annotations.Operation; | |
| 15 | +import io.swagger.v3.oas.annotations.Parameter; | |
| 16 | +import io.swagger.v3.oas.annotations.tags.Tag; | |
| 17 | +import jakarta.annotation.Resource; | |
| 18 | +import jakarta.servlet.http.HttpServletResponse; | |
| 19 | +import jakarta.validation.Valid; | |
| 20 | +import org.springframework.security.access.prepost.PreAuthorize; | |
| 21 | +import org.springframework.validation.annotation.Validated; | |
| 22 | +import org.springframework.web.bind.annotation.*; | |
| 23 | + | |
| 24 | +import java.io.IOException; | |
| 25 | +import java.util.List; | |
| 26 | + | |
| 27 | +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; | |
| 28 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | |
| 29 | + | |
| 30 | +@Tag(name = "管理后台 - IoT 设备") | |
| 31 | +@RestController | |
| 32 | +@RequestMapping("/iot/device") | |
| 33 | +@Validated | |
| 34 | +public class IotDeviceController { | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + private IotDeviceService deviceService; | |
| 38 | + | |
| 39 | + @PostMapping("/create") | |
| 40 | + @Operation(summary = "创建IoT 设备") | |
| 41 | + @PreAuthorize("@ss.hasPermission('iot:device:create')") | |
| 42 | + public CommonResult<Long> createDevice(@Valid @RequestBody IotDeviceSaveReqVO createReqVO) { | |
| 43 | + return success(deviceService.createDevice(createReqVO)); | |
| 44 | + } | |
| 45 | + | |
| 46 | + @PutMapping("/update-status") | |
| 47 | + @Operation(summary = "更新IoT 设备状态") | |
| 48 | + @Parameter(name = "id", description = "编号", required = true) | |
| 49 | + @Parameter(name = "status", description = "状态", required = true, example = "1") | |
| 50 | + @PreAuthorize("@ss.hasPermission('iot:device:update')") | |
| 51 | + public CommonResult<Boolean> updateDeviceStatus(@RequestParam("id") Long id, | |
| 52 | + @RequestParam("status") Integer status) { | |
| 53 | + deviceService.updateDeviceStatus(id, status); | |
| 54 | + return success(true); | |
| 55 | + } | |
| 56 | + | |
| 57 | + @PutMapping("/update") | |
| 58 | + @Operation(summary = "更新IoT 设备") | |
| 59 | + @PreAuthorize("@ss.hasPermission('iot:device:update')") | |
| 60 | + public CommonResult<Boolean> updateDevice(@Valid @RequestBody IotDeviceSaveReqVO updateReqVO) { | |
| 61 | + deviceService.updateDevice(updateReqVO); | |
| 62 | + return success(true); | |
| 63 | + } | |
| 64 | + | |
| 65 | + @DeleteMapping("/delete") | |
| 66 | + @Operation(summary = "删除IoT 设备") | |
| 67 | + @Parameter(name = "id", description = "编号", required = true) | |
| 68 | + @PreAuthorize("@ss.hasPermission('iot:device:delete')") | |
| 69 | + public CommonResult<Boolean> deleteDevice(@RequestParam("id") Long id) { | |
| 70 | + deviceService.deleteDevice(id); | |
| 71 | + return success(true); | |
| 72 | + } | |
| 73 | + | |
| 74 | + @GetMapping("/get") | |
| 75 | + @Operation(summary = "获得IoT 设备") | |
| 76 | + @Parameter(name = "id", description = "编号", required = true, example = "1024") | |
| 77 | + @PreAuthorize("@ss.hasPermission('iot:device:query')") | |
| 78 | + public CommonResult<IotDeviceRespVO> getDevice(@RequestParam("id") Long id) { | |
| 79 | + IotDeviceDO device = deviceService.getDevice(id); | |
| 80 | + return success(BeanUtils.toBean(device, IotDeviceRespVO.class)); | |
| 81 | + } | |
| 82 | + | |
| 83 | + @GetMapping("/page") | |
| 84 | + @Operation(summary = "获得IoT 设备分页") | |
| 85 | + @PreAuthorize("@ss.hasPermission('iot:device:query')") | |
| 86 | + public CommonResult<PageResult<IotDeviceRespVO>> getDevicePage(@Valid IotDevicePageReqVO pageReqVO) { | |
| 87 | + PageResult<IotDeviceDO> pageResult = deviceService.getDevicePage(pageReqVO); | |
| 88 | + return success(BeanUtils.toBean(pageResult, IotDeviceRespVO.class)); | |
| 89 | + } | |
| 90 | + | |
| 91 | + @GetMapping("/export-excel") | |
| 92 | + @Operation(summary = "导出IoT 设备 Excel") | |
| 93 | + @PreAuthorize("@ss.hasPermission('iot:device:export')") | |
| 94 | + @ApiAccessLog(operateType = EXPORT) | |
| 95 | + public void exportDeviceExcel(@Valid IotDevicePageReqVO pageReqVO, | |
| 96 | + HttpServletResponse response) throws IOException { | |
| 97 | + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); | |
| 98 | + List<IotDeviceDO> list = deviceService.getDevicePage(pageReqVO).getList(); | |
| 99 | + // 导出 Excel | |
| 100 | + ExcelUtils.write(response, "IoT 设备.xls", "数据", IotDeviceRespVO.class, | |
| 101 | + BeanUtils.toBean(list, IotDeviceRespVO.class)); | |
| 102 | + } | |
| 103 | + | |
| 104 | +} | |
| 0 | 105 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDevicePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; | |
| 2 | + | |
| 3 | +import lombok.*; | |
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | |
| 6 | +import java.math.BigDecimal; | |
| 7 | +import org.springframework.format.annotation.DateTimeFormat; | |
| 8 | +import java.time.LocalDateTime; | |
| 9 | + | |
| 10 | +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 11 | + | |
| 12 | +@Schema(description = "管理后台 - IoT 设备分页 Request VO") | |
| 13 | +@Data | |
| 14 | +@EqualsAndHashCode(callSuper = true) | |
| 15 | +@ToString(callSuper = true) | |
| 16 | +public class IotDevicePageReqVO extends PageParam { | |
| 17 | + | |
| 18 | + @Schema(description = "设备唯一标识符,全局唯一,用于识别设备") | |
| 19 | + private String deviceKey; | |
| 20 | + | |
| 21 | + @Schema(description = "设备名称,在产品内唯一,用于标识设备", example = "王五") | |
| 22 | + private String deviceName; | |
| 23 | + | |
| 24 | + @Schema(description = "产品 ID,关联 iot_product 表的 id", example = "26202") | |
| 25 | + private Long productId; | |
| 26 | + | |
| 27 | + @Schema(description = "产品 Key,关联 iot_product 表的 product_key") | |
| 28 | + private String productKey; | |
| 29 | + | |
| 30 | + @Schema(description = "设备类型:0 - 直连设备,1 - 网关子设备,2 - 网关设备", example = "1") | |
| 31 | + private Integer deviceType; | |
| 32 | + | |
| 33 | + @Schema(description = "设备备注名称,供用户自定义备注", example = "张三") | |
| 34 | + private String nickname; | |
| 35 | + | |
| 36 | + @Schema(description = "网关设备 ID,子设备需要关联的网关设备 ID", example = "16380") | |
| 37 | + private Long gatewayId; | |
| 38 | + | |
| 39 | + @Schema(description = "设备状态:0 - 未激活,1 - 在线,2 - 离线,3 - 已禁用", example = "1") | |
| 40 | + private Integer status; | |
| 41 | + | |
| 42 | + @Schema(description = "设备状态最后更新时间") | |
| 43 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 44 | + private LocalDateTime[] statusLastUpdateTime; | |
| 45 | + | |
| 46 | + @Schema(description = "最后上线时间") | |
| 47 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 48 | + private LocalDateTime[] lastOnlineTime; | |
| 49 | + | |
| 50 | + @Schema(description = "最后离线时间") | |
| 51 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 52 | + private LocalDateTime[] lastOfflineTime; | |
| 53 | + | |
| 54 | + @Schema(description = "设备激活时间") | |
| 55 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 56 | + private LocalDateTime[] activeTime; | |
| 57 | + | |
| 58 | + @Schema(description = "设备的 IP 地址") | |
| 59 | + private String ip; | |
| 60 | + | |
| 61 | + @Schema(description = "设备的固件版本") | |
| 62 | + private String firmwareVersion; | |
| 63 | + | |
| 64 | + @Schema(description = "设备密钥,用于设备认证,需安全存储") | |
| 65 | + private String deviceSecret; | |
| 66 | + | |
| 67 | + @Schema(description = "MQTT 客户端 ID", example = "24602") | |
| 68 | + private String mqttClientId; | |
| 69 | + | |
| 70 | + @Schema(description = "MQTT 用户名", example = "芋艿") | |
| 71 | + private String mqttUsername; | |
| 72 | + | |
| 73 | + @Schema(description = "MQTT 密码") | |
| 74 | + private String mqttPassword; | |
| 75 | + | |
| 76 | + @Schema(description = "认证类型(如一机一密、动态注册)", example = "2") | |
| 77 | + private String authType; | |
| 78 | + | |
| 79 | + @Schema(description = "设备位置的纬度,范围 -90.000000 ~ 90.000000") | |
| 80 | + private BigDecimal latitude; | |
| 81 | + | |
| 82 | + @Schema(description = "设备位置的经度,范围 -180.000000 ~ 180.000000") | |
| 83 | + private BigDecimal longitude; | |
| 84 | + | |
| 85 | + @Schema(description = "地区编码,符合国家地区编码标准,关联地区表", example = "16995") | |
| 86 | + private Integer areaId; | |
| 87 | + | |
| 88 | + @Schema(description = "设备详细地址") | |
| 89 | + private String address; | |
| 90 | + | |
| 91 | + @Schema(description = "设备序列号") | |
| 92 | + private String serialNumber; | |
| 93 | + | |
| 94 | + @Schema(description = "创建时间") | |
| 95 | + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) | |
| 96 | + private LocalDateTime[] createTime; | |
| 97 | + | |
| 98 | +} | |
| 0 | 99 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; | |
| 2 | + | |
| 3 | +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; | |
| 4 | +import com.alibaba.excel.annotation.ExcelProperty; | |
| 5 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 6 | +import lombok.Data; | |
| 7 | + | |
| 8 | +import java.math.BigDecimal; | |
| 9 | +import java.time.LocalDateTime; | |
| 10 | + | |
| 11 | +@Schema(description = "管理后台 - IoT 设备 Response VO") | |
| 12 | +@Data | |
| 13 | +@ExcelIgnoreUnannotated | |
| 14 | +public class IotDeviceRespVO { | |
| 15 | + | |
| 16 | + @Schema(description = "设备 ID,主键,自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") | |
| 17 | + private Long id; | |
| 18 | + | |
| 19 | + @Schema(description = "设备唯一标识符", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 20 | + @ExcelProperty("设备唯一标识符") | |
| 21 | + private String deviceKey; | |
| 22 | + | |
| 23 | + @Schema(description = "设备名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") | |
| 24 | + @ExcelProperty("设备名称备") | |
| 25 | + private String deviceName; | |
| 26 | + | |
| 27 | + @Schema(description = "产品 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202") | |
| 28 | + @ExcelProperty("产品 ID") | |
| 29 | + private Long productId; | |
| 30 | + | |
| 31 | + @Schema(description = "产品 Key", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 32 | + @ExcelProperty("产品 Key") | |
| 33 | + private String productKey; | |
| 34 | + | |
| 35 | + @Schema(description = "设备类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 36 | + @ExcelProperty("设备类型") | |
| 37 | + private Integer deviceType; | |
| 38 | + | |
| 39 | + @Schema(description = "设备备注名称", example = "张三") | |
| 40 | + @ExcelProperty("设备备注名称") | |
| 41 | + private String nickname; | |
| 42 | + | |
| 43 | + @Schema(description = "网关设备 ID", example = "16380") | |
| 44 | + @ExcelProperty("网关设备 ID") | |
| 45 | + private Long gatewayId; | |
| 46 | + | |
| 47 | + @Schema(description = "设备状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | |
| 48 | + @ExcelProperty("设备状态") | |
| 49 | + private Integer status; | |
| 50 | + | |
| 51 | + @Schema(description = "设备状态最后更新时间") | |
| 52 | + @ExcelProperty("设备状态最后更新时间") | |
| 53 | + private LocalDateTime statusLastUpdateTime; | |
| 54 | + | |
| 55 | + @Schema(description = "最后上线时间") | |
| 56 | + @ExcelProperty("最后上线时间") | |
| 57 | + private LocalDateTime lastOnlineTime; | |
| 58 | + | |
| 59 | + @Schema(description = "最后离线时间") | |
| 60 | + @ExcelProperty("最后离线时间") | |
| 61 | + private LocalDateTime lastOfflineTime; | |
| 62 | + | |
| 63 | + @Schema(description = "设备激活时间") | |
| 64 | + @ExcelProperty("设备激活时间") | |
| 65 | + private LocalDateTime activeTime; | |
| 66 | + | |
| 67 | + @Schema(description = "设备的 IP 地址") | |
| 68 | + @ExcelProperty("设备的 IP 地址") | |
| 69 | + private String ip; | |
| 70 | + | |
| 71 | + @Schema(description = "设备的固件版本") | |
| 72 | + @ExcelProperty("设备的固件版本") | |
| 73 | + private String firmwareVersion; | |
| 74 | + | |
| 75 | + @Schema(description = "设备密钥,用于设备认证,需安全存储") | |
| 76 | + @ExcelProperty("设备密钥") | |
| 77 | + private String deviceSecret; | |
| 78 | + | |
| 79 | + @Schema(description = "MQTT 客户端 ID", example = "24602") | |
| 80 | + @ExcelProperty("MQTT 客户端 ID") | |
| 81 | + private String mqttClientId; | |
| 82 | + | |
| 83 | + @Schema(description = "MQTT 用户名", example = "芋艿") | |
| 84 | + @ExcelProperty("MQTT 用户名") | |
| 85 | + private String mqttUsername; | |
| 86 | + | |
| 87 | + @Schema(description = "MQTT 密码") | |
| 88 | + @ExcelProperty("MQTT 密码") | |
| 89 | + private String mqttPassword; | |
| 90 | + | |
| 91 | + @Schema(description = "认证类型(如一机一密、动态注册)", example = "2") | |
| 92 | + @ExcelProperty("认证类型(如一机一密、动态注册)") | |
| 93 | + private String authType; | |
| 94 | + | |
| 95 | + @Schema(description = "设备位置的纬度,范围 -90.000000 ~ 90.000000") | |
| 96 | + @ExcelProperty("设备位置的纬度,范围 -90.000000 ~ 90.000000") | |
| 97 | + private BigDecimal latitude; | |
| 98 | + | |
| 99 | + @Schema(description = "设备位置的经度,范围 -180.000000 ~ 180.000000") | |
| 100 | + @ExcelProperty("设备位置的经度,范围 -180.000000 ~ 180.000000") | |
| 101 | + private BigDecimal longitude; | |
| 102 | + | |
| 103 | + @Schema(description = "地区编码,符合国家地区编码标准,关联地区表", example = "16995") | |
| 104 | + @ExcelProperty("地区编码,符合国家地区编码标准,关联地区表") | |
| 105 | + private Integer areaId; | |
| 106 | + | |
| 107 | + @Schema(description = "设备详细地址") | |
| 108 | + @ExcelProperty("设备详细地址") | |
| 109 | + private String address; | |
| 110 | + | |
| 111 | + @Schema(description = "设备序列号") | |
| 112 | + @ExcelProperty("设备序列号") | |
| 113 | + private String serialNumber; | |
| 114 | + | |
| 115 | + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) | |
| 116 | + @ExcelProperty("创建时间") | |
| 117 | + private LocalDateTime createTime; | |
| 118 | + | |
| 119 | +} | |
| 0 | 120 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/device/vo/IotDeviceSaveReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.controller.admin.device.vo; | |
| 2 | + | |
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | |
| 4 | +import lombok.Data; | |
| 5 | + | |
| 6 | +@Schema(description = "管理后台 - IoT 设备新增/修改 Request VO") | |
| 7 | +@Data | |
| 8 | +public class IotDeviceSaveReqVO { | |
| 9 | + | |
| 10 | + @Schema(description = "设备 ID,主键,自增", requiredMode = Schema.RequiredMode.REQUIRED, example = "177") | |
| 11 | + private Long id; | |
| 12 | + | |
| 13 | + @Schema(description = "设备名称,在产品内唯一,用于标识设备", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") | |
| 14 | + private String deviceName; | |
| 15 | + | |
| 16 | + @Schema(description = "设备备注名称,供用户自定义备注", example = "张三") | |
| 17 | + private String nickname; | |
| 18 | + | |
| 19 | + @Schema(description = "产品 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26202") | |
| 20 | + private Long productId; | |
| 21 | + | |
| 22 | +} | |
| 0 | 23 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/convert/package-info.java
0 → 100644
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/device/IotDeviceDO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.dal.dataobject.device; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | |
| 4 | +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | |
| 5 | +import com.baomidou.mybatisplus.annotation.KeySequence; | |
| 6 | +import com.baomidou.mybatisplus.annotation.TableId; | |
| 7 | +import com.baomidou.mybatisplus.annotation.TableName; | |
| 8 | +import lombok.*; | |
| 9 | + | |
| 10 | +import java.math.BigDecimal; | |
| 11 | +import java.time.LocalDateTime; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * IoT 设备 DO | |
| 15 | + * | |
| 16 | + * @author 芋道源码 | |
| 17 | + */ | |
| 18 | +@TableName("iot_device") | |
| 19 | +@KeySequence("iot_device_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 | |
| 20 | +@Data | |
| 21 | +@EqualsAndHashCode(callSuper = true) | |
| 22 | +@ToString(callSuper = true) | |
| 23 | +@Builder | |
| 24 | +@NoArgsConstructor | |
| 25 | +@AllArgsConstructor | |
| 26 | +public class IotDeviceDO extends BaseDO { | |
| 27 | + | |
| 28 | + /** | |
| 29 | + * 设备 ID,主键,自增 | |
| 30 | + */ | |
| 31 | + @TableId | |
| 32 | + private Long id; | |
| 33 | + /** | |
| 34 | + * 设备唯一标识符,全局唯一,用于识别设备 | |
| 35 | + */ | |
| 36 | + private String deviceKey; | |
| 37 | + /** | |
| 38 | + * 设备名称,在产品内唯一,用于标识设备 | |
| 39 | + */ | |
| 40 | + private String deviceName; | |
| 41 | + /** | |
| 42 | + * 产品 ID,关联 iot_product 表的 id | |
| 43 | + * 关联 {@link IotProductDO#getId()} | |
| 44 | + */ | |
| 45 | + private Long productId; | |
| 46 | + /** | |
| 47 | + * 产品 Key,关联 iot_product 表的 product_key | |
| 48 | + * 关联 {@link IotProductDO#getProductKey()} | |
| 49 | + */ | |
| 50 | + private String productKey; | |
| 51 | + /** | |
| 52 | + * 设备类型:0 - 直连设备,1 - 网关子设备,2 - 网关设备 | |
| 53 | + * 关联 {@link IotProductDO#getDeviceType()} | |
| 54 | + */ | |
| 55 | + private Integer deviceType; | |
| 56 | + /** | |
| 57 | + * 设备备注名称,供用户自定义备注 | |
| 58 | + */ | |
| 59 | + private String nickname; | |
| 60 | + /** | |
| 61 | + * 网关设备 ID,子设备需要关联的网关设备 ID | |
| 62 | + */ | |
| 63 | + private Long gatewayId; | |
| 64 | + /** | |
| 65 | + * 设备状态:0 - 未激活,1 - 在线,2 - 离线,3 - 已禁用 | |
| 66 | + * 关联 {@link cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum} | |
| 67 | + */ | |
| 68 | + private Integer status; | |
| 69 | + /** | |
| 70 | + * 设备状态最后更新时间 | |
| 71 | + */ | |
| 72 | + private LocalDateTime statusLastUpdateTime; | |
| 73 | + /** | |
| 74 | + * 最后上线时间 | |
| 75 | + */ | |
| 76 | + private LocalDateTime lastOnlineTime; | |
| 77 | + /** | |
| 78 | + * 最后离线时间 | |
| 79 | + */ | |
| 80 | + private LocalDateTime lastOfflineTime; | |
| 81 | + /** | |
| 82 | + * 设备激活时间 | |
| 83 | + */ | |
| 84 | + private LocalDateTime activeTime; | |
| 85 | + /** | |
| 86 | + * 设备的 IP 地址 | |
| 87 | + */ | |
| 88 | + private String ip; | |
| 89 | + /** | |
| 90 | + * 设备的固件版本 | |
| 91 | + */ | |
| 92 | + private String firmwareVersion; | |
| 93 | + /** | |
| 94 | + * 设备密钥,用于设备认证,需安全存储 | |
| 95 | + */ | |
| 96 | + private String deviceSecret; | |
| 97 | + /** | |
| 98 | + * MQTT 客户端 ID | |
| 99 | + */ | |
| 100 | + private String mqttClientId; | |
| 101 | + /** | |
| 102 | + * MQTT 用户名 | |
| 103 | + */ | |
| 104 | + private String mqttUsername; | |
| 105 | + /** | |
| 106 | + * MQTT 密码 | |
| 107 | + */ | |
| 108 | + private String mqttPassword; | |
| 109 | + /** | |
| 110 | + * 认证类型(如一机一密、动态注册) | |
| 111 | + */ | |
| 112 | + private String authType; | |
| 113 | + /** | |
| 114 | + * 设备位置的纬度,范围 -90.000000 ~ 90.000000 | |
| 115 | + */ | |
| 116 | + private BigDecimal latitude; | |
| 117 | + /** | |
| 118 | + * 设备位置的经度,范围 -180.000000 ~ 180.000000 | |
| 119 | + */ | |
| 120 | + private BigDecimal longitude; | |
| 121 | + /** | |
| 122 | + * 地区编码,符合国家地区编码标准,关联地区表 | |
| 123 | + */ | |
| 124 | + private Integer areaId; | |
| 125 | + /** | |
| 126 | + * 设备详细地址 | |
| 127 | + */ | |
| 128 | + private String address; | |
| 129 | + /** | |
| 130 | + * 设备序列号 | |
| 131 | + */ | |
| 132 | + private String serialNumber; | |
| 133 | + | |
| 134 | +} | |
| 0 | 135 | \ 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
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.dal.mysql.device; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | |
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; | |
| 7 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | |
| 8 | +import org.apache.ibatis.annotations.Mapper; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * IoT 设备 Mapper | |
| 12 | + * | |
| 13 | + * @author 芋道源码 | |
| 14 | + */ | |
| 15 | +@Mapper | |
| 16 | +public interface IotDeviceMapper extends BaseMapperX<IotDeviceDO> { | |
| 17 | + | |
| 18 | + default PageResult<IotDeviceDO> selectPage(IotDevicePageReqVO reqVO) { | |
| 19 | + return selectPage(reqVO, new LambdaQueryWrapperX<IotDeviceDO>() | |
| 20 | + .eqIfPresent(IotDeviceDO::getDeviceKey, reqVO.getDeviceKey()) | |
| 21 | + .likeIfPresent(IotDeviceDO::getDeviceName, reqVO.getDeviceName()) | |
| 22 | + .eqIfPresent(IotDeviceDO::getProductId, reqVO.getProductId()) | |
| 23 | + .eqIfPresent(IotDeviceDO::getProductKey, reqVO.getProductKey()) | |
| 24 | + .eqIfPresent(IotDeviceDO::getDeviceType, reqVO.getDeviceType()) | |
| 25 | + .likeIfPresent(IotDeviceDO::getNickname, reqVO.getNickname()) | |
| 26 | + .eqIfPresent(IotDeviceDO::getGatewayId, reqVO.getGatewayId()) | |
| 27 | + .eqIfPresent(IotDeviceDO::getStatus, reqVO.getStatus()) | |
| 28 | + .betweenIfPresent(IotDeviceDO::getStatusLastUpdateTime, reqVO.getStatusLastUpdateTime()) | |
| 29 | + .betweenIfPresent(IotDeviceDO::getLastOnlineTime, reqVO.getLastOnlineTime()) | |
| 30 | + .betweenIfPresent(IotDeviceDO::getLastOfflineTime, reqVO.getLastOfflineTime()) | |
| 31 | + .betweenIfPresent(IotDeviceDO::getActiveTime, reqVO.getActiveTime()) | |
| 32 | + .eqIfPresent(IotDeviceDO::getIp, reqVO.getIp()) | |
| 33 | + .eqIfPresent(IotDeviceDO::getFirmwareVersion, reqVO.getFirmwareVersion()) | |
| 34 | + .eqIfPresent(IotDeviceDO::getDeviceSecret, reqVO.getDeviceSecret()) | |
| 35 | + .eqIfPresent(IotDeviceDO::getMqttClientId, reqVO.getMqttClientId()) | |
| 36 | + .likeIfPresent(IotDeviceDO::getMqttUsername, reqVO.getMqttUsername()) | |
| 37 | + .eqIfPresent(IotDeviceDO::getMqttPassword, reqVO.getMqttPassword()) | |
| 38 | + .eqIfPresent(IotDeviceDO::getAuthType, reqVO.getAuthType()) | |
| 39 | + .eqIfPresent(IotDeviceDO::getLatitude, reqVO.getLatitude()) | |
| 40 | + .eqIfPresent(IotDeviceDO::getLongitude, reqVO.getLongitude()) | |
| 41 | + .eqIfPresent(IotDeviceDO::getAreaId, reqVO.getAreaId()) | |
| 42 | + .eqIfPresent(IotDeviceDO::getAddress, reqVO.getAddress()) | |
| 43 | + .eqIfPresent(IotDeviceDO::getSerialNumber, reqVO.getSerialNumber()) | |
| 44 | + .betweenIfPresent(IotDeviceDO::getCreateTime, reqVO.getCreateTime()) | |
| 45 | + .orderByDesc(IotDeviceDO::getId)); | |
| 46 | + } | |
| 47 | + | |
| 48 | + default IotDeviceDO selectByProductKeyAndDeviceName(String productKey, String deviceName) { | |
| 49 | + return selectOne(IotDeviceDO::getProductKey, productKey, | |
| 50 | + IotDeviceDO::getDeviceName, deviceName); | |
| 51 | + } | |
| 52 | + | |
| 53 | + default long selectCountByGatewayId(Long id) { | |
| 54 | + return selectCount(IotDeviceDO::getGatewayId, id); | |
| 55 | + } | |
| 56 | +} | |
| 0 | 57 | \ 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
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.device; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.StrUtil; | |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | |
| 6 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDevicePageReqVO; | |
| 7 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.IotDeviceSaveReqVO; | |
| 8 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | |
| 9 | +import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | |
| 10 | +import cn.iocoder.yudao.module.iot.dal.mysql.device.IotDeviceMapper; | |
| 11 | +import cn.iocoder.yudao.module.iot.dal.mysql.product.IotProductMapper; | |
| 12 | +import cn.iocoder.yudao.module.iot.enums.device.IotDeviceStatusEnum; | |
| 13 | +import jakarta.annotation.Resource; | |
| 14 | +import lombok.extern.slf4j.Slf4j; | |
| 15 | +import org.springframework.stereotype.Service; | |
| 16 | +import org.springframework.transaction.annotation.Transactional; | |
| 17 | +import org.springframework.validation.annotation.Validated; | |
| 18 | + | |
| 19 | +import java.time.LocalDateTime; | |
| 20 | +import java.util.UUID; | |
| 21 | + | |
| 22 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | |
| 23 | +import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; | |
| 24 | + | |
| 25 | +/** | |
| 26 | + * IoT 设备 Service 实现类 | |
| 27 | + * | |
| 28 | + * @author 芋道源码 | |
| 29 | + */ | |
| 30 | +@Service | |
| 31 | +@Validated | |
| 32 | +@Slf4j | |
| 33 | +public class DeviceServiceImpl implements IotDeviceService { | |
| 34 | + | |
| 35 | + @Resource | |
| 36 | + private IotDeviceMapper deviceMapper; | |
| 37 | + @Resource | |
| 38 | + private IotProductMapper productMapper; | |
| 39 | + | |
| 40 | + /** | |
| 41 | + * 创建 IoT 设备 | |
| 42 | + * | |
| 43 | + * @param createReqVO 创建请求 VO | |
| 44 | + * @return 设备 ID | |
| 45 | + */ | |
| 46 | + @Override | |
| 47 | + @Transactional(rollbackFor = Exception.class) | |
| 48 | + public Long createDevice(IotDeviceSaveReqVO createReqVO) { | |
| 49 | + // 1. 转换 VO 为 DO | |
| 50 | + IotDeviceDO device = BeanUtils.toBean(createReqVO, IotDeviceDO.class); | |
| 51 | + | |
| 52 | + // 2. 根据产品 ID 查询产品信息 | |
| 53 | + IotProductDO product = productMapper.selectById(createReqVO.getProductId()); | |
| 54 | + if (product == null) { | |
| 55 | + throw exception(PRODUCT_NOT_EXISTS); | |
| 56 | + } | |
| 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())); | |
| 63 | + } | |
| 64 | + | |
| 65 | + // 4. 校验设备名称在同一产品下是否唯一 | |
| 66 | + validateDeviceNameUnique(device.getProductKey(), device.getDeviceName()); | |
| 67 | + | |
| 68 | + // 5. 生成并设置必要的字段 | |
| 69 | + device.setDeviceKey(generateUniqueDeviceKey()); | |
| 70 | + device.setDeviceSecret(generateDeviceSecret()); | |
| 71 | + device.setMqttClientId(generateMqttClientId()); | |
| 72 | + device.setMqttUsername(generateMqttUsername(device.getDeviceName(), device.getProductKey())); | |
| 73 | + device.setMqttPassword(generateMqttPassword()); | |
| 74 | + | |
| 75 | + // 6. 设置设备状态为未激活 | |
| 76 | + device.setStatus(IotDeviceStatusEnum.INACTIVE.getStatus()); | |
| 77 | + device.setStatusLastUpdateTime(LocalDateTime.now()); | |
| 78 | + | |
| 79 | + // 7. 插入到数据库 | |
| 80 | + deviceMapper.insert(device); | |
| 81 | + | |
| 82 | + // 8. 返回生成的设备 ID | |
| 83 | + return device.getId(); | |
| 84 | + } | |
| 85 | + | |
| 86 | + /** | |
| 87 | + * 校验设备名称在同一产品下是否唯一 | |
| 88 | + * | |
| 89 | + * @param productKey 产品 Key | |
| 90 | + * @param deviceName 设备名称 | |
| 91 | + */ | |
| 92 | + private void validateDeviceNameUnique(String productKey, String deviceName) { | |
| 93 | + IotDeviceDO existingDevice = deviceMapper.selectByProductKeyAndDeviceName(productKey, deviceName); | |
| 94 | + if (existingDevice != null) { | |
| 95 | + throw exception(DEVICE_NAME_EXISTS); | |
| 96 | + } | |
| 97 | + } | |
| 98 | + | |
| 99 | + /** | |
| 100 | + * 生成唯一的 deviceKey | |
| 101 | + * | |
| 102 | + * @return 生成的 deviceKey | |
| 103 | + */ | |
| 104 | + private String generateUniqueDeviceKey() { | |
| 105 | + return UUID.randomUUID().toString(); | |
| 106 | + } | |
| 107 | + | |
| 108 | + /** | |
| 109 | + * 生成 deviceSecret | |
| 110 | + * | |
| 111 | + * @return 生成的 deviceSecret | |
| 112 | + */ | |
| 113 | + private String generateDeviceSecret() { | |
| 114 | + // 32 位随机字符串 | |
| 115 | + return UUID.randomUUID().toString().replace("-", ""); | |
| 116 | + } | |
| 117 | + | |
| 118 | + /** | |
| 119 | + * 生成 MQTT Client ID | |
| 120 | + * | |
| 121 | + * @return 生成的 MQTT Client ID | |
| 122 | + */ | |
| 123 | + private String generateMqttClientId() { | |
| 124 | + return UUID.randomUUID().toString(); | |
| 125 | + } | |
| 126 | + | |
| 127 | + /** | |
| 128 | + * 生成 MQTT Username | |
| 129 | + * | |
| 130 | + * @param deviceName 设备名称 | |
| 131 | + * @param productKey 产品 Key | |
| 132 | + * @return 生成的 MQTT Username | |
| 133 | + */ | |
| 134 | + private String generateMqttUsername(String deviceName, String productKey) { | |
| 135 | + return deviceName + "&" + productKey; | |
| 136 | + } | |
| 137 | + | |
| 138 | + /** | |
| 139 | + * 生成 MQTT Password | |
| 140 | + * | |
| 141 | + * @return 生成的 MQTT Password | |
| 142 | + */ | |
| 143 | + private String generateMqttPassword() { | |
| 144 | + // 在实际应用中,建议使用更安全的方法生成 MQTT Password,如加密或哈希 | |
| 145 | + return UUID.randomUUID().toString(); | |
| 146 | + } | |
| 147 | + | |
| 148 | + /** | |
| 149 | + * 生成唯一的 DeviceName | |
| 150 | + * | |
| 151 | + * @param productId 产品 ID | |
| 152 | + * @return 生成的唯一 DeviceName | |
| 153 | + */ | |
| 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); | |
| 175 | + } | |
| 176 | + return product.getProductKey(); | |
| 177 | + } | |
| 178 | + | |
| 179 | + @Override | |
| 180 | + @Transactional(rollbackFor = Exception.class) | |
| 181 | + public void updateDevice(IotDeviceSaveReqVO updateReqVO) { | |
| 182 | + // 校验存在 | |
| 183 | + IotDeviceDO existingDevice = validateDeviceExists(updateReqVO.getId()); | |
| 184 | + | |
| 185 | + // 设备名称 和 产品 ID 不能修改 | |
| 186 | + if (updateReqVO.getDeviceName() != null && !updateReqVO.getDeviceName().equals(existingDevice.getDeviceName())) { | |
| 187 | + throw exception(DEVICE_NAME_CANNOT_BE_MODIFIED); | |
| 188 | + } | |
| 189 | + if (updateReqVO.getProductId() != null && !updateReqVO.getProductId().equals(existingDevice.getProductId())) { | |
| 190 | + throw exception(DEVICE_PRODUCT_CANNOT_BE_MODIFIED); | |
| 191 | + } | |
| 192 | + | |
| 193 | + // 更新 DO 对象 | |
| 194 | + IotDeviceDO updateObj = BeanUtils.toBean(updateReqVO, IotDeviceDO.class); | |
| 195 | + | |
| 196 | + // 更新到数据库 | |
| 197 | + deviceMapper.updateById(updateObj); | |
| 198 | + } | |
| 199 | + | |
| 200 | + @Override | |
| 201 | + @Transactional(rollbackFor = Exception.class) | |
| 202 | + 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 | + } | |
| 212 | + } | |
| 213 | + | |
| 214 | + // 删除设备 | |
| 215 | + deviceMapper.deleteById(id); | |
| 216 | + } | |
| 217 | + | |
| 218 | + /** | |
| 219 | + * 校验设备是否存在 | |
| 220 | + * | |
| 221 | + * @param id 设备 ID | |
| 222 | + * @return 设备对象 | |
| 223 | + */ | |
| 224 | + private IotDeviceDO validateDeviceExists(Long id) { | |
| 225 | + IotDeviceDO iotDeviceDO = deviceMapper.selectById(id); | |
| 226 | + if (iotDeviceDO == null) { | |
| 227 | + throw exception(DEVICE_NOT_EXISTS); | |
| 228 | + } | |
| 229 | + return iotDeviceDO; | |
| 230 | + } | |
| 231 | + | |
| 232 | + @Override | |
| 233 | + public IotDeviceDO getDevice(Long id) { | |
| 234 | + IotDeviceDO device = deviceMapper.selectById(id); | |
| 235 | + if (device == null) { | |
| 236 | + throw exception(DEVICE_NOT_EXISTS); | |
| 237 | + } | |
| 238 | + return device; | |
| 239 | + } | |
| 240 | + | |
| 241 | + @Override | |
| 242 | + public PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO) { | |
| 243 | + return deviceMapper.selectPage(pageReqVO); | |
| 244 | + } | |
| 245 | + | |
| 246 | + @Override | |
| 247 | + @Transactional(rollbackFor = Exception.class) | |
| 248 | + public void updateDeviceStatus(Long id, Integer status) { | |
| 249 | + // 校验存在 | |
| 250 | + validateDeviceExists(id); | |
| 251 | + | |
| 252 | + // 校验状态是否合法 | |
| 253 | + if (!IotDeviceStatusEnum.isValidStatus(status)) { | |
| 254 | + throw exception(DEVICE_INVALID_DEVICE_STATUS); | |
| 255 | + } | |
| 256 | + | |
| 257 | + // 更新状态和更新时间 | |
| 258 | + IotDeviceDO updateObj = new IotDeviceDO() | |
| 259 | + .setId(id) | |
| 260 | + .setStatus(status) | |
| 261 | + .setStatusLastUpdateTime(LocalDateTime.now()); | |
| 262 | + deviceMapper.updateById(updateObj); | |
| 263 | + } | |
| 264 | +} | |
| 0 | 265 | \ 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
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.device; | |
| 2 | + | |
| 3 | +import jakarta.validation.*; | |
| 4 | +import cn.iocoder.yudao.module.iot.controller.admin.device.vo.*; | |
| 5 | +import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | |
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * IoT 设备 Service 接口 | |
| 10 | + * | |
| 11 | + * @author 芋道源码 | |
| 12 | + */ | |
| 13 | +public interface IotDeviceService { | |
| 14 | + | |
| 15 | + /** | |
| 16 | + * 创建IoT 设备 | |
| 17 | + * | |
| 18 | + * @param createReqVO 创建信息 | |
| 19 | + * @return 编号 | |
| 20 | + */ | |
| 21 | + Long createDevice(@Valid IotDeviceSaveReqVO createReqVO); | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * 更新IoT 设备 | |
| 25 | + * | |
| 26 | + * @param updateReqVO 更新信息 | |
| 27 | + */ | |
| 28 | + void updateDevice(@Valid IotDeviceSaveReqVO updateReqVO); | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 删除IoT 设备 | |
| 32 | + * | |
| 33 | + * @param id 编号 | |
| 34 | + */ | |
| 35 | + void deleteDevice(Long id); | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * 获得IoT 设备 | |
| 39 | + * | |
| 40 | + * @param id 编号 | |
| 41 | + * @return IoT 设备 | |
| 42 | + */ | |
| 43 | + IotDeviceDO getDevice(Long id); | |
| 44 | + | |
| 45 | + /** | |
| 46 | + * 获得IoT 设备分页 | |
| 47 | + * | |
| 48 | + * @param pageReqVO 分页查询 | |
| 49 | + * @return IoT 设备分页 | |
| 50 | + */ | |
| 51 | + PageResult<IotDeviceDO> getDevicePage(IotDevicePageReqVO pageReqVO); | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * 更新IoT 设备状态 | |
| 55 | + * | |
| 56 | + * @param id 编号 | |
| 57 | + * @param status 状态 | |
| 58 | + */ | |
| 59 | + void updateDeviceStatus(Long id, Integer status); | |
| 60 | +} | |
| 0 | 61 | \ No newline at end of file | ... | ... |
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDeviceMapper.xml
0 → 100644
| 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> | |
| 0 | 13 | \ 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
0 → 100644
| 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 | +} | |
| 0 | 220 | \ No newline at end of file | ... | ... |