feat(ThreePhaseDevice): enhance device and data DTOs, add RabbitMQ listener for data processing

This commit is contained in:
2025-12-14 10:39:40 +08:00
parent 8a3dee9f72
commit 4403b69eda
11 changed files with 1424 additions and 26 deletions

View File

@@ -0,0 +1,483 @@
package cn.lihongjie.coal.dataCollector.listener;
import cn.lihongjie.coal.rabbitmq.RabbitMQConfiguration;
import cn.lihongjie.coal.threePhaseDevice.entity.ThreePhaseDeviceEntity;
import cn.lihongjie.coal.threePhaseDevice.repository.ThreePhaseDeviceRepository;
import cn.lihongjie.coal.threePhaseDeviceData.entity.ThreePhaseDeviceDataEntity;
import cn.lihongjie.coal.threePhaseDeviceData.repository.ThreePhaseDeviceDataRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.List;
/**
* 三相设备数据监听器
* 监听 zh6042 通过 RabbitMQ 上报的三相电量数据
*/
@Component
@Slf4j
public class ThreePhaseDeviceListener {
@Autowired
private ObjectMapper objectMapper;
@Autowired
private ThreePhaseDeviceRepository threePhaseDeviceRepository;
@Autowired
private ThreePhaseDeviceDataRepository threePhaseDeviceDataRepository;
/**
* 监听 zh6042 上报的三相设备数据
* 路由键格式: zh6042.data.{deviceId}
*/
@SneakyThrows
@RabbitListener(
bindings = {
@QueueBinding(
value =
@org.springframework.amqp.rabbit.annotation.Queue(
value = "zh6042.threePhaseDevice.data",
durable = "true"),
exchange =
@Exchange(
value = RabbitMQConfiguration.SYS_EXCHANGE,
declare = Exchange.FALSE),
key = "zh6042.data.*")
})
@Transactional
public void handleThreePhaseDeviceData(Message message) {
var body = new String(message.getBody(), StandardCharsets.UTF_8);
try {
JsonNode jsonNode = objectMapper.readTree(body);
// 获取设备ID (deviceId 或 sn)
String deviceId = getJsonValue(jsonNode, "deviceId");
String sno = getJsonValue(jsonNode, "sn");
if (StringUtils.isEmpty(deviceId) && StringUtils.isEmpty(sno)) {
log.error("deviceId and sn are both empty in message");
return;
}
// 根据 sno 查找设备使用code字段匹配
String searchKey = StringUtils.isNotEmpty(sno) ? sno : deviceId;
List<ThreePhaseDeviceEntity> devices = threePhaseDeviceRepository.findAll(
new Specification<ThreePhaseDeviceEntity>() {
@Override
public Predicate toPredicate(
Root<ThreePhaseDeviceEntity> root,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("code"), searchKey);
}
});
if (devices.isEmpty()) {
log.warn("No ThreePhaseDevice found for code: {}", searchKey);
return;
}
ThreePhaseDeviceEntity device = devices.get(0);
// 更新设备信息
updateDeviceInfo(device, jsonNode);
threePhaseDeviceRepository.save(device);
// 保存数据
saveDeviceData(device, jsonNode);
log.debug("Successfully processed data for device: {}", device.getCode());
} catch (Exception e) {
log.error("Error processing three phase device data: {}", body, e);
}
}
/**
* 监听设备上线事件
* 路由键格式: zh6042.event.online.{deviceId}
*/
@SneakyThrows
@RabbitListener(
bindings = {
@QueueBinding(
value =
@org.springframework.amqp.rabbit.annotation.Queue(
value = "zh6042.threePhaseDevice.event.online",
durable = "true"),
exchange =
@Exchange(
value = RabbitMQConfiguration.SYS_EXCHANGE,
declare = Exchange.FALSE),
key = "zh6042.event.online.*")
})
@Transactional
public void handleDeviceOnline(Message message) {
var body = new String(message.getBody(), StandardCharsets.UTF_8);
try {
JsonNode jsonNode = objectMapper.readTree(body);
String deviceId = getJsonValue(jsonNode, "deviceId");
String sno = getJsonValue(jsonNode, "sn");
String imei = getJsonValue(jsonNode, "imei");
String iccid = getJsonValue(jsonNode, "iccid");
if (StringUtils.isEmpty(sno) && StringUtils.isEmpty(deviceId)) {
log.error("sno and deviceId are both empty in online event");
return;
}
String searchKey = StringUtils.isNotEmpty(sno) ? sno : deviceId;
List<ThreePhaseDeviceEntity> devices = threePhaseDeviceRepository.findAll(
new Specification<ThreePhaseDeviceEntity>() {
@Override
public Predicate toPredicate(
Root<ThreePhaseDeviceEntity> root,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("code"), searchKey);
}
});
if (devices.isEmpty()) {
log.warn("No ThreePhaseDevice found for online event, code: {}", searchKey);
return;
}
ThreePhaseDeviceEntity device = devices.get(0);
// 更新设备信息
if (StringUtils.isNotEmpty(sno)) {
device.setSno(sno);
}
if (StringUtils.isNotEmpty(imei)) {
device.setImei(imei);
}
if (StringUtils.isNotEmpty(iccid)) {
device.setIccid(iccid);
}
device.setLastOnlineTime(LocalDateTime.now());
threePhaseDeviceRepository.save(device);
log.info("Device online: {} (imei: {}, iccid: {})", device.getCode(), imei, iccid);
} catch (Exception e) {
log.error("Error processing device online event: {}", body, e);
}
}
/**
* 监听设备离线事件
* 路由键格式: zh6042.event.offline.{deviceId}
*/
@SneakyThrows
@RabbitListener(
bindings = {
@QueueBinding(
value =
@org.springframework.amqp.rabbit.annotation.Queue(
value = "zh6042.threePhaseDevice.event.offline",
durable = "true"),
exchange =
@Exchange(
value = RabbitMQConfiguration.SYS_EXCHANGE,
declare = Exchange.FALSE),
key = "zh6042.event.offline.*")
})
@Transactional
public void handleDeviceOffline(Message message) {
var body = new String(message.getBody(), StandardCharsets.UTF_8);
try {
JsonNode jsonNode = objectMapper.readTree(body);
String deviceId = getJsonValue(jsonNode, "deviceId");
if (StringUtils.isEmpty(deviceId)) {
log.error("deviceId is empty in offline event");
return;
}
List<ThreePhaseDeviceEntity> devices = threePhaseDeviceRepository.findAll(
new Specification<ThreePhaseDeviceEntity>() {
@Override
public Predicate toPredicate(
Root<ThreePhaseDeviceEntity> root,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("code"), deviceId);
}
});
if (devices.isEmpty()) {
log.warn("No ThreePhaseDevice found for offline event, code: {}", deviceId);
return;
}
ThreePhaseDeviceEntity device = devices.get(0);
device.setLastOfflineTime(LocalDateTime.now());
threePhaseDeviceRepository.save(device);
log.info("Device offline: {}", device.getCode());
} catch (Exception e) {
log.error("Error processing device offline event: {}", body, e);
}
}
/**
* 更新设备信息imei、iccid、在线时间等
*/
private void updateDeviceInfo(ThreePhaseDeviceEntity device, JsonNode jsonNode) {
// 更新 sno如果有
String sno = getJsonValue(jsonNode, "sn");
if (StringUtils.isNotEmpty(sno)) {
device.setSno(sno);
}
// 更新 imei如果有
String imei = getJsonValue(jsonNode, "imei");
if (StringUtils.isNotEmpty(imei)) {
device.setImei(imei);
}
// 更新 iccid如果有
String iccid = getJsonValue(jsonNode, "iccid");
if (StringUtils.isNotEmpty(iccid)) {
device.setIccid(iccid);
}
// 更新最后在线时间
device.setLastOnlineTime(LocalDateTime.now());
}
/**
* 保存设备数据
*/
private void saveDeviceData(ThreePhaseDeviceEntity device, JsonNode jsonNode) {
ThreePhaseDeviceDataEntity data = new ThreePhaseDeviceDataEntity();
data.setOrganizationId(device.getOrganizationId());
data.setDevice(device);
// 设置数据采集时间
String timestampStr = getJsonValue(jsonNode, "timestamp");
if (StringUtils.isNotEmpty(timestampStr)) {
try {
data.setTimestamp(LocalDateTime.parse(timestampStr));
} catch (Exception e) {
data.setTimestamp(LocalDateTime.now());
}
} else {
data.setTimestamp(LocalDateTime.now());
}
// 电压
data.setVoltageA(getJsonDouble(jsonNode, "voltageA"));
data.setVoltageB(getJsonDouble(jsonNode, "voltageB"));
data.setVoltageC(getJsonDouble(jsonNode, "voltageC"));
data.setLineVoltageAB(getJsonDouble(jsonNode, "lineVoltageAB"));
data.setLineVoltageBC(getJsonDouble(jsonNode, "lineVoltageBC"));
data.setLineVoltageCA(getJsonDouble(jsonNode, "lineVoltageCA"));
data.setVoltageVectorSum(getJsonDouble(jsonNode, "voltageVectorSum"));
// 电流
data.setCurrentA(getJsonDouble(jsonNode, "currentA"));
data.setCurrentB(getJsonDouble(jsonNode, "currentB"));
data.setCurrentC(getJsonDouble(jsonNode, "currentC"));
data.setZeroSequenceCurrent(getJsonDouble(jsonNode, "zeroSequenceCurrent"));
data.setCurrentVectorSum(getJsonDouble(jsonNode, "currentVectorSum"));
// 有功功率
data.setActivePowerA(getJsonDouble(jsonNode, "activePowerA"));
data.setActivePowerB(getJsonDouble(jsonNode, "activePowerB"));
data.setActivePowerC(getJsonDouble(jsonNode, "activePowerC"));
data.setTotalActivePower(getJsonDouble(jsonNode, "totalActivePower"));
// 无功功率
data.setReactivePowerA(getJsonDouble(jsonNode, "reactivePowerA"));
data.setReactivePowerB(getJsonDouble(jsonNode, "reactivePowerB"));
data.setReactivePowerC(getJsonDouble(jsonNode, "reactivePowerC"));
data.setTotalReactivePower(getJsonDouble(jsonNode, "totalReactivePower"));
// 视在功率
data.setApparentPowerA(getJsonDouble(jsonNode, "apparentPowerA"));
data.setApparentPowerB(getJsonDouble(jsonNode, "apparentPowerB"));
data.setApparentPowerC(getJsonDouble(jsonNode, "apparentPowerC"));
data.setTotalApparentPower(getJsonDouble(jsonNode, "totalApparentPower"));
// 功率因数
data.setPowerFactorA(getJsonDouble(jsonNode, "powerFactorA"));
data.setPowerFactorB(getJsonDouble(jsonNode, "powerFactorB"));
data.setPowerFactorC(getJsonDouble(jsonNode, "powerFactorC"));
data.setAveragePowerFactor(getJsonDouble(jsonNode, "averagePowerFactor"));
// 频率
data.setFrequency(getJsonDouble(jsonNode, "frequency"));
// 电能
data.setForwardActiveEnergy(getJsonDouble(jsonNode, "forwardActiveEnergy"));
data.setForwardReactiveEnergy(getJsonDouble(jsonNode, "forwardReactiveEnergy"));
data.setReverseActiveEnergy(getJsonDouble(jsonNode, "reverseActiveEnergy"));
data.setReverseReactiveEnergy(getJsonDouble(jsonNode, "reverseReactiveEnergy"));
// 谐波总含量
data.setVoltageTHDA(getJsonDouble(jsonNode, "voltageTHDA"));
data.setVoltageTHDB(getJsonDouble(jsonNode, "voltageTHDB"));
data.setVoltageTHDC(getJsonDouble(jsonNode, "voltageTHDC"));
data.setCurrentTHDA(getJsonDouble(jsonNode, "currentTHDA"));
data.setCurrentTHDB(getJsonDouble(jsonNode, "currentTHDB"));
data.setCurrentTHDC(getJsonDouble(jsonNode, "currentTHDC"));
// 谐波有效值
data.setVoltageHarmonicRmsA(getJsonDouble(jsonNode, "voltageHarmonicRmsA"));
data.setVoltageHarmonicRmsB(getJsonDouble(jsonNode, "voltageHarmonicRmsB"));
data.setVoltageHarmonicRmsC(getJsonDouble(jsonNode, "voltageHarmonicRmsC"));
data.setCurrentHarmonicRmsA(getJsonDouble(jsonNode, "currentHarmonicRmsA"));
data.setCurrentHarmonicRmsB(getJsonDouble(jsonNode, "currentHarmonicRmsB"));
data.setCurrentHarmonicRmsC(getJsonDouble(jsonNode, "currentHarmonicRmsC"));
// 基波
data.setFundamentalVoltageA(getJsonDouble(jsonNode, "fundamentalVoltageA"));
data.setFundamentalVoltageB(getJsonDouble(jsonNode, "fundamentalVoltageB"));
data.setFundamentalVoltageC(getJsonDouble(jsonNode, "fundamentalVoltageC"));
data.setFundamentalCurrentA(getJsonDouble(jsonNode, "fundamentalCurrentA"));
data.setFundamentalCurrentB(getJsonDouble(jsonNode, "fundamentalCurrentB"));
data.setFundamentalCurrentC(getJsonDouble(jsonNode, "fundamentalCurrentC"));
data.setFundamentalPowerA(getJsonDouble(jsonNode, "fundamentalPowerA"));
data.setFundamentalPowerB(getJsonDouble(jsonNode, "fundamentalPowerB"));
data.setFundamentalPowerC(getJsonDouble(jsonNode, "fundamentalPowerC"));
data.setFundamentalReactivePowerA(getJsonDouble(jsonNode, "fundamentalReactivePowerA"));
data.setFundamentalReactivePowerB(getJsonDouble(jsonNode, "fundamentalReactivePowerB"));
data.setFundamentalReactivePowerC(getJsonDouble(jsonNode, "fundamentalReactivePowerC"));
// 谐波功率
data.setTotalHarmonicActivePower(getJsonDouble(jsonNode, "totalHarmonicActivePower"));
data.setHarmonicActivePowerA(getJsonDouble(jsonNode, "harmonicActivePowerA"));
data.setHarmonicActivePowerB(getJsonDouble(jsonNode, "harmonicActivePowerB"));
data.setHarmonicActivePowerC(getJsonDouble(jsonNode, "harmonicActivePowerC"));
// 总基波功率
data.setTotalFundamentalActivePower(getJsonDouble(jsonNode, "totalFundamentalActivePower"));
data.setTotalFundamentalReactivePower(getJsonDouble(jsonNode, "totalFundamentalReactivePower"));
// 温度
data.setTemperature1(getJsonDouble(jsonNode, "temperature1"));
data.setTemperature2(getJsonDouble(jsonNode, "temperature2"));
data.setTemperature3(getJsonDouble(jsonNode, "temperature3"));
data.setTemperature4(getJsonDouble(jsonNode, "temperature4"));
// 模拟量
data.setAnalog1(getJsonDouble(jsonNode, "analog1"));
data.setAnalog2(getJsonDouble(jsonNode, "analog2"));
data.setAnalog3(getJsonDouble(jsonNode, "analog3"));
data.setAnalog4(getJsonDouble(jsonNode, "analog4"));
data.setAnalog5(getJsonDouble(jsonNode, "analog5"));
data.setAnalog6(getJsonDouble(jsonNode, "analog6"));
// 电阻值
data.setResistance1(getJsonDouble(jsonNode, "resistance1"));
data.setResistance2(getJsonDouble(jsonNode, "resistance2"));
data.setResistance3(getJsonDouble(jsonNode, "resistance3"));
data.setResistance4(getJsonDouble(jsonNode, "resistance4"));
// 状态
data.setRelay1Status(getJsonBoolean(jsonNode, "relay1Status"));
data.setRelay2Status(getJsonBoolean(jsonNode, "relay2Status"));
data.setSwitch1Status(getJsonBoolean(jsonNode, "switch1Status"));
data.setSwitch2Status(getJsonBoolean(jsonNode, "switch2Status"));
// 接线方式和相序
data.setWiringMode(getJsonInteger(jsonNode, "wiringMode"));
data.setPhaseSequence(getJsonInteger(jsonNode, "phaseSequence"));
threePhaseDeviceDataRepository.save(data);
}
/**
* 获取 JSON 字符串值
*/
private String getJsonValue(JsonNode jsonNode, String fieldName) {
JsonNode field = jsonNode.get(fieldName);
if (field != null && !field.isNull()) {
return field.asText();
}
return null;
}
/**
* 获取 JSON Double 值
*/
private Double getJsonDouble(JsonNode jsonNode, String fieldName) {
JsonNode field = jsonNode.get(fieldName);
if (field != null && !field.isNull()) {
try {
return field.asDouble();
} catch (Exception e) {
log.warn("Failed to parse double field: {}", fieldName);
}
}
return null;
}
/**
* 获取 JSON Integer 值
*/
private Integer getJsonInteger(JsonNode jsonNode, String fieldName) {
JsonNode field = jsonNode.get(fieldName);
if (field != null && !field.isNull()) {
try {
return field.asInt();
} catch (Exception e) {
log.warn("Failed to parse integer field: {}", fieldName);
}
}
return null;
}
/**
* 获取 JSON Boolean 值
*/
private Boolean getJsonBoolean(JsonNode jsonNode, String fieldName) {
JsonNode field = jsonNode.get(fieldName);
if (field != null && !field.isNull()) {
try {
return field.asBoolean();
} catch (Exception e) {
log.warn("Failed to parse boolean field: {}", fieldName);
}
}
return null;
}
}

