fix: 微信小程序接口对接,测试问题修复。

This commit is contained in:
tianyongbao
2026-01-12 18:47:11 +08:00
parent 38ba328966
commit a41248e405
22 changed files with 2060 additions and 202 deletions

View File

@@ -0,0 +1,128 @@
package com.intc.fishery.constant;
import java.util.HashMap;
import java.util.Map;
/**
* 设备故障码常量定义
*
* @author intc
*/
public class DefineDeviceErrorCode {
/**
* 电源关闭
*/
public static final int PowerOff = 1;
// 三相电压告警 (2-7)
/**
* 三相A相过压
*/
public static final int Three_PhaseA_OverVoltage = 2;
/**
* 三相B相过压
*/
public static final int Three_PhaseB_OverVoltage = 3;
/**
* 三相C相过压
*/
public static final int Three_PhaseC_OverVoltage = 4;
/**
* 三相A相欠压
*/
public static final int Three_PhaseA_UnderVoltage = 5;
/**
* 三相B相欠压
*/
public static final int Three_PhaseB_UnderVoltage = 6;
/**
* 三相C相欠压
*/
public static final int Three_PhaseC_UnderVoltage = 7;
// 三相电流告警 (8-23)
/**
* 三相开关1路A相过流
*/
public static final int Three_Switch1OverElectricA = 8;
public static final int Three_Switch1OverElectricB = 9;
public static final int Three_Switch1OverElectricC = 10;
public static final int Three_Switch2OverElectricA = 11;
public static final int Three_Switch2OverElectricB = 12;
public static final int Three_Switch2OverElectricC = 13;
public static final int Three_Switch3OverElectricA = 14;
public static final int Three_Switch3OverElectricB = 15;
public static final int Three_Switch3OverElectricC = 16;
public static final int Three_Switch4OverElectricA = 17;
public static final int Three_Switch4OverElectricB = 18;
public static final int Three_Switch4OverElectricC = 19;
/**
* 三相开关1路缺相
*/
public static final int Three_Switch1ElectricEmpty = 20;
public static final int Three_Switch2ElectricEmpty = 21;
public static final int Three_Switch3ElectricEmpty = 22;
public static final int Three_Switch4ElectricEmpty = 23;
// 单相电流告警 (24-31)
/**
* 单相开关1路过流
*/
public static final int One_Switch1OverElectric = 24;
public static final int One_Switch2OverElectric = 25;
public static final int One_Switch3OverElectric = 26;
public static final int One_Switch4OverElectric = 27;
/**
* 单相开关1路缺相
*/
public static final int One_Switch1ElectricEmpty = 28;
public static final int One_Switch2ElectricEmpty = 29;
public static final int One_Switch3ElectricEmpty = 30;
public static final int One_Switch4ElectricEmpty = 31;
private static final Map<Integer, String> ERROR_CODE_MESSAGES = new HashMap<>();
static {
ERROR_CODE_MESSAGES.put(PowerOff, "电源关闭");
ERROR_CODE_MESSAGES.put(Three_PhaseA_OverVoltage, "A相过压");
ERROR_CODE_MESSAGES.put(Three_PhaseB_OverVoltage, "B相过压");
ERROR_CODE_MESSAGES.put(Three_PhaseC_OverVoltage, "C相过压");
ERROR_CODE_MESSAGES.put(Three_PhaseA_UnderVoltage, "A相欠压");
ERROR_CODE_MESSAGES.put(Three_PhaseB_UnderVoltage, "B相欠压");
ERROR_CODE_MESSAGES.put(Three_PhaseC_UnderVoltage, "C相欠压");
ERROR_CODE_MESSAGES.put(Three_Switch1OverElectricA, "1路A相过流");
ERROR_CODE_MESSAGES.put(Three_Switch1OverElectricB, "1路B相过流");
ERROR_CODE_MESSAGES.put(Three_Switch1OverElectricC, "1路C相过流");
ERROR_CODE_MESSAGES.put(Three_Switch2OverElectricA, "2路A相过流");
ERROR_CODE_MESSAGES.put(Three_Switch2OverElectricB, "2路B相过流");
ERROR_CODE_MESSAGES.put(Three_Switch2OverElectricC, "2路C相过流");
ERROR_CODE_MESSAGES.put(Three_Switch3OverElectricA, "3路A相过流");
ERROR_CODE_MESSAGES.put(Three_Switch3OverElectricB, "3路B相过流");
ERROR_CODE_MESSAGES.put(Three_Switch3OverElectricC, "3路C相过流");
ERROR_CODE_MESSAGES.put(Three_Switch4OverElectricA, "4路A相过流");
ERROR_CODE_MESSAGES.put(Three_Switch4OverElectricB, "4路B相过流");
ERROR_CODE_MESSAGES.put(Three_Switch4OverElectricC, "4路C相过流");
ERROR_CODE_MESSAGES.put(Three_Switch1ElectricEmpty, "1路缺相");
ERROR_CODE_MESSAGES.put(Three_Switch2ElectricEmpty, "2路缺相");
ERROR_CODE_MESSAGES.put(Three_Switch3ElectricEmpty, "3路缺相");
ERROR_CODE_MESSAGES.put(Three_Switch4ElectricEmpty, "4路缺相");
ERROR_CODE_MESSAGES.put(One_Switch1OverElectric, "1路过流");
ERROR_CODE_MESSAGES.put(One_Switch2OverElectric, "2路过流");
ERROR_CODE_MESSAGES.put(One_Switch3OverElectric, "3路过流");
ERROR_CODE_MESSAGES.put(One_Switch4OverElectric, "4路过流");
ERROR_CODE_MESSAGES.put(One_Switch1ElectricEmpty, "1路缺相");
ERROR_CODE_MESSAGES.put(One_Switch2ElectricEmpty, "2路缺相");
ERROR_CODE_MESSAGES.put(One_Switch3ElectricEmpty, "3路缺相");
ERROR_CODE_MESSAGES.put(One_Switch4ElectricEmpty, "4路缺相");
}
private DefineDeviceErrorCode() {
}
/**
* 获取故障码对应的错误信息
*/
public static String getErrorMessage(int errorCode) {
return ERROR_CODE_MESSAGES.getOrDefault(errorCode, "");
}
}

View File

@@ -0,0 +1,96 @@
package com.intc.fishery.constant;
/**
* 设备告警码常量定义
*
* @author intc
*/
public class DefineDeviceWarnCode {
/**
* 无告警
*/
public static final int None = 0;
/**
* 用户自定义告警最大码
*/
public static final int UserMaxCode = 65536;
/**
* 低溶氧告警
*/
public static final int LowOxygen = 1;
/**
* 高温告警
*/
public static final int HighTemperature = 2;
/**
* 低温告警
*/
public static final int LowTemperature = 4;
/**
* PH值告警
*/
public static final int PHAlarm = 8;
/**
* 盐度告警
*/
public static final int SalinityAlarm = 16;
/**
* 设备离线
*/
public static final int DeviceOffline = 32;
/**
* 设备失联
*/
public static final int DeviceDead = 64;
private DefineDeviceWarnCode() {
}
/**
* 转换告警码为告警描述
*/
public static String toWarnDescription(int warnCode) {
if (warnCode == None) {
return "";
}
StringBuilder sb = new StringBuilder();
if ((warnCode & LowOxygen) != 0) {
sb.append("低溶氧告警,");
}
if ((warnCode & HighTemperature) != 0) {
sb.append("高温告警,");
}
if ((warnCode & LowTemperature) != 0) {
sb.append("低温告警,");
}
if ((warnCode & PHAlarm) != 0) {
sb.append("PH值告警");
}
if ((warnCode & SalinityAlarm) != 0) {
sb.append("盐度告警,");
}
if ((warnCode & DeviceOffline) != 0) {
sb.append("设备离线,");
}
if ((warnCode & DeviceDead) != 0) {
sb.append("设备失联,");
}
// 移除最后的逗号
if (sb.length() > 0) {
sb.setLength(sb.length() - 1);
}
return sb.toString();
}
}

View File

