|
|
|
@ -1,8 +1,22 @@
|
|
|
|
package cn.iocoder.yudao.module.iot.framework.mqtt.consumer;
|
|
|
|
package cn.iocoder.yudao.module.iot.framework.mqtt.consumer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceBasicStatusEnum;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.controller.admin.device.enums.DeviceStatusEnum;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.controller.admin.devicemodelrules.vo.PointRulesRespVO;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.device.DeviceDO;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.devicecontactmodel.DeviceContactModelDO;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.deviceoperationrecord.DeviceOperationRecordDO;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.devicepointrules.DevicePointRulesDO;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.devicewarinningrecord.DeviceWarinningRecordDO;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.iotorganization.IotOrganizationDO;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.iotorganization.IotOrganizationDO;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.mqttrecord.MqttRecordDO;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.dataobject.mqttrecord.MqttRecordDO;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.device.DeviceMapper;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.devicecontactmodel.DeviceContactModelMapper;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.deviceoperationrecord.DeviceOperationRecordMapper;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.devicepointrules.DevicePointRulesMapper;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.devicewarinningrecord.DeviceWarinningRecordMapper;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.mqttrecord.MqttRecordMapper;
|
|
|
|
import cn.iocoder.yudao.module.iot.dal.mysql.mqttrecord.MqttRecordMapper;
|
|
|
|
import cn.iocoder.yudao.module.iot.framework.constant.Constants;
|
|
|
|
import cn.iocoder.yudao.module.iot.framework.constant.Constants;
|
|
|
|
import cn.iocoder.yudao.module.iot.framework.mqtt.common.SuperConsumer;
|
|
|
|
import cn.iocoder.yudao.module.iot.framework.mqtt.common.SuperConsumer;
|
|
|
|
@ -11,14 +25,27 @@ import cn.iocoder.yudao.module.iot.framework.mqtt.entity.MqttData;
|
|
|
|
import cn.iocoder.yudao.module.iot.framework.mqtt.utils.DateUtils;
|
|
|
|
import cn.iocoder.yudao.module.iot.framework.mqtt.utils.DateUtils;
|
|
|
|
import cn.iocoder.yudao.module.iot.framework.mqtt.utils.MqttDataUtils;
|
|
|
|
import cn.iocoder.yudao.module.iot.framework.mqtt.utils.MqttDataUtils;
|
|
|
|
import cn.iocoder.yudao.module.iot.service.device.DeviceService;
|
|
|
|
import cn.iocoder.yudao.module.iot.service.device.DeviceService;
|
|
|
|
|
|
|
|
import cn.iocoder.yudao.module.iot.service.device.TDengineService;
|
|
|
|
import cn.iocoder.yudao.module.iot.service.iotorganization.IotOrganizationService;
|
|
|
|
import cn.iocoder.yudao.module.iot.service.iotorganization.IotOrganizationService;
|
|
|
|
import cn.iocoder.yudao.module.iot.service.mqttrecord.MqttRecordService;
|
|
|
|
import cn.iocoder.yudao.module.iot.service.mqttrecord.MqttRecordService;
|
|
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.core.JsonParser;
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
|
|
|
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
|
|
|
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
|
|
|
|
|
|
|
import org.springframework.context.annotation.Lazy;
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
import java.time.LocalDateTime;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
|
@Slf4j
|
|
|
|
@Slf4j
|
|
|
|
@Component
|
|
|
|
@Component
|
|
|
|
@ -27,7 +54,8 @@ public class MqttDataHandler extends SuperConsumer<String> {
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private IotOrganizationService organizationService;
|
|
|
|
private IotOrganizationService organizationService;
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private DeviceService deviceService;
|
|
|
|
@Lazy
|
|
|
|
|
|
|
|
private DeviceMapper deviceMapper;
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private AsyncService asyncService;
|
|
|
|
private AsyncService asyncService;
|
|
|
|
|
|
|
|
|
|
|
|
@ -36,6 +64,25 @@ public class MqttDataHandler extends SuperConsumer<String> {
|
|
|
|
@Resource
|
|
|
|
@Resource
|
|
|
|
private MqttRecordMapper mqttRecordMapper;
|
|
|
|
private MqttRecordMapper mqttRecordMapper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
private DeviceOperationRecordMapper deviceOperationRecordMapper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
private DeviceContactModelMapper deviceContactModelMapper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
private TDengineService tDengineService;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
private DevicePointRulesMapper devicePointRulesMapper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
|
|
|
private DeviceWarinningRecordMapper deviceWarinningRecordMapper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public String decoder(MqttMessage msg) {
|
|
|
|
public String decoder(MqttMessage msg) {
|
|
|
|
return new String(msg.getPayload());
|
|
|
|
return new String(msg.getPayload());
|
|
|
|
@ -58,33 +105,633 @@ public class MqttDataHandler extends SuperConsumer<String> {
|
|
|
|
// }catch (Exception e){
|
|
|
|
// }catch (Exception e){
|
|
|
|
// log.error("asyncService.transferBase error:"+entity);
|
|
|
|
// log.error("asyncService.transferBase error:"+entity);
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
//异步线程查询时需带TenantId,框架TenantContextHolder限制
|
|
|
|
|
|
|
|
//TODO 后续是否要其他TenantId
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
// 设置租户ID
|
|
|
|
|
|
|
|
TenantContextHolder.setTenantId(1L);
|
|
|
|
|
|
|
|
save(machine, entity, data, topic);
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
TenantContextHolder.clear();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void save(IotOrganizationDO machine, String entity, MqttData data,String topic) {
|
|
|
|
|
|
|
|
// try {
|
|
|
|
|
|
|
|
// long timestamp = DateUtils.strToTimeStamp(data.getDeviceDataTime(), Constants.MQTT_timestamp_format);
|
|
|
|
|
|
|
|
// //timestamp = DateUtils.getMillsLong();
|
|
|
|
|
|
|
|
// LocalDateTime date = DateUtils.strToLocalDateTime(data.getDeviceDataTime());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 如果 deviceDataTime 为空,使用当前时间
|
|
|
|
|
|
|
|
// LocalDateTime date;
|
|
|
|
|
|
|
|
// long timestamp;
|
|
|
|
|
|
|
|
// if (data.getDeviceDataTime() != null && !data.getDeviceDataTime().isEmpty()) {
|
|
|
|
|
|
|
|
// try {
|
|
|
|
|
|
|
|
// timestamp = DateUtils.strToTimeStamp(data.getDeviceDataTime(), Constants.MQTT_timestamp_format);
|
|
|
|
|
|
|
|
// date = DateUtils.strToLocalDateTime(data.getDeviceDataTime());
|
|
|
|
|
|
|
|
// } catch (Exception e) {
|
|
|
|
|
|
|
|
// log.warn("解析 deviceDataTime 异常, 使用当前时间: {}", data.getDeviceDataTime(), e);
|
|
|
|
|
|
|
|
// timestamp = DateUtils.getMillsLong();
|
|
|
|
|
|
|
|
// date = LocalDateTime.now();
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
// } else {
|
|
|
|
|
|
|
|
// timestamp = DateUtils.getMillsLong();
|
|
|
|
|
|
|
|
// date = LocalDateTime.now();
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// MqttRecordDO recordDO = new MqttRecordDO();
|
|
|
|
|
|
|
|
// recordDO.setDeviceCode(data.getDeviceID());
|
|
|
|
|
|
|
|
// recordDO.setGatewayCode(data.getGatewayID());
|
|
|
|
|
|
|
|
// recordDO.setDeviceData(entity);
|
|
|
|
|
|
|
|
// recordDO.setDeviceDataTime(date);
|
|
|
|
|
|
|
|
// recordDO.setDeviceDataTimeLong(timestamp);
|
|
|
|
|
|
|
|
// /**直接保存原始mqtt*/
|
|
|
|
|
|
|
|
// mqttRecordMapper.insert(recordDO);
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(entity)) {
|
|
|
|
|
|
|
|
log.warn("MQTT消息为空 topic={}", topic);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(topic)) {
|
|
|
|
|
|
|
|
log.warn("MQTT topic为空");
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JsonNode rootNode = OBJECT_MAPPER.readTree(entity);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JsonNode devListNode = rootNode.get("devList");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (devListNode == null || devListNode.isEmpty()) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
JsonNode varListNode =
|
|
|
|
|
|
|
|
devListNode.get(0).get("varList");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (varListNode == null) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, Object> varListMap =
|
|
|
|
|
|
|
|
OBJECT_MAPPER.convertValue(
|
|
|
|
|
|
|
|
varListNode,
|
|
|
|
|
|
|
|
Map.class
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DeviceDO deviceDO = deviceMapper.selectOne(Wrappers.<DeviceDO>lambdaQuery().eq(DeviceDO::getTopic,topic));
|
|
|
|
|
|
|
|
log.info("getDeviceByMqttTopic参数:{}", topic);
|
|
|
|
|
|
|
|
if (deviceDO == null) {
|
|
|
|
|
|
|
|
log.info("getDeviceByMqttTopic查询出来deviceDO为空");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
processDeviceDataFromMqtt(
|
|
|
|
|
|
|
|
deviceDO,
|
|
|
|
|
|
|
|
varListMap
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.error("MQTT数据处理异常", e);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void processDeviceDataFromMqtt(DeviceDO device,
|
|
|
|
|
|
|
|
Map<String, Object> varListMap) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Long deviceId = device.getId();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 查询点位配置
|
|
|
|
|
|
|
|
List<DeviceContactModelDO> points = getDevicePoints(deviceId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(points)) {
|
|
|
|
|
|
|
|
|
|
|
|
save(machine, entity, data);
|
|
|
|
log.warn("设备 {} 未配置点位", device.getId());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DeviceOperationRecordDO record = new DeviceOperationRecordDO();
|
|
|
|
|
|
|
|
record.setDeviceId(deviceId);
|
|
|
|
|
|
|
|
record.setRule(DeviceStatusEnum.STANDBY.getCode());
|
|
|
|
|
|
|
|
//TODO 待优化
|
|
|
|
|
|
|
|
record.setTotalStandbyTime(device.getSampleCycle());
|
|
|
|
|
|
|
|
record.setCreator("1");
|
|
|
|
|
|
|
|
record.setUpdater("1");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deviceOperationRecordMapper.insert(record);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (varListMap == null || varListMap.isEmpty()) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.warn("设备 {} MQTT varList 为空", deviceId);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 查询RUNNING点位规则
|
|
|
|
|
|
|
|
DevicePointRulesDO devicePoints = getDevicePointRules(deviceId);
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(devicePoints.getFieldRule())){
|
|
|
|
|
|
|
|
log.warn("设备 {} 没有RUNNING点位规则", device.getId());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DeviceOperationRecordDO record = new DeviceOperationRecordDO();
|
|
|
|
|
|
|
|
record.setDeviceId(deviceId);
|
|
|
|
|
|
|
|
record.setRule(DeviceStatusEnum.STANDBY.getCode());
|
|
|
|
|
|
|
|
//TODO 待优化
|
|
|
|
|
|
|
|
record.setTotalStandbyTime(device.getSampleCycle());
|
|
|
|
|
|
|
|
record.setCreator("1");
|
|
|
|
|
|
|
|
record.setUpdater("1");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deviceOperationRecordMapper.insert(record);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void save(IotOrganizationDO machine, String entity, MqttData data) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("设备 {} MQTT 数据点位数量 {}", deviceId, varListMap.size());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int successCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<DeviceContactModelDO> validDataList = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 遍历数据库点位,通过 code 匹配 MQTT
|
|
|
|
|
|
|
|
for (DeviceContactModelDO point : points) {
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
long timestamp = DateUtils.strToTimeStamp(data.getDeviceDataTime(), Constants.MQTT_timestamp_format);
|
|
|
|
|
|
|
|
//timestamp = DateUtils.getMillsLong();
|
|
|
|
|
|
|
|
LocalDateTime date = DateUtils.strToLocalDateTime(data.getDeviceDataTime());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MqttRecordDO recordDO = new MqttRecordDO();
|
|
|
|
String code = point.getAttributeCode();
|
|
|
|
recordDO.setDeviceCode(data.getDeviceID());
|
|
|
|
|
|
|
|
recordDO.setGatewayCode(data.getGatewayID());
|
|
|
|
Object value = varListMap.get(code);
|
|
|
|
recordDO.setDeviceData(entity);
|
|
|
|
|
|
|
|
recordDO.setDeviceDataTime(date);
|
|
|
|
if (value == null) {
|
|
|
|
recordDO.setDeviceDataTimeLong(timestamp);
|
|
|
|
validDataList.add(point);
|
|
|
|
/**直接保存原始mqtt*/
|
|
|
|
continue;
|
|
|
|
mqttRecordMapper.insert(recordDO);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String processedValue = processOpcValue(value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 规则判断
|
|
|
|
|
|
|
|
judgmentRules(
|
|
|
|
|
|
|
|
processedValue,
|
|
|
|
|
|
|
|
code,
|
|
|
|
|
|
|
|
device,
|
|
|
|
|
|
|
|
point.getId()
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
point.setAddressValue(processedValue);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
successCount++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
validDataList.add(point);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.error("处理 MQTT 点位异常 deviceId={}, code={}",
|
|
|
|
|
|
|
|
deviceId,
|
|
|
|
|
|
|
|
point.getAttributeCode(),
|
|
|
|
|
|
|
|
e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 入库
|
|
|
|
|
|
|
|
if (!validDataList.isEmpty()) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
saveToDatabase(
|
|
|
|
|
|
|
|
deviceId,
|
|
|
|
|
|
|
|
validDataList,
|
|
|
|
|
|
|
|
successCount
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.warn("设备 {} 未匹配到 MQTT 数据", deviceId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private DevicePointRulesDO getDevicePointRules(Long deviceId) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
List<DevicePointRulesDO> list =
|
|
|
|
|
|
|
|
devicePointRulesMapper.selectList(
|
|
|
|
|
|
|
|
Wrappers.<DevicePointRulesDO>lambdaQuery()
|
|
|
|
|
|
|
|
.eq(DevicePointRulesDO::getDeviceId, deviceId)
|
|
|
|
|
|
|
|
.eq(DevicePointRulesDO::getIdentifier, "RUNNING")
|
|
|
|
|
|
|
|
.orderByDesc(DevicePointRulesDO::getCreateTime)
|
|
|
|
|
|
|
|
.last("LIMIT 1")
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(list)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("设备 {} 未找到 RUNNING 规则", deviceId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DevicePointRulesDO rule = list.get(0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
log.info("设备 {} 使用 RUNNING 规则,规则ID={}, 创建时间={}",
|
|
|
|
|
|
|
|
deviceId,
|
|
|
|
|
|
|
|
rule.getId(),
|
|
|
|
|
|
|
|
rule.getCreateTime());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return rule;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 获取设备点位
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private List<DeviceContactModelDO> getDevicePoints(Long deviceId) {
|
|
|
|
|
|
|
|
LambdaQueryWrapper<DeviceContactModelDO> query = new LambdaQueryWrapper<>();
|
|
|
|
|
|
|
|
query.eq(DeviceContactModelDO::getDeviceId, deviceId);
|
|
|
|
|
|
|
|
return deviceContactModelMapper.selectList(query);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 处理OPC值
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private String processOpcValue(Object value) {
|
|
|
|
|
|
|
|
if (value == null) {
|
|
|
|
|
|
|
|
return "";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (value instanceof String) {
|
|
|
|
|
|
|
|
return ((String) value).trim();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return value.toString();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 保存到数据库
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private void saveToDatabase(Long deviceId, List<DeviceContactModelDO> dataList, int successCount) {
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
for (DeviceContactModelDO deviceContactModelDO : dataList) {
|
|
|
|
|
|
|
|
deviceContactModelDO.setAddress(null);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
String json = JSON.toJSONString(dataList);
|
|
|
|
|
|
|
|
boolean inserted = tDengineService.insertDeviceData(deviceId, json);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (inserted) {
|
|
|
|
|
|
|
|
log.info("设备 {} 数据入库成功,总数: {},有效: {}",
|
|
|
|
|
|
|
|
deviceId, dataList.size(), successCount);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log.error("设备 {} 数据入库失败", deviceId);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.error("设备 {} 数据入库异常", deviceId, e);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void judgmentRules(String processedValue, String attributeCode, DeviceDO device, Long modelId) {
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(processedValue)) {
|
|
|
|
|
|
|
|
log.warn("待判断的值为空,编码attributeCode: {}, deviceId: {}", attributeCode, device.getId());
|
|
|
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 查询设备规则
|
|
|
|
|
|
|
|
List<DevicePointRulesDO> devicePointRulesDOList = devicePointRulesMapper.selectList(
|
|
|
|
|
|
|
|
Wrappers.<DevicePointRulesDO>lambdaQuery()
|
|
|
|
|
|
|
|
.eq(DevicePointRulesDO::getDeviceId, device.getId()).orderByDesc(DevicePointRulesDO::getCreateTime));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(devicePointRulesDOList)) {
|
|
|
|
|
|
|
|
log.debug("设备 {} 未配置规则", device.getId());
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 遍历规则
|
|
|
|
|
|
|
|
for (DevicePointRulesDO devicePointRulesDO : devicePointRulesDOList) {
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(devicePointRulesDO.getFieldRule())) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 解析规则列表
|
|
|
|
|
|
|
|
List<PointRulesRespVO> pointRulesVOList = JSON.parseArray(
|
|
|
|
|
|
|
|
devicePointRulesDO.getFieldRule(), PointRulesRespVO.class);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (CollectionUtils.isEmpty(pointRulesVOList)) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 找到对应modelId的规则并进行判断
|
|
|
|
|
|
|
|
for (PointRulesRespVO pointRulesRespVO : pointRulesVOList) {
|
|
|
|
|
|
|
|
if (pointRulesRespVO.getCode() != null &&
|
|
|
|
|
|
|
|
pointRulesRespVO.getCode().equals(attributeCode)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
boolean matched = matchRule(processedValue, pointRulesRespVO);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (matched) {
|
|
|
|
|
|
|
|
log.info("规则匹配成功: modelId={}, value={}, rule={}",
|
|
|
|
|
|
|
|
attributeCode, processedValue,
|
|
|
|
|
|
|
|
JSON.toJSONString(pointRulesRespVO));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 执行匹配成功后的逻辑
|
|
|
|
|
|
|
|
handleMatchedSuccessRule(devicePointRulesDO, pointRulesRespVO, processedValue, device, attributeCode, modelId);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log.debug("规则不匹配: modelId={}, value={}, rule={}",
|
|
|
|
|
|
|
|
attributeCode, processedValue,
|
|
|
|
|
|
|
|
JSON.toJSONString(pointRulesRespVO));
|
|
|
|
|
|
|
|
// 执行匹配失败后的逻辑
|
|
|
|
|
|
|
|
handleMatchedFailureRule(devicePointRulesDO, pointRulesRespVO, processedValue, device, attributeCode);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void handleMatchedFailureRule(DevicePointRulesDO devicePointRulesDO, PointRulesRespVO pointRulesRespVO, String processedValue, DeviceDO device, String attributeCode) {
|
|
|
|
|
|
|
|
//TODO 离线待优化
|
|
|
|
|
|
|
|
// if (devicePointRulesDO.getIdentifier().equals(DeviceBasicStatusEnum.RUNNING.getCode())){
|
|
|
|
|
|
|
|
// DeviceOperationRecordDO record = new DeviceOperationRecordDO();
|
|
|
|
|
|
|
|
// record.setDeviceId(device.getId());
|
|
|
|
|
|
|
|
// record.setModelId(modelId);
|
|
|
|
|
|
|
|
// record.setRule(pointRulesRespVO.getRule());
|
|
|
|
|
|
|
|
// record.setAddressValue(processedValue);
|
|
|
|
|
|
|
|
// record.setRecordType(getRecordType(devicePointRulesDO));
|
|
|
|
|
|
|
|
// record.setRuleId(devicePointRulesDO.getId());
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void handleMatchedSuccessRule(DevicePointRulesDO devicePointRulesDO,
|
|
|
|
|
|
|
|
PointRulesRespVO pointRulesRespVO,
|
|
|
|
|
|
|
|
String processedValue,
|
|
|
|
|
|
|
|
DeviceDO device,
|
|
|
|
|
|
|
|
String attributeCode,
|
|
|
|
|
|
|
|
Long modelId) {
|
|
|
|
|
|
|
|
DeviceContactModelDO deviceContactModelDO = deviceContactModelMapper.selectById(modelId);
|
|
|
|
|
|
|
|
if (deviceContactModelDO == null) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//分别处理运行记录和告警记录
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(devicePointRulesDO.getAlarmLevel())) {
|
|
|
|
|
|
|
|
DeviceOperationRecordDO record = new DeviceOperationRecordDO();
|
|
|
|
|
|
|
|
record.setDeviceId(device.getId());
|
|
|
|
|
|
|
|
record.setModelId(modelId);
|
|
|
|
|
|
|
|
record.setRule(pointRulesRespVO.getRule());
|
|
|
|
|
|
|
|
record.setAddressValue(processedValue);
|
|
|
|
|
|
|
|
record.setRecordType(getRecordType(devicePointRulesDO));
|
|
|
|
|
|
|
|
record.setRuleId(devicePointRulesDO.getId());
|
|
|
|
|
|
|
|
//TODO 创建人和更新人为内置默认管理员
|
|
|
|
|
|
|
|
record.setCreator("1");
|
|
|
|
|
|
|
|
record.setUpdater("1");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 处理累计时间
|
|
|
|
|
|
|
|
calculateAndSetTotalTime(record, pointRulesRespVO.getRule(), device.getSampleCycle());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deviceOperationRecordMapper.insert(record);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DeviceWarinningRecordDO deviceWarinningRecordDO = new DeviceWarinningRecordDO();
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setDeviceId(device.getId());
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setModelId(modelId);
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setRule(pointRulesRespVO.getRule());
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setAlarmLevel(devicePointRulesDO.getAlarmLevel());
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setAddressValue(processedValue);
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setRuleId(devicePointRulesDO.getId());
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setDeviceName(device.getDeviceName());
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setModelName(deviceContactModelDO.getAttributeName());
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setRuleName(devicePointRulesDO.getFieldName());
|
|
|
|
|
|
|
|
//TODO 创建人和更新人为内置默认管理员
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setCreator("1");
|
|
|
|
|
|
|
|
deviceWarinningRecordDO.setUpdater("1");
|
|
|
|
|
|
|
|
deviceWarinningRecordMapper.insert(deviceWarinningRecordDO);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void calculateAndSetTotalTime(DeviceOperationRecordDO record, String ruleCode, Double sampleCycle) {
|
|
|
|
|
|
|
|
if (!isTimeRelatedStatus(ruleCode)) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DeviceOperationRecordDO lastRecord = deviceOperationRecordMapper.selectOne(
|
|
|
|
|
|
|
|
// Wrappers.<DeviceOperationRecordDO>lambdaQuery()
|
|
|
|
|
|
|
|
// .eq(DeviceOperationRecordDO::getRule, ruleCode)
|
|
|
|
|
|
|
|
// .orderByDesc(DeviceOperationRecordDO::getCreateTime)
|
|
|
|
|
|
|
|
// .last("LIMIT 1")
|
|
|
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ruleCode.equals(DeviceStatusEnum.RUNNING.getCode())) {
|
|
|
|
|
|
|
|
// Double totalTime = (lastRecord != null && lastRecord.getTotalRunningTime() != null)
|
|
|
|
|
|
|
|
// ? lastRecord.getTotalRunningTime() + sampleCycle
|
|
|
|
|
|
|
|
// : sampleCycle;
|
|
|
|
|
|
|
|
record.setTotalRunningTime(sampleCycle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else if (ruleCode.equals(DeviceStatusEnum.STANDBY.getCode())) {
|
|
|
|
|
|
|
|
// Double totalTime = (lastRecord != null && lastRecord.getTotalStandbyTime() != null)
|
|
|
|
|
|
|
|
// ? lastRecord.getTotalStandbyTime() + sampleCycle
|
|
|
|
|
|
|
|
// : sampleCycle;
|
|
|
|
|
|
|
|
record.setTotalStandbyTime(sampleCycle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else if (ruleCode.equals(DeviceStatusEnum.FAULT_STANDBY.getCode())) {
|
|
|
|
|
|
|
|
// Double totalTime = (lastRecord != null && lastRecord.getTotalFaultTime() != null)
|
|
|
|
|
|
|
|
// ? lastRecord.getTotalFaultTime() + sampleCycle
|
|
|
|
|
|
|
|
// : sampleCycle;
|
|
|
|
|
|
|
|
record.setTotalFaultTime(sampleCycle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} else if (ruleCode.equals(DeviceStatusEnum.ALARM_RUNNING.getCode())) {
|
|
|
|
|
|
|
|
// Double totalTime = (lastRecord != null && lastRecord.getTotalWarningTime() != null)
|
|
|
|
|
|
|
|
// ? lastRecord.getTotalWarningTime() + sampleCycle
|
|
|
|
|
|
|
|
// : sampleCycle;
|
|
|
|
|
|
|
|
record.setTotalWarningTime(sampleCycle);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Integer getRecordType(DevicePointRulesDO devicePointRulesDO) {
|
|
|
|
|
|
|
|
return devicePointRulesDO.getIdentifier()
|
|
|
|
|
|
|
|
.equals(DeviceBasicStatusEnum.RUNNING.getDescription())
|
|
|
|
|
|
|
|
? Integer.parseInt(DeviceBasicStatusEnum.RUNNING.getCode())
|
|
|
|
|
|
|
|
: Integer.parseInt(DeviceBasicStatusEnum.ALARM.getCode());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private boolean isTimeRelatedStatus(String ruleCode) {
|
|
|
|
|
|
|
|
return ruleCode.equals(DeviceStatusEnum.RUNNING.getCode()) ||
|
|
|
|
|
|
|
|
ruleCode.equals(DeviceStatusEnum.STANDBY.getCode()) ||
|
|
|
|
|
|
|
|
ruleCode.equals(DeviceStatusEnum.FAULT_STANDBY.getCode()) ||
|
|
|
|
|
|
|
|
ruleCode.equals(DeviceStatusEnum.ALARM_RUNNING.getCode());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* 保存解析后的mqtt数据
|
|
|
|
* 判断值是否符合规则
|
|
|
|
* */
|
|
|
|
* 支持操作符: EQ(等于), NE(不等于), GT(大于), GE(大于等于),
|
|
|
|
//tsMqttService.insertDataAddress(data, taskId, timestamp, equipment);
|
|
|
|
* LT(小于), LE(小于等于), TRUE(为真), FALSE(为假)
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private boolean matchRule(String value, PointRulesRespVO rule) {
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(value) || rule == null ||
|
|
|
|
|
|
|
|
StringUtils.isBlank(rule.getOperator())) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
String operator = rule.getOperator().toUpperCase();
|
|
|
|
|
|
|
|
String inputValue = value.trim().toLowerCase();
|
|
|
|
|
|
|
|
String ruleValue = StringUtils.trimToEmpty(rule.getOperatorRule());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 处理布尔值判断
|
|
|
|
|
|
|
|
if ("TRUE".equals(operator) || "FALSE".equals(operator)) {
|
|
|
|
|
|
|
|
return matchBooleanRule(inputValue, operator);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 如果operatorRule为空,且不是布尔操作符,则返回false
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(ruleValue)) {
|
|
|
|
|
|
|
|
log.warn("规则比较值为空,但操作符不是布尔类型: operator={}", operator);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ruleValue = ruleValue.trim();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 尝试数值比较
|
|
|
|
|
|
|
|
if (isNumeric(inputValue) && isNumeric(ruleValue)) {
|
|
|
|
|
|
|
|
Double num1 = Double.parseDouble(inputValue);
|
|
|
|
|
|
|
|
Double num2 = Double.parseDouble(ruleValue);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return compareNumbers(num1, num2, operator);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// 4. 字符串比较
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
|
|
|
return compareStrings(inputValue, ruleValue, operator);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
} catch (Exception e) {
|
|
|
|
log.error("-----mqttTableName:");
|
|
|
|
log.error("规则匹配异常: value={}, rule={}, error={}",
|
|
|
|
e.printStackTrace();
|
|
|
|
value, JSON.toJSONString(rule), e.getMessage());
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 处理布尔值判断
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private boolean matchBooleanRule(String value, String operator) {
|
|
|
|
|
|
|
|
// 常见布尔值表示
|
|
|
|
|
|
|
|
boolean booleanValue = parseBoolean(value);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ("TRUE".equals(operator)) {
|
|
|
|
|
|
|
|
return booleanValue;
|
|
|
|
|
|
|
|
} else if ("FALSE".equals(operator)) {
|
|
|
|
|
|
|
|
return !booleanValue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 解析字符串为布尔值
|
|
|
|
|
|
|
|
* 支持: true, false, 1, 0, yes, no, on, off等
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private boolean parseBoolean(String value) {
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(value)) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String lowerValue = value.toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 常见真值表示
|
|
|
|
|
|
|
|
if ("true".equals(lowerValue) ||
|
|
|
|
|
|
|
|
"1".equals(lowerValue) ||
|
|
|
|
|
|
|
|
"yes".equals(lowerValue) ||
|
|
|
|
|
|
|
|
"on".equals(lowerValue) ||
|
|
|
|
|
|
|
|
"是".equals(lowerValue) || // 中文支持
|
|
|
|
|
|
|
|
"成功".equals(lowerValue)) {
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 常见假值表示
|
|
|
|
|
|
|
|
if ("false".equals(lowerValue) ||
|
|
|
|
|
|
|
|
"0".equals(lowerValue) ||
|
|
|
|
|
|
|
|
"no".equals(lowerValue) ||
|
|
|
|
|
|
|
|
"off".equals(lowerValue) ||
|
|
|
|
|
|
|
|
"否".equals(lowerValue) || // 中文支持
|
|
|
|
|
|
|
|
"失败".equals(lowerValue)) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 尝试转换为布尔值
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
return Boolean.parseBoolean(lowerValue);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.warn("无法解析为布尔值: {}", value);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 数值比较
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private boolean compareNumbers(Double value, Double ruleValue, String operator) {
|
|
|
|
|
|
|
|
switch (operator) {
|
|
|
|
|
|
|
|
case "EQ":
|
|
|
|
|
|
|
|
return Math.abs(value - ruleValue) < 0.000001; // 处理浮点数精度
|
|
|
|
|
|
|
|
case "NE":
|
|
|
|
|
|
|
|
return Math.abs(value - ruleValue) >= 0.000001;
|
|
|
|
|
|
|
|
case "GT":
|
|
|
|
|
|
|
|
return value > ruleValue;
|
|
|
|
|
|
|
|
case "GE":
|
|
|
|
|
|
|
|
return value >= ruleValue;
|
|
|
|
|
|
|
|
case "LT":
|
|
|
|
|
|
|
|
return value < ruleValue;
|
|
|
|
|
|
|
|
case "LE":
|
|
|
|
|
|
|
|
return value <= ruleValue;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
log.warn("不支持的操作符: {}", operator);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 字符串比较
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private boolean compareStrings(String value, String ruleValue, String operator) {
|
|
|
|
|
|
|
|
switch (operator) {
|
|
|
|
|
|
|
|
case "EQ":
|
|
|
|
|
|
|
|
return value.equals(ruleValue);
|
|
|
|
|
|
|
|
case "NE":
|
|
|
|
|
|
|
|
return !value.equals(ruleValue);
|
|
|
|
|
|
|
|
case "GT":
|
|
|
|
|
|
|
|
return value.compareTo(ruleValue) > 0;
|
|
|
|
|
|
|
|
case "GE":
|
|
|
|
|
|
|
|
return value.compareTo(ruleValue) >= 0;
|
|
|
|
|
|
|
|
case "LT":
|
|
|
|
|
|
|
|
return value.compareTo(ruleValue) < 0;
|
|
|
|
|
|
|
|
case "LE":
|
|
|
|
|
|
|
|
return value.compareTo(ruleValue) <= 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
log.warn("不支持的操作符: {}", operator);
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* 判断字符串是否为数字
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private boolean isNumeric(String str) {
|
|
|
|
|
|
|
|
if (StringUtils.isBlank(str)) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
Double.parseDouble(str);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|