View File

@@ -20,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/threePhaseDevice")
@SysLog(module = "")
@SysLog(module = "三相设备管理")
@Slf4j
@OrgScope
public class ThreePhaseDeviceController {

View File

@@ -4,16 +4,29 @@ import cn.lihongjie.coal.base.dto.OrgCommonDto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class CreateThreePhaseDeviceDto extends OrgCommonDto {
/** 供应商ID */
private String supplier;
String supplier;
/** 位置 */
private String location;
/** IMEI号 */
private String imei;
/** ICCID号 */
private String iccid;
/** 设备序列号 (sno) */
private String sno;
/** 最后在线时间 */
private LocalDateTime lastOnlineTime;
/** 最后离线时间 */
private LocalDateTime lastOfflineTime;
}

View File

@@ -5,17 +5,29 @@ import cn.lihongjie.coal.threePhaseDeviceSupplier.dto.ThreePhaseDeviceSupplierDt
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ThreePhaseDeviceDto extends OrgCommonDto {
/** 供应商 */
private ThreePhaseDeviceSupplierDto supplier;
ThreePhaseDeviceSupplierDto supplier;
/** 位置 */
private String location;
/** IMEI号 */
private String imei;
/** ICCID号 */
private String iccid;
/** 设备序列号 (sno) - 对应code字段 */
private String sno;
/** 最后在线时间 */
private LocalDateTime lastOnlineTime;
/** 最后离线时间 */
private LocalDateTime lastOfflineTime;
}

View File

@@ -4,16 +4,29 @@ import cn.lihongjie.coal.base.dto.OrgCommonDto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UpdateThreePhaseDeviceDto extends OrgCommonDto {
/** 供应商ID */
private String supplier;
String supplier;
/** 位置 */
private String location;
/** IMEI号 */
private String imei;
/** ICCID号 */
private String iccid;
/** 设备序列号 (sno) */
private String sno;
/** 最后在线时间 */
private LocalDateTime lastOnlineTime;
/** 最后离线时间 */
private LocalDateTime lastOfflineTime;
}

View File

@@ -9,6 +9,7 @@ import cn.lihongjie.coal.threePhaseDeviceData.dto.ThreePhaseDeviceDataDto;
import cn.lihongjie.coal.threePhaseDeviceData.dto.UpdateThreePhaseDeviceDataDto;
import cn.lihongjie.coal.threePhaseDeviceData.service.ThreePhaseDeviceDataService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -18,9 +19,11 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
@RestController
@RequestMapping("/threePhaseDeviceData")
@SysLog(module = "")
@SysLog(module = "三相设备数据")
@Slf4j
@OrgScope
public class ThreePhaseDeviceDataController {
@@ -51,4 +54,43 @@ public class ThreePhaseDeviceDataController {
public Page<ThreePhaseDeviceDataDto> list(@RequestBody CommonQuery request) {
return this.service.list(request);
}
/**
* 按设备ID和时间范围查询数据
*/
@PostMapping("/listByDeviceAndTime")
public Page<ThreePhaseDeviceDataDto> listByDeviceAndTime(
@RequestBody DeviceDataQueryRequest request) {
return this.service.listByDeviceAndTime(
request.getDeviceId(),
request.getStartTime(),
request.getEndTime(),
request.getPageNo(),
request.getPageSize());
}
/**
* 获取设备最新数据
*/
@PostMapping("/getLatestByDevice")
public ThreePhaseDeviceDataDto getLatestByDevice(@RequestBody IdRequest request) {
return this.service.getLatestByDevice(request.getId());
}
/**
* 设备数据查询请求对象
*/
@Data
public static class DeviceDataQueryRequest {
/** 设备ID */
private String deviceId;
/** 开始时间 */
private LocalDateTime startTime;
/** 结束时间 */
private LocalDateTime endTime;
/** 页码 */
private Integer pageNo = 0;
/** 页大小 */
private Integer pageSize = 20;
}
}

View File

@@ -4,5 +4,149 @@ import cn.lihongjie.coal.base.dto.OrgCommonDto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class CreateThreePhaseDeviceDataDto extends OrgCommonDto {}
public class CreateThreePhaseDeviceDataDto extends OrgCommonDto {
/** 设备ID */
private String device;
/** 数据采集时间 */
private LocalDateTime timestamp;
// ========== 电压 (V) ==========
private Double voltageA;
private Double voltageB;
private Double voltageC;
private Double lineVoltageAB;
private Double lineVoltageBC;
private Double lineVoltageCA;
private Double voltageVectorSum;
// ========== 电流 (A) ==========
private Double currentA;
private Double currentB;
private Double currentC;
private Double zeroSequenceCurrent;
private Double currentVectorSum;
// ========== 有功功率 (W/kW) ==========
private Double activePowerA;
private Double activePowerB;
private Double activePowerC;
private Double totalActivePower;
// ========== 无功功率 (Var/kVar) ==========
private Double reactivePowerA;
private Double reactivePowerB;
private Double reactivePowerC;
private Double totalReactivePower;
// ========== 视在功率 (VA/kVA) ==========
private Double apparentPowerA;
private Double apparentPowerB;
private Double apparentPowerC;
private Double totalApparentPower;
// ========== 功率因数 ==========
private Double powerFactorA;
private Double powerFactorB;
private Double powerFactorC;
private Double averagePowerFactor;
// ========== 频率 (Hz) ==========
private Double frequency;
// ========== 电能 (kWh/kVarh) ==========
private Double forwardActiveEnergy;
private Double forwardReactiveEnergy;
private Double reverseActiveEnergy;
private Double reverseReactiveEnergy;
// ========== 谐波总含量 (%) ==========
private Double voltageTHDA;
private Double voltageTHDB;
private Double voltageTHDC;
private Double currentTHDA;
private Double currentTHDB;
private Double currentTHDC;
// ========== 谐波有效值 ==========
private Double voltageHarmonicRmsA;
private Double voltageHarmonicRmsB;
private Double voltageHarmonicRmsC;
private Double currentHarmonicRmsA;
private Double currentHarmonicRmsB;
private Double currentHarmonicRmsC;
// ========== 基波 ==========
private Double fundamentalVoltageA;
private Double fundamentalVoltageB;
private Double fundamentalVoltageC;
private Double fundamentalCurrentA;
private Double fundamentalCurrentB;
private Double fundamentalCurrentC;
private Double fundamentalPowerA;
private Double fundamentalPowerB;
private Double fundamentalPowerC;
private Double fundamentalReactivePowerA;
private Double fundamentalReactivePowerB;
private Double fundamentalReactivePowerC;
// ========== 谐波功率 (W/kW) ==========
private Double totalHarmonicActivePower;
private Double harmonicActivePowerA;
private Double harmonicActivePowerB;
private Double harmonicActivePowerC;
// ========== 总基波功率 ==========
private Double totalFundamentalActivePower;
private Double totalFundamentalReactivePower;
// ========== 温度 (℃) ==========
private Double temperature1;
private Double temperature2;
private Double temperature3;
private Double temperature4;
// ========== 模拟量 ==========
private Double analog1;
private Double analog2;
private Double analog3;
private Double analog4;
private Double analog5;
private Double analog6;
// ========== 电阻值 (Ω) ==========
private Double resistance1;
private Double resistance2;
private Double resistance3;
private Double resistance4;
// ========== 状态 ==========
private Boolean relay1Status;
private Boolean relay2Status;
private Boolean switch1Status;
private Boolean switch2Status;
private Integer wiringMode;
private Integer phaseSequence;
}

View File

@@ -1,8 +1,236 @@
package cn.lihongjie.coal.threePhaseDeviceData.dto;
import cn.lihongjie.coal.base.dto.OrgCommonDto;
import cn.lihongjie.coal.threePhaseDevice.dto.ThreePhaseDeviceDto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ThreePhaseDeviceDataDto extends OrgCommonDto {}
public class ThreePhaseDeviceDataDto extends OrgCommonDto {
/** 设备 */
private ThreePhaseDeviceDto device;
/** 数据采集时间 */
private LocalDateTime timestamp;
// ========== 电压 (V) ==========
/** A相电压 */
private Double voltageA;
/** B相电压 */
private Double voltageB;
/** C相电压 */
private Double voltageC;
/** AB线电压 */
private Double lineVoltageAB;
/** BC线电压 */
private Double lineVoltageBC;
/** CA线电压 */
private Double lineVoltageCA;
/** 三相电压矢量和 */
private Double voltageVectorSum;
// ========== 电流 (A) ==========
/** A相电流 */
private Double currentA;
/** B相电流 */
private Double currentB;
/** C相电流 */
private Double currentC;
/** 零序电流 */
private Double zeroSequenceCurrent;
/** 三相电流矢量和 */
private Double currentVectorSum;
// ========== 有功功率 (W/kW) ==========
/** A相有功功率 */
private Double activePowerA;
/** B相有功功率 */
private Double activePowerB;
/** C相有功功率 */
private Double activePowerC;
/** 总有功功率 */
private Double totalActivePower;
// ========== 无功功率 (Var/kVar) ==========
/** A相无功功率 */
private Double reactivePowerA;
/** B相无功功率 */
private Double reactivePowerB;
/** C相无功功率 */
private Double reactivePowerC;
/** 总无功功率 */
private Double totalReactivePower;
// ========== 视在功率 (VA/kVA) ==========
/** A相视在功率 */
private Double apparentPowerA;
/** B相视在功率 */
private Double apparentPowerB;
/** C相视在功率 */
private Double apparentPowerC;
/** 总视在功率 */
private Double totalApparentPower;
// ========== 功率因数 ==========
/** A相功率因数 */
private Double powerFactorA;
/** B相功率因数 */
private Double powerFactorB;
/** C相功率因数 */
private Double powerFactorC;
/** 三相平均功率因数 */
private Double averagePowerFactor;
// ========== 频率 (Hz) ==========
/** 频率 */
private Double frequency;
// ========== 电能 (kWh/kVarh) ==========
/** 正向有功电度 */
private Double forwardActiveEnergy;
/** 正向无功电度 */
private Double forwardReactiveEnergy;
/** 反向有功电度 */
private Double reverseActiveEnergy;
/** 反向无功电度 */
private Double reverseReactiveEnergy;
// ========== 谐波总含量 (%) ==========
/** A相电压谐波总含量 */
private Double voltageTHDA;
/** B相电压谐波总含量 */
private Double voltageTHDB;
/** C相电压谐波总含量 */
private Double voltageTHDC;
/** A相电流谐波总含量 */
private Double currentTHDA;
/** B相电流谐波总含量 */
private Double currentTHDB;
/** C相电流谐波总含量 */
private Double currentTHDC;
// ========== 谐波有效值 ==========
/** A电压谐波有效值 */
private Double voltageHarmonicRmsA;
/** B电压谐波有效值 */
private Double voltageHarmonicRmsB;
/** C电压谐波有效值 */
private Double voltageHarmonicRmsC;
/** A电流谐波有效值 */
private Double currentHarmonicRmsA;
/** B电流谐波有效值 */
private Double currentHarmonicRmsB;
/** C电流谐波有效值 */
private Double currentHarmonicRmsC;
// ========== 基波 ==========
/** A相基波电压 */
private Double fundamentalVoltageA;
/** B相基波电压 */
private Double fundamentalVoltageB;
/** C相基波电压 */
private Double fundamentalVoltageC;
/** A相基波电流 */
private Double fundamentalCurrentA;
/** B相基波电流 */
private Double fundamentalCurrentB;
/** C相基波电流 */
private Double fundamentalCurrentC;
/** A相基波有功 */
private Double fundamentalPowerA;
/** B相基波有功 */
private Double fundamentalPowerB;
/** C相基波有功 */
private Double fundamentalPowerC;
/** A相基波无功功率 */
private Double fundamentalReactivePowerA;
/** B相基波无功功率 */
private Double fundamentalReactivePowerB;
/** C相基波无功功率 */
private Double fundamentalReactivePowerC;
// ========== 谐波功率 (W/kW) ==========
/** 总谐波有功功率 */
private Double totalHarmonicActivePower;
/** A相谐波有功功率 */
private Double harmonicActivePowerA;
/** B相谐波有功功率 */
private Double harmonicActivePowerB;
/** C相谐波有功功率 */
private Double harmonicActivePowerC;
// ========== 总基波功率 ==========
/** 总基波有功功率 */
private Double totalFundamentalActivePower;
/** 总基波无功功率 */
private Double totalFundamentalReactivePower;
// ========== 温度 (℃) ==========
/** 温度1 */
private Double temperature1;
/** 温度2 */
private Double temperature2;
/** 温度3 */
private Double temperature3;
/** 温度4 */
private Double temperature4;
// ========== 模拟量 ==========
/** 模拟量1 */
private Double analog1;
/** 模拟量2 */
private Double analog2;
/** 模拟量3 */
private Double analog3;
/** 模拟量4 */
private Double analog4;
/** 模拟量5 */
private Double analog5;
/** 模拟量6 */
private Double analog6;
// ========== 电阻值 (Ω) ==========
/** 电阻值1 */
private Double resistance1;
/** 电阻值2 */
private Double resistance2;
/** 电阻值3 */
private Double resistance3;
/** 电阻值4 */
private Double resistance4;
// ========== 状态 ==========
/** 继电器1状态 */
private Boolean relay1Status;
/** 继电器2状态 */
private Boolean relay2Status;
/** 开关量1状态 */
private Boolean switch1Status;
/** 开关量2状态 */
private Boolean switch2Status;
/** 接线方式: 0x0033=三相三线, 0x0034=三相四线 */
private Integer wiringMode;
/** 相序: 0=正序, 1=相序错 */
private Integer phaseSequence;
}

View File

@@ -4,5 +4,149 @@ import cn.lihongjie.coal.base.dto.OrgCommonDto;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UpdateThreePhaseDeviceDataDto extends OrgCommonDto {}
public class UpdateThreePhaseDeviceDataDto extends OrgCommonDto {
/** 设备ID */
private String device;
/** 数据采集时间 */
private LocalDateTime timestamp;
// ========== 电压 (V) ==========
private Double voltageA;
private Double voltageB;
private Double voltageC;
private Double lineVoltageAB;
private Double lineVoltageBC;
private Double lineVoltageCA;
private Double voltageVectorSum;
// ========== 电流 (A) ==========
private Double currentA;
private Double currentB;
private Double currentC;
private Double zeroSequenceCurrent;
private Double currentVectorSum;
// ========== 有功功率 (W/kW) ==========
private Double activePowerA;
private Double activePowerB;
private Double activePowerC;
private Double totalActivePower;
// ========== 无功功率 (Var/kVar) ==========
private Double reactivePowerA;
private Double reactivePowerB;
private Double reactivePowerC;
private Double totalReactivePower;
// ========== 视在功率 (VA/kVA) ==========
private Double apparentPowerA;
private Double apparentPowerB;
private Double apparentPowerC;
private Double totalApparentPower;
// ========== 功率因数 ==========
private Double powerFactorA;
private Double powerFactorB;
private Double powerFactorC;
private Double averagePowerFactor;
// ========== 频率 (Hz) ==========
private Double frequency;
// ========== 电能 (kWh/kVarh) ==========
private Double forwardActiveEnergy;
private Double forwardReactiveEnergy;
private Double reverseActiveEnergy;
private Double reverseReactiveEnergy;
// ========== 谐波总含量 (%) ==========
private Double voltageTHDA;
private Double voltageTHDB;
private Double voltageTHDC;
private Double currentTHDA;
private Double currentTHDB;
private Double currentTHDC;
// ========== 谐波有效值 ==========
private Double voltageHarmonicRmsA;
private Double voltageHarmonicRmsB;
private Double voltageHarmonicRmsC;
private Double currentHarmonicRmsA;
private Double currentHarmonicRmsB;
private Double currentHarmonicRmsC;
// ========== 基波 ==========
private Double fundamentalVoltageA;
private Double fundamentalVoltageB;
private Double fundamentalVoltageC;
private Double fundamentalCurrentA;
private Double fundamentalCurrentB;
private Double fundamentalCurrentC;
private Double fundamentalPowerA;
private Double fundamentalPowerB;
private Double fundamentalPowerC;
private Double fundamentalReactivePowerA;
private Double fundamentalReactivePowerB;
private Double fundamentalReactivePowerC;
// ========== 谐波功率 (W/kW) ==========
private Double totalHarmonicActivePower;
private Double harmonicActivePowerA;
private Double harmonicActivePowerB;
private Double harmonicActivePowerC;
// ========== 总基波功率 ==========
private Double totalFundamentalActivePower;
private Double totalFundamentalReactivePower;
// ========== 温度 (℃) ==========
private Double temperature1;
private Double temperature2;
private Double temperature3;
private Double temperature4;
// ========== 模拟量 ==========
private Double analog1;
private Double analog2;
private Double analog3;
private Double analog4;
private Double analog5;
private Double analog6;
// ========== 电阻值 (Ω) ==========
private Double resistance1;
private Double resistance2;
private Double resistance3;
private Double resistance4;
// ========== 状态 ==========
private Boolean relay1Status;
private Boolean relay2Status;
private Boolean switch1Status;
private Boolean switch2Status;
private Integer wiringMode;
private Integer phaseSequence;
}

View File

@@ -1,17 +1,254 @@
package cn.lihongjie.coal.threePhaseDeviceData.entity;
import cn.lihongjie.coal.base.entity.OrgCommonEntity;
import cn.lihongjie.coal.threePhaseDevice.entity.ThreePhaseDeviceEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@Entity
@Table(
indexes =
indexes = {
@jakarta.persistence.Index(
name = "idx_threePhaseDeviceData_org_id",
columnList = "organization_id"))
public class ThreePhaseDeviceDataEntity extends OrgCommonEntity {}
columnList = "organization_id"),
@jakarta.persistence.Index(
name = "idx_threePhaseDeviceData_device_id",
columnList = "device_id"),
@jakarta.persistence.Index(
name = "idx_threePhaseDeviceData_timestamp",
columnList = "timestamp")
})
public class ThreePhaseDeviceDataEntity extends OrgCommonEntity {
@ManyToOne(fetch = FetchType.LAZY)
private ThreePhaseDeviceEntity device;
/** 数据采集时间 */
private LocalDateTime timestamp;
// ========== 电压 (V) ==========
/** A相电压 */
private Double voltageA;
/** B相电压 */
private Double voltageB;
/** C相电压 */
private Double voltageC;
/** AB线电压 */
private Double lineVoltageAB;
/** BC线电压 */
private Double lineVoltageBC;
/** CA线电压 */
private Double lineVoltageCA;
/** 三相电压矢量和 */
private Double voltageVectorSum;
// ========== 电流 (A) ==========
/** A相电流 */
private Double currentA;
/** B相电流 */
private Double currentB;
/** C相电流 */
private Double currentC;
/** 零序电流 */
private Double zeroSequenceCurrent;
/** 三相电流矢量和 */
private Double currentVectorSum;
// ========== 有功功率 (W/kW) ==========
/** A相有功功率 */
private Double activePowerA;
/** B相有功功率 */
private Double activePowerB;
/** C相有功功率 */
private Double activePowerC;
/** 总有功功率 */
private Double totalActivePower;
// ========== 无功功率 (Var/kVar) ==========
/** A相无功功率 */
private Double reactivePowerA;
/** B相无功功率 */
private Double reactivePowerB;
/** C相无功功率 */
private Double reactivePowerC;
/** 总无功功率 */
private Double totalReactivePower;
// ========== 视在功率 (VA/kVA) ==========
/** A相视在功率 */
private Double apparentPowerA;
/** B相视在功率 */
private Double apparentPowerB;
/** C相视在功率 */
private Double apparentPowerC;
/** 总视在功率 */
private Double totalApparentPower;
// ========== 功率因数 ==========
/** A相功率因数 */
private Double powerFactorA;
/** B相功率因数 */
private Double powerFactorB;
/** C相功率因数 */
private Double powerFactorC;
/** 三相平均功率因数 */
private Double averagePowerFactor;
// ========== 频率 (Hz) ==========
/** 频率 */
private Double frequency;
// ========== 电能 (kWh/kVarh) ==========
/** 正向有功电度 */
private Double forwardActiveEnergy;
/** 正向无功电度 */
private Double forwardReactiveEnergy;
/** 反向有功电度 */
private Double reverseActiveEnergy;
/** 反向无功电度 */
private Double reverseReactiveEnergy;
// ========== 谐波总含量 (%) ==========
/** A相电压谐波总含量 */
private Double voltageTHDA;
/** B相电压谐波总含量 */
private Double voltageTHDB;
/** C相电压谐波总含量 */
private Double voltageTHDC;
/** A相电流谐波总含量 */
private Double currentTHDA;
/** B相电流谐波总含量 */
private Double currentTHDB;
/** C相电流谐波总含量 */
private Double currentTHDC;
// ========== 谐波有效值 ==========
/** A电压谐波有效值 */
private Double voltageHarmonicRmsA;
/** B电压谐波有效值 */
private Double voltageHarmonicRmsB;
/** C电压谐波有效值 */
private Double voltageHarmonicRmsC;
/** A电流谐波有效值 */
private Double currentHarmonicRmsA;
/** B电流谐波有效值 */
private Double currentHarmonicRmsB;
/** C电流谐波有效值 */
private Double currentHarmonicRmsC;
// ========== 基波 ==========
/** A相基波电压 */
private Double fundamentalVoltageA;
/** B相基波电压 */
private Double fundamentalVoltageB;
/** C相基波电压 */
private Double fundamentalVoltageC;
/** A相基波电流 */
private Double fundamentalCurrentA;
/** B相基波电流 */
private Double fundamentalCurrentB;
/** C相基波电流 */
private Double fundamentalCurrentC;
/** A相基波有功 */
private Double fundamentalPowerA;
/** B相基波有功 */
private Double fundamentalPowerB;
/** C相基波有功 */
private Double fundamentalPowerC;
/** A相基波无功功率 */
private Double fundamentalReactivePowerA;
/** B相基波无功功率 */
private Double fundamentalReactivePowerB;
/** C相基波无功功率 */
private Double fundamentalReactivePowerC;
// ========== 谐波功率 (W/kW) ==========
/** 总谐波有功功率 */
private Double totalHarmonicActivePower;
/** A相谐波有功功率 */
private Double harmonicActivePowerA;
/** B相谐波有功功率 */
private Double harmonicActivePowerB;
/** C相谐波有功功率 */
private Double harmonicActivePowerC;
// ========== 总基波功率 ==========
/** 总基波有功功率 */
private Double totalFundamentalActivePower;
/** 总基波无功功率 */
private Double totalFundamentalReactivePower;
// ========== 温度 (℃) ==========
/** 温度1 */
private Double temperature1;
/** 温度2 */
private Double temperature2;
/** 温度3 */
private Double temperature3;
/** 温度4 */
private Double temperature4;
// ========== 模拟量 ==========
/** 模拟量1 */
private Double analog1;
/** 模拟量2 */
private Double analog2;
/** 模拟量3 */
private Double analog3;
/** 模拟量4 */
private Double analog4;
/** 模拟量5 */
private Double analog5;
/** 模拟量6 */
private Double analog6;
// ========== 电阻值 (Ω) ==========
/** 电阻值1 */
private Double resistance1;
/** 电阻值2 */
private Double resistance2;
/** 电阻值3 */
private Double resistance3;
/** 电阻值4 */
private Double resistance4;
// ========== 状态 ==========
/** 继电器1状态 */
private Boolean relay1Status;
/** 继电器2状态 */
private Boolean relay2Status;
/** 开关量1状态 */
private Boolean switch1Status;
/** 开关量2状态 */
private Boolean switch2Status;
/** 接线方式: 0x0033=三相三线, 0x0034=三相四线 */
private Integer wiringMode;
/** 相序: 0=正序, 1=相序错 */
private Integer phaseSequence;
}

