From a64d96fb78a9cee792ff2b44950fe7c0fcf8fe86 Mon Sep 17 00:00:00 2001 From: HuangHuiKang Date: Mon, 16 Mar 2026 14:45:47 +0800 Subject: [PATCH] =?UTF-8?q?fix:=E4=BF=AE=E5=A4=8D=E5=B7=B2=E7=9F=A5bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RecipeDeviceRecordController.java | 2 + .../vo/RecipeDeviceRecordRespVO.java | 2 + .../vo/RecipeDeviceRecordSaveReqVO.java | 5 ++ .../RecipePlanDetailController.java | 10 ++++ .../vo/CollectTimeResult.java | 20 ++++++++ .../vo/RecipePointRecordRespVO.java | 3 ++ .../RecipeDeviceRecordDO.java | 5 ++ .../mqtt/consumer/impl/MqttserviceImpl.java | 47 ++++++++++++------- .../iot/service/device/TDengineService.java | 2 +- .../RecipePlanDetailService.java | 1 + .../RecipePlanDetailServiceImpl.java | 46 ++++++++++++++++++ 11 files changed, 125 insertions(+), 18 deletions(-) create mode 100644 yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/vo/CollectTimeResult.java diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/RecipeDeviceRecordController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/RecipeDeviceRecordController.java index ccadf63ef..941feb0e2 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/RecipeDeviceRecordController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/RecipeDeviceRecordController.java @@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.Operation; import javax.validation.*; import javax.servlet.http.*; +import java.time.LocalDateTime; import java.util.*; import java.io.IOException; import java.util.function.Function; @@ -221,6 +222,7 @@ public class RecipeDeviceRecordController { // recipeDeviceRecordDO.setValue(data.get("addressValue").toString()); // } recipeDeviceRecordDO.setValue(map.get(deviceContactModelDO.getAttributeCode())); + recipeDeviceRecordDO.setCollectionTime((LocalDateTime) map.get("ts")); recipeDeviceRecordService.createRecipeDeviceRecord(BeanUtils.toBean(recipeDeviceRecordDO, RecipeDeviceRecordSaveReqVO.class)); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordRespVO.java index ce4473233..3a19f8dfe 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordRespVO.java @@ -63,4 +63,6 @@ public class RecipeDeviceRecordRespVO { @Schema(description = "配方id", example = "32535") private Long recipeId; + @Schema(description = "采集时间", example = "32535") + private LocalDateTime collectionTime; } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordSaveReqVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordSaveReqVO.java index 2bb926699..73e74994a 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordSaveReqVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipedevicerecord/vo/RecipeDeviceRecordSaveReqVO.java @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.iot.controller.admin.recipedevicerecord.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; + +import java.time.LocalDateTime; import java.util.*; import javax.validation.constraints.*; @@ -45,4 +47,7 @@ public class RecipeDeviceRecordSaveReqVO { @Schema(description = "配方id", example = "32535") private Long recipeId; + @Schema(description = "采集时间", example = "32535") + private LocalDateTime collectionTime; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/RecipePlanDetailController.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/RecipePlanDetailController.java index 341f0344d..4dc2c50fb 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/RecipePlanDetailController.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/RecipePlanDetailController.java @@ -72,6 +72,16 @@ public class RecipePlanDetailController { return success(BeanUtils.toBean(recipePlanDetail, RecipePlanDetailRespVO.class)); } + + @GetMapping("/getCollectLatestTime") + @Operation(summary = "获得数据采集最新时间") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('iot:recipe-plan-detail:query')") + public CommonResult getCollectLatestTime(@RequestParam("id") Long id) { + CollectTimeResult collectTimeResult = recipePlanDetailService.getCollectLatestTime(id); + return success(collectTimeResult); + } + // @GetMapping("/page") // @Operation(summary = "获得配方计划详情表(配方库)分页") // @PreAuthorize("@ss.hasPermission('iot:recipe-plan-detail:query')") diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/vo/CollectTimeResult.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/vo/CollectTimeResult.java new file mode 100644 index 000000000..a50ee6108 --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipeplandetail/vo/CollectTimeResult.java @@ -0,0 +1,20 @@ +package cn.iocoder.yudao.module.iot.controller.admin.recipeplandetail.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class CollectTimeResult { + + + @Schema(description = "采集时间") + private LocalDateTime collectTime; + + @Schema(description = "是否时间异常") + private Boolean timeAbnormal; + +} \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipepointrecord/vo/RecipePointRecordRespVO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipepointrecord/vo/RecipePointRecordRespVO.java index 809809b23..7818d11a0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipepointrecord/vo/RecipePointRecordRespVO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/recipepointrecord/vo/RecipePointRecordRespVO.java @@ -49,6 +49,9 @@ public class RecipePointRecordRespVO { @ExcelProperty("创建时间") private LocalDateTime createTime; + @Schema(description = "更新时间") + private LocalDateTime updateTime; + @Schema(description = "参考值") @ExcelProperty("参考值") private BigDecimal refer; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/recipedevicerecord/RecipeDeviceRecordDO.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/recipedevicerecord/RecipeDeviceRecordDO.java index 1e2a1f9b4..977556191 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/recipedevicerecord/RecipeDeviceRecordDO.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/recipedevicerecord/RecipeDeviceRecordDO.java @@ -73,4 +73,9 @@ public class RecipeDeviceRecordDO extends BaseDO { */ private Long recipeId; + /** + * 采集时间 + */ + private LocalDateTime collectionTime; + } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/MqttserviceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/MqttserviceImpl.java index a59c600cb..b172159e0 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/MqttserviceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/mqtt/consumer/impl/MqttserviceImpl.java @@ -58,26 +58,32 @@ public class MqttserviceImpl implements IMqttservice, ApplicationListener gatewayList = gatewayService.selectListByIsEnable(IsEnableConstant.IsEnableTrue); - for (GatewayDO gateway : gatewayList) { - if (StringUtils.isNotBlank(gateway.getTopic())) { - SubscriptTopic topic = new SubscriptTopic(gateway.getTopic(), - gateway.getTopic(), Pattern.QUEUE, 0, dataHandler); - defaultBizTopicSet.getTopicMap().add(topic); - try { - client.subscribe(topic.getSubTopic(), 0, dataHandler); - } catch (MqttException e) { - e.printStackTrace(); - } + if (!client.isConnected()) { + try { + log.warn("MQTT未连接,尝试重新连接..."); + client.reconnect(); + } catch (Exception e) { + log.error("MQTT重连失败", e); + return; + } + } + + List gatewayList = gatewayService.selectListByIsEnable(IsEnableConstant.IsEnableTrue); + for (GatewayDO gateway : gatewayList) { + if (StringUtils.isNotBlank(gateway.getTopic())) { + SubscriptTopic topic = new SubscriptTopic(gateway.getTopic(), + gateway.getTopic(), Pattern.QUEUE, 0, dataHandler); + defaultBizTopicSet.getTopicMap().add(topic); + try { + client.subscribe(topic.getSubTopic(), 0, dataHandler); + } catch (MqttException e) { + e.printStackTrace(); } } - client.setCallback(new MqttCallbackImpl(defaultBizTopicSet.getTopicMap(), client, options)); - log.info("共订阅: " + defaultBizTopicSet.getTopicMap().size() + " 个主题!"); - } else { - log.error("Mqtt is not connected !"); } + client.setCallback(new MqttCallbackImpl(defaultBizTopicSet.getTopicMap(), client, options)); + log.info("共订阅: " + defaultBizTopicSet.getTopicMap().size() + " 个主题!"); + } @@ -86,6 +92,13 @@ public class MqttserviceImpl implements IMqttservice, ApplicationListener row = list.get(0); - // 转换 ts 为 LocalDateTime(推荐) + // 转换 ts 为 LocalDateTime Object tsObj = row.get("ts"); if (tsObj instanceof Timestamp) { row.put("ts", ((Timestamp) tsObj).toLocalDateTime()); diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipeplandetail/RecipePlanDetailService.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipeplandetail/RecipePlanDetailService.java index dd6b4d1a4..b21d66623 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipeplandetail/RecipePlanDetailService.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipeplandetail/RecipePlanDetailService.java @@ -52,4 +52,5 @@ public interface RecipePlanDetailService { PageResult pageRecipePlanDetail(RecipePlanDetailPageReqVO reqVO); + CollectTimeResult getCollectLatestTime(Long id); } \ No newline at end of file diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipeplandetail/RecipePlanDetailServiceImpl.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipeplandetail/RecipePlanDetailServiceImpl.java index 0336dcdaa..f0a120263 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipeplandetail/RecipePlanDetailServiceImpl.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/recipeplandetail/RecipePlanDetailServiceImpl.java @@ -1,6 +1,10 @@ package cn.iocoder.yudao.module.iot.service.recipeplandetail; +import cn.iocoder.yudao.module.iot.dal.dataobject.recipe.RecipeDO; +import cn.iocoder.yudao.module.iot.dal.mysql.recipe.RecipeMapper; +import cn.iocoder.yudao.module.iot.service.device.TDengineService; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import javax.annotation.Resource; import org.springframework.validation.annotation.Validated; @@ -12,6 +16,9 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.iot.dal.mysql.recipeplandetail.RecipePlanDetailMapper; +import java.time.LocalDateTime; +import java.util.Map; + import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.*; @@ -28,6 +35,13 @@ public class RecipePlanDetailServiceImpl implements RecipePlanDetailService { @Resource private RecipePlanDetailMapper recipePlanDetailMapper; + @Resource + @Lazy + private RecipeMapper recipeMapper; + + @Resource + private TDengineService tDengineService; + @Override public Long createRecipePlanDetail(RecipePlanDetailSaveReqVO createReqVO) { // 校验编码是否存在 @@ -84,4 +98,36 @@ public class RecipePlanDetailServiceImpl implements RecipePlanDetailService { return recipePlanDetailMapper.selectPageWithRelationsWrap(reqVO); } + @Override + public CollectTimeResult getCollectLatestTime(Long id) { + + // 查询配方计划详情 + RecipePlanDetailDO recipePlanDetailDO = recipePlanDetailMapper.selectById(id); + if (recipePlanDetailDO == null) { + throw exception(RECIPE_PLAN_DETAIL_NOT_EXISTS); + } + + // 查询配方 + RecipeDO recipeDO = recipeMapper.selectById(recipePlanDetailDO.getRecipeId()); + if (recipeDO == null) { + throw exception(RECIPE_NOT_EXISTS); + } + + // 查询 TDengine 最新采集数据 + Map map = tDengineService.newSelectLatestRow(recipeDO.getMachineId()); + + LocalDateTime collectTime = null; + if (map != null) { + collectTime = (LocalDateTime) map.get("ts"); + } + + // 当前时间 + LocalDateTime now = LocalDateTime.now(); + + // 判断是否异常 + boolean abnormal = collectTime != null && now.isAfter(collectTime.plusMinutes(2)); + + return new CollectTimeResult(collectTime, abnormal); + } + } \ No newline at end of file