kkk-ops 4 weeks ago
commit 0f450424ab

@ -6,6 +6,7 @@ import org.quartz.*;
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.NOT_IMPLEMENTED;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static org.quartz.Scheduler.DEFAULT_GROUP;
/**
* {@link org.quartz.Scheduler}
@ -41,13 +42,15 @@ public class SchedulerManager {
Integer retryCount, Integer retryInterval)
throws SchedulerException {
validateScheduler();
String jobKeyName = jobHandlerName+ "_"+ jobId;
JobKey jobKey = JobKey.jobKey(jobKeyName, DEFAULT_GROUP);
// 创建 JobDetail 对象
JobDetail jobDetail = JobBuilder.newJob(JobHandlerInvoker.class)
.usingJobData(JobDataKeyEnum.JOB_ID.name(), jobId)
.usingJobData(JobDataKeyEnum.JOB_HANDLER_NAME.name(), jobHandlerName)
.withIdentity(jobHandlerName).build();
.withIdentity(jobKey).build();
// 创建 Trigger 对象
Trigger trigger = this.buildTrigger(jobHandlerName, jobHandlerParam, cronExpression, retryCount, retryInterval);
Trigger trigger = this.buildTrigger(jobKey, jobHandlerParam, cronExpression, retryCount, retryInterval);
// 新增 Job 调度
scheduler.scheduleJob(jobDetail, trigger);
}
@ -140,6 +143,18 @@ public class SchedulerManager {
.build();
}
private Trigger buildTrigger(JobKey jobKey, String jobHandlerParam, String cronExpression,
Integer retryCount, Integer retryInterval) {
return TriggerBuilder.newTrigger()
.withIdentity(jobKey.getName())
.forJob(jobKey)
.withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
.usingJobData(JobDataKeyEnum.JOB_HANDLER_PARAM.name(), jobHandlerParam)
.usingJobData(JobDataKeyEnum.JOB_RETRY_COUNT.name(), retryCount)
.usingJobData(JobDataKeyEnum.JOB_RETRY_INTERVAL.name(), retryInterval)
.build();
}
private void validateScheduler() {
if (scheduler == null) {
throw exception0(NOT_IMPLEMENTED.getCode(),

@ -61,7 +61,7 @@ public class JobServiceImpl implements JobService {
jobMapper.insert(job);
// 3.1 添加 Job 到 Quartz 中
schedulerManager.addJob(job.getId(), job.getName(), job.getHandlerParam(), job.getCronExpression(),
schedulerManager.addJob(job.getId(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
createReqVO.getRetryCount(), createReqVO.getRetryInterval());
// 3.2 更新 JobDO
JobDO updateObj = JobDO.builder().id(job.getId()).status(JobStatusEnum.STOP.getStatus()).build();
@ -135,7 +135,7 @@ public class JobServiceImpl implements JobService {
JobDO job = validateJobExists(id);
// 触发 Quartz 中的 Job
schedulerManager.triggerJob(job.getId(), job.getName(), job.getHandlerParam());
schedulerManager.triggerJob(job.getId(), job.getHandlerName(), job.getHandlerParam());
}
@Override

@ -49,5 +49,8 @@ public interface ErrorCodeConstants {
ErrorCode OPC_CLOSE_CONNECT_FAILURE= new ErrorCode(1_003_000_000, "OPC断开连接失败");
ErrorCode OPC_PARAMETER_DOES_NOT_EXIST= new ErrorCode(1_003_000_000, "连接关闭参数不存在");
ErrorCode CREATE_TDENGINE_FAILURE= new ErrorCode(1_003_000_000, "创建Tdengine子表失败");
ErrorCode RECIPE_NOT_EXISTS = new ErrorCode(1_003_000_000, "配方主不存在");
ErrorCode RECIPE_POINT_NOT_EXISTS = new ErrorCode(1_003_000_000, "配方点位配置表(配方与设备点位关联)不存在");
ErrorCode RECIPE_TYPE_NOT_EXISTS = new ErrorCode(1_003_000_000, "配方类型表(基础字典)不存在");
}

@ -176,13 +176,22 @@ public class DeviceController {
@GetMapping("/singleDevice")
@Operation(summary = "单设备查看")
// @PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<Map<String, List<DeviceContactModelDO>> > singleDevice(@RequestParam("deviceId") Long deviceId) throws JsonProcessingException {
Map<String, List<DeviceContactModelDO>> deviceContactModelDO=deviceService.singleDevice(deviceId);
public CommonResult<Map<String, List<Map<String, Object>>>> singleDevice(@RequestParam("deviceId") Long deviceId) throws JsonProcessingException {
Map<String, List<Map<String, Object>>> deviceContactModelDO=deviceService.singleDevice(deviceId);
return success(deviceContactModelDO);
}
@GetMapping("/historyRecord")
@Operation(summary = "历史记录查询")
// @PreAuthorize("@ss.hasPermission('iot:device:query')")
public CommonResult<List<Map<String, Object>>> historyRecord(@RequestParam("deviceId") Long deviceId,
@RequestParam(name = "collectionStartTime", required = false) String collectionStartTime,
@RequestParam(name = "collectionEndTime", required = false) String collectionEndTime
) throws JsonProcessingException {
List<Map<String, Object>> deviceContactModelDO=deviceService.historyRecord(deviceId,collectionStartTime,collectionEndTime);
return success(deviceContactModelDO);
}

@ -31,4 +31,10 @@ public class LineDeviceRequestVO extends PageParam {
@Schema(description = "采集时间")
private LocalDateTime collectionTime;
@Schema(description = "开始采集时间")
private String collectionStartTime;
@Schema(description = "结束采集时间")
private String collectionEndTime;
}

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.iot.controller.admin.device.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 单设备监控 Resp VO")
@Data
public class SingleDeviceRespVO {
@Schema(description = "点位名称", example = "26404")
private String pointName;
@Schema(description = "点位值", example = "26404")
private String pointValue;
@Schema(description = "采集时间")
private String collectTime;
}

@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipe;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipe.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipe.RecipeDO;
import cn.iocoder.yudao.module.iot.service.recipe.RecipeService;
@Tag(name = "管理后台 - 配方管理主")
@RestController
@RequestMapping("/iot/recipe")
@Validated
public class RecipeController {
@Resource
private RecipeService recipeService;
@PostMapping("/create")
@Operation(summary = "创建配方管理主")
@PreAuthorize("@ss.hasPermission('iot:recipe:create')")
public CommonResult<Long> createRecipe(@Valid @RequestBody RecipeSaveReqVO createReqVO) {
return success(recipeService.createRecipe(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新配方管理主")
@PreAuthorize("@ss.hasPermission('iot:recipe:update')")
public CommonResult<Boolean> updateRecipe(@Valid @RequestBody RecipeSaveReqVO updateReqVO) {
recipeService.updateRecipe(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除配方管理主")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('iot:recipe:delete')")
public CommonResult<Boolean> deleteRecipe(@RequestParam("id") Long id) {
recipeService.deleteRecipe(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得配方管理主")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('iot:recipe:query')")
public CommonResult<RecipeRespVO> getRecipe(@RequestParam("id") Long id) {
RecipeDO recipe = recipeService.getRecipe(id);
return success(BeanUtils.toBean(recipe, RecipeRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得配方管理主分页")
@PreAuthorize("@ss.hasPermission('iot:recipe:query')")
public CommonResult<PageResult<RecipeRespVO>> getRecipePage(@Valid RecipePageReqVO pageReqVO) {
PageResult<RecipeDO> pageResult = recipeService.getRecipePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, RecipeRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出配方管理主 Excel")
@PreAuthorize("@ss.hasPermission('iot:recipe:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportRecipeExcel(@Valid RecipePageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<RecipeDO> list = recipeService.getRecipePage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "配方管理主.xls", "数据", RecipeRespVO.class,
BeanUtils.toBean(list, RecipeRespVO.class));
}
}

@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipe.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 配方管理主分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RecipePageReqVO extends PageParam {
@Schema(description = "配方名称", example = "赵六")
private String name;
@Schema(description = "配方编码")
private String recipeCode;
@Schema(description = "配方类型关联配方类型表code", example = "2")
private String recipeType;
@Schema(description = "关联产品名称", example = "李四")
private String productName;
@Schema(description = "关联设备ID关联iot_device.id", example = "14725")
private Long machineId;
@Schema(description = "关联设备编码(冗余字段)")
private String machineCode;
@Schema(description = "关联设备名称(冗余字段)", example = "芋艿")
private String machineName;
@Schema(description = "配方描述")
private String recipeDesc;
@Schema(description = "是否启用0-禁用1-启用)")
private Boolean isEnable;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
@Schema(description = "数据单位")
private String dataUnit;
}

@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipe.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 配方管理主 Response VO")
@Data
@ExcelIgnoreUnannotated
public class RecipeRespVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "6587")
@ExcelProperty("主键ID")
private Long id;
@Schema(description = "配方名称", example = "赵六")
@ExcelProperty("配方名称")
private String name;
@Schema(description = "配方编码")
@ExcelProperty("配方编码")
private String recipeCode;
@Schema(description = "配方类型关联配方类型表code", example = "2")
@ExcelProperty("配方类型关联配方类型表code")
private String recipeType;
@Schema(description = "关联产品名称", example = "李四")
@ExcelProperty("关联产品名称")
private String productName;
@Schema(description = "关联设备ID关联iot_device.id", example = "14725")
@ExcelProperty("关联设备ID关联iot_device.id")
private Long machineId;
@Schema(description = "关联设备编码(冗余字段)")
@ExcelProperty("关联设备编码(冗余字段)")
private String machineCode;
@Schema(description = "关联设备名称(冗余字段)", example = "芋艿")
@ExcelProperty("关联设备名称(冗余字段)")
private String machineName;
@Schema(description = "配方描述")
@ExcelProperty("配方描述")
private String recipeDesc;
@Schema(description = "是否启用0-禁用1-启用)", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("是否启用0-禁用1-启用)")
private Boolean isEnable;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
@Schema(description = "数据单位")
@ExcelProperty("数据单位")
private String dataUnit;
}

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipe.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 配方管理主新增/修改 Request VO")
@Data
public class RecipeSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "6587")
private Long id;
@Schema(description = "配方名称", example = "赵六")
private String name;
@Schema(description = "配方编码")
private String recipeCode;
@Schema(description = "配方类型关联配方类型表code", example = "2")
private String recipeType;
@Schema(description = "关联产品名称", example = "李四")
private String productName;
@Schema(description = "关联设备ID关联iot_device.id", example = "14725")
private Long machineId;
@Schema(description = "关联设备编码(冗余字段)")
private String machineCode;
@Schema(description = "关联设备名称(冗余字段)", example = "芋艿")
private String machineName;
@Schema(description = "配方描述")
private String recipeDesc;
@Schema(description = "是否启用0-禁用1-启用)", requiredMode = Schema.RequiredMode.REQUIRED)
// @NotNull(message = "是否启用0-禁用1-启用)不能为空")
private Boolean isEnable;
@Schema(description = "数据单位")
private String dataUnit;
}

@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipepoint;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipepoint.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipepoint.RecipePointDO;
import cn.iocoder.yudao.module.iot.service.recipepoint.RecipePointService;
@Tag(name = "管理后台 - 配方点位配置表(配方与设备点位关联)")
@RestController
@RequestMapping("/iot/recipe-point")
@Validated
public class RecipePointController {
@Resource
private RecipePointService recipePointService;
@PostMapping("/create")
@Operation(summary = "创建配方点位配置表(配方与设备点位关联)")
@PreAuthorize("@ss.hasPermission('iot:recipe-point:create')")
public CommonResult<Long> createRecipePoint(@Valid @RequestBody RecipePointSaveReqVO createReqVO) {
return success(recipePointService.createRecipePoint(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新配方点位配置表(配方与设备点位关联)")
@PreAuthorize("@ss.hasPermission('iot:recipe-point:update')")
public CommonResult<Boolean> updateRecipePoint(@Valid @RequestBody RecipePointSaveReqVO updateReqVO) {
recipePointService.updateRecipePoint(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除配方点位配置表(配方与设备点位关联)")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('iot:recipe-point:delete')")
public CommonResult<Boolean> deleteRecipePoint(@RequestParam("id") Long id) {
recipePointService.deleteRecipePoint(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得配方点位配置表(配方与设备点位关联)")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('iot:recipe-point:query')")
public CommonResult<RecipePointRespVO> getRecipePoint(@RequestParam("id") Long id) {
RecipePointDO recipePoint = recipePointService.getRecipePoint(id);
return success(BeanUtils.toBean(recipePoint, RecipePointRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得配方点位配置表(配方与设备点位关联)分页")
@PreAuthorize("@ss.hasPermission('iot:recipe-point:query')")
public CommonResult<PageResult<RecipePointRespVO>> getRecipePointPage(@Valid RecipePointPageReqVO pageReqVO) {
PageResult<RecipePointDO> pageResult = recipePointService.getRecipePointPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, RecipePointRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出配方点位配置表(配方与设备点位关联) Excel")
@PreAuthorize("@ss.hasPermission('iot:recipe-point:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportRecipePointExcel(@Valid RecipePointPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<RecipePointDO> list = recipePointService.getRecipePointPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "配方点位配置表(配方与设备点位关联).xls", "数据", RecipePointRespVO.class,
BeanUtils.toBean(list, RecipePointRespVO.class));
}
}

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipepoint.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 配方点位配置表(配方与设备点位关联)分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RecipePointPageReqVO extends PageParam {
@Schema(description = "关联配方ID关联iot_recipe.id", example = "8760")
private Long recipeId;
@Schema(description = "点位编码关联iot_device_attribute_type.code")
private String pointCode;
@Schema(description = "点位名称", example = "李四")
private String pointName;
@Schema(description = "点位类型", example = "1")
private String pointType;
@Schema(description = "数据类型Time、Number等", example = "1")
private String dataType;
@Schema(description = "单位s、min等")
private String unit;
@Schema(description = "设定值")
private String setValue;
@Schema(description = "实际值(从设备上载同步)")
private String actualValue;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipepoint.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 配方点位配置表(配方与设备点位关联) Response VO")
@Data
@ExcelIgnoreUnannotated
public class RecipePointRespVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "27323")
@ExcelProperty("主键ID")
private Long id;
@Schema(description = "关联配方ID关联iot_recipe.id", example = "8760")
@ExcelProperty("关联配方ID关联iot_recipe.id")
private Long recipeId;
@Schema(description = "点位编码关联iot_device_attribute_type.code")
@ExcelProperty("点位编码关联iot_device_attribute_type.code")
private String pointCode;
@Schema(description = "点位名称", example = "李四")
@ExcelProperty("点位名称")
private String pointName;
@Schema(description = "点位类型", example = "1")
@ExcelProperty("点位类型")
private String pointType;
@Schema(description = "数据类型Time、Number等", example = "1")
@ExcelProperty("数据类型Time、Number等")
private String dataType;
@Schema(description = "单位s、min等")
@ExcelProperty("单位s、min等")
private String unit;
@Schema(description = "设定值")
@ExcelProperty("设定值")
private String setValue;
@Schema(description = "实际值(从设备上载同步)")
@ExcelProperty("实际值(从设备上载同步)")
private String actualValue;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipepoint.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 配方点位配置表(配方与设备点位关联)新增/修改 Request VO")
@Data
public class RecipePointSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "27323")
private Long id;
@Schema(description = "关联配方ID关联iot_recipe.id", example = "8760")
private Long recipeId;
@Schema(description = "点位编码关联iot_device_attribute_type.code")
private String pointCode;
@Schema(description = "点位名称", example = "李四")
private String pointName;
@Schema(description = "点位类型", example = "1")
private String pointType;
@Schema(description = "数据类型Time、Number等", example = "1")
private String dataType;
@Schema(description = "单位s、min等")
private String unit;
@Schema(description = "设定值")
private String setValue;
@Schema(description = "实际值(从设备上载同步)")
private String actualValue;
}

@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipetype;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.constraints.*;
import javax.validation.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.IOException;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipetype.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipetype.RecipeTypeDO;
import cn.iocoder.yudao.module.iot.service.recipetype.RecipeTypeService;
@Tag(name = "管理后台 - 配方类型表(基础字典)")
@RestController
@RequestMapping("/iot/recipe-type")
@Validated
public class RecipeTypeController {
@Resource
private RecipeTypeService recipeTypeService;
@PostMapping("/create")
@Operation(summary = "创建配方类型表(基础字典)")
@PreAuthorize("@ss.hasPermission('iot:recipe-type:create')")
public CommonResult<Long> createRecipeType(@Valid @RequestBody RecipeTypeSaveReqVO createReqVO) {
return success(recipeTypeService.createRecipeType(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新配方类型表(基础字典)")
@PreAuthorize("@ss.hasPermission('iot:recipe-type:update')")
public CommonResult<Boolean> updateRecipeType(@Valid @RequestBody RecipeTypeSaveReqVO updateReqVO) {
recipeTypeService.updateRecipeType(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除配方类型表(基础字典)")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('iot:recipe-type:delete')")
public CommonResult<Boolean> deleteRecipeType(@RequestParam("id") Long id) {
recipeTypeService.deleteRecipeType(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得配方类型表(基础字典)")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('iot:recipe-type:query')")
public CommonResult<RecipeTypeRespVO> getRecipeType(@RequestParam("id") Long id) {
RecipeTypeDO recipeType = recipeTypeService.getRecipeType(id);
return success(BeanUtils.toBean(recipeType, RecipeTypeRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得配方类型表(基础字典)分页")
@PreAuthorize("@ss.hasPermission('iot:recipe-type:query')")
public CommonResult<PageResult<RecipeTypeRespVO>> getRecipeTypePage(@Valid RecipeTypePageReqVO pageReqVO) {
PageResult<RecipeTypeDO> pageResult = recipeTypeService.getRecipeTypePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, RecipeTypeRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出配方类型表(基础字典) Excel")
@PreAuthorize("@ss.hasPermission('iot:recipe-type:export')")
@ApiAccessLog(operateType = EXPORT)
public void exportRecipeTypeExcel(@Valid RecipeTypePageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<RecipeTypeDO> list = recipeTypeService.getRecipeTypePage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "配方类型表(基础字典).xls", "数据", RecipeTypeRespVO.class,
BeanUtils.toBean(list, RecipeTypeRespVO.class));
}
}

@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipetype.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 配方类型表(基础字典)分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class RecipeTypePageReqVO extends PageParam {
@Schema(description = "类型编码(唯一标识)")
private String code;
@Schema(description = "类型名称", example = "张三")
private String name;
@Schema(description = "关联工序")
private String process;
@Schema(description = "配方类型描述", example = "你说的对")
private String remark;
@Schema(description = "排序字段")
private Integer sort;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipetype.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 配方类型表(基础字典) Response VO")
@Data
@ExcelIgnoreUnannotated
public class RecipeTypeRespVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21350")
@ExcelProperty("主键ID")
private Long id;
@Schema(description = "类型编码(唯一标识)")
@ExcelProperty("类型编码(唯一标识)")
private String code;
@Schema(description = "类型名称", example = "张三")
@ExcelProperty("类型名称")
private String name;
@Schema(description = "关联工序")
@ExcelProperty("关联工序")
private String process;
@Schema(description = "配方类型描述", example = "你说的对")
@ExcelProperty("配方类型描述")
private String remark;
@Schema(description = "排序字段", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("排序字段")
private Integer sort;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.iot.controller.admin.recipetype.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
@Schema(description = "管理后台 - 配方类型表(基础字典)新增/修改 Request VO")
@Data
public class RecipeTypeSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "21350")
private Long id;
@Schema(description = "类型编码(唯一标识)")
private String code;
@Schema(description = "类型名称", example = "张三")
private String name;
@Schema(description = "关联工序")
private String process;
@Schema(description = "配方类型描述", example = "你说的对")
private String remark;
@Schema(description = "排序字段", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "排序字段不能为空")
private Integer sort;
}

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.recipe;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* DO
*
* @author
*/
@TableName("iot_recipe")
@KeySequence("iot_recipe_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RecipeDO extends BaseDO {
/**
* ID
*/
@TableId
private Long id;
/**
*
*/
private String name;
/**
*
*/
private String recipeCode;
/**
* code
*/
private String recipeType;
/**
*
*/
private String productName;
/**
* IDiot_device.id
*/
private Long machineId;
/**
*
*/
private String machineCode;
/**
*
*/
private String machineName;
/**
*
*/
private String recipeDesc;
/**
* 0-1-
*/
private Boolean isEnable;
/**
*
*/
private String dataUnit;
}