@@ -21,6 +21,19 @@ import com.intc.fishery.domain.vo.DeviceVo;
import com.intc.fishery.domain.bo.DeviceBo;
import com.intc.fishery.service.IDeviceService;
import com.intc.common.mybatis.core.page.TableDataInfo;
import com.intc.fishery.domain.vo.PublicDeviceSimpleVo;
import com.intc.fishery.domain.vo.PublicPondIdNameVo;
import com.intc.fishery.domain.vo.PublicDeviceSwitchSimpleVo;
import com.intc.fishery.domain.Device;
import com.intc.fishery.domain.DeviceSwitch;
import com.intc.fishery.domain.Pond;
import com.intc.fishery.mapper.DeviceMapper;
import com.intc.fishery.mapper.DeviceSwitchMapper;
import com.intc.fishery.mapper.PondMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import java.util.stream.Collectors;
import java.util.Map;
import java.util.function.Function;
/**
* 设备管理
@@ -35,6 +48,9 @@ import com.intc.common.mybatis.core.page.TableDataInfo;
public class DeviceController extends BaseController {
private final IDeviceService deviceService;
private final DeviceMapper deviceMapper;
private final DeviceSwitchMapper deviceSwitchMapper;
private final PondMapper pondMapper;
/**
* 查询设备管理列表
@@ -102,4 +118,146 @@ public class DeviceController extends BaseController {
@PathVariable Long[] ids) {
return toAjax(deviceService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 查询用户所有设备简化列表
* type=1: 返回完整信息(包含塘口信息和开关列表)
* type!=1: 只返回基本信息id, deviceName, deviceType, deadTime
*
* @param rootUserId 用户ID
* @param type 查询类型1-完整信息,其他-基本信息)
*/
@GetMapping("/list_all_device")
public R<List<PublicDeviceSimpleVo>> getDeviceSimpleList(
@RequestParam("rootUserId") Long rootUserId,
@RequestParam("type") Integer type) {
// 查询用户的所有设备
List<Device> devices = deviceMapper.selectList(
new LambdaQueryWrapper<Device>()
.eq(Device::getUserId, rootUserId)
.orderByDesc(Device::getCreateTime)
);
if (devices == null || devices.isEmpty()) {
return R.ok(List.of());
}
// type=1: 返回完整信息
if (type == 1) {
// 批量查询所有需要的塘口信息
List<Long> pondIds = devices.stream()
.map(Device::getPondId)
.filter(id -> id != null && id > 0)
.distinct()
.collect(Collectors.toList());
Map<Long, Pond> pondMap = Map.of();
if (!pondIds.isEmpty()) {
List<Pond> ponds = pondMapper.selectBatchIds(pondIds);
pondMap = ponds.stream()
.collect(Collectors.toMap(Pond::getId, Function.identity()));
}
// 批量查询所有开关
List<Long> deviceIds = devices.stream()
.map(Device::getId)
.collect(Collectors.toList());
List<DeviceSwitch> allSwitches = deviceSwitchMapper.selectList(
new LambdaQueryWrapper<DeviceSwitch>()
.in(DeviceSwitch::getDeviceId, deviceIds)
.orderByAsc(DeviceSwitch::getIndex)
);
// 按设备ID分组开关
Map<Long, List<DeviceSwitch>> switchMap = allSwitches.stream()
.collect(Collectors.groupingBy(DeviceSwitch::getDeviceId));
// 开关关联的塘口ID
List<Long> switchPondIds = allSwitches.stream()
.map(DeviceSwitch::getPondId)
.filter(id -> id != null && id > 0)
.distinct()
.collect(Collectors.toList());
// 合并塘口查询
if (!switchPondIds.isEmpty()) {
List<Pond> switchPonds = pondMapper.selectBatchIds(switchPondIds);
Map<Long, Pond> finalPondMap = pondMap;
pondMap = switchPonds.stream()
.collect(Collectors.toMap(Pond::getId, Function.identity(), (v1, v2) -> v1));
pondMap.putAll(finalPondMap);
}
Map<Long, Pond> finalPondMapForLambda = pondMap;
// 构建返回结果
List<PublicDeviceSimpleVo> result = devices.stream()
.map(device -> {
PublicDeviceSimpleVo vo = new PublicDeviceSimpleVo();
vo.setId(device.getId());
vo.setDeviceName(device.getDeviceName());
vo.setDeviceType(device.getDeviceType());
vo.setDeadTime(device.getDeadTime());
vo.setIsOxygenUsed(device.getIsOxygenUsed());
// 设置塘口信息
if (device.getPondId() != null && device.getPondId() > 0) {
Pond pond = finalPondMapForLambda.get(device.getPondId());
if (pond != null) {
PublicPondIdNameVo pondInfo = new PublicPondIdNameVo();
pondInfo.setId(pond.getId());
pondInfo.setPondName(pond.getPondName());
vo.setPondInfo(pondInfo);
}
}
// 设置开关列表
List<DeviceSwitch> switches = switchMap.getOrDefault(device.getId(), List.of());
List<PublicDeviceSwitchSimpleVo> switchVos = switches.stream()
.map(s -> {
PublicDeviceSwitchSimpleVo switchVo = new PublicDeviceSwitchSimpleVo();
switchVo.setId(s.getId());
switchVo.setIndex(s.getIndex());
switchVo.setSwitchName(s.getSwitchName());
// 设置开关的塘口信息
if (s.getPondId() != null && s.getPondId() > 0) {
Pond pond = finalPondMapForLambda.get(s.getPondId());
if (pond != null) {
PublicPondIdNameVo pondInfo = new PublicPondIdNameVo();
pondInfo.setId(pond.getId());
pondInfo.setPondName(pond.getPondName());
switchVo.setPondInfo(pondInfo);
}
}
return switchVo;
})
.collect(Collectors.toList());
vo.setListSwitch(switchVos);
return vo;
})
.collect(Collectors.toList());
return R.ok(result);
}
// type!=1: 只返回基本信息
else {
List<PublicDeviceSimpleVo> result = devices.stream()
.map(device -> {
PublicDeviceSimpleVo vo = new PublicDeviceSimpleVo();
vo.setId(device.getId());
vo.setDeviceName(device.getDeviceName());
vo.setDeviceType(device.getDeviceType());
vo.setDeadTime(device.getDeadTime());
return vo;
})
.collect(Collectors.toList());
return R.ok(result);
}
}
}

View File

