From 750e5351b3fda0b8b8c0fa39bc1b9dd2fe33688a Mon Sep 17 00:00:00 2001 From: tianyongbao Date: Mon, 12 Jan 2026 00:33:59 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=BE=AE=E4=BF=A1=E5=B0=8F=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E6=8E=A5=E5=8F=A3=E5=AF=B9=E6=8E=A5=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/intc/web/domain/vo/LoginVo.java | 6 + .../web/service/impl/SmsAuthStrategy.java | 1 + .../src/main/resources/application-dev.yml | 8 +- .../fishery/controller/DeviceController.java | 2 +- .../controller/MessageOpRecordController.java | 2 +- .../controller/MessageWarnController.java | 21 +- .../controller/PayOrderController.java | 2 +- .../fishery/controller/PondController.java | 6 +- .../fishery/service/IMessageWarnService.java | 16 + .../service/impl/MessageWarnServiceImpl.java | 34 ++ .../intc/iot/config/AliyunIotProperties.java | 42 ++ .../intc/iot/controller/IotController.java | 367 +++++++++++++++++- .../iot/domain/bo/AddDeviceDetectorBo.java | 49 +++ 13 files changed, 531 insertions(+), 25 deletions(-) create mode 100644 intc-modules/intc-iot/src/main/java/com/intc/iot/domain/bo/AddDeviceDetectorBo.java diff --git a/intc-admin/src/main/java/com/intc/web/domain/vo/LoginVo.java b/intc-admin/src/main/java/com/intc/web/domain/vo/LoginVo.java index 207ddf9..aabd21b 100644 --- a/intc-admin/src/main/java/com/intc/web/domain/vo/LoginVo.java +++ b/intc-admin/src/main/java/com/intc/web/domain/vo/LoginVo.java @@ -51,4 +51,10 @@ public class LoginVo { */ private String openid; + + /** + * 用户ID + */ + private Long userId; + } diff --git a/intc-admin/src/main/java/com/intc/web/service/impl/SmsAuthStrategy.java b/intc-admin/src/main/java/com/intc/web/service/impl/SmsAuthStrategy.java index db8b47b..75cef79 100644 --- a/intc-admin/src/main/java/com/intc/web/service/impl/SmsAuthStrategy.java +++ b/intc-admin/src/main/java/com/intc/web/service/impl/SmsAuthStrategy.java @@ -72,6 +72,7 @@ public class SmsAuthStrategy implements IAuthStrategy { loginVo.setAccessToken(StpUtil.getTokenValue()); loginVo.setExpireIn(StpUtil.getTokenTimeout()); loginVo.setClientId(client.getClientId()); + loginVo.setUserId(loginUser.getUserId()); return loginVo; } diff --git a/intc-admin/src/main/resources/application-dev.yml b/intc-admin/src/main/resources/application-dev.yml index fceea89..c535f60 100644 --- a/intc-admin/src/main/resources/application-dev.yml +++ b/intc-admin/src/main/resources/application-dev.yml @@ -219,10 +219,16 @@ aliyun: app-key: 334224397 # App Secret app-secret: 70de3018ec39423e9ca1e1b6a6a84ad6 + # 设备类型到 ProductKey 的映射配置 + device-type: + # 1-水质检测仪 ProductKey(请填写实际的 ProductKey) + water-quality-monitor: a15hA3oBPmB # TODO: 请替换为实际的水质检测仪 ProductKey + # 2-控制一体机 ProductKey(请填写实际的 ProductKey) + control-integrated: a15hA3oBPmB # TODO: 请替换为实际的控制一体机 ProductKey # AMQP 服务端订阅配置(使用数据同步的 AppKey + AppSecret) amqp: # 是否启用 - enabled: true + enabled: false # 数据同步 AppKey(与上面的 app-key 不同!) data-sync-app-key: 334224409 # 数据同步 AppSecret(请在阿里云控制台查看) diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/DeviceController.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/DeviceController.java index 94e6ff7..280b329 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/DeviceController.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/DeviceController.java @@ -71,7 +71,7 @@ public class DeviceController extends BaseController { /** * 新增设备管理 */ - @SaCheckPermission("fishery:device:add") +// @SaCheckPermission("fishery:device:add") @Log(title = "设备管理", businessType = BusinessType.INSERT) @RepeatSubmit() @PostMapping() diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/MessageOpRecordController.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/MessageOpRecordController.java index e1a0be0..8a7ef22 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/MessageOpRecordController.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/MessageOpRecordController.java @@ -38,7 +38,7 @@ public class MessageOpRecordController extends BaseController { /** * 查询用户操作记录列表 */ - @SaCheckPermission("fishery:messageOpRecord:list") +// @SaCheckPermission("fishery:messageOpRecord:list") @GetMapping("/list") public TableDataInfo list(MessageOpRecordBo bo, PageQuery pageQuery) { return messageOpRecordService.queryPageList(bo, pageQuery); diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/MessageWarnController.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/MessageWarnController.java index 039d670..f3d2668 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/MessageWarnController.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/MessageWarnController.java @@ -1,7 +1,9 @@ package com.intc.fishery.controller; import java.util.List; +import java.util.Map; import com.intc.common.excel.utils.ExcelUtil; +import com.intc.common.satoken.utils.LoginHelper; import lombok.RequiredArgsConstructor; import jakarta.servlet.http.HttpServletResponse; import jakarta.validation.constraints.*; @@ -38,7 +40,7 @@ public class MessageWarnController extends BaseController { /** * 查询设备告警记录列表 */ - @SaCheckPermission("fishery:messageWarn:list") +// @SaCheckPermission("fishery:messageWarn:list") @GetMapping("/list") public TableDataInfo list(MessageWarnBo bo, PageQuery pageQuery) { return messageWarnService.queryPageList(bo, pageQuery); @@ -101,4 +103,21 @@ public class MessageWarnController extends BaseController { @PathVariable Long[] ids) { return toAjax(messageWarnService.deleteWithValidByIds(List.of(ids), true)); } + + /** + * 已读一条消息 + */ + @PutMapping("/read") + public R readMessage(@RequestParam("id") Long id) { + return toAjax(messageWarnService.readMessage(id)); + } + + /** + * 已读全部消息 + */ + @PutMapping("/read/all") + public R readAllMessages() { + Long userId = LoginHelper.getUserId(); + return toAjax(messageWarnService.readAllMessages(userId)); + } } diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/PayOrderController.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/PayOrderController.java index 12a2fb6..946c6a3 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/PayOrderController.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/PayOrderController.java @@ -39,7 +39,7 @@ public class PayOrderController extends BaseController { /** * 查询充值订单列表 */ - @SaCheckPermission("fishery:payOrder:list") +// @SaCheckPermission("fishery:payOrder:list") @GetMapping("/list") public TableDataInfo list(PayOrderBo bo, PageQuery pageQuery) { return payOrderService.queryPageList(bo, pageQuery); diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/PondController.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/PondController.java index 0eada45..1e4f86f 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/PondController.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/controller/PondController.java @@ -69,7 +69,7 @@ public class PondController extends BaseController { * * @param id 主键 */ - @SaCheckPermission("fishery:pond:query") +// @SaCheckPermission("fishery:pond:query") @GetMapping("/{id}") public R getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) { @@ -120,7 +120,7 @@ public class PondController extends BaseController { /** * 修改塘口管理 */ - @SaCheckPermission("fishery:pond:edit") +// @SaCheckPermission("fishery:pond:edit") @Log(title = "塘口管理", businessType = BusinessType.UPDATE) @RepeatSubmit() @PutMapping() @@ -133,7 +133,7 @@ public class PondController extends BaseController { * * @param ids 主键串 */ - @SaCheckPermission("fishery:pond:remove") +// @SaCheckPermission("fishery:pond:remove") @Log(title = "塘口管理", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") public R remove(@NotEmpty(message = "主键不能为空") diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/IMessageWarnService.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/IMessageWarnService.java index 2031cf2..2eac679 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/IMessageWarnService.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/IMessageWarnService.java @@ -65,4 +65,20 @@ public interface IMessageWarnService { * @return 是否删除成功 */ Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 已读一条消息 + * + * @param id 消息ID + * @return 是否成功 + */ + Boolean readMessage(Long id); + + /** + * 已读全部消息 + * + * @param userId 用户ID + * @return 是否成功 + */ + Boolean readAllMessages(Long userId); } diff --git a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/impl/MessageWarnServiceImpl.java b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/impl/MessageWarnServiceImpl.java index ad24df2..6341bdd 100644 --- a/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/impl/MessageWarnServiceImpl.java +++ b/intc-modules/intc-fishery/src/main/java/com/intc/fishery/service/impl/MessageWarnServiceImpl.java @@ -197,4 +197,38 @@ public class MessageWarnServiceImpl implements IMessageWarnService { } return baseMapper.deleteByIds(ids) > 0; } + + /** + * 已读一条消息 + * + * @param id 消息ID + * @return 是否成功 + */ + @Override + public Boolean readMessage(Long id) { + MessageWarn messageWarn = baseMapper.selectById(id); + if (messageWarn == null) { + return false; + } + messageWarn.setIsRead(1); + return baseMapper.updateById(messageWarn) > 0; + } + + /** + * 已读全部消息 + * + * @param userId 用户ID + * @return 是否成功 + */ + @Override + public Boolean readAllMessages(Long userId) { + MessageWarn update = new MessageWarn(); + update.setIsRead(1); + + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(MessageWarn.class) + .eq(MessageWarn::getUserId, userId) + .eq(MessageWarn::getIsRead, 0); + + return baseMapper.update(update, wrapper) > 0; + } } diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/config/AliyunIotProperties.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/config/AliyunIotProperties.java index a1b4888..23294c4 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/config/AliyunIotProperties.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/config/AliyunIotProperties.java @@ -47,6 +47,13 @@ public class AliyunIotProperties { */ private String categoryKey; + /** + * 设备类型到产品Key的映射 + * 1: 水质检测仪 + * 2: 控制一体机 + */ + private DeviceTypeMapping deviceType = new DeviceTypeMapping(); + /** * MQTT 配置(可选,用于设备直连) */ @@ -158,4 +165,39 @@ public class AliyunIotProperties { private Integer reconnectDelay = 30000; } + /** + * 设备类型映射配置 + */ + @Data + public static class DeviceTypeMapping { + /** + * 水质检测仪 ProductKey + */ + private String waterQualityMonitor; + + /** + * 控制一体机 ProductKey + */ + private String controlIntegrated; + + /** + * 根据设备类型获取 ProductKey + * @param deviceType 1-水质检测仪, 2-控制一体机 + * @return ProductKey + */ + public String getProductKeyByType(Integer deviceType) { + if (deviceType == null) { + return null; + } + switch (deviceType) { + case 1: + return waterQualityMonitor; + case 2: + return controlIntegrated; + default: + return null; + } + } + } + } diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/controller/IotController.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/controller/IotController.java index a328ebb..721195c 100644 --- a/intc-modules/intc-iot/src/main/java/com/intc/iot/controller/IotController.java +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/controller/IotController.java @@ -3,7 +3,13 @@ package com.intc.iot.controller; import com.intc.common.core.domain.R; import com.intc.common.mybatis.core.page.PageQuery; import com.intc.common.mybatis.core.page.TableDataInfo; +import com.intc.common.satoken.utils.LoginHelper; import com.intc.common.web.core.BaseController; +import com.intc.fishery.domain.Device; +import com.intc.fishery.mapper.DeviceMapper; +import com.intc.fishery.mapper.PondMapper; +import com.intc.iot.config.AliyunIotProperties; +import com.intc.iot.domain.bo.AddDeviceDetectorBo; import com.intc.iot.domain.bo.DeviceRealtimeDataBo; import com.intc.iot.domain.vo.DeviceRealtimeDataVo; import com.intc.iot.service.DeviceDataService; @@ -23,6 +29,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import java.util.Date; import java.util.Map; /** @@ -37,6 +44,8 @@ import java.util.Map; @Tag(name = "生活物联网平台管理", description = "阿里云飞燕平台对接接口") public class IotController extends BaseController { + private final AliyunIotProperties aliyunIotProperties; + @Autowired(required = false) private IotDeviceService iotDeviceService; @@ -64,6 +73,12 @@ public class IotController extends BaseController { @Autowired(required = false) private javax.jms.Connection amqpConnection; + @Autowired(required = false) + private DeviceMapper deviceMapper; + + @Autowired(required = false) + private PondMapper pondMapper; + @Operation(summary = "测试接口") @GetMapping("/test") public R test() { @@ -74,7 +89,7 @@ public class IotController extends BaseController { @GetMapping("/amqp/status") public R> getAmqpStatus() { Map status = new java.util.HashMap<>(); - + if (amqpConnection == null) { status.put("configured", false); status.put("connected", false); @@ -94,7 +109,7 @@ public class IotController extends BaseController { status.put("message", "AMQP 连接已关闭或异常: " + e.getMessage()); } } - + return R.ok(status); } @@ -111,7 +126,7 @@ public class IotController extends BaseController { String virtualHost = AliyunAmqpSignUtil.generateVirtualHost(accessKeyId); String username = AliyunAmqpSignUtil.generateUsername(accessKeyId); String password = AliyunAmqpSignUtil.generatePassword(accessKeySecret, consumerGroupId); - + Map config = new java.util.HashMap<>(); config.put("host", host); config.put("port", 5672); @@ -119,14 +134,14 @@ public class IotController extends BaseController { config.put("username", username); config.put("password", password); config.put("consumerGroupId", consumerGroupId); - + return R.ok(config); } catch (Exception e) { log.error("生成 AMQP 配置失败", e); return R.fail("生成配置失败: " + e.getMessage()); } } - + @Operation(summary = "根据 ProductKey 和 DeviceName 查询设备信息") @GetMapping("/device/find") public R> findDeviceByProductKeyAndName( @@ -143,7 +158,7 @@ public class IotController extends BaseController { return R.fail("查询设备信息失败: " + e.getMessage()); } } - + @Operation(summary = "查询设备列表") @GetMapping("/device/list") public R> queryDeviceList( @@ -261,19 +276,93 @@ public class IotController extends BaseController { } } - @Operation(summary = "查询设备当前状态") + @Operation(summary = "查询设备当前状态(从物联网平台)") @GetMapping("/device/status") - public R queryDeviceStatus( - @Parameter(description = "产品Key") @RequestParam String productKey, - @Parameter(description = "设备名称") @RequestParam String deviceName) { + public R queryDeviceStatus( + @Parameter(description = "设备类型(1-水质检测仪, 2-控制一体机)") @RequestParam Integer devicetype, + @Parameter(description = "设备编号/设备名称") @RequestParam String serialnum) { try { - if (deviceStatusService == null) { - return R.fail("设备状态服务未启用"); + if (iotDeviceService == null) { + return R.fail("飞燕平台配置未启用"); } - com.intc.iot.domain.IotDeviceStatus status = deviceStatusService.queryDeviceStatus(productKey, deviceName); - return R.ok(status); + + // 根据设备类型获取 ProductKey + String productKey = aliyunIotProperties.getDeviceType().getProductKeyByType(devicetype); + if (productKey == null || productKey.isEmpty()) { + return R.fail("未配置该设备类型的 ProductKey,请检查配置文件(deviceType: " + devicetype + ")"); + } + + // 先查询设备信息获取 iotId + Map deviceInfo = iotDeviceService.findDeviceByProductKeyAndName(productKey, serialnum); + + // 检查返回结果 + if (deviceInfo == null || !Boolean.TRUE.equals(deviceInfo.get("success"))) { + String errorMsg = deviceInfo != null ? (String) deviceInfo.get("errorMessage") : "查询失败"; + return R.fail("未找到该设备,ProductKey: " + productKey + ", DeviceName: " + serialnum + ", 原因: " + errorMsg); + } + + // 从返回结构中提取设备信息 + Map data = (Map) deviceInfo.get("data"); + if (data == null || !data.containsKey("deviceList")) { + return R.fail("设备信息格式异常"); + } + + java.util.List deviceList = (java.util.List) data.get("deviceList"); + if (deviceList == null || deviceList.isEmpty()) { + return R.fail("未找到该设备,ProductKey: " + productKey + ", DeviceName: " + serialnum); + } + + // 获取第一个设备对象 + Object deviceObj = deviceList.get(0); + String iotId = null; + + // 尝试从设备对象中提取 iotId + if (deviceObj instanceof Map) { + iotId = (String) ((Map) deviceObj).get("iotId"); + } else { + // 如果是 SDK 对象,尝试通过反射获取 + try { + java.lang.reflect.Method method = deviceObj.getClass().getMethod("getIotId"); + iotId = (String) method.invoke(deviceObj); + } catch (Exception ex) { + log.error("无法从设备对象中获取 iotId: {}", ex.getMessage()); + } + } + + if (iotId == null || iotId.isEmpty()) { + return R.fail("设备 iotId 为空"); + } + // 查询设备详情(包含在线状态) + Map deviceDetail = iotDeviceService.queryDeviceInfo(iotId); + + // 从设备详情中提取状态并转换为前端需要的状态码 + Object detailData = deviceDetail.get("data"); + Integer statusCode = 0; // 默认为设备未激活 + + if (detailData instanceof Map) { + Map detailMap = (Map) detailData; + Object statusObj = detailMap.get("status"); + + if (statusObj != null) { + String statusStr = statusObj.toString(); + // 根据物联网平台返回的状态转换为前端状态码 + // 0-未激活, 1-在线, 3-离线, 8-禁用 + if ("ONLINE".equalsIgnoreCase(statusStr) || "online".equals(statusStr)) { + statusCode = 1; + } else if ("OFFLINE".equalsIgnoreCase(statusStr) || "offline".equals(statusStr)) { + statusCode = 3; + } else if ("UNACTIVE".equalsIgnoreCase(statusStr) || "unactive".equals(statusStr)) { + statusCode = 0; + } else if ("DISABLE".equalsIgnoreCase(statusStr) || "disable".equals(statusStr)) { + statusCode = 8; + } + } + } + + // 直接返回状态码给前端 + return R.ok(statusCode); } catch (Exception e) { - log.error("查询设备状态失败: {}", e.getMessage()); + log.error("查询设备状态失败: {}", e.getMessage(), e); return R.fail("查询设备状态失败: " + e.getMessage()); } } @@ -465,13 +554,13 @@ public class IotController extends BaseController { com.intc.iot.domain.VmsNoticeResponse response = vmsNoticeService.sendTtsCall( phoneNumber, paramsMap, outId != null ? outId : "TEST" ); - + Map result = new java.util.HashMap<>(); result.put("success", response.isSuccess()); result.put("code", response.getCode()); result.put("message", response.getMessage()); result.put("callId", response.getCallId()); - + return R.ok(result); } catch (Exception e) { log.error("发送语音通知失败: {}", e.getMessage()); @@ -582,4 +671,248 @@ public class IotController extends BaseController { return R.fail("清理失败: " + e.getMessage()); } } + + // ======================== 设备管理相关接口 ======================== + + @Operation(summary = "添加设备探测器(水质检测仪)") + @PostMapping("/device/add_device_detector") + public R addDeviceDetector(@RequestBody AddDeviceDetectorBo bo) { + try { + if (iotDeviceService == null) { + return R.fail("飞燕平台配置未启用"); + } + if (deviceMapper == null) { + return R.fail("设备数据库服务未启用"); + } + + // 获取当前登录用户ID + Long userId = LoginHelper.getUserId(); + if (userId == null) { + return R.fail("未登录或登录已过期"); + } + + // 验证塘口是否存在且属于当前用户 + if (bo.getPondId() != null && bo.getPondId() > 0 && pondMapper != null) { + long count = pondMapper.selectCount( + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(com.intc.fishery.domain.Pond::getUserId, userId) + .eq(com.intc.fishery.domain.Pond::getId, bo.getPondId()) + ); + if (count == 0) { + return R.fail("塘口不存在或无权限访问"); + } + } + + // 检查设备是否已被绑定 + Device existDevice = deviceMapper.selectOne( + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(Device::getDeviceType, 1) // 1-水质检测仪 + .eq(Device::getSerialNum, bo.getSerialNum()) + ); + if (existDevice != null && existDevice.getUserId() != null) { + return R.fail("该设备号已被绑定"); + } + + // 获取水质检测仪的 ProductKey + String productKey = aliyunIotProperties.getDeviceType().getWaterQualityMonitor(); + if (productKey == null || productKey.isEmpty()) { + return R.fail("未配置水质检测仪的 ProductKey"); + } + + // 查询设备基础信息 + Map deviceInfo = iotDeviceService.findDeviceByProductKeyAndName(productKey, bo.getSerialNum()); + if (deviceInfo == null || !Boolean.TRUE.equals(deviceInfo.get("success"))) { + return R.fail("设备不存在"); + } + + // 提取设备数据 + Map data = (Map) deviceInfo.get("data"); + if (data == null || !data.containsKey("deviceList")) { + return R.fail("设备信息格式异常"); + } + + java.util.List deviceList = (java.util.List) data.get("deviceList"); + if (deviceList == null || deviceList.isEmpty()) { + return R.fail("设备不存在"); + } + + Object deviceObj = deviceList.get(0); + String iotId = null; + String status = null; + + // 提取 iotId 和 status + if (deviceObj instanceof Map) { + Map deviceMap = (Map) deviceObj; + iotId = (String) deviceMap.get("iotId"); + Object statusObj = deviceMap.get("status"); + status = statusObj != null ? statusObj.toString() : null; + } else { + try { + iotId = (String) deviceObj.getClass().getMethod("getIotId").invoke(deviceObj); + Object statusObj = deviceObj.getClass().getMethod("getStatus").invoke(deviceObj); + status = statusObj != null ? statusObj.toString() : null; + } catch (Exception ex) { + log.error("无法从设备对象中获取信息: {}", ex.getMessage()); + } + } + + if (iotId == null || iotId.isEmpty()) { + return R.fail("设备 iotId 为空"); + } + + // 检查设备状态 + if (status != null) { + if ("UNACTIVE".equalsIgnoreCase(status)) { + return R.fail("设备未激活"); + } + if ("DISABLE".equalsIgnoreCase(status)) { + return R.fail("设备禁用"); + } + } + + // 设置盐度补偿 + Map properties = new java.util.HashMap<>(); + properties.put("salinitySet", bo.getSalinityCompensation()); + String propertiesJson = cn.hutool.json.JSONUtil.toJsonStr(properties); + Map setResult = iotDeviceService.setDeviceProperty(iotId, propertiesJson); + if (setResult == null || !Boolean.TRUE.equals(setResult.get("success"))) { + return R.fail("设置盐度补偿失败"); + } + + // 获取设备属性 + Map deviceProperties = iotDeviceService.queryDeviceProperties(iotId); + + // 初始化警告码:默认探头未校准 (0x0001) + int warnCode = 0x0001; + + // 计算设备数量,用于生成设备名称 + long deviceCount = deviceMapper.selectCount( + new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() + .eq(Device::getUserId, userId) + .eq(Device::getDeviceType, 1) + ); + + // 创建或更新设备信息 + Date now = new Date(); + boolean isNew = (existDevice == null); + Device device = isNew ? new Device() : existDevice; + + if (isNew) { + device.setIotId(iotId); + device.setSerialNum(bo.getSerialNum()); + device.setDeviceType(1); // 1-水质检测仪 + device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); // 一年后到期 + device.setIsOxygenUsed(1); + } else { + // 检查是否已过期 + if (device.getDeadTime() != null && now.after(device.getDeadTime())) { + warnCode |= 0x0040; // 设备时间到期 (0x0040) + } + } + + // 设置基本信息 + device.setUserId(userId); + device.setDeviceName("溶解氧" + (deviceCount + 1)); + device.setBindTime(now); + device.setPondId(bo.getPondId() != null && bo.getPondId() > 0 ? bo.getPondId() : null); + device.setSalinityCompensation(bo.getSalinityCompensation()); + device.setOxyWarnLower(bo.getOxyWarnLower()); + device.setOxyWarnCallOpen(1); + device.setOxyWarnCallNoDis(1); + device.setTempWarnCallOpen(0); + device.setTempWarnCallNoDis(0); + + // 解析并设置设备属性 + if (deviceProperties != null && Boolean.TRUE.equals(deviceProperties.get("success"))) { + Object propData = deviceProperties.get("data"); + if (propData instanceof Map) { + Map propMap = (Map) propData; + Object listObj = propMap.get("list"); + if (listObj instanceof java.util.List) { + java.util.List propList = (java.util.List) listObj; + for (Object item : propList) { + if (item instanceof Map) { + Map prop = (Map) item; + String attribute = (String) prop.get("identifier"); + Object value = prop.get("value"); + + if (attribute != null && value != null) { + switch (attribute) { + case "dissolvedOxygen": // 溶解氧 + device.setValueDissolvedOxygen(Double.parseDouble(value.toString())); + break; + case "currentTemperature": // 温度 + device.setValueTemperature(Double.parseDouble(value.toString())); + break; + case "dosat": // 饱和度 + device.setValueSaturability(Double.parseDouble(value.toString())); + break; + case "PH": + device.setValuePh(Double.parseDouble(value.toString())); + break; + case "salinity": // 盐度 + device.setValueSalinity(Double.parseDouble(value.toString())); + break; + case "sensorErrorCode": // 警告码 + try { + int errorCode = Integer.parseInt(value.toString()); + warnCode |= errorCode; + } catch (NumberFormatException e) { + log.warn("无法解析警告码: {}", value); + } + break; + case "Tcorrect": // 设备校准状态 + try { + int tcorrect = Integer.parseInt(value.toString()); + if (tcorrect == 1) { + warnCode &= ~0x0001; // 清除未校准标记 + } + } catch (NumberFormatException e) { + log.warn("无法解析校准状态: {}", value); + } + break; + case "ICCID": // 物联网卡号 + device.setIccId(value.toString()); + break; + case "Treference": // 参比值 + device.setTreference(Double.parseDouble(value.toString())); + break; + case "Tfluorescence": // 荧光值 + device.setTfluorescence(Double.parseDouble(value.toString())); + break; + } + } + } + } + } + } + } + + // 如果设备离线,添加离线警告 + if (status != null && "OFFLINE".equalsIgnoreCase(status)) { + warnCode |= 0x0080; // 设备离线 (0x0080) + } + + device.setWarnCode(warnCode); + + // 验证 ICCID 是否存在 + if (device.getIccId() == null || device.getIccId().isEmpty()) { + return R.fail("设备缺少物联网卡号(ICCID)"); + } + + // 保存到数据库 + if (isNew) { + deviceMapper.insert(device); + log.info("新设备添加成功: userId={}, iotId={}, serialNum={}", userId, iotId, bo.getSerialNum()); + } else { + deviceMapper.updateById(device); + log.info("设备更新成功: userId={}, iotId={}, serialNum={}", userId, iotId, bo.getSerialNum()); + } + + return R.ok(); + } catch (Exception e) { + log.error("添加设备失败: {}", e.getMessage(), e); + return R.fail("添加设备失败: " + e.getMessage()); + } + } } diff --git a/intc-modules/intc-iot/src/main/java/com/intc/iot/domain/bo/AddDeviceDetectorBo.java b/intc-modules/intc-iot/src/main/java/com/intc/iot/domain/bo/AddDeviceDetectorBo.java new file mode 100644 index 0000000..5193302 --- /dev/null +++ b/intc-modules/intc-iot/src/main/java/com/intc/iot/domain/bo/AddDeviceDetectorBo.java @@ -0,0 +1,49 @@ +package com.intc.iot.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 添加设备探测器业务对象 + * + * @author intc + */ +@Data +@Schema(description = "添加设备探测器请求对象") +public class AddDeviceDetectorBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 设备编号/序列号 + */ + @Schema(description = "设备编号/序列号") + @NotBlank(message = "设备编号不能为空") + private String serialNum; + + /** + * 塘口ID + */ + @Schema(description = "塘口ID") + private Long pondId; + + /** + * 盐度补偿值 + */ + @Schema(description = "盐度补偿值") + @NotNull(message = "盐度补偿值不能为空") + private Double salinityCompensation; + + /** + * 溶解氧报警下限 + */ + @Schema(description = "溶解氧报警下限") + @NotNull(message = "溶解氧报警下限不能为空") + private Double oxyWarnLower; +}