fix: 设备删除增加用户校验、ACK 在线状态刷新改非阻塞、停止跟踪日志

- AppDeviceServiceImpl#deleteWithValidByIds 校验设备归属当前用户,
  避免任意已登录用户越权删除其他用户的设备及关联数据
- MqttCommandAckService#refreshDeviceOnline 由 lock.lock 改为 tryLock,
  防止 MQTT 处理线程在状态刷新锁上排队堆积;状态写入幂等,跳过本次刷新
  由后续 ACK 兜底
- 从版本控制中移除 logs/ 历史日志,gitignore 已忽略该目录

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
yuhaiming
2026-06-23 15:36:38 +08:00
parent 5548512633
commit 694e32abbf
17 changed files with 73 additions and 665 deletions

View File

@@ -109,9 +109,7 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
AppDevice add = MapstructUtils.convert(bo, AppDevice.class);
validEntityBeforeInsert(add);
normalizeMacAddress(add);
if (baseMapper.selectByMac(add.getMacAddress()) != null) {
throw new ServiceException("MAC地址已存在不能重复新增设备");
}
ensureMacAddressAvailable(add);
return baseMapper.insert(add) > 0;
}
@@ -120,6 +118,8 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
// fillBindTokenHash(bo);
AppDevice update = MapstructUtils.convert(bo, AppDevice.class);
validEntityBeforeUpdate(update);
normalizeMacAddress(update);
ensureMacAddressAvailable(update);
return baseMapper.updateById(update) > 0;
}
@@ -219,7 +219,8 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
throw new ServiceException("设备绑定失败,请重试");
}
//下发设备已绑定状态
deviceCommandService.sendBindDeviceCommand(exists.getDeviceNo());
//deviceCommandService.sendBindDeviceCommand(exists.getDeviceNo());
deviceCommandService.sendBindDeviceCommand(exists.getMacAddress());
return baseMapper.selectVoById(exists.getDeviceNo());
}
@@ -246,7 +247,8 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
AppDeviceVo device = baseMapper.selectVoById(deviceNo);
if (ObjectUtil.isNotNull(device) && StringUtils.isNotBlank(device.getDeviceNo())) {
// 通过业务层统一下发命令(含浇水日志记录)
String commandId = deviceCommandService.sendSwitchCommand(deviceNo, workStatus, startTime, durationMin);
//String commandId = deviceCommandService.sendSwitchCommand(deviceNo, workStatus, startTime, durationMin);
String commandId = deviceCommandService.sendSwitchCommand(device.getMacAddress(), workStatus, startTime, durationMin);
log.info("[设备] 开关命令已下发 设备编号={} 命令编号={}", deviceNo, commandId);
}
@@ -302,6 +304,16 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
}
}
private void ensureMacAddressAvailable(AppDevice entity) {
if (StringUtils.isBlank(entity.getMacAddress())) {
return;
}
AppDevice exists = baseMapper.selectByMac(entity.getMacAddress());
if (exists != null && !Objects.equals(exists.getDeviceNo(), entity.getDeviceNo())) {
throw new ServiceException("MAC地址已存在不能重复绑定设备");
}
}
private void validEntityBeforeUpdate(AppDevice entity) {
if (StringUtils.isBlank(entity.getDeviceNo())) {
throw new ServiceException("设备编号不能为空");
@@ -311,12 +323,42 @@ public class AppDeviceServiceImpl implements IAppDeviceService {
@Override
@Transactional
public Boolean deleteWithValidByIds(Collection<String> deviceNos, Boolean isValid) {
boolean flag = baseMapper.deleteByIds(deviceNos) > 0;
if (deviceNos == null || deviceNos.isEmpty()) {
throw new ServiceException("设备编号不能为空");
}
List<String> distinctDeviceNos = deviceNos.stream()
.filter(StringUtils::isNotBlank)
.distinct()
.toList();
if (distinctDeviceNos.isEmpty()) {
throw new ServiceException("设备编号不能为空");
}
Long userId = LoginHelper.getUserId();
if (userId == null) {
throw new ServiceException("用户未登录");
}
Long ownedCount = baseMapper.selectCount(
Wrappers.<AppDevice>lambdaQuery()
.in(AppDevice::getDeviceNo, distinctDeviceNos)
.eq(AppDevice::getUserId, userId)
);
if (ownedCount == null || ownedCount.intValue() != distinctDeviceNos.size()) {
throw new ServiceException("设备不存在或无权操作");
}
boolean flag = baseMapper.delete(
Wrappers.<AppDevice>lambdaUpdate()
.in(AppDevice::getDeviceNo, distinctDeviceNos)
.eq(AppDevice::getUserId, userId)
) > 0;
if (flag) {
schedulingDeviceMapper.delete(
new QueryWrapper<AppSchedulingDevice>().in("device_no", deviceNos)
new QueryWrapper<AppSchedulingDevice>().in("device_no", distinctDeviceNos)
);
wateringLogMapper.delete(new QueryWrapper<AppWateringLog>().in("device_no", deviceNos));
wateringLogMapper.delete(new QueryWrapper<AppWateringLog>().in("device_no", distinctDeviceNos));
}
return flag;
}