@@ -27,6 +27,36 @@ import com.intc.fishery.domain.vo.AquUserVo;
import com.intc.fishery.domain.bo.AquUserBo;
import com.intc.system.service.ISysUserService;
import com.intc.system.domain.vo.SysUserVo;
import com.intc.fishery.domain.vo.PondDeviceListVo;
import com.intc.fishery.domain.vo.DeviceVo;
import com.intc.fishery.domain.vo.DeviceWithSwitchVo;
import com.intc.fishery.domain.vo.DeviceSwitchVo;
import com.intc.fishery.mapper.DeviceMapper;
import com.intc.fishery.mapper.DeviceSwitchMapper;
import com.intc.fishery.domain.Device;
import com.intc.fishery.domain.DeviceSwitch;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import java.util.stream.Collectors;
import com.intc.common.core.utils.MapstructUtils;
import cn.hutool.core.bean.BeanUtil;
import com.intc.fishery.domain.bo.PondSelectDeviceOrSwitchBo;
import com.intc.fishery.domain.Pond;
import com.intc.fishery.mapper.PondMapper;
import com.intc.fishery.mapper.LinkedCtrlMapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashSet;
import java.util.Set;
import java.util.ArrayList;
import com.intc.fishery.domain.LinkedCtrl;
import com.intc.fishery.domain.vo.PublicPondMode1Vo;
import com.intc.fishery.domain.vo.PondMode1WarnCodeInfo;
import com.intc.fishery.mapper.DeviceErrorCodeMapper;
import com.intc.fishery.domain.DeviceErrorCode;
import com.intc.fishery.constant.DefineDeviceWarnCode;
import com.intc.fishery.constant.DefineDeviceErrorCode;
import com.intc.fishery.mapper.TimingCtrlMapper;
import com.intc.fishery.domain.TimingCtrl;
/**
* 塘口管理
@@ -43,6 +73,12 @@ public class PondController extends BaseController {
private final IPondService pondService;
private final IAquUserService aquUserService;
private final ISysUserService sysUserService;
private final DeviceMapper deviceMapper;
private final DeviceSwitchMapper deviceSwitchMapper;
private final PondMapper pondMapper;
private final LinkedCtrlMapper linkedCtrlMapper;
private final DeviceErrorCodeMapper deviceErrorCodeMapper;
private final TimingCtrlMapper timingCtrlMapper;
/**
* 查询塘口管理列表
@@ -140,4 +176,636 @@ public class PondController extends BaseController {
@PathVariable Long[] ids) {
return toAjax(pondService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 根据塘口ID查询设备列表
* 返回数据按设备类型分类:
* - listDetector: 水质检测仪列表deviceType=1+ 开启溶氧检测的测控一体机deviceType=2 && isOxygenUsed=1
* - listController: 测控一体机列表deviceType=2包含开关列表
*
* @param pondId 塘口ID
*/
@GetMapping("/devices/{pondId}")
public R<PondDeviceListVo> getDevicesByPondId(@NotNull(message = "塘口ID不能为空")
@PathVariable Long pondId) {
PondDeviceListVo result = new PondDeviceListVo();
// 1. 查询塘口下的所有设备
List<Device> devices = deviceMapper.selectList(
new LambdaQueryWrapper<Device>()
.eq(Device::getPondId, pondId)
.orderByAsc(Device::getDeviceType)
.orderByDesc(Device::getCreateTime)
);
if (devices == null || devices.isEmpty()) {
result.setListDetector(List.of());
result.setListController(List.of());
return R.ok(result);
}
// 2. 收集所有设备ID,用于查询联动控制和故障码
List<Long> deviceIds = devices.stream()
.map(Device::getId)
.collect(Collectors.toList());
// 3. 批量查询所有设备的联动控制
List<LinkedCtrl> allLinkedCtrls = linkedCtrlMapper.selectList(
new LambdaQueryWrapper<LinkedCtrl>()
.in(LinkedCtrl::getDeviceId, deviceIds)
);
java.util.Map<Long, List<LinkedCtrl>> linkedCtrlsByDevice = allLinkedCtrls.stream()
.collect(Collectors.groupingBy(LinkedCtrl::getDeviceId));
// 4. 批量查询塘口的所有开关
List<DeviceSwitch> allSwitches = deviceSwitchMapper.selectList(
new LambdaQueryWrapper<DeviceSwitch>()
.eq(DeviceSwitch::getPondId, pondId)
.orderByAsc(DeviceSwitch::getIndex)
);
// 5. 收集开关ID,查询定时控制
List<Long> switchIds = allSwitches.stream()
.map(DeviceSwitch::getId)
.collect(Collectors.toList());
java.util.Map<Long, List<TimingCtrl>> timingCtrlsBySwitch = new java.util.HashMap<>();
if (!switchIds.isEmpty()) {
List<TimingCtrl> allTimingCtrls = timingCtrlMapper.selectList(
new LambdaQueryWrapper<TimingCtrl>()
.in(TimingCtrl::getSwitchId, switchIds)
);
timingCtrlsBySwitch = allTimingCtrls.stream()
.collect(Collectors.groupingBy(TimingCtrl::getSwitchId));
}
// 6. 按设备ID分组开关
java.util.Map<Long, List<DeviceSwitch>> switchesByDevice = allSwitches.stream()
.collect(Collectors.groupingBy(DeviceSwitch::getDeviceId));
// 7. 批量查询故障码
Set<Long> controllerIds = devices.stream()
.filter(d -> d.getDeviceType() != null && d.getDeviceType() == 2)
.map(Device::getId)
.collect(Collectors.toSet());
List<DeviceErrorCode> errorCodes = new ArrayList<>();
if (!controllerIds.isEmpty()) {
errorCodes = deviceErrorCodeMapper.selectList(
new LambdaQueryWrapper<DeviceErrorCode>()
.in(DeviceErrorCode::getDeviceId, controllerIds)
);
}
// 8. 处理探测器列表 (包含: deviceType=1 + deviceType=2且isOxygenUsed=1)
List<DeviceVo> detectorList = new ArrayList<>();
for (Device device : devices) {
// 水质检测仪 或 开启溶氧检测的测控一体机
if ((device.getDeviceType() != null && device.getDeviceType() == 1)
|| (device.getDeviceType() != null && device.getDeviceType() == 2
&& device.getIsOxygenUsed() != null && device.getIsOxygenUsed() == 1)) {
DeviceVo deviceVo = MapstructUtils.convert(device, DeviceVo.class);
// 设置告警码信息
if (device.getWarnCode() != null && device.getWarnCode() < DefineDeviceWarnCode.UserMaxCode) {
// 告警码信息已在DeviceVo中,无需额外处理
} else {
deviceVo.setWarnCode(DefineDeviceWarnCode.None);
}
// 判断是否有联动控制
deviceVo.setIsLinkCtrl(false);
List<LinkedCtrl> linkedCtrls = linkedCtrlsByDevice.get(device.getId());
if (linkedCtrls != null && !linkedCtrls.isEmpty()) {
for (LinkedCtrl linkedCtrl : linkedCtrls) {
if ((linkedCtrl.getOxyLowerOpen() != null && linkedCtrl.getOxyLowerOpen() == 1)
|| (linkedCtrl.getOxyUpperOpen() != null && linkedCtrl.getOxyUpperOpen() == 1)) {
deviceVo.setIsLinkCtrl(true);
break;
}
}
}
detectorList.add(deviceVo);
}
}
// 9. 处理控制器列表 (仅测控一体机 deviceType=2)
List<DeviceWithSwitchVo> controllerList = new ArrayList<>();
for (Device device : devices) {
if (device.getDeviceType() != null && device.getDeviceType() == 2) {
// 转换为DeviceVo再复制到DeviceWithSwitchVo
DeviceVo baseDeviceVo = MapstructUtils.convert(device, DeviceVo.class);
DeviceWithSwitchVo deviceVo = new DeviceWithSwitchVo();
BeanUtil.copyProperties(baseDeviceVo, deviceVo);
// 初始化告警码信息
PondMode1WarnCodeInfo warnCodeInfo = new PondMode1WarnCodeInfo();
if (device.getWarnCode() != null && device.getWarnCode() < DefineDeviceWarnCode.UserMaxCode) {
warnCodeInfo.setWarnCode(device.getWarnCode());
warnCodeInfo.setWarnDescription(DefineDeviceWarnCode.toWarnDescription(device.getWarnCode()));
} else {
warnCodeInfo.setWarnCode(DefineDeviceWarnCode.None);
warnCodeInfo.setWarnDescription("");
}
deviceVo.setWarnCodeInfo(warnCodeInfo);
// 处理设备级故障码 (switchIndex=0的故障)
StringBuilder errorMessage = new StringBuilder();
for (DeviceErrorCode errorCode : errorCodes) {
if (!errorCode.getDeviceId().equals(device.getId())) {
continue;
}
if (errorCode.getSwitchIndex() == null || errorCode.getSwitchIndex() != 0) {
continue;
}
if (errorCode.getErrorCode() == null || errorCode.getErrorCode() > DefineDeviceErrorCode.PowerOff) {
continue;
}
// 电压告警需要开关打开
boolean isVoltageError = errorCode.getErrorCode() >= DefineDeviceErrorCode.Three_PhaseA_OverVoltage
&& errorCode.getErrorCode() <= DefineDeviceErrorCode.Three_PhaseC_UnderVoltage;
if (device.getVoltageWarnOpen() != null && device.getVoltageWarnOpen() == 1 && isVoltageError
|| !isVoltageError) {
String msg = DefineDeviceErrorCode.getErrorMessage(errorCode.getErrorCode());
if (msg != null && !msg.isEmpty()) {
if (errorMessage.length() > 0 && !errorMessage.toString().contains(msg)) {
errorMessage.append("");
}
if (!errorMessage.toString().contains(msg)) {
errorMessage.append(msg);
}
}
}
}
deviceVo.setErrorMessage(errorMessage.toString());
// 查询该设备的开关列表
List<DeviceSwitch> switches = switchesByDevice.getOrDefault(device.getId(), new ArrayList<>());
List<DeviceSwitchVo> switchVoList = new ArrayList<>();
for (DeviceSwitch sw : switches) {
DeviceSwitchVo switchVo = MapstructUtils.convert(sw, DeviceSwitchVo.class);
// 判断是否有联动控制
switchVo.setIsLinkedCtrl(0);
if (sw.getLinkedCtrlId() != null) {
LinkedCtrl linkedCtrl = linkedCtrlMapper.selectById(sw.getLinkedCtrlId());
if (linkedCtrl != null
&& ((linkedCtrl.getOxyLowerOpen() != null && linkedCtrl.getOxyLowerOpen() == 1)
|| (linkedCtrl.getOxyUpperOpen() != null && linkedCtrl.getOxyUpperOpen() == 1))) {
switchVo.setIsLinkedCtrl(1);
}
}
// 判断是否有定时控制
switchVo.setIsTimingCtrl(false);
List<TimingCtrl> timingCtrls = timingCtrlsBySwitch.get(sw.getId());
if (timingCtrls != null && !timingCtrls.isEmpty()) {
for (TimingCtrl timingCtrl : timingCtrls) {
if (timingCtrl.getIsOpen() != null && timingCtrl.getIsOpen() == 1) {
switchVo.setIsTimingCtrl(true);
break;
}
}
}
// 处理开关的故障码
switchVo.setHasErrorCode(false);
for (DeviceErrorCode errorCode : errorCodes) {
if (!errorCode.getDeviceId().equals(sw.getDeviceId())) {
continue;
}
if (errorCode.getSwitchIndex() == null || !errorCode.getSwitchIndex().equals(sw.getIndex())) {
continue;
}
if (errorCode.getErrorCode() == null || errorCode.getErrorCode() > DefineDeviceErrorCode.PowerOff) {
continue;
}
// 电流告警范围
boolean hasElectricError = (errorCode.getErrorCode() >= DefineDeviceErrorCode.Three_Switch1OverElectricA
&& errorCode.getErrorCode() <= DefineDeviceErrorCode.Three_Switch4ElectricEmpty)
|| (errorCode.getErrorCode() >= DefineDeviceErrorCode.One_Switch1OverElectric
&& errorCode.getErrorCode() <= DefineDeviceErrorCode.One_Switch4ElectricEmpty);
if (sw.getElectricWarnOpen() != null && sw.getElectricWarnOpen() == 1 && hasElectricError) {
switchVo.setHasErrorCode(true);
String msg = DefineDeviceErrorCode.getErrorMessage(errorCode.getErrorCode());
if (msg != null && !msg.isEmpty()) {
if (errorMessage.length() > 0 && !errorMessage.toString().contains(msg)) {
errorMessage.append("");
}
if (!errorMessage.toString().contains(msg)) {
errorMessage.append(msg);
}
}
}
}
switchVoList.add(switchVo);
}
// 更新设备的错误信息(包含开关的错误)
deviceVo.setErrorMessage(errorMessage.toString());
deviceVo.setListSwitch(switchVoList);
controllerList.add(deviceVo);
}
}
result.setListDetector(detectorList);
result.setListController(controllerList);
return R.ok(result);
}
/**
* 为塘口选择/移除设备或开关
* 将指定的设备(探测器)和开关分配到塘口,或从塘口移除
*
* @param request 请求参数(包含塘口ID、设备ID列表、开关ID列表)
*/
@PutMapping("/bind/device")
@Transactional(rollbackFor = Exception.class)
public R<Void> selectDeviceOrSwitch(
@Validated @RequestBody PondSelectDeviceOrSwitchBo request) {
// 获取当前登录用户ID
Long userId = LoginHelper.getUserId();
// 查询塘口信息并验证权限
Pond pond = pondMapper.selectById(request.getPondId());
if (pond == null || !pond.getUserId().equals(userId)) {
return R.fail("塘口不存在或无权限访问");
}
// 处理空列表
List<Long> listDetectorId = request.getListDetectorId() != null ? request.getListDetectorId() : new ArrayList<>();
List<Long> listSwitchId = request.getListSwitchId() != null ? request.getListSwitchId() : new ArrayList<>();
// 查询当前塘口已有的设备
List<Device> currentDevices = deviceMapper.selectList(
new LambdaQueryWrapper<Device>()
.eq(Device::getPondId, request.getPondId())
);
// 查询当前塘口已有的开关
List<DeviceSwitch> currentSwitches = deviceSwitchMapper.selectList(
new LambdaQueryWrapper<DeviceSwitch>()
.eq(DeviceSwitch::getPondId, request.getPondId())
);
// 查询要添加的设备详细信息
List<Device> newDevices = new ArrayList<>();
if (!listDetectorId.isEmpty()) {
newDevices = deviceMapper.selectBatchIds(listDetectorId);
}
// 查询要添加的开关详细信息(包含设备信息)
List<DeviceSwitch> newSwitches = new ArrayList<>();
if (!listSwitchId.isEmpty()) {
newSwitches = deviceSwitchMapper.selectList(
new LambdaQueryWrapper<DeviceSwitch>()
.in(DeviceSwitch::getId, listSwitchId)
);
// 查询开关关联的设备信息
if (!newSwitches.isEmpty()) {
List<Long> deviceIds = newSwitches.stream()
.map(DeviceSwitch::getDeviceId)
.distinct()
.collect(Collectors.toList());
List<Device> switchDevices = deviceMapper.selectBatchIds(deviceIds);
java.util.Map<Long, Device> deviceMap = switchDevices.stream()
.collect(Collectors.toMap(Device::getId, d -> d));
// 这里可以用于后续操作记录
}
}
// 构建字典:要添加的设备
java.util.Map<Long, Device> dictDeviceAdd = new java.util.HashMap<>();
for (Device device : newDevices) {
dictDeviceAdd.put(device.getId(), device);
}
// 构建字典:要添加的开关
java.util.Map<Long, DeviceSwitch> dictSwitchAdd = new java.util.HashMap<>();
for (DeviceSwitch sw : newSwitches) {
dictSwitchAdd.put(sw.getId(), sw);
}
// 构建字典:要移除的设备
java.util.Map<Long, Device> dictDeviceRemove = new java.util.HashMap<>();
Set<Long> hashDeviceRemove = new HashSet<>();
for (Device device : currentDevices) {
dictDeviceRemove.put(device.getId(), device);
hashDeviceRemove.add(device.getId());
dictDeviceAdd.remove(device.getId()); // 已存在的不算新增
}
// 从要移除的列表中排除仍要保留的
for (Long id : listDetectorId) {
dictDeviceRemove.remove(id);
hashDeviceRemove.remove(id);
}
// 构建字典:要移除的开关
java.util.Map<Long, DeviceSwitch> dictSwitchRemove = new java.util.HashMap<>();
Set<Long> hashSwitchRemove = new HashSet<>();
for (DeviceSwitch sw : currentSwitches) {
dictSwitchRemove.put(sw.getId(), sw);
hashSwitchRemove.add(sw.getId());
dictSwitchAdd.remove(sw.getId()); // 已存在的不算新增
}
// 从要移除的列表中排除仍要保留的
for (Long id : listSwitchId) {
dictSwitchRemove.remove(id);
hashSwitchRemove.remove(id);
}
// 1. 添加/更新设备到塘口
if (!listDetectorId.isEmpty()) {
deviceMapper.update(null,
new LambdaUpdateWrapper<Device>()
.in(Device::getId, listDetectorId)
.set(Device::getPondId, request.getPondId())
);
}
// 2. 从塘口移除设备
if (!hashDeviceRemove.isEmpty()) {
// 删除关联的联动控制
linkedCtrlMapper.delete(
new LambdaQueryWrapper<LinkedCtrl>()
.in(LinkedCtrl::getDeviceId, hashDeviceRemove)
);
// 移除设备的塘口关联,并清除告警状态
deviceMapper.update(null,
new LambdaUpdateWrapper<Device>()
.in(Device::getId, hashDeviceRemove)
.set(Device::getPondId, null)
.set(Device::getIsTempWarnExist, 0)
.set(Device::getIsOxygenWarnExist, 0)
);
}
// 3. 添加/更新开关到塘口
if (!listSwitchId.isEmpty()) {
deviceSwitchMapper.update(null,
new LambdaUpdateWrapper<DeviceSwitch>()
.in(DeviceSwitch::getId, listSwitchId)
.set(DeviceSwitch::getPondId, request.getPondId())
);
}
// 4. 从塘口移除开关
if (!hashSwitchRemove.isEmpty()) {
deviceSwitchMapper.update(null,
new LambdaUpdateWrapper<DeviceSwitch>()
.in(DeviceSwitch::getId, hashSwitchRemove)
.set(DeviceSwitch::getPondId, null)
.set(DeviceSwitch::getLinkedCtrlId, null)
);
}
// TODO: 清除设备报警数据(类似C#中的EventHelper.RemoveDeviceWarnWaitAndNotice)
// for (Long deviceId : hashDeviceRemove) {
// // 清除该设备的报警等待和通知数据
// }
// TODO: 操作记录(类似C#中的CacheData.AddMessageOpRecordUser)
// 可以集成到日志系统中记录以下操作:
// 1. 设备添加到塘口: dictDeviceAdd
// 2. 设备从塘口移除: dictDeviceRemove
// 3. 开关添加到塘口: dictSwitchAdd
// 4. 开关从塘口移除: dictSwitchRemove
return R.ok();
}
/**
* 查询用户塘口列表 - 模式1(用于首页展示)
* 返回塘口的设备和开关汇总信息,包括最大水质参数、告警状态、故障信息等
*
* @param rootUserId 用户ID
* @return 塘口列表
*/
@GetMapping("/list_mode1")
public R<List<PublicPondMode1Vo>> getPondListMode1(
@RequestParam("rootUserId") Long rootUserId) {
// 1. 查询用户的所有塘口
List<Pond> ponds = pondMapper.selectList(
new LambdaQueryWrapper<Pond>()
.eq(Pond::getUserId, rootUserId)
.orderByDesc(Pond::getCreateTime)
);
List<PublicPondMode1Vo> listData = new ArrayList<>();
if (ponds == null || ponds.isEmpty()) {
return R.ok(listData);
}
// 提取塘口ID列表
List<Long> pondIds = ponds.stream()
.map(Pond::getId)
.collect(Collectors.toList());
// 2. 批量查询所有塘口的设备
List<Device> allDevices = deviceMapper.selectList(
new LambdaQueryWrapper<Device>()
.in(Device::getPondId, pondIds)
);
// 按塘口ID分组
java.util.Map<Long, List<Device>> devicesByPond = allDevices.stream()
.collect(Collectors.groupingBy(Device::getPondId));
// 3. 批量查询所有塘口的开关
List<DeviceSwitch> allSwitches = deviceSwitchMapper.selectList(
new LambdaQueryWrapper<DeviceSwitch>()
.in(DeviceSwitch::getPondId, pondIds)
);
// 按塘口ID分组
java.util.Map<Long, List<DeviceSwitch>> switchesByPond = allSwitches.stream()
.collect(Collectors.groupingBy(DeviceSwitch::getPondId));
// 4. 收集所有控制器ID(用于查询故障码)
Set<Long> hashSetControllerId = new HashSet<>();
for (Device device : allDevices) {
if (device.getDeviceType() != null && device.getDeviceType() == 2) {
hashSetControllerId.add(device.getId());
}
}
for (DeviceSwitch sw : allSwitches) {
if (sw.getDeviceId() != null) {
hashSetControllerId.add(sw.getDeviceId());
}
}
// 5. 查询控制器的故障码
List<DeviceErrorCode> listErrorCodes = new ArrayList<>();
if (!hashSetControllerId.isEmpty()) {
listErrorCodes = deviceErrorCodeMapper.selectList(
new LambdaQueryWrapper<DeviceErrorCode>()
.in(DeviceErrorCode::getDeviceId, hashSetControllerId)
);
}
// 6. 查询开关关联的设备(用于获取WarnCode)
Set<Long> switchDeviceIds = allSwitches.stream()
.map(DeviceSwitch::getDeviceId)
.collect(Collectors.toSet());
java.util.Map<Long, Device> switchDeviceMap = new java.util.HashMap<>();
if (!switchDeviceIds.isEmpty()) {
List<Device> switchDevices = deviceMapper.selectBatchIds(switchDeviceIds);
switchDeviceMap = switchDevices.stream()
.collect(Collectors.toMap(Device::getId, d -> d));
}
// 7. 为每个塘口聚合数据
for (Pond pond : ponds) {
PublicPondMode1Vo data = new PublicPondMode1Vo();
data.setId(pond.getId());
data.setPondName(pond.getPondName());
Set<Long> hashSetDeviceId = new HashSet<>();
int warnCode = DefineDeviceWarnCode.None;
// 获取该塘口的设备列表
List<Device> pondDevices = devicesByPond.getOrDefault(pond.getId(), new ArrayList<>());
// 处理设备数据
for (Device device : pondDevices) {
hashSetDeviceId.add(device.getId());
// 聚合告警码
if (device.getWarnCode() != null
&& device.getWarnCode() != DefineDeviceWarnCode.None
&& device.getWarnCode() < DefineDeviceWarnCode.UserMaxCode) {
warnCode |= device.getWarnCode();
}
// 判断设备是否在线(简化判断,实际应根据业务逻辑)
if (device.getWarnCode() == null
|| (device.getWarnCode() != DefineDeviceWarnCode.DeviceDead
&& device.getWarnCode() != DefineDeviceWarnCode.DeviceOffline)) {
data.setIsAllDead(false);
}
// 聚合最大水质参数
if (device.getValueDissolvedOxygen() != null
&& data.getValueDissolvedOxygen() < device.getValueDissolvedOxygen()) {
data.setValueDissolvedOxygen(device.getValueDissolvedOxygen());
}
if (device.getValueTemperature() != null
&& data.getValueTemperature() < device.getValueTemperature()) {
data.setValueTemperature(device.getValueTemperature());
}
if (device.getValueSaturability() != null
&& data.getValueSaturability() < device.getValueSaturability()) {
data.setValueSaturability(device.getValueSaturability());
}
if (device.getValuePh() != null
&& data.getValuePH() < device.getValuePh()) {
data.setValuePH(device.getValuePh());
}
if (device.getValueSalinity() != null
&& data.getValueSalinity() < device.getValueSalinity()) {
data.setValueSalinity(device.getValueSalinity());
}
// 处理设备的故障码(电压告警)
if (device.getVoltageWarnOpen() != null && device.getVoltageWarnOpen() == 1) {
for (DeviceErrorCode errorCode : listErrorCodes) {
if (!errorCode.getDeviceId().equals(device.getId())) {
continue;
}
if (errorCode.getErrorCode() == null
|| errorCode.getErrorCode() > DefineDeviceErrorCode.PowerOff) {
continue;
}
// 电压告警范围
if (errorCode.getErrorCode() >= DefineDeviceErrorCode.Three_PhaseA_OverVoltage
&& errorCode.getErrorCode() <= DefineDeviceErrorCode.Three_PhaseC_UnderVoltage) {
String errorMessage = DefineDeviceErrorCode.getErrorMessage(errorCode.getErrorCode());
if (errorMessage != null && !errorMessage.isEmpty()) {
if (data.getErrorMessage() == null || data.getErrorMessage().isEmpty()) {
data.setErrorMessage(errorMessage);
} else if (!data.getErrorMessage().contains(errorMessage)) {
data.setErrorMessage(data.getErrorMessage() + "" + errorMessage);
}
}
}
}
}
}
// 获取该塘口的开关列表
List<DeviceSwitch> pondSwitches = switchesByPond.getOrDefault(pond.getId(), new ArrayList<>());
// 处理开关数据
for (DeviceSwitch sw : pondSwitches) {
if (sw.getDeviceId() != null) {
hashSetDeviceId.add(sw.getDeviceId());
}
// 聚合开关关联设备的告警码
Device switchDevice = switchDeviceMap.get(sw.getDeviceId());
if (switchDevice != null
&& switchDevice.getWarnCode() != null
&& switchDevice.getWarnCode() != DefineDeviceWarnCode.None
&& switchDevice.getWarnCode() < DefineDeviceWarnCode.UserMaxCode) {
warnCode |= switchDevice.getWarnCode();
}
// 统计开启的开关数量
if (sw.getIsOpen() != null && sw.getIsOpen() == 1) {
data.setSwitchOpenCount(data.getSwitchOpenCount() + 1);
}
// 处理开关的故障码(电流告警)
if (sw.getElectricWarnOpen() != null && sw.getElectricWarnOpen() == 1) {
for (DeviceErrorCode errorCode : listErrorCodes) {
if (!errorCode.getDeviceId().equals(sw.getDeviceId())) {
continue;
}
if (errorCode.getErrorCode() == null
|| errorCode.getErrorCode() > DefineDeviceErrorCode.PowerOff) {
continue;
}
// 电流告警范围
boolean hasElectricError = (errorCode.getErrorCode() >= DefineDeviceErrorCode.Three_Switch1OverElectricA
&& errorCode.getErrorCode() <= DefineDeviceErrorCode.Three_Switch4ElectricEmpty)
|| (errorCode.getErrorCode() >= DefineDeviceErrorCode.One_Switch1OverElectric
&& errorCode.getErrorCode() <= DefineDeviceErrorCode.One_Switch4ElectricEmpty);
if (hasElectricError) {
String errorMessage = DefineDeviceErrorCode.getErrorMessage(errorCode.getErrorCode());
if (errorMessage != null && !errorMessage.isEmpty()) {
if (data.getErrorMessage() == null || data.getErrorMessage().isEmpty()) {
data.setErrorMessage(errorMessage);
} else if (!data.getErrorMessage().contains(errorMessage)) {
data.setErrorMessage(data.getErrorMessage() + "" + errorMessage);
}
}
}
}
}
}
// 设置汇总数据
data.setDeviceCount(hashSetDeviceId.size());
data.setSwitchCount(pondSwitches.size());
data.getWarnCodeInfo().setWarnCode(warnCode);
data.getWarnCodeInfo().setWarnDescription(DefineDeviceWarnCode.toWarnDescription(warnCode));
listData.add(data);
}
return R.ok(listData);
}
}