@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.recipepoint;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* DO
*
* @author
*/
@TableName("iot_recipe_point")
@KeySequence("iot_recipe_point_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RecipePointDO extends BaseDO {
/**
* ID
*/
@TableId
private Long id;
/**
* IDiot_recipe.id
*/
private Long recipeId;
/**
* iot_device_attribute_type.code
*/
private String pointCode;
/**
*
*/
private String pointName;
/**
*
*/
private String pointType;
/**
* TimeNumber
*/
private String dataType;
/**
* smin
*/
private String unit;
/**
*
*/
private String setValue;
/**
*
*/
private String actualValue;
}

@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.iot.dal.dataobject.recipetype;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* DO
*
* @author
*/
@TableName("iot_recipe_type")
@KeySequence("iot_recipe_type_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class RecipeTypeDO extends BaseDO {
/**
* ID
*/
@TableId
private Long id;
/**
*
*/
private String code;
/**
*
*/
private String name;
/**
*
*/
private String process;
/**
*
*/
private String remark;
/**
*
*/
private Integer sort;
}

@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.iot.dal.mysql.recipe;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipe.RecipeDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.iot.controller.admin.recipe.vo.*;
/**
* Mapper
*
* @author
*/
@Mapper
public interface RecipeMapper extends BaseMapperX<RecipeDO> {
default PageResult<RecipeDO> selectPage(RecipePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<RecipeDO>()
.likeIfPresent(RecipeDO::getName, reqVO.getName())
.eqIfPresent(RecipeDO::getRecipeCode, reqVO.getRecipeCode())
.eqIfPresent(RecipeDO::getRecipeType, reqVO.getRecipeType())
.likeIfPresent(RecipeDO::getProductName, reqVO.getProductName())
.eqIfPresent(RecipeDO::getMachineId, reqVO.getMachineId())
.eqIfPresent(RecipeDO::getMachineCode, reqVO.getMachineCode())
.likeIfPresent(RecipeDO::getMachineName, reqVO.getMachineName())
.eqIfPresent(RecipeDO::getRecipeDesc, reqVO.getRecipeDesc())
.eqIfPresent(RecipeDO::getIsEnable, reqVO.getIsEnable())
.betweenIfPresent(RecipeDO::getCreateTime, reqVO.getCreateTime())
.eqIfPresent(RecipeDO::getDataUnit, reqVO.getDataUnit())
.orderByDesc(RecipeDO::getId));
}
}

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.iot.dal.mysql.recipepoint;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipepoint.RecipePointDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.iot.controller.admin.recipepoint.vo.*;
/**
* Mapper
*
* @author
*/
@Mapper
public interface RecipePointMapper extends BaseMapperX<RecipePointDO> {
default PageResult<RecipePointDO> selectPage(RecipePointPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<RecipePointDO>()
.eqIfPresent(RecipePointDO::getRecipeId, reqVO.getRecipeId())
.eqIfPresent(RecipePointDO::getPointCode, reqVO.getPointCode())
.likeIfPresent(RecipePointDO::getPointName, reqVO.getPointName())
.eqIfPresent(RecipePointDO::getPointType, reqVO.getPointType())
.eqIfPresent(RecipePointDO::getDataType, reqVO.getDataType())
.eqIfPresent(RecipePointDO::getUnit, reqVO.getUnit())
.eqIfPresent(RecipePointDO::getSetValue, reqVO.getSetValue())
.eqIfPresent(RecipePointDO::getActualValue, reqVO.getActualValue())
.betweenIfPresent(RecipePointDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(RecipePointDO::getId));
}
}

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.iot.dal.mysql.recipetype;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipetype.RecipeTypeDO;
import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.iot.controller.admin.recipetype.vo.*;
/**
* Mapper
*
* @author
*/
@Mapper
public interface RecipeTypeMapper extends BaseMapperX<RecipeTypeDO> {
default PageResult<RecipeTypeDO> selectPage(RecipeTypePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<RecipeTypeDO>()
.eqIfPresent(RecipeTypeDO::getCode, reqVO.getCode())
.likeIfPresent(RecipeTypeDO::getName, reqVO.getName())
.eqIfPresent(RecipeTypeDO::getProcess, reqVO.getProcess())
.eqIfPresent(RecipeTypeDO::getRemark, reqVO.getRemark())
.eqIfPresent(RecipeTypeDO::getSort, reqVO.getSort())
.betweenIfPresent(RecipeTypeDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(RecipeTypeDO::getId));
}
}