View File

@@ -12,6 +12,11 @@ import cn.lihongjie.coal.threePhaseDeviceData.entity.ThreePhaseDeviceDataEntity;
import cn.lihongjie.coal.threePhaseDeviceData.mapper.ThreePhaseDeviceDataMapper;
import cn.lihongjie.coal.threePhaseDeviceData.repository.ThreePhaseDeviceDataRepository;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -19,9 +24,14 @@ import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Service
@Slf4j
@Transactional
@@ -78,4 +88,76 @@ public class ThreePhaseDeviceDataService
return page.map(this.mapper::toDto);
}
/**
* 按设备ID和时间范围查询数据
*/
public Page<ThreePhaseDeviceDataDto> listByDeviceAndTime(
String deviceId,
LocalDateTime startTime,
LocalDateTime endTime,
Integer pageNo,
Integer pageSize) {
Specification<ThreePhaseDeviceDataEntity> spec = new Specification<ThreePhaseDeviceDataEntity>() {
@Override
public Predicate toPredicate(
Root<ThreePhaseDeviceDataEntity> root,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
// 设备ID条件
if (deviceId != null) {
predicates.add(criteriaBuilder.equal(root.get("device").get("id"), deviceId));
}
// 时间范围条件
if (startTime != null) {
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("timestamp"), startTime));
}
if (endTime != null) {
predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("timestamp"), endTime));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
}
};
Page<ThreePhaseDeviceDataEntity> page = repository.findAll(
spec,
PageRequest.of(
pageNo != null ? pageNo : 0,
pageSize != null ? pageSize : 20,
Sort.by(Sort.Direction.DESC, "timestamp")));
return page.map(this.mapper::toDto);
}
/**
* 获取设备最新数据
*/
public ThreePhaseDeviceDataDto getLatestByDevice(String deviceId) {
Specification<ThreePhaseDeviceDataEntity> spec = new Specification<ThreePhaseDeviceDataEntity>() {
@Override
public Predicate toPredicate(
Root<ThreePhaseDeviceDataEntity> root,
CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
return criteriaBuilder.equal(root.get("device").get("id"), deviceId);
}
};
Page<ThreePhaseDeviceDataEntity> page = repository.findAll(
spec,
PageRequest.of(0, 1, Sort.by(Sort.Direction.DESC, "timestamp")));
if (page.hasContent()) {
return mapper.toDto(page.getContent().get(0));
}
return null;
}
}