View File

@@ -0,0 +1,35 @@
package com.intc.fishery.domain.bo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 塘口选择设备或开关请求对象
*
* @author intc
* @date 2026-01-12
*/
@Data
public class PondSelectDeviceOrSwitchBo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 塘口ID
*/
private Long pondId;
/**
* 探测器(水质检测仪)ID列表
*/
private List<Long> listDetectorId;
/**
* 开关ID列表
*/
private List<Long> listSwitchId;
}

View File

@@ -154,4 +154,14 @@ public class DeviceSwitchVo implements Serializable {
private Date updateTime;
/**
* 是否有定时控制
*/
private Boolean isTimingCtrl;
/**
* 是否有故障码
*/
private Boolean hasErrorCode;
}

View File

@@ -341,4 +341,9 @@ public class DeviceVo implements Serializable {
private Date updateTime;
/**
* 是否有联动控制
*/
private Boolean isLinkCtrl;
}

View File

@@ -0,0 +1,35 @@
package com.intc.fishery.domain.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.intc.common.json.handler.DoubleSerializer;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
import java.util.List;
/**
* 带开关列表的设备视图对象
*
* @author intc
* @date 2026-01-12
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class DeviceWithSwitchVo extends DeviceVo {
/**
* 开关列表(仅测控一体机有)
*/
private List<DeviceSwitchVo> listSwitch;
/**
* 故障信息
*/
private String errorMessage;
/**
* 告警码信息
*/
private PondMode1WarnCodeInfo warnCodeInfo;
}