@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceAttributeDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.devicemodelattribute.DeviceModelAttributeDO;
import cn.iocoder.yudao.module.iot.dal.devicecontactmodel.DeviceContactModelDO;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.web.bind.annotation.RequestParam;
import javax.validation.Valid;
import java.util.Collection;
@ -124,5 +125,7 @@ public interface DeviceService {
PageResult<LineDeviceRespVO> lineDevicePage(LineDeviceRequestVO pageReqVO);
Map<String, List<DeviceContactModelDO>> singleDevice(Long deviceId) throws JsonProcessingException;
Map<String, List<Map<String, Object>>> singleDevice(Long deviceId) throws JsonProcessingException;
List<Map<String, Object>> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime);
}

@ -393,11 +393,6 @@ public class DeviceServiceImpl implements DeviceService {
}
}else {
throw exception(OPC_CONNECT_FAILURE_DOES_NOT_EXIST);
}
@ -502,93 +497,139 @@ public class DeviceServiceImpl implements DeviceService {
return lineDeviceRespVOPageResult;
}
@Override
public Map<String, List<DeviceContactModelDO>> singleDevice(Long deviceId) throws JsonProcessingException {
public Map<String, List<Map<String, Object>>> singleDevice(Long deviceId) throws JsonProcessingException {
List<DeviceContactModelDO> resultList = new ArrayList<>();
Map<String, List<Map<String, Object>>> resultMap = new LinkedHashMap<>();
try {
// 获取设备数据列表
List<Map<String, Object>> deviceDataList = tdengineService.getAllDeviceDataOrderByTimeDesc(deviceId);
// 1. 获取设备数据列表
List<Map<String, Object>> deviceDataList = tdengineService.getNewestDeviceDataOrderByTimeDesc(deviceId);
// 2. 获取属性类型映射
Map<Long, String> idToNameMap = deviceAttributeTypeMapper.selectList()
.stream()
.collect(Collectors.toMap(
DeviceAttributeTypeDO::getId,
DeviceAttributeTypeDO::getName
));
// 3. 遍历并处理
for (Map<String, Object> deviceData : deviceDataList) {
String queryDataJson = (String) deviceData.get("queryData");
Timestamp timestamp = (Timestamp) deviceData.get("timestamp");
if (queryDataJson != null && !queryDataJson.isEmpty()) {
ObjectMapper objectMapper = new ObjectMapper();
// 简化配置,只注册基础模块
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 忽略未知属性,避免因缺少字段而报错
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 解析JSON数组为对象列表
List<DeviceContactModelDO> models = objectMapper.readValue(
if (StringUtils.isNotBlank(queryDataJson)) {
// 使用TypeReference解析为List<Map>而不是具体的DO对象
List<Map<String, Object>> dataList = new ObjectMapper().readValue(
queryDataJson,
new TypeReference<List<DeviceContactModelDO>>() {}
new TypeReference<List<Map<String, Object>>>() {}
);
// 可以为每个对象设置时间戳(如果需要)
for (DeviceContactModelDO model : models) {
// 设置查询时间戳
model.setLatestCollectionTime(String.valueOf(timestamp));
resultList.add(model);
for (Map<String, Object> data : dataList) {
// 获取属性类型名称
String attributeTypeName = "其他";
String typeStr = (String) data.get("attributeType");
if (typeStr != null) {
try {
attributeTypeName = typeStr;
} catch (Exception e) {
attributeTypeName = "未知";
}
}
// 提取需要的字段
Map<String, Object> simplifiedData = new HashMap<>();
simplifiedData.put("addressValue", data.get("addressValue"));
simplifiedData.put("attributeName", data.get("attributeName"));
resultMap
.computeIfAbsent(attributeTypeName, k -> new ArrayList<>())
.add(simplifiedData);
}
}
}
} catch (Exception e) {
System.out.println("处理设备数据时发生异常: " + e.getMessage());
}
return resultMap;
}
@Override
public List<Map<String, Object>> historyRecord(Long deviceId,String collectionStartTime, String collectionEndTime) {
List<DeviceAttributeTypeDO> deviceAttributeTypeDOS = deviceAttributeTypeMapper.selectList();
// 最基本的转换方式
Map<Long, String> idToNameMap = deviceAttributeTypeDOS.stream()
.collect(Collectors.toMap(DeviceAttributeTypeDO::getId, DeviceAttributeTypeDO::getName));
List<Map<String, Object>> resultList = new ArrayList<>();
try {
// 1. 获取设备数据列表
List<Map<String, Object>> deviceDataList = tdengineService.getstDeviceDataOrderByTimeDesc(deviceId,collectionStartTime,collectionEndTime);
// 2. 获取属性类型映射
Map<Long, String> idToNameMap = deviceAttributeTypeMapper.selectList()
.stream()
.collect(Collectors.toMap(
DeviceAttributeTypeDO::getId,
DeviceAttributeTypeDO::getName
));
// 3. 遍历每个时间点的数据
for (Map<String, Object> deviceData : deviceDataList) {
String queryDataJson = (String) deviceData.get("queryData");
Timestamp timestamp = (Timestamp) deviceData.get("timestamp");
// 分组并排序
Map<String, List<DeviceContactModelDO>> groupedAndSorted = resultList.stream()
.collect(Collectors.groupingBy(
// 处理attributeType为null的情况设为"其他"
item -> {
String typeStr = item.getAttributeType();
if (typeStr == null) {
return "其他";
}
if (StringUtils.isNotBlank(queryDataJson) && timestamp != null) {
List<Map<String, Object>> dataList = new ObjectMapper().readValue(
queryDataJson,
new TypeReference<List<Map<String, Object>>>() {}
);
// 按属性类型分组
Map<String, List<Map<String, Object>>> groupedData = new LinkedHashMap<>();
for (Map<String, Object> data : dataList) {
String attributeTypeName = "其他";
String typeStr = (String) data.get("attributeType");
if (typeStr != null) {
try {
// 关键步骤:将 String 转换为 Long
Long typeLong = Long.valueOf(typeStr);
String name = idToNameMap.get(typeLong);
return (name == null) ? "未知" : name;
} catch (NumberFormatException e) {
// 如果字符串不能转换为Long则归类为"未知"
return "未知";
attributeTypeName = typeStr;
} catch (Exception e) {
attributeTypeName = "未知";
}
}, // 使用LinkedHashMap保持分组顺序可选
LinkedHashMap::new,
// 对每个分组内的元素按latestCollectionTime倒序排序
Collectors.collectingAndThen(
Collectors.toList(),
list -> list.stream()
.sorted(Comparator.comparing(
DeviceContactModelDO::getLatestCollectionTime,
Comparator.nullsLast(Comparator.reverseOrder()) // 处理latestCollectionTime为null的情况
))
.collect(Collectors.toList())
)
));
return groupedAndSorted;
}
Map<String, Object> simplifiedData = new HashMap<>();
simplifiedData.put("addressValue", data.get("addressValue"));
simplifiedData.put("attributeName", data.get("attributeName"));
groupedData
.computeIfAbsent(attributeTypeName, k -> new ArrayList<>())
.add(simplifiedData);
}
// 创建当前时间点的Map
Map<String, Object> timePointData = new LinkedHashMap<>();
// 添加属性分组
for (Map.Entry<String, List<Map<String, Object>>> entry : groupedData.entrySet()) {
timePointData.put(entry.getKey(), entry.getValue());
}
// 添加收集时间
timePointData.put("collectTime", timestamp.toString());
resultList.add(timePointData);
}
}
} catch (Exception e) {
System.out.println("处理设备数据时发生异常: " + e.getMessage());
}
return resultList;
}
private void validateDeviceAttributeExists(Long id) {

@ -1,19 +1,25 @@
package cn.iocoder.yudao.module.iot.service.device;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taosdata.jdbc.utils.BlobUtil;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.sql.Blob;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
@Service
public class TDengineService {
@ -40,7 +46,7 @@ public class TDengineService {
// 3. 创建超级表
String createSuperTableSQL = "CREATE STABLE IF NOT EXISTS device_data (" +
"ts TIMESTAMP, " +
"query_data NCHAR(2048)" +
"query_data BLOB" +
") TAGS (device_id BIGINT)";
jdbcTemplate.execute(createSuperTableSQL);
@ -82,8 +88,44 @@ public class TDengineService {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestampStr = sdf.format(ts);
result.put("timestamp", timestampStr);
String queryData = rs.getString("query_data");
result.put("queryData", queryData);
byte[] blob = rs.getBytes("query_data");
if (blob != null) {
String jsonStr = new String(blob, StandardCharsets.UTF_8);
try {
// 1. 先去除外层的双引号(如果存在)
String trimmed = jsonStr.trim();
if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) {
trimmed = trimmed.substring(1, trimmed.length() - 1);
}
// 2. 检查是否是十六进制字符串
if (isHexString(trimmed)) {
// 3. 十六进制解码
String decodedJson = hexToString(trimmed);
result.put("queryData", decodedJson);
} else {
// 如果不是十六进制,尝试直接解析
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String, Object>> queryData = objectMapper.readValue(
trimmed,
new TypeReference<List<Map<String, Object>>>() {}
);
result.put("queryData", queryData);
}
} catch (Exception e) {
System.err.println("解析JSON失败: " + e.getMessage());
result.put("queryData", new ArrayList<>());
result.put("rawData", jsonStr);
}
} else {
result.put("queryData", new ArrayList<>());
}
result.put("deviceId", id);
result.put("tableName", tableName);
return result;
@ -112,12 +154,11 @@ public class TDengineService {
/**
*
* @param id ID
* @param queryData
* @param timestamp
* @return
*/
@DS("tdengine")
public boolean insertDeviceData(Long id, String queryData, Timestamp timestamp) {
public boolean insertDeviceData(Long id, String jsonString, Timestamp timestamp) {
try {
// 确保使用正确的数据库
jdbcTemplate.execute("USE besure");
@ -125,9 +166,12 @@ public class TDengineService {
String tableName = "d_" + id;
String sql = "INSERT INTO " + tableName + " (ts, query_data) VALUES (?, ?)";
int affectedRows = jdbcTemplate.update(sql, timestamp, queryData);
System.out.println("向设备" + id + "插入数据成功,时间戳: " + timestamp);
return affectedRows > 0;
return jdbcTemplate.update(sql, ps -> {
ps.setTimestamp(1, timestamp);
// JSON字符串转byte数组
byte[] blobData = jsonString.getBytes(StandardCharsets.UTF_8);
ps.setBytes(2, blobData);
}) > 0;
} catch (Exception e) {
System.out.println("向设备" + id + "插入数据时发生异常: " + e.getMessage());
return false;
@ -140,24 +184,202 @@ public class TDengineService {
* @return
*/
@DS("tdengine")
public List<Map<String, Object>> getAllDeviceDataOrderByTimeDesc(Long id) {
public List<Map<String, Object>> getNewestDeviceDataOrderByTimeDesc(Long id) {
String tableName = "d_" + id;
String sql = "SELECT ts, query_data FROM besure." + tableName + " ORDER BY ts DESC";
String sql = "SELECT ts, query_data FROM besure." + tableName + " ORDER BY ts DESC LIMIT 1";
try {
return jdbcTemplate.query(sql, new RowMapper<Map<String, Object>>() {
return Collections.singletonList(jdbcTemplate.queryForObject(sql, new RowMapper<Map<String, Object>>() {
@Override
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
Map<String, Object> result = new HashMap<>();
result.put("timestamp", rs.getTimestamp("ts"));
result.put("queryData", rs.getString("query_data"));
result.put("deviceId", id);
byte[] blob = rs.getBytes("query_data");
if (blob != null) {
String jsonStr = new String(blob, StandardCharsets.UTF_8);
try {
// 1. 先去除外层的双引号(如果存在)
String trimmed = jsonStr.trim();
if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) {
trimmed = trimmed.substring(1, trimmed.length() - 1);
}
// 2. 检查是否是十六进制字符串
if (isHexString(trimmed)) {
// 3. 十六进制解码
String decodedJson = hexToString(trimmed);
result.put("queryData", decodedJson);
} else {
// 如果不是十六进制,尝试直接解析
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String, Object>> queryData = objectMapper.readValue(
trimmed,
new TypeReference<List<Map<String, Object>>>() {}
);
result.put("queryData", queryData);
}
} catch (Exception e) {
System.err.println("解析JSON失败: " + e.getMessage());
result.put("queryData", new ArrayList<>());
result.put("rawData", jsonStr);
}
} else {
result.put("queryData", new ArrayList<>());
}
return result;
}
});
}));
} catch (EmptyResultDataAccessException e) {
return Collections.singletonList(createEmptyResult(id));
} catch (Exception e) {
System.out.println("查询设备" + id + "的全部数据时发生异常: " + e.getMessage());
System.err.println("查询设备" + id + "的最新数据时发生异常: " + e.getMessage());
e.printStackTrace();
return Collections.singletonList(createEmptyResult(id));
}
}
// 检查是否是十六进制字符串
private boolean isHexString(String str) {
if (str == null || str.trim().isEmpty()) {
return false;
}
String s = str.trim();
// 检查是否只包含十六进制字符0-9, a-f, A-F
return s.matches("^[0-9a-fA-F]+$");
}
// 十六进制解码
private String hexToString(String hex) {
try {
// 使用 Apache Commons Codec
byte[] bytes = Hex.decodeHex(hex);
return new String(bytes, StandardCharsets.UTF_8);
} catch (DecoderException e) {
throw new RuntimeException("十六进制解码失败: " + e.getMessage(), e);
}
}
private List<Map<String, Object>> parseJsonData(byte[] blob) {
if (blob == null || blob.length == 0) {
return new ArrayList<>();
}
try {
String jsonStr = new String(blob, StandardCharsets.UTF_8);
// 检查是否是有效的JSON
if (jsonStr.trim().startsWith("[") && jsonStr.trim().endsWith("]")) {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.readValue(jsonStr,
new TypeReference<List<Map<String, Object>>>() {});
} else {
// 可能是字符串化的JSON尝试去除引号
jsonStr = jsonStr.trim();
if (jsonStr.startsWith("\"") && jsonStr.endsWith("\"")) {
jsonStr = jsonStr.substring(1, jsonStr.length() - 1);
return new ObjectMapper().readValue(jsonStr,
new TypeReference<List<Map<String, Object>>>() {});
}
}
} catch (Exception e) {
System.err.println("解析JSON数据失败: " + e.getMessage());
}
return new ArrayList<>();
}
private Map<String, Object> createEmptyResult(Long deviceId) {
Map<String, Object> result = new HashMap<>();
result.put("deviceId", deviceId);
result.put("queryData", new ArrayList<>());
result.put("timestamp", null);
return result;
}
/**
*
* @param id ID
* @return
*/
@DS("tdengine")
public List<Map<String, Object>> getstDeviceDataOrderByTimeDesc(Long id,String collectionStartTime, String collectionEndTime) {
String tableName = "d_" + id;
StringBuilder sqlBuilder = new StringBuilder();
List<Object> params = new ArrayList<>();
sqlBuilder.append("SELECT ts, query_data FROM besure.").append(tableName).append(" WHERE 1=1");
if (collectionStartTime != null) {
// 直接将时间字符串拼接到SQL中
sqlBuilder.append(" AND ts >= '").append(collectionStartTime).append("'");
}
if (collectionEndTime != null) {
sqlBuilder.append(" AND ts <= '").append(collectionEndTime).append("'");
}
sqlBuilder.append(" ORDER BY ts DESC");
try {
return jdbcTemplate.query(sqlBuilder.toString(), new RowMapper<Map<String, Object>>() {
@Override
public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
Map<String, Object> result = new HashMap<>();
result.put("timestamp", rs.getTimestamp("ts"));
result.put("deviceId", id);
byte[] blob = rs.getBytes("query_data");
if (blob != null) {
String jsonStr = new String(blob, StandardCharsets.UTF_8);
try {
// 1. 先去除外层的双引号(如果存在)
String trimmed = jsonStr.trim();
if (trimmed.startsWith("\"") && trimmed.endsWith("\"")) {
trimmed = trimmed.substring(1, trimmed.length() - 1);
}
// 2. 检查是否是十六进制字符串
if (isHexString(trimmed)) {
// 3. 十六进制解码
String decodedJson = hexToString(trimmed);
result.put("queryData", decodedJson);
} else {
// 如果不是十六进制,尝试直接解析
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String, Object>> queryData = objectMapper.readValue(
trimmed,
new TypeReference<List<Map<String, Object>>>() {}
);
result.put("queryData", queryData);
}
} catch (Exception e) {
System.err.println("解析JSON失败: " + e.getMessage());
result.put("queryData", new ArrayList<>());
result.put("rawData", jsonStr);
}
} else {
result.put("queryData", new ArrayList<>());
}
return result;
}
});
} catch (EmptyResultDataAccessException e) {
return Collections.singletonList(createEmptyResult(id));
} catch (Exception e) {
System.err.println("查询设备" + id + "的最新数据时发生异常: " + e.getMessage());
e.printStackTrace();
return Collections.singletonList(createEmptyResult(id));
}
}
}

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.iot.service.recipe;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipe.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipe.RecipeDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* Service
*
* @author
*/
public interface RecipeService {
/**
*
*
* @param createReqVO
* @return
*/
Long createRecipe(@Valid RecipeSaveReqVO createReqVO);
/**
*
*
* @param updateReqVO
*/
void updateRecipe(@Valid RecipeSaveReqVO updateReqVO);
/**
*
*
* @param id
*/
void deleteRecipe(Long id);
/**
*
*
* @param id
* @return
*/
RecipeDO getRecipe(Long id);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<RecipeDO> getRecipePage(RecipePageReqVO pageReqVO);
}

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.iot.service.recipe;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipe.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipe.RecipeDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.dal.mysql.recipe.RecipeMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
/**
* Service
*
* @author
*/
@Service
@Validated
public class RecipeServiceImpl implements RecipeService {
@Resource
private RecipeMapper recipeMapper;
@Override
public Long createRecipe(RecipeSaveReqVO createReqVO) {
// 插入
RecipeDO recipe = BeanUtils.toBean(createReqVO, RecipeDO.class);
recipeMapper.insert(recipe);
// 返回
return recipe.getId();
}
@Override
public void updateRecipe(RecipeSaveReqVO updateReqVO) {
// 校验存在
validateRecipeExists(updateReqVO.getId());
// 更新
RecipeDO updateObj = BeanUtils.toBean(updateReqVO, RecipeDO.class);
recipeMapper.updateById(updateObj);
}
@Override
public void deleteRecipe(Long id) {
// 校验存在
validateRecipeExists(id);
// 删除
recipeMapper.deleteById(id);
}
private void validateRecipeExists(Long id) {
if (recipeMapper.selectById(id) == null) {
throw exception(RECIPE_NOT_EXISTS);
}
}
@Override
public RecipeDO getRecipe(Long id) {
return recipeMapper.selectById(id);
}
@Override
public PageResult<RecipeDO> getRecipePage(RecipePageReqVO pageReqVO) {
return recipeMapper.selectPage(pageReqVO);
}
}

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.iot.service.recipepoint;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipepoint.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipepoint.RecipePointDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* Service
*
* @author
*/
public interface RecipePointService {
/**
*
*
* @param createReqVO
* @return
*/
Long createRecipePoint(@Valid RecipePointSaveReqVO createReqVO);
/**
*
*
* @param updateReqVO
*/
void updateRecipePoint(@Valid RecipePointSaveReqVO updateReqVO);
/**
*
*
* @param id
*/
void deleteRecipePoint(Long id);
/**
*
*
* @param id
* @return
*/
RecipePointDO getRecipePoint(Long id);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<RecipePointDO> getRecipePointPage(RecipePointPageReqVO pageReqVO);
}

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.iot.service.recipepoint;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipepoint.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipepoint.RecipePointDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.dal.mysql.recipepoint.RecipePointMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
/**
* Service
*
* @author
*/
@Service
@Validated
public class RecipePointServiceImpl implements RecipePointService {
@Resource
private RecipePointMapper recipePointMapper;
@Override
public Long createRecipePoint(RecipePointSaveReqVO createReqVO) {
// 插入
RecipePointDO recipePoint = BeanUtils.toBean(createReqVO, RecipePointDO.class);
recipePointMapper.insert(recipePoint);
// 返回
return recipePoint.getId();
}
@Override
public void updateRecipePoint(RecipePointSaveReqVO updateReqVO) {
// 校验存在
validateRecipePointExists(updateReqVO.getId());
// 更新
RecipePointDO updateObj = BeanUtils.toBean(updateReqVO, RecipePointDO.class);
recipePointMapper.updateById(updateObj);
}
@Override
public void deleteRecipePoint(Long id) {
// 校验存在
validateRecipePointExists(id);
// 删除
recipePointMapper.deleteById(id);
}
private void validateRecipePointExists(Long id) {
if (recipePointMapper.selectById(id) == null) {
throw exception(RECIPE_POINT_NOT_EXISTS);
}
}
@Override
public RecipePointDO getRecipePoint(Long id) {
return recipePointMapper.selectById(id);
}
@Override
public PageResult<RecipePointDO> getRecipePointPage(RecipePointPageReqVO pageReqVO) {
return recipePointMapper.selectPage(pageReqVO);
}
}

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.iot.service.recipetype;
import java.util.*;
import javax.validation.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipetype.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipetype.RecipeTypeDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
/**
* Service
*
* @author
*/
public interface RecipeTypeService {
/**
*
*
* @param createReqVO
* @return
*/
Long createRecipeType(@Valid RecipeTypeSaveReqVO createReqVO);
/**
*
*
* @param updateReqVO
*/
void updateRecipeType(@Valid RecipeTypeSaveReqVO updateReqVO);
/**
*
*
* @param id
*/
void deleteRecipeType(Long id);
/**
*
*
* @param id
* @return
*/
RecipeTypeDO getRecipeType(Long id);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<RecipeTypeDO> getRecipeTypePage(RecipeTypePageReqVO pageReqVO);
}

@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.iot.service.recipetype;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import cn.iocoder.yudao.module.iot.controller.admin.recipetype.vo.*;
import cn.iocoder.yudao.module.iot.dal.dataobject.recipetype.RecipeTypeDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.iot.dal.mysql.recipetype.RecipeTypeMapper;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*;
/**
* Service
*
* @author
*/
@Service
@Validated
public class RecipeTypeServiceImpl implements RecipeTypeService {
@Resource
private RecipeTypeMapper recipeTypeMapper;
@Override
public Long createRecipeType(RecipeTypeSaveReqVO createReqVO) {
// 插入
RecipeTypeDO recipeType = BeanUtils.toBean(createReqVO, RecipeTypeDO.class);
recipeTypeMapper.insert(recipeType);
// 返回
return recipeType.getId();
}
@Override
public void updateRecipeType(RecipeTypeSaveReqVO updateReqVO) {
// 校验存在
validateRecipeTypeExists(updateReqVO.getId());
// 更新
RecipeTypeDO updateObj = BeanUtils.toBean(updateReqVO, RecipeTypeDO.class);
recipeTypeMapper.updateById(updateObj);
}
@Override
public void deleteRecipeType(Long id) {
// 校验存在
validateRecipeTypeExists(id);
// 删除
recipeTypeMapper.deleteById(id);
}
private void validateRecipeTypeExists(Long id) {
if (recipeTypeMapper.selectById(id) == null) {
throw exception(RECIPE_TYPE_NOT_EXISTS);
}
}
@Override
public RecipeTypeDO getRecipeType(Long id) {
return recipeTypeMapper.selectById(id);
}
@Override
public PageResult<RecipeTypeDO> getRecipeTypePage(RecipeTypePageReqVO pageReqVO) {
return recipeTypeMapper.selectPage(pageReqVO);
}
}

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.recipe.RecipeMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.recipepoint.RecipePointMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.yudao.module.iot.dal.mysql.recipetype.RecipeTypeMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>

@ -106,12 +106,12 @@ public class DeviceServiceImplTest extends BaseDbUnitTest {
}
@Test
public void testCreateTDengine() {
// 准备参数
Long id = 12313L;
deviceService.createTDengine(id);
}
// @Test
// public void testCreateTDengine() {
// // 准备参数
// Long id = 12313L;
// deviceService.createTDengine(id);
// }

@ -112,7 +112,7 @@ public class EnergyDeviceServiceImplTest extends BaseDbUnitTest {
EnergyDeviceDO dbEnergyDevice = randomPojo(EnergyDeviceDO.class, o -> { // 等会查询到
o.setName(null);
o.setCode(null);
o.setDeviceType(null);
// o.setDeviceType(null);
o.setInfo(null);
o.setCheckCron(null);
o.setLastCheckTime(null);
@ -127,7 +127,7 @@ public class EnergyDeviceServiceImplTest extends BaseDbUnitTest {
// 测试 code 不匹配
energyDeviceMapper.insert(cloneIgnoreId(dbEnergyDevice, o -> o.setCode(null)));
// 测试 deviceType 不匹配
energyDeviceMapper.insert(cloneIgnoreId(dbEnergyDevice, o -> o.setDeviceType(null)));
// energyDeviceMapper.insert(cloneIgnoreId(dbEnergyDevice, o -> o.setDeviceType(null)));
// 测试 info 不匹配
energyDeviceMapper.insert(cloneIgnoreId(dbEnergyDevice, o -> o.setInfo(null)));
// 测试 checkCron 不匹配
@ -146,7 +146,7 @@ public class EnergyDeviceServiceImplTest extends BaseDbUnitTest {
EnergyDevicePageReqVO reqVO = new EnergyDevicePageReqVO();
reqVO.setName(null);
reqVO.setCode(null);
reqVO.setDeviceType(null);
// reqVO.setDeviceType(null);
reqVO.setInfo(null);
reqVO.setCheckCron(null);
reqVO.setLastCheckTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28));

Loading…
Cancel
Save