package com.battery.business.service.impl; import java.net.InetAddress; import java.util.Date; import java.util.List; import java.util.regex.Pattern; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.battery.business.domain.TCollector; import com.battery.business.domain.TDeviceAdapter; import com.battery.business.mapper.TCollectorMapper; import com.battery.business.mapper.TDeviceAdapterMapper; import com.battery.business.service.ITCollectorService; /** * 采集器 Service 实现。 * *
采集器按 ADR-0064 不建独立设备模型:它的测量是「同一来源的辅助测量」,
* 由所链接的来源实例提供。因此这里不再走发现候选(真实来源根本不产出采集器候选),
* 而是由操作员手工登记来源身份,在线状态直接取所链接来源实例的连接结果。
*
* @author ruoyi
*/
@Service
public class TCollectorServiceImpl implements ITCollectorService
{
private static final String ENABLED = "0";
private static final String DISABLED = "1";
private static final String OFFLINE = "0";
private static final String IDLE = "0";
private static final String TESTING = "1";
private static final Pattern IPV4_PATTERN = Pattern.compile(
"^(25[0-5]|2[0-4]\\d|1?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|1?\\d?\\d)){3}$");
private static final Pattern MAC_PATTERN = Pattern.compile("^[0-9A-F]{2}(:[0-9A-F]{2}){5}$");
private final TCollectorMapper collectorMapper;
private final TDeviceAdapterMapper adapterMapper;
public TCollectorServiceImpl(TCollectorMapper collectorMapper, TDeviceAdapterMapper adapterMapper)
{
this.collectorMapper = collectorMapper;
this.adapterMapper = adapterMapper;
}
/**
* 查询未删除采集器,不存在时抛出业务异常。
*/
@Override
public TCollector selectCollectorById(Long collectorId)
{
if (collectorId == null)
{
throw new ServiceException("采集器ID不能为空");
}
TCollector collector = collectorMapper.selectCollectorById(collectorId);
if (collector == null)
{
throw new ServiceException("采集器不存在");
}
return collector;
}
/**
* 按设备、创建时间区间和状态查询采集器列表。
*/
@Override
public List 前置条件:设备编码与名称非空且设备编码未登记过;IP 与 MAC 可留空(辅助测量不是独立硬件,
* 系统不连它),填了则校验格式。在线状态取所链接来源实例的连接结果,未链接来源时保持离线。
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertCollector(TCollector collector)
{
if (collector == null) throw new ServiceException("采集器信息不能为空");
validateCollector(collector, false);
requireLinkedAdapter(collector);
collector.setOnlineStatus(OFFLINE);
// 测试占用由 t_test_collector_channel + t_test 派生,采集器登记表不写入这些字段。
collector.setTestStatus(IDLE);
collector.setRelatedTests(List.of());
collector.setCurrentSampleName(null);
if (StringUtils.isEmpty(collector.getStatus()))
{
collector.setStatus(ENABLED);
}
else
{
validateStatus(collector.getStatus(), "状态");
}
collector.setDelFlag("0");
collector.setCreateTime(new Date());
// 登记只确认来源身份:在线事实由所链接来源实例的连接结果表达,登记时不改写它。
return collectorMapper.insertCollector(collector);
}
/**
* 编辑采集器基础资料和启禁用状态,不覆盖设备运行状态。
*/
@Override
public int updateCollector(TCollector collector)
{
TCollector current = collector == null || collector.getCollectorId() == null ? null
: collectorMapper.selectCollectorById(collector.getCollectorId());
if (current == null) throw new ServiceException("采集器不存在");
if (collector.getCollectorCode() != null && !collector.getCollectorCode().trim().equals(current.getCollectorCode()))
throw new ServiceException("设备编码不可修改");
// 编辑只提交可改字段:未提交的 IP/MAC 不能被当成"清空"。
fillMissingFromCurrent(collector, current);
validateCollector(collector, true);
requireLinkedAdapter(collector);
validateStatus(collector.getStatus(), "状态");
if (DISABLED.equals(collector.getStatus()))
{
validateCollectorNotTesting(selectCollectorById(collector.getCollectorId()), "禁用");
}
collector.setUpdateTime(new Date());
return collectorMapper.updateCollector(collector);
}
/**
* 启用或禁用采集器;测试中的采集器禁止禁用。
*/
@Override
public int updateCollectorStatus(TCollector collector)
{
if (collector == null || collector.getCollectorId() == null)
{
throw new ServiceException("采集器ID不能为空");
}
validateStatus(collector.getStatus(), "状态");
TCollector current = selectCollectorById(collector.getCollectorId());
if (DISABLED.equals(collector.getStatus()))
{
validateCollectorNotTesting(current, "禁用");
}
if (collector.getStatus().equals(current.getStatus()))
{
return 1;
}
collector.setUpdateTime(new Date());
return collectorMapper.updateCollectorStatus(collector);
}
/**
* 先校验全部记录,再在同一事务中执行批量逻辑删除。
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int deleteCollectorByIds(Long[] collectorIds, String updateBy)
{
if (collectorIds == null || collectorIds.length == 0)
{
throw new ServiceException("请选择要删除的采集器");
}
for (Long collectorId : collectorIds)
{
validateCollectorNotTesting(selectCollectorById(collectorId), "删除");
}
int rows = 0;
for (Long collectorId : collectorIds)
{
rows += collectorMapper.deleteCollectorById(collectorId, updateBy);
}
return rows;
}
/**
* 校验并规范化采集器基础资料,同时检查唯一性。
*/
private void validateCollector(TCollector collector, boolean update)
{
if (collector == null)
{
throw new ServiceException("采集器信息不能为空");
}
if (update)
{
selectCollectorById(collector.getCollectorId());
}
collector.setCollectorCode(trimToNull(collector.getCollectorCode()));
collector.setIpAddress(trimToNull(collector.getIpAddress()));
collector.setMacAddress(normalizeOptionalMacAddress(collector.getMacAddress()));
collector.setCollectorName(trimToNull(collector.getCollectorName()));
collector.setManufacturer(trimToNull(collector.getManufacturer()));
collector.setModel(trimToNull(collector.getModel()));
collector.setRemark(trimToNull(collector.getRemark()));
if (StringUtils.isEmpty(collector.getCollectorCode()))
{
throw new ServiceException("设备编码不能为空");
}
if (StringUtils.isEmpty(collector.getCollectorName()))
{
throw new ServiceException("采集器名称不能为空");
}
validateOptionalIpAddress(collector.getIpAddress());
if (collector.getChannelCount() == null || collector.getChannelCount() < 1
|| collector.getChannelCount() > 10000)
{
throw new ServiceException("通道数必须在1到10000之间");
}
validateUnique(collector);
}
/** 编辑时用既有值补齐未提交的字段,避免把"没传"当成"清空"。 */
private void fillMissingFromCurrent(TCollector collector, TCollector current)
{
if (collector.getCollectorCode() == null) collector.setCollectorCode(current.getCollectorCode());
if (collector.getIpAddress() == null) collector.setIpAddress(current.getIpAddress());
if (collector.getMacAddress() == null) collector.setMacAddress(current.getMacAddress());
if (collector.getCollectorName() == null) collector.setCollectorName(current.getCollectorName());
if (collector.getChannelCount() == null) collector.setChannelCount(current.getChannelCount());
if (collector.getStatus() == null) collector.setStatus(current.getStatus());
if (collector.getAdapterId() == null) collector.setAdapterId(current.getAdapterId());
}
/** 校验所链接的来源实例存在;来源是采集器测量的提供方,链接后可读它的连接结果。 */
private void requireLinkedAdapter(TCollector collector)
{
if (collector.getAdapterId() == null)
{
return;
}
TDeviceAdapter adapter = adapterMapper.selectDeviceAdapterById(collector.getAdapterId());
if (adapter == null)
{
throw new ServiceException("所链接的数据来源不存在");
}
}
/**
* 校验设备编码唯一;MAC 地址只在填写时参与唯一性。
*/
private void validateUnique(TCollector collector)
{
TCollector sameCode = collectorMapper.selectCollectorByCode(collector.getCollectorCode());
if (isDifferentCollector(sameCode, collector.getCollectorId()))
{
throw new ServiceException("设备编码已存在");
}
if (collector.getMacAddress() == null)
{
return;
}
TCollector sameMac = collectorMapper.selectCollectorByMacAddress(collector.getMacAddress());
if (isDifferentCollector(sameMac, collector.getCollectorId()))
{
throw new ServiceException("MAC地址已存在");
}
}
/**
* 判断唯一性查询结果是否属于其他采集器。
*/
private boolean isDifferentCollector(TCollector existing, Long collectorId)
{
return existing != null && (collectorId == null || !collectorId.equals(existing.getCollectorId()));
}
/**
* 规范化并校验 MAC 地址;未填写时保持为空。
*/
private String normalizeOptionalMacAddress(String macAddress)
{
String normalized = trimToNull(macAddress);
if (normalized == null)
{
return null;
}
normalized = normalized.replace('-', ':').toUpperCase();
if (!MAC_PATTERN.matcher(normalized).matches())
{
throw new ServiceException("MAC地址格式不正确");
}
return normalized;
}
/**
* 校验 IPv4 或 IPv6 地址,不接受主机名;未填写时跳过。
*/
private void validateOptionalIpAddress(String ipAddress)
{
if (StringUtils.isEmpty(ipAddress))
{
return;
}
if (IPV4_PATTERN.matcher(ipAddress).matches())
{
return;
}
if (!ipAddress.contains(":"))
{
throw new ServiceException("IP地址格式不正确");
}
try
{
InetAddress address = InetAddress.getByName(ipAddress);
if (address.getAddress().length != 16)
{
throw new ServiceException("IP地址格式不正确");
}
}
catch (Exception exception)
{
throw new ServiceException("IP地址格式不正确");
}
}
/**
* 校验可选的二值状态查询条件。
*/
private void validateOptionalStatus(String status, String name)
{
if (StringUtils.isNotEmpty(status))
{
validateStatus(status, name);
}
}
/**
* 校验状态值只能为 0 或 1。
*/
private void validateStatus(String status, String name)
{
if (!ENABLED.equals(status) && !DISABLED.equals(status))
{
throw new ServiceException(name + "只能为0或1");
}
}
/**
* 删除或禁用前校验采集器不处于测试中。
*/
private void validateCollectorNotTesting(TCollector collector, String operation)
{
if (TESTING.equals(collector.getTestStatus()))
{
throw new ServiceException("采集器正在测试,不能" + operation);
}
}
/**
* 去除字符串首尾空白,并将空字符串转换为 null。
*/
private String trimToNull(String value)
{
return StringUtils.isEmpty(StringUtils.trim(value)) ? null : StringUtils.trim(value);
}
}