View File

@@ -0,0 +1,30 @@
package com.intc.fishery.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
/**
* 塘口设备列表视图对象
*
* @author intc
* @date 2026-01-12
*/
@Data
public class PondDeviceListVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 探测器列表(水质检测仪)
*/
private List<DeviceVo> listDetector;
/**
* 控制器列表(测控一体机,包含开关列表)
*/
private List<DeviceWithSwitchVo> listController;
}

View File

@@ -0,0 +1,27 @@
package com.intc.fishery.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 塘口告警码信息
*
* @author intc
*/
@Data
public class PondMode1WarnCodeInfo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 告警码
*/
private Integer warnCode;
/**
* 告警描述
*/
private String warnDescription;
}

View File

@@ -0,0 +1,56 @@
package com.intc.fishery.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 设备简化视图对象
*
* @author intc
* @date 2026-01-12
*/
@Data
public class PublicDeviceSimpleVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 设备ID
*/
private Long id;
/**
* 设备名称
*/
private String deviceName;
/**
* 设备类型
*/
private Integer deviceType;
/**
* 服务到期时间
*/
private Date deadTime;
/**
* 溶解氧参数配置开关
*/
private Integer isOxygenUsed;
/**
* 塘口信息
*/
private PublicPondIdNameVo pondInfo;
/**
* 开关列表
*/
private List<PublicDeviceSwitchSimpleVo> listSwitch;
}

