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 selectCollectorList(TCollector collector) { if (collector == null) { collector = new TCollector(); } collector.setDevice(trimToNull(collector.getDevice())); collector.setBeginCreateTime(trimToNull(collector.getBeginCreateTime())); collector.setEndCreateTime(trimToNull(collector.getEndCreateTime())); String requestedOnlineStatus = collector.getOnlineStatus(); validateOptionalStatus(requestedOnlineStatus, "在线状态"); // 在线状态由 SQL 从所链接来源实例的连接结果派生,这里不再从存储列过滤。 collector.setOnlineStatus(null); validateOptionalStatus(collector.getTestStatus(), "测试状态"); validateOptionalStatus(collector.getStatus(), "状态"); return collectorMapper.selectCollectorList(collector); } /** * 新增采集器并初始化运行状态。 * *

前置条件:设备编码与名称非空且设备编码未登记过;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); } }