View File

@@ -0,0 +1,39 @@
package com.intc.fishery.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 设备开关简化视图对象
*
* @author intc
* @date 2026-01-12
*/
@Data
public class PublicDeviceSwitchSimpleVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 开关ID
*/
private Long id;
/**
* 序号
*/
private Integer index;
/**
* 开关名称
*/
private String switchName;
/**
* 塘口信息
*/
private PublicPondIdNameVo pondInfo;
}

View File

@@ -0,0 +1,29 @@
package com.intc.fishery.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 塘口ID和名称视图对象
*
* @author intc
* @date 2026-01-12
*/
@Data
public class PublicPondIdNameVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 塘口ID
*/
private Long id;
/**
* 塘口名称
*/
private String pondName;
}

View File

@@ -0,0 +1,98 @@
package com.intc.fishery.domain.vo;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 塘口模式1视图对象 - 用于首页展示
*
* @author intc
*/
@Data
public class PublicPondMode1Vo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 塘口ID
*/
private Long id;
/**
* 塘口名称
*/
private String pondName;
/**
* 设备数量
*/
private Integer deviceCount;
/**
* 开关数量
*/
private Integer switchCount;
/**
* 开启的开关数量
*/
private Integer switchOpenCount;
/**
* 是否全部设备离线/失联
*/
private Boolean isAllDead;
/**
* 溶解氧(最大值)
*/
private Double valueDissolvedOxygen;
/**
* 水温(最大值)
*/
private Double valueTemperature;
/**
* 饱和度(最大值)
*/
private Double valueSaturability;
/**
* PH值(最大值)
*/
private Double valuePH;
/**
* 盐度(最大值)
*/
private Double valueSalinity;
/**
* 故障信息
*/
private String errorMessage;
/**
* 告警码信息
*/
private PondMode1WarnCodeInfo warnCodeInfo;
public PublicPondMode1Vo() {
this.deviceCount = 0;
this.switchCount = 0;
this.switchOpenCount = 0;
this.isAllDead = true;
this.valueDissolvedOxygen = 0.0;
this.valueTemperature = 0.0;
this.valueSaturability = 0.0;
this.valuePH = 0.0;
this.valueSalinity = 0.0;
this.errorMessage = "";
this.warnCodeInfo = new PondMode1WarnCodeInfo();
this.warnCodeInfo.setWarnCode(0);
this.warnCodeInfo.setWarnDescription("");
}
}

View File

@@ -25,6 +25,12 @@ import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import com.intc.fishery.mapper.DeviceMapper;
import com.intc.fishery.mapper.DeviceSwitchMapper;
import com.intc.fishery.domain.Device;
import com.intc.fishery.domain.DeviceSwitch;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
/**
* 塘口管理Service业务层处理
@@ -41,6 +47,10 @@ public class PondServiceImpl implements IPondService {
private final FishMapper fishMapper;
private final DeviceMapper deviceMapper;
private final DeviceSwitchMapper deviceSwitchMapper;
/**
* 查询塘口管理
*
@@ -206,6 +216,7 @@ public class PondServiceImpl implements IPondService {
/**
* 校验并批量删除塘口管理信息
* 删除时会级联删除关联的设备和开关
*
* @param ids 待删除的主键集合
* @param isValid 是否进行有效性校验
@@ -216,6 +227,23 @@ public class PondServiceImpl implements IPondService {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
// 删除前先级联删除关联数据
for (Long pondId : ids) {
// 1. 删除关联的设备
deviceMapper.delete(
new LambdaQueryWrapper<Device>()
.eq(Device::getPondId, pondId)
);
// 2. 删除关联的开关
deviceSwitchMapper.delete(
new LambdaQueryWrapper<DeviceSwitch>()
.eq(DeviceSwitch::getPondId, pondId)
);
}
// 删除塘口
return baseMapper.deleteByIds(ids) > 0;
}

View File

@@ -180,6 +180,14 @@ public class AliyunIotProperties {
*/
private String controlIntegrated;
/**
* 控制器 ProductKey (别名,指向 controlIntegrated)
* @return ProductKey
*/
public String getController() {
return controlIntegrated;
}
/**
* 根据设备类型获取 ProductKey
* @param deviceType 1-水质检测仪, 2-控制一体机

View File

@@ -9,6 +9,7 @@ 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.AddDeviceControllerBo;
import com.intc.iot.domain.bo.AddDeviceDetectorBo;
import com.intc.iot.domain.bo.DeviceRealtimeDataBo;
import com.intc.iot.domain.vo.DeviceRealtimeDataVo;
@@ -79,6 +80,9 @@ public class IotController extends BaseController {
@Autowired(required = false)
private PondMapper pondMapper;
@Autowired(required = false)
private com.intc.fishery.mapper.DeviceSwitchMapper deviceSwitchMapper;
@Operation(summary = "测试接口")
@GetMapping("/test")
public R<String> test() {
@@ -339,23 +343,30 @@ public class IotController extends BaseController {
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;
if (detailData != null) {
// 通过反射获取 status
try {
java.lang.reflect.Method getStatusMethod = detailData.getClass().getMethod("getStatus");
Object statusObj = getStatusMethod.invoke(detailData);
if (statusObj != null) {
String statusStr = statusObj.toString();
log.info("从 SDK 对象获取到的状态: {}", statusStr);
// 根据物联网平台返回的状态转换为前端状态码
// 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;
}
}
} catch (Exception ex) {
log.error("无法从 SDK 对象中获取 status: {}", ex.getMessage(), ex);
}
}
@@ -674,6 +685,443 @@ public class IotController extends BaseController {
// ======================== 设备管理相关接口 ========================
@Operation(summary = "添加测控一体机")
@PostMapping("/device/add_device_controller")
public R<Void> addDeviceController(@RequestBody AddDeviceControllerBo bo) {
try {
if (iotDeviceService == null) {
return R.fail("飞燕平台配置未启用");
}
if (deviceMapper == null) {
return R.fail("设备数据库服务未启用");
}
if (deviceSwitchMapper == 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<com.intc.fishery.domain.Pond>()
.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<Device>()
.eq(Device::getDeviceType, 2) // 2-测控一体机
.eq(Device::getSerialNum, bo.getSerialNum())
);
if (existDevice != null && existDevice.getUserId() != null) {
return R.fail("该设备号已被绑定");
}
// 获取控制器的 ProductKey
String productKey = aliyunIotProperties.getDeviceType().getControlIntegrated();
if (productKey == null || productKey.isEmpty()) {
return R.fail("未配置测控一体机的 ProductKey");
}
// 查询设备基础信息
Map<String, Object> deviceInfo = iotDeviceService.findDeviceByProductKeyAndName(productKey, bo.getSerialNum());
if (deviceInfo == null || !Boolean.TRUE.equals(deviceInfo.get("success"))) {
return R.fail("设备不存在");
}
// 提取设备数据
Map<String, Object> data = (Map<String, Object>) 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("设备禁用");
}
}
// 查询物模型,确定 rated_voltage 的正确格式
log.info("查询控制器物模型ProductKey: {}", productKey);
Map<String, Object> thingModel = iotDeviceService.queryThingModel(productKey);
log.info("物模型查询结果: {}", thingModel);
// 先查询现有设备属性,了解 rated_voltage 的正确格式
try {
Map<String, Object> currentProps = iotDeviceService.queryDeviceProperties(iotId);
log.info("当前设备属性: {}", currentProps);
} catch (Exception e) {
log.warn("查询当前设备属性失败: {}", e.getMessage());
}
// 设置设备属性
Map<String, Object> properties = new java.util.HashMap<>();
// 设置额定电压 - rated_voltage 是结构体类型
// 根据C#代码,需要调用 ControllerHelper.GetInputVoltageProperty
// 该方法返回一个特定结构的对象
Integer voltValue = getVoltageValue(bo.getInputVoltage());
if (voltValue == null) {
return R.fail("电压参数错误");
}
// 暂时跳过 rated_voltage 设置,先设置其他属性
// properties.put("rated_voltage", ratedVoltageStruct);
// 如果是三相380V四线设置输出电压
if (bo.getInputVoltage() == 4) {
for (int i = 1; i <= 4; i++) {
properties.put("Switch" + i + "_volt", 380);
}
}
// 清空定时控制数据和设置额定电流
java.util.List<Object> emptyTimerList = new java.util.ArrayList<>();
Double defaultRatingSwitch = 10.0; // 默认额定电流10A
for (int i = 1; i <= 4; i++) {
properties.put("rating_switch" + i, defaultRatingSwitch);
properties.put("localTimer_switch" + i, emptyTimerList);
}
// 如果使用溶解氧功能,设置盐度
if (bo.getIsOxygenUsed()) {
properties.put("salinitySet", bo.getSalinityCompensation());
}
String propertiesJson = cn.hutool.json.JSONUtil.toJsonStr(properties);
log.info("准备设置设备属性iotId: {}, properties: {}", iotId, propertiesJson);
Map<String, Object> setResult = iotDeviceService.setDeviceProperty(iotId, propertiesJson);
log.info("设置设备属性返回结果: {}", setResult);
if (setResult == null) {
log.error("设置设备属性返回null");
return R.fail("设置设备属性失败:返回结果为空");
}
if (!Boolean.TRUE.equals(setResult.get("success"))) {
String errorMsg = setResult.get("errorMessage") != null ?
setResult.get("errorMessage").toString() : "未知错误";
log.error("设置设备属性失败,错误信息: {}", errorMsg);
return R.fail("设置设备属性失败: " + errorMsg);
}
// 获取设备属性
Map<String, Object> deviceProperties = iotDeviceService.queryDeviceProperties(iotId);
// 初始化警告码:默认探头离线且未校准 (0x0081)
int warnCode = 0x0081;
// 如果设备离线,添加设备离线警告
if (status != null && "OFFLINE".equalsIgnoreCase(status)) {
warnCode |= 0x0080; // 设备离线 (0x0080)
}
// 计算设备数量,用于生成设备名称
long deviceCount = deviceMapper.selectCount(
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Device>()
.eq(Device::getUserId, userId)
.eq(Device::getDeviceType, 2)
);
// 创建或更新设备信息
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(2); // 2-测控一体机
device.setDeadTime(new Date(now.getTime() + 365L * 24 * 60 * 60 * 1000)); // 一年后到期
} else {
// 检查是否已过期
if (device.getDeadTime() != null && now.after(device.getDeadTime())) {
warnCode |= 0x0040; // 设备时间到期 (0x0040)
}
}
// 设置基本信息
device.setUserId(userId);
device.setDeviceName("控制器" + (deviceCount + 1));
device.setBindTime(now);
device.setInputVoltage(bo.getInputVoltage());
device.setVoltageWarnOpen(0);
device.setIsOxygenUsed(bo.getIsOxygenUsed() ? 1 : 0);
// 设置必填字段默认值(数据库非空约束)
if (device.getValuePh() == null) {
device.setValuePh(0.0);
}
if (device.getIsOxygenWarnExist() == null) {
device.setIsOxygenWarnExist(0);
}
if (device.getTfluorescence() == null) {
device.setTfluorescence(0.0);
}
if (device.getTreference() == null) {
device.setTreference(0.0);
}
if (device.getPhaseDifference() == null) {
device.setPhaseDifference(0.0);
}
if (bo.getIsOxygenUsed()) {
device.setSalinityCompensation(bo.getSalinityCompensation());
device.setOxyWarnLower(bo.getOxyWarnLower());
device.setOxyWarnCallOpen(1);
device.setOxyWarnCallNoDis(1);
// 如果有塘口且开关列表包含0设备自身则设置设备的塘口ID
if (bo.getPondId() != null && bo.getPondId() > 0
&& bo.getListPutPondPartId() != null && bo.getListPutPondPartId().contains(0)) {
device.setPondId(bo.getPondId());
} else {
device.setPondId(null);
}
} else {
device.setPondId(null);
}
device.setTempWarnCallOpen(0);
device.setTempWarnCallNoDis(0);
// 创建开关列表
java.util.List<com.intc.fishery.domain.DeviceSwitch> switches = new java.util.ArrayList<>();
for (int i = 1; i <= 4; i++) {
com.intc.fishery.domain.DeviceSwitch deviceSwitch = new com.intc.fishery.domain.DeviceSwitch();
deviceSwitch.setIndex(i);
deviceSwitch.setSwitchName(device.getDeviceName() + "_开关_" + i);
deviceSwitch.setConnectVoltageType(bo.getInputVoltage());
deviceSwitch.setRateElectricValue(defaultRatingSwitch);
deviceSwitch.setElectricWarnOpen(0);
deviceSwitch.setIsOpen(0);
// 初始化电流和电压默认值,避免数据库非空约束错误
deviceSwitch.setDetectElectricValue(0.0);
deviceSwitch.setDetectVoltageValue(0.0);
// 如果有塘口且开关列表包含当前索引则设置开关的塘口ID
if (bo.getPondId() != null && bo.getPondId() > 0
&& bo.getListPutPondPartId() != null && bo.getListPutPondPartId().contains(i)) {
deviceSwitch.setPondId(bo.getPondId());
}
switches.add(deviceSwitch);
}
// 解析并设置设备属性
int errorCode = 0;
if (deviceProperties != null && Boolean.TRUE.equals(deviceProperties.get("success"))) {
Object propData = deviceProperties.get("data");
if (propData != null) {
try {
java.lang.reflect.Method getListMethod = propData.getClass().getMethod("getList");
Object listObj = getListMethod.invoke(propData);
if (listObj instanceof java.util.List) {
java.util.List<?> propList = (java.util.List<?>) listObj;
for (Object item : propList) {
if (item != null) {
try {
java.lang.reflect.Method getIdentifierMethod = item.getClass().getMethod("getIdentifier");
java.lang.reflect.Method getValueMethod = item.getClass().getMethod("getValue");
String attribute = (String) getIdentifierMethod.invoke(item);
Object value = getValueMethod.invoke(item);
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 "errorCode": // 故障码
try {
errorCode = Integer.parseInt(value.toString());
} catch (NumberFormatException e) {
log.warn("无法解析故障码: {}", value);
}
break;
case "sensorErrorCode": // 传感器错误码
try {
int sensorErrorCode = Integer.parseInt(value.toString());
warnCode |= sensorErrorCode;
} 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;
// 开关状态
case "switch1":
switches.get(0).setIsOpen(Integer.parseInt(value.toString()));
break;
case "switch2":
switches.get(1).setIsOpen(Integer.parseInt(value.toString()));
break;
case "switch3":
switches.get(2).setIsOpen(Integer.parseInt(value.toString()));
break;
case "switch4":
switches.get(3).setIsOpen(Integer.parseInt(value.toString()));
break;
// 开关电压电流
case "switch1_VoltCur":
parseSwitchVoltCur(value.toString(), switches.get(0));
break;
case "switch2_VoltCur":
parseSwitchVoltCur(value.toString(), switches.get(1));
break;
case "switch3_VoltCur":
parseSwitchVoltCur(value.toString(), switches.get(2));
break;
case "switch4_VoltCur":
parseSwitchVoltCur(value.toString(), switches.get(3));
break;
}
}
} catch (Exception ex) {
log.error("无法从 SDK 对象中获取属性: {}", ex.getMessage(), ex);
}
}
}
}
} catch (Exception ex) {
log.error("无法从 SDK 对象中获取属性列表: {}", ex.getMessage(), ex);
}
}
}
// 验证 ICCID 是否存在
if (device.getIccId() == null || device.getIccId().isEmpty()) {
return R.fail("设备缺少物联网卡号(ICCID)");
}
device.setWarnCode(warnCode);
// 保存到数据库
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());
}
// 保存开关信息
for (com.intc.fishery.domain.DeviceSwitch deviceSwitch : switches) {
deviceSwitch.setDeviceId(device.getId());
deviceSwitchMapper.insert(deviceSwitch);
}
// TODO: 记录绑定历史和故障码(如果需要的话)
return R.ok();
} catch (Exception e) {
log.error("添加测控一体机失败: {}", e.getMessage(), e);
return R.fail("添加测控一体机失败: " + e.getMessage());
}
}
/**
* 根据输入电压类型获取电压值
*/
private Integer getVoltageValue(Integer inputVoltage) {
switch (inputVoltage) {
case 1: return 220;
case 2: return 380;
case 3: return 380;
case 4: return 380;
default: return null;
}
}
/**
* 解析开关电压电流数据
*/
private void parseSwitchVoltCur(String json, com.intc.fishery.domain.DeviceSwitch deviceSwitch) {
try {
// 清理JSON字符串
json = json.replace("\"{", "{").replace("}\"", "}").replace("\\", "");
Map<String, Object> content = cn.hutool.json.JSONUtil.toBean(json, Map.class);
if (content.containsKey("voltage")) {
deviceSwitch.setDetectVoltageValue(Double.parseDouble(content.get("voltage").toString()));
}
if (content.containsKey("current")) {
deviceSwitch.setDetectElectricValue(Double.parseDouble(content.get("current").toString()));
}
} catch (Exception e) {
log.warn("解析开关电压电流数据失败: {}", e.getMessage());
}
}
@Operation(summary = "添加设备探测器(水质检测仪)")
@PostMapping("/device/add_device_detector")
public R<Void> addDeviceDetector(@RequestBody AddDeviceDetectorBo bo) {
@@ -825,65 +1273,79 @@ public class IotController extends BaseController {
// 解析并设置设备属性
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);
if (propData != null) {
// 通过反射获取属性列表
try {
java.lang.reflect.Method getListMethod = propData.getClass().getMethod("getList");
Object listObj = getListMethod.invoke(propData);
if (listObj instanceof java.util.List) {
java.util.List<?> propList = (java.util.List<?>) listObj;
for (Object item : propList) {
if (item != null) {
// 通过反射获取属性标识符和值
try {
java.lang.reflect.Method getIdentifierMethod = item.getClass().getMethod("getIdentifier");
java.lang.reflect.Method getValueMethod = item.getClass().getMethod("getValue");
String attribute = (String) getIdentifierMethod.invoke(item);
Object value = getValueMethod.invoke(item);
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;
}
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;
}
} catch (Exception ex) {
log.error("无法从 SDK 对象中获取属性: {}", ex.getMessage(), ex);
}
}
}
}
} catch (Exception ex) {
log.error("无法从 SDK 对象中获取属性列表: {}", ex.getMessage(), ex);
}
}
}

View File

@@ -0,0 +1,70 @@
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;
import java.util.List;
/**
* 添加测控一体机业务对象
*
* @author intc
*/
@Data
@Schema(description = "添加测控一体机请求对象")
public class AddDeviceControllerBo 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;
/**
* 输入电压类型1-单相220V, 2-单相380V, 3-三相380V, 4-三相380V四线
*/
@Schema(description = "输入电压类型")
@NotNull(message = "输入电压类型不能为空")
private Integer inputVoltage;
/**
* 是否使用溶解氧功能
*/
@Schema(description = "是否使用溶解氧功能")
@NotNull(message = "是否使用溶解氧功能不能为空")
private Boolean isOxygenUsed;
/**
* 开关放置塘口分区ID列表0表示设备自身1-4表示开关1-4
*/
@Schema(description = "开关放置塘口分区ID列表")
private List<Integer> listPutPondPartId;
}

View File

@@ -132,6 +132,13 @@ public class IotDeviceServiceImpl implements IotDeviceService {
Map<String, Object> result = new HashMap<>();
result.put("success", response.getSuccess());
result.put("data", response.getData());
result.put("errorMessage", response.getErrorMessage());
result.put("code", response.getCode());
if (!response.getSuccess()) {
log.error("设置设备属性失败Code: {}, ErrorMessage: {}", response.getCode(), response.getErrorMessage());
}
return result;
}

View File

@@ -1,99 +0,0 @@
# 阿里云生活物联网平台(飞燕平台)配置
# 使用说明:
# 1. 替换下面的占位符为实际值
# 2. AccessKey/Secret 建议使用环境变量或配置中心管理
# 3. 推荐使用 AMQP 服务端订阅接收所有设备数据
aliyun:
living-iot:
# ========== 基础配置 ==========
# 阿里云 AccessKey ID必填- 从阿里云控制台获取
# 安全提示:请通过环境变量配置,不要在代码中暴露真实密钥
access-key-id: LTAI5tRnPowmTLjH181nSbsR
# 阿里云 AccessKey Secret必填- 从阿里云控制台获取
# 安全提示:请通过环境变量配置,不要在代码中暴露真实密钥
access-key-secret: Vh2LoAM1t3XuMUVy2wTWSACJ97kOUW
# 地域节点必填cn-shanghai
region-id: cn-shanghai
# 飞燕平台项目IDProject ID必填
project-id: a123nMibvh0q4UnU
# App Key必填
app-key: 334224397
# App Secret必填
app-secret: 70de3018ec39423e9ca1e1b6a6a84ad6
# ========== AMQP 服务端订阅配置(推荐) ==========
# 说明AMQP 服务端订阅用于接收所有设备的数据,无需为每个设备配置 MQTT
# 配置步骤:
# 1. 在阿里云IoT控制台创建消费组
# 路径IoT控制台 -> 规则引擎 -> 服务端订阅 -> 消费组管理 -> 创建消费组
# 2. 获取 AMQP 接入点信息
# 路径IoT控制台 -> 实例详情 -> 开发配置 -> 服务端订阅 -> 查看 AMQP 接入点
# 3. 使用工具生成认证信息:
# username: {accessKeyId}|authMode=aksign|
# password: 使用 HMAC-SHA1 签名生成,可使用阿里云提供的在线工具
amqp:
# 是否启用 AMQP 订阅
enabled: true
# AMQP 接入点地址(格式:${uid}.iot-amqp.${region}.aliyuncs.com
# 示例1234567890.iot-amqp.cn-shanghai.aliyuncs.com
host: 1572610294777992.iot-amqp.cn-shanghai.aliyuncs.com
# AMQP 端口(默认 5672
port: 5672
# 虚拟主机(默认为 AccessKey 的前 8 位)
virtual-host:
# 用户名(格式:${accessKeyId}|authMode=aksign|
username:
# 密码(使用 HMAC-SHA1 签名生成)
password:
# 消费组 ID在阿里云 IoT 控制台创建)
consumer-group-id: HPaJu3YgmDnUAP43Z7xd000100
# 连接超时时间(毫秒)
connection-timeout: 30000
# 自动重连
auto-reconnect: true
# 预取数量(每次批量拉取的消息数)
prefetch-count: 50
# ========== MQTT 配置(可选,用于设备直连) ==========
# 说明MQTT 适用于单个设备直连,不推荐用于大量设备场景
# 如需启用,请确保以下配置正确
# 注:大量设备请使用 AMQP 服务端订阅
mqtt:
# MQTT Broker 地址格式tcp://{ProductKey}.iot-as-mqtt.{RegionId}.aliyuncs.com:1883
# 示例tcp://a1Xj9dagTIx.iot-as-mqtt.cn-shanghai.aliyuncs.com:1883
broker-url:
# 客户端ID
client-id:
# 用户名(格式:{DeviceName}&{ProductKey}
username:
# 密码(使用 AliyunIotSignUtil.generatePassword() 方法生成)
password:
# 连接超时时间(秒)
connection-timeout: 30
# 保活时间(秒)
keep-alive-interval: 60
# 自动重连
auto-reconnect: true
# 清除会话
clean-session: true
# ========== 配置说明 ==========
#
# 1. AMQP 服务端订阅配置步骤:
# a. 登录阿里云 IoT 控制台
# b. 进入实例管理 -> 选择你的实例 -> 规则引擎 -> 服务端订阅
# c. 创建消费组(记住消费组 ID
# d. 查看 AMQP 接入点信息,获取 host 和 virtualHost
# e. 生成认证信息:
# username: {accessKeyId}|authMode=aksign|
# password: 使用 HMAC-SHA1("{accessKeySecret}", "{consumerGroupId}") 计算
# 可使用阿里云提供的在线签名工具
#
# 2. AMQP vs MQTT 选择:
# - AMQP推荐适用于服务端接收所有设备数据无需为每个设备配置
# - MQTT适用于单个设备直连需要设备三元组ProductKey/DeviceName/DeviceSecret
#
# 3. 关键信息获取路径:
# - AMQP 接入点IoT控制台 -> 实例详情 -> 开发配置 -> 服务端订阅
# - 消费组 IDIoT控制台 -> 规则引擎 -> 服务端订阅 -> 消费组管理
# - AccessKey阿里云控制台 -> AccessKey 管理