yanderong
2026-09-27 8fab4df47dcd0dc7efe4a13f2f31897ce5751963
feat: 储能数据同步-能量链修改,新增需求
7个文件已添加
9个文件已修改
541 ■■■■■ 已修改文件
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/domain/EmindEnergyChain.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/domain/EmindEnergyChainDetail.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/mapper/EmindEnergyChainDetailMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/service/EmindEnergyChainDetailService.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/service/EmindEnergyChainManagementService.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/service/EmindEnergyChainSyncService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TStationCollectServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/task/ExchangeCodeExpireTask.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/BatteryStackState.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/ElectricMeterState.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/PcsState.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/TmsState.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/UdanEmsDeviceState.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/service/UdanEmsMessageProcessor.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/EmindEnergyChainMapper.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/resources/sql/t_screen_content_alter.sql 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/domain/EmindEnergyChain.java
@@ -125,4 +125,13 @@
    /** Data status, not persisted. */
    @ApiModelProperty("Data status, not persisted")
    private Integer dataStatus;
    /**
     * 设备类型(平台为华为时展示)
     */
    @TableField("station_type")
    private String stationType;
    @TableField(exist = false)
    private String detail;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/domain/EmindEnergyChainDetail.java
New file
@@ -0,0 +1,56 @@
package com.ruoyi.other.emind.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/** eMind 储能能量链本地冗余数据。 */
@Data
@ApiModel("EmindEnergyChainDetail")
@TableName("t_emind_energy_chain_detail")
public class EmindEnergyChainDetail {
    @TableId(value = "id", type = IdType.AUTO)
    @ApiModelProperty("Primary key")
    private Long id;
    @TableField("platform_name")
    private String platformName;
    @TableField("station_id")
    private String stationId;
    @TableField("station_code")
    private String stationCode;
    @TableField("station_type")
    private String stationType;
    @TableField("detail")
    private String detail;
    @TableField("data_source")
    @ApiModelProperty("Data source: 1 remote, 2 manual")
    private Integer dataSource;
    @TableField("del_flag")
    private Integer delFlag;
    @TableField("sync_time")
    private LocalDateTime syncTime;
    @TableField("create_time")
    private LocalDateTime createTime;
    @TableField("update_time")
    private LocalDateTime updateTime;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/mapper/EmindEnergyChainDetailMapper.java
New file
@@ -0,0 +1,8 @@
package com.ruoyi.other.emind.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.other.emind.domain.EmindEnergyChainDetail;
/** 能量链详情数据访问层。 */
public interface EmindEnergyChainDetailMapper extends BaseMapper<EmindEnergyChainDetail> {
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/service/EmindEnergyChainDetailService.java
New file
@@ -0,0 +1,111 @@
package com.ruoyi.other.emind.service;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.other.emind.domain.EmindEnergyChainDetail;
import com.ruoyi.other.emind.mapper.EmindEnergyChainDetailMapper;
import com.ruoyi.other.udan.model.UdanEmsDeviceState;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/** 能量链详情服务。 */
@Service
public class EmindEnergyChainDetailService {
    private final EmindEnergyChainDetailMapper mapper;
    private final Clock clock;
    @Autowired
    public EmindEnergyChainDetailService(EmindEnergyChainDetailMapper mapper) {
        this(mapper, Clock.systemDefaultZone());
    }
    EmindEnergyChainDetailService(EmindEnergyChainDetailMapper mapper, Clock clock) {
        this.mapper = mapper;
        this.clock = clock;
    }
    /** 新增能量链详情。 */
    @Transactional(rollbackFor = Exception.class)
    public EmindEnergyChainDetail add(EmindEnergyChainDetail input) {
        LocalDateTime now = LocalDateTime.now(clock);
        EmindEnergyChainDetail detail = new EmindEnergyChainDetail();
        detail.setPlatformName(input.getPlatformName());
        detail.setStationId(input.getStationId());
        detail.setStationCode(input.getStationCode());
        detail.setStationType(input.getStationType());
        detail.setDetail(input.getDetail());
        detail.setDataSource(input.getDataSource());
        detail.setDelFlag(0);
        detail.setSyncTime(now);
        detail.setCreateTime(now);
        detail.setUpdateTime(now);
        mapper.insert(detail);
        return detail;
    }
    /** 根据设备类型和设备编号查询有效的能量链详情。 */
    public List<EmindEnergyChainDetail> getByStationTypes(List<String> stationTypes) {
        return mapper.selectList(new LambdaQueryWrapper<EmindEnergyChainDetail>()
                .eq(EmindEnergyChainDetail::getDelFlag, 0)
                .in(EmindEnergyChainDetail::getStationType, stationTypes));
    }
    @Transactional
    public void saveAll(UdanEmsDeviceState udanEmsDeviceState) {
       List<EmindEnergyChainDetail> detailList = new ArrayList<>();
        // 保存BMS
        if(Objects.nonNull(udanEmsDeviceState.getTmsState())) {
            EmindEnergyChainDetail detail = new EmindEnergyChainDetail();
            detail.setPlatformName("欧力特");
            detail.setDataSource(2);
            detail.setStationCode(udanEmsDeviceState.getEmsSystemId());
            detail.setDetail(JSON.toJSONString(udanEmsDeviceState.getTmsState()));
            detail.setStationId(udanEmsDeviceState.getTmsState().getDeviceId());
            detail.setStationType("TMS");
            detailList.add(detail);
        }
        if(Objects.nonNull(udanEmsDeviceState.getPcsState())) {
            EmindEnergyChainDetail detail = new EmindEnergyChainDetail();
            detail.setPlatformName("欧力特");
            detail.setDataSource(2);
            detail.setStationCode(udanEmsDeviceState.getEmsSystemId());
            detail.setDetail(JSON.toJSONString(udanEmsDeviceState.getPcsState()));
            detail.setStationId(udanEmsDeviceState.getTmsState().getDeviceId());
            detail.setStationType("PCS");
            detailList.add(detail);
        }
        if(Objects.nonNull(udanEmsDeviceState.getElectricMeterState())) {
            EmindEnergyChainDetail detail = new EmindEnergyChainDetail();
            detail.setPlatformName("欧力特");
            detail.setDataSource(2);
            detail.setStationCode(udanEmsDeviceState.getEmsSystemId());
            detail.setDetail(JSON.toJSONString(udanEmsDeviceState.getElectricMeterState()));
            detail.setStationId(udanEmsDeviceState.getTmsState().getDeviceId());
            detail.setStationType("电表");
            detailList.add(detail);
        }
        if(Objects.nonNull(udanEmsDeviceState.getBatteryStackState())) {
            EmindEnergyChainDetail detail = new EmindEnergyChainDetail();
            detail.setPlatformName("欧力特");
            detail.setDataSource(2);
            detail.setStationCode(udanEmsDeviceState.getEmsSystemId());
            detail.setDetail(JSON.toJSONString(udanEmsDeviceState.getBatteryStackState()));
            detail.setStationId(udanEmsDeviceState.getTmsState().getDeviceId());
            detail.setStationType("BMS");
            detailList.add(detail);
        }
        detailList.forEach(d -> {
            mapper.delete(new QueryWrapper<EmindEnergyChainDetail>().eq("station_code", d.getStationCode()).eq("station_type", d.getStationType()));
            add(d);
        });
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/service/EmindEnergyChainManagementService.java
@@ -3,10 +3,12 @@
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.other.emind.domain.EmindEnergyChain;
import com.ruoyi.other.emind.domain.EmindEnergyChainDetail;
import com.ruoyi.other.emind.domain.EmindStation;
import com.ruoyi.other.emind.dto.EmindEnergyChainPageQuery;
import com.ruoyi.other.emind.mapper.EmindEnergyChainMapper;
import com.ruoyi.other.emind.mapper.EmindStationMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -14,32 +16,49 @@
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class EmindEnergyChainManagementService {
    private final EmindEnergyChainMapper mapper;
    private final EmindStationMapper stationMapper;
    private final Clock clock;
    private final EmindEnergyChainDetailService emindEnergyChainDetailService;
    @Autowired
    public EmindEnergyChainManagementService(EmindEnergyChainMapper mapper,
                                             EmindStationMapper stationMapper) {
        this(mapper, stationMapper, Clock.systemDefaultZone());
                                             EmindStationMapper stationMapper, EmindEnergyChainDetailService emindEnergyChainDetailService) {
        this(mapper, stationMapper, Clock.systemDefaultZone(), emindEnergyChainDetailService);
    }
    EmindEnergyChainManagementService(EmindEnergyChainMapper mapper,
                                      EmindStationMapper stationMapper,
                                      Clock clock) {
                                      Clock clock, EmindEnergyChainDetailService emindEnergyChainDetailService) {
        this.mapper = mapper;
        this.stationMapper = stationMapper;
        this.clock = clock;
        this.emindEnergyChainDetailService = emindEnergyChainDetailService;
    }
    public IPage<EmindEnergyChain> page(EmindEnergyChainPageQuery query) {
        int pageNumber = query.getPageCurr() == null || query.getPageCurr() < 1 ? 1 : query.getPageCurr();
        int pageSize = query.getPageSize() == null || query.getPageSize() < 1
                ? 10 : Math.min(query.getPageSize(), 100);
        return mapper.selectManagementPage(Page.of(pageNumber, pageSize), query);
        IPage<EmindEnergyChain> pageData = mapper.selectManagementPage(Page.of(pageNumber, pageSize), query);
        if(CollectionUtils.isNotEmpty(pageData.getRecords())) {
            List<String> stationTypeList = pageData.getRecords().stream().filter(r -> "欧力特".equals(r.getPlatformName())).map(EmindEnergyChain::getStationType).collect(Collectors.toList());
            if(CollectionUtils.isNotEmpty(stationTypeList)) {
                List<EmindEnergyChainDetail> chainDetails = emindEnergyChainDetailService.getByStationTypes(stationTypeList);
                if(CollectionUtils.isNotEmpty(chainDetails)) {
                    pageData.getRecords().forEach(rd -> chainDetails.forEach(cd -> {
                        if(cd.getStationType().equals(rd.getStationType())) {
                            rd.setDetail(cd.getDetail());
                        }
                    }));
                }
            }
        }
        return pageData;
    }
    @Transactional(rollbackFor = Exception.class)
@@ -97,5 +116,6 @@
        target.setElinkId(input.getElinkId());
        target.setUuid(input.getUuid().trim());
        target.setElinkName(input.getElinkName().trim());
        target.setStationType(input.getStationType());
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/emind/service/EmindEnergyChainSyncService.java
@@ -49,11 +49,13 @@
        LocalDateTime syncedAt = LocalDateTime.now(clock);
        List<EmindEnergyChain> chains = new ArrayList<>();
        for (EmindStation station : stations) {
            if(station.getSourceType() == 1) {
            List<EmindEnergyChainDto> remoteChains = api.getEnergyChains(station.getStationId());
            for (EmindEnergyChainDto remote : remoteChains) {
                chains.add(toEntity(station, remote, syncedAt));
            }
        }
        }
        chainMapper.markAllDeleted(syncedAt);
        if (!chains.isEmpty()) {
            chainMapper.upsertBatch(chains);
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TStationCollectServiceImpl.java
@@ -349,6 +349,13 @@
            fillDefaults(collect);
            insertList.add(collect);
        }
        // 设置电站名称
        List<TStationInfo> infoList = stationInfoMapper.selectList(new LambdaQueryWrapper<>());
        insertList.forEach(s -> infoList.forEach(info -> {
            if(StringUtils.isNotEmpty(s.getStationId()) && s.getStationId().equals(info.getStationId())) {
                s.setStationName(info.getStationName());
            }
        }));
        return CollectionUtils.isNotEmpty(insertList) && saveBatch(insertList);
    }
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/task/ExchangeCodeExpireTask.java
@@ -30,7 +30,7 @@
    }
    /**
     * 每6小时执行科士达的数据同步
     * 每6小时执行华为光伏的数据同步
     */
    @Scheduled(cron = "0 0 */6 * * ?")
    public void syncHuaWeiDataTask() {
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/BatteryStackState.java
New file
@@ -0,0 +1,39 @@
package com.ruoyi.other.udan.model;
import lombok.Data;
import java.io.Serializable;
/**
 * 优旦储能-BMS 实时数据
 *
 * @author Lux
 * @version 1.0.0
 * @since 2026/9/26 16:23
 */
@Data
public class BatteryStackState implements Serializable {
    /**
     * 设备id
     */
    private String deviceId;
    // 故障状态
    private String errorStatus;
    private String  workStatus; // 工作状态
    private String  warningStatus; // 告警状态
    private Double tv; // 总电压  单位V
    private Double tc; // 总电流  单位A
    private Double soh; // soh  单位%
    private Double soc; // soc  单位%
    private Double rechargeableCapacityKwh; // 可充电量  单位kWh
    private Double dischargeableCapacityKwh; // 可放电量  单位kWh
    private Double cumulativeDischargeKwh; // 累计放电量  单位kWh
    private Double cumulativeChargeKwh; // 累计充电量  单位kWh
    private Double minCellVoltMv; // 单体最低电压  单位mV
    private Double maxCellVoltMv; // 单体最高电压  单位mV
    private Double avgCellTemp; // 电芯平均温度  保存需减40偏移量
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/ElectricMeterState.java
New file
@@ -0,0 +1,32 @@
package com.ruoyi.other.udan.model;
import lombok.Data;
import java.io.Serializable;
/**
 * 优旦储能-电表实时数据
 *
 * @author Lux
 * @version 1.0.0
 * @since 2026/9/26 16:23
 */
@Data
public class ElectricMeterState implements Serializable {
    /**
     * 设备id
     */
    private String deviceId;
    private Double aPhaseVolt; // A相电压 单位V
    private Double bPhaseVolt; // B相电压 单位V
    private Double cPhaseVolt; // C相电压 单位V
    private Double aPhaseCurrent; // A相电流 单位A
    private Double bPhaseCurrent; // B相电流 单位A
    private Double cPhaseCurrent; // C相电流 单位A
    private Double acTotalActivePower; // 总有功功率        单位KW
    private Double frequency; // 频率          单位Hz
    private Double acTotalPowerFactor; // 总交流功率因数
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/PcsState.java
New file
@@ -0,0 +1,59 @@
package com.ruoyi.other.udan.model;
import lombok.Data;
import java.io.Serializable;
/**
 * 优旦储能-pcs实时数据
 *
 * @author Lux
 * @version 1.0.0
 * @since 2026/9/26 16:23
 */
@Data
public class PcsState implements Serializable {
    /**
     * 设备id
     */
    private String deviceId;
    // 故障状态
    private String errorStatus;
    // 告警状态
    private String alarmStatus;
    // 运行状态
    private String runStatus;
    // 并网状态
    private String onGridStatus;
    // 有功功率A相
    private Double aPhaseActivePower;
    // 有功功率B相
    private Double bPhaseActivePower;
    // 有功功率C相
    private Double cPhaseActivePower;
    // 总交流有功功率
    private Double acTotalActivePower;
    // 日充电量  单位kWh
    private Double dailyChargeEnergyKwh;
    // 日放电量  单位kWh
    private Double dailyDischargeEnergyKwh;
    // 总充电量  单位kWh
    private Double totalChargeEnergyKwh;
    // 总放电量  单位kWh
    private Double totalDischargeEnergyKwh;
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/TmsState.java
New file
@@ -0,0 +1,35 @@
package com.ruoyi.other.udan.model;
import lombok.Data;
import java.io.Serializable;
/**
 * 优旦储能-TNS 实时数据
 *
 * @author Lux
 * @version 1.0.0
 * @since 2026/9/26 16:23
 */
@Data
public class TmsState implements Serializable {
    /**
     * 设备id
     */
    private String deviceId;
    private String run_status; // 运行状态
    private String  error_status; // 故障状态 0 预留 1 故障 2 正常
    private String compress_status; // 压缩机状态
    private String compress_heat_status; // 压缩机加热带状态
    private String electric_heat_status; // 电加热运行状态
    private String pump_status; // 水泵状态
    private String fan_status; // 风机运行状态
    private Double supply_temp; // 供液温度
    private Double return_temp; // 回液温度
    private Double env_temp; // 环境温度
    private Double backwater_press; // 回水压力 回液压力
    private Double out_water_press; // 出水压力 供液压力
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/model/UdanEmsDeviceState.java
@@ -21,6 +21,11 @@
    private EmsState emsState;
    private PowerMeterState powerMeterState;
    private TmsState tmsState;
    private PcsState pcsState;
    private ElectricMeterState electricMeterState;
    private BatteryStackState batteryStackState;
    public List<EmindIncomeHistoryDto> convert(Long stationId) {
        List<EmindIncomeHistoryDto> dailyList = new ArrayList<>();
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/udan/service/UdanEmsMessageProcessor.java
@@ -7,8 +7,13 @@
import com.ruoyi.other.emind.dto.EmindIncomeHistoryDto;
import com.ruoyi.other.emind.mapper.EmindStationMapper;
import com.ruoyi.other.emind.mapper.EmindStorageDashboardMapper;
import com.ruoyi.other.emind.service.EmindEnergyChainDetailService;
import com.ruoyi.other.udan.model.EmsState;
import com.ruoyi.other.udan.model.BatteryStackState;
import com.ruoyi.other.udan.model.ElectricMeterState;
import com.ruoyi.other.udan.model.PcsState;
import com.ruoyi.other.udan.model.PowerMeterState;
import com.ruoyi.other.udan.model.TmsState;
import com.ruoyi.other.udan.model.UdanEmsDeviceState;
import com.ruoyi.other.udan.util.UdanEmsKafkaUtil;
import lombok.extern.slf4j.Slf4j;
@@ -34,11 +39,13 @@
    private final UdanEmsKafkaUtil kafkaUtil;
    private final EmindStorageDashboardMapper dashboardMapper;
    private final EmindStationMapper stationMapper;
    private final EmindEnergyChainDetailService emindEnergyChainDetailService;
    public UdanEmsMessageProcessor(UdanEmsKafkaUtil kafkaUtil, EmindStorageDashboardMapper dashboardMapper, EmindStationMapper stationMapper) {
    public UdanEmsMessageProcessor(UdanEmsKafkaUtil kafkaUtil, EmindStorageDashboardMapper dashboardMapper, EmindStationMapper stationMapper, EmindEnergyChainDetailService emindEnergyChainDetailService) {
        this.kafkaUtil = kafkaUtil;
        this.dashboardMapper = dashboardMapper;
        this.stationMapper = stationMapper;
        this.emindEnergyChainDetailService = emindEnergyChainDetailService;
    }
    @PostConstruct
@@ -67,6 +74,8 @@
                }else{
                    log.error("UDAN EMS数据同步错误-电站信息不存在!");
                }
                // 保存设备其它信息
                emindEnergyChainDetailService.saveAll(udanEmsDeviceState);
            }
        } catch (IOException e) {
@@ -87,7 +96,19 @@
        read(bytes, (field, type, in) -> {
            if (field == 1 && type == 0) state.setTime(in.readUInt64());
            else if (type == 2 && field == 2) state.setEmsState(parseEms(in.readBytes().toByteArray()));
            else if (type == 2 && field == 4 && state.getPcsState() == null) {
                state.setPcsState(parsePcs(in.readBytes().toByteArray()));
            }
            else if (type == 2 && field == 5 && state.getBatteryStackState() == null) {
                state.setBatteryStackState(parseBatteryStack(in.readBytes().toByteArray()));
            }
            else if (type == 2 && field == 7 && state.getElectricMeterState() == null) {
                state.setElectricMeterState(parseElectricMeter(in.readBytes().toByteArray()));
            }
            else if (type == 2 && field == 8) state.setPowerMeterState(parsePowerMeter(in.readBytes().toByteArray()));
            else if (type == 2 && field == 14 && state.getTmsState() == null) {
                state.setTmsState(parseTms(in.readBytes().toByteArray()));
            }
            else skip(in, type);
        });
        return state;
@@ -200,6 +221,96 @@
        return value;
    }
    private PcsState parsePcs(byte[] bytes) throws IOException {
        PcsState value = new PcsState();
        read(bytes, (field, type, input) -> {
            switch (field) {
                case 2: value.setDeviceId(str(input, type)); break;
                case 3: value.setErrorStatus(enumName(input, type, "ErrorStatus")); break;
                case 4: value.setAlarmStatus(enumName(input, type, "AlarmStatus")); break;
                case 5: value.setRunStatus(enumName(input, type, "RunStatus")); break;
                case 6: value.setOnGridStatus(enumName(input, type, "GridStatus")); break;
                case 13: value.setAcTotalActivePower((double) f32(input, type)); break;
                case 17: value.setDailyChargeEnergyKwh((double) f32(input, type)); break;
                case 18: value.setDailyDischargeEnergyKwh((double) f32(input, type)); break;
                case 19: value.setTotalChargeEnergyKwh(f64(input, type)); break;
                case 20: value.setTotalDischargeEnergyKwh(f64(input, type)); break;
                case 31: value.setAPhaseActivePower((double) f32(input, type)); break;
                case 34: value.setBPhaseActivePower((double) f32(input, type)); break;
                case 37: value.setCPhaseActivePower((double) f32(input, type)); break;
                default: skip(input, type);
            }
        });
        return value;
    }
    private BatteryStackState parseBatteryStack(byte[] bytes) throws IOException {
        BatteryStackState value = new BatteryStackState();
        read(bytes, (field, type, input) -> {
            switch (field) {
                case 2: value.setDeviceId(str(input, type)); break;
                case 4: value.setWorkStatus(enumName(input, type, "BatteryWorkStatus")); break;
                case 5: value.setErrorStatus(enumName(input, type, "ErrorStatus")); break;
                case 6: value.setWarningStatus(enumName(input, type, "AlarmStatus")); break;
                case 7: value.setTv((double) f32(input, type)); break;
                case 8: value.setTc((double) f32(input, type)); break;
                case 9: value.setSoh((double) f32(input, type)); break;
                case 10: value.setSoc((double) f32(input, type)); break;
                case 15: value.setMinCellVoltMv((double) u32(input, type)); break;
                case 23: value.setMaxCellVoltMv((double) u32(input, type)); break;
                case 31: value.setRechargeableCapacityKwh((double) f32(input, type)); break;
                case 32: value.setDischargeableCapacityKwh((double) f32(input, type)); break;
                case 33: value.setCumulativeDischargeKwh(f64(input, type)); break;
                case 34: value.setCumulativeChargeKwh(f64(input, type)); break;
                case 36: value.setAvgCellTemp((double) u32(input, type) - 40D); break;
                default: skip(input, type);
            }
        });
        return value;
    }
    private ElectricMeterState parseElectricMeter(byte[] bytes) throws IOException {
        ElectricMeterState value = new ElectricMeterState();
        read(bytes, (field, type, input) -> {
            switch (field) {
                case 2: value.setDeviceId(str(input, type)); break;
                case 48: value.setAPhaseVolt((double) f32(input, type)); break;
                case 49: value.setBPhaseVolt((double) f32(input, type)); break;
                case 50: value.setCPhaseVolt((double) f32(input, type)); break;
                case 51: value.setAPhaseCurrent((double) f32(input, type)); break;
                case 52: value.setBPhaseCurrent((double) f32(input, type)); break;
                case 53: value.setCPhaseCurrent((double) f32(input, type)); break;
                case 54: value.setAcTotalActivePower((double) f32(input, type)); break;
                case 69: value.setFrequency((double) f32(input, type)); break;
                case 74: value.setAcTotalPowerFactor((double) f32(input, type)); break;
                default: skip(input, type);
            }
        });
        return value;
    }
    private TmsState parseTms(byte[] bytes) throws IOException {
        TmsState value = new TmsState();
        read(bytes, (field, type, input) -> {
            switch (field) {
                case 1: value.setDeviceId(str(input, type)); break;
                case 2: value.setRun_status(enumName(input, type, "TmsRunStatus")); break;
                case 3: value.setError_status(enumName(input, type, "ErrorStatus")); break;
                case 4: value.setSupply_temp((double) f32(input, type)); break;
                case 5: value.setReturn_temp((double) f32(input, type)); break;
                case 8: value.setEnv_temp((double) f32(input, type)); break;
                case 9: value.setBackwater_press((double) f32(input, type)); break;
                case 10: value.setOut_water_press((double) f32(input, type)); break;
                case 13: value.setCompress_status(enumName(input, type, "RunStatus")); break;
                case 14: value.setCompress_heat_status(enumName(input, type, "RunStatus")); break;
                case 15: value.setElectric_heat_status(enumName(input, type, "RunStatus")); break;
                case 16: value.setPump_status(enumName(input, type, "RunStatus")); break;
                default: skip(input, type);
            }
        });
        return value;
    }
    private void read(byte[] bytes, Reader reader) throws IOException {
        CodedInputStream input = CodedInputStream.newInstance(bytes == null ? new byte[0] : bytes);
        while (!input.isAtEnd()) {
@@ -261,6 +372,18 @@
        if ("CommStatus".equals(prefix)) {
            return new String[]{"COMM_STATUS_UNKNOWN", "COMM_STATUS_DISCONNECTED", "COMM_STATUS_CONNECTED"}[number < 0 || number > 2 ? 0 : number];
        }
        if ("ErrorStatus".equals(prefix)) {
            return new String[]{"ERROR_STATUS_UNKNOWN", "ERROR_STATUS_OCCURRING", "ERROR_STATUS_NONE"}[number < 0 || number > 2 ? 0 : number];
        }
        if ("AlarmStatus".equals(prefix)) {
            return new String[]{"ALARM_STATUS_UNKNOWN", "ALARM_STATUS_OCCURRING", "ALARM_STATUS_NONE"}[number < 0 || number > 2 ? 0 : number];
        }
        if ("BatteryWorkStatus".equals(prefix)) {
            return new String[]{"BATTERY_WORK_STATUS_UNKNOWN", "BATTERY_WORK_STATUS_CHARGING", "BATTERY_WORK_STATUS_DISCHARGING", "BATTERY_WORK_STATUS_IDLE"}[number < 0 || number > 3 ? 0 : number];
        }
        if ("TmsRunStatus".equals(prefix)) {
            return new String[]{"TMS_RUN_STATUS_UNKNOWN", "TMS_RUN_STATUS_IDLE", "TMS_RUN_STATUS_READY_TO_START", "TMS_RUN_STATUS_RUNNING", "TMS_RUN_STATUS_DELAYED_SHUTDOWN", "TMS_RUN_STATUS_SELF_TEST", "TMS_RUN_STATUS_ALARMING"}[number < 0 || number > 6 ? 0 : number];
        }
        return prefix + "_" + number;
    }
    private static <T> T enumAt(T[] values, CodedInputStream input, int type) throws IOException {
ruoyi-service/ruoyi-other/src/main/resources/mapper/other/EmindEnergyChainMapper.xml
@@ -26,6 +26,7 @@
        <result column="synced_at" property="syncedAt"/>
        <result column="platform_name" property="platformName"/>
        <result column="data_status" property="dataStatus"/>
        <result column="station_type" property="stationType"/>
    </resultMap>
    <update id="markAllDeleted">
@@ -77,7 +78,7 @@
               chain.phase_a_current, chain.phase_b_current, chain.phase_c_current,
               chain.dischargeable_energy, chain.soc, chain.charged_energy_today,
               chain.discharged_energy_today, chain.record_time, chain.synced_at,
               chain.source_type, chain.management_deleted,
               chain.source_type, chain.management_deleted, chain.station_type,
               station.platform_name,
               CASE WHEN chain.record_time IS NULL OR chain.record_time = '' THEN 0 ELSE 1 END AS data_status
        FROM t_emind_energy_chain chain
ruoyi-service/ruoyi-other/src/main/resources/sql/t_screen_content_alter.sql
@@ -7,4 +7,24 @@
ALTER TABLE t_emind_dashboard_daily MODIFY COLUMN id BIGINT NOT NULL AUTO_INCREMENT comment '主键ID';
-- 3、储能能量连表增加(设备类型)
alter table t_emind_energy_chain add column station_type varchar(255)   default ''  null comment '设备类型';
-- 增加能量链详情表
create table t_emind_energy_chain_detail
(
    id                 bigint auto_increment comment '主键ID'
        primary key,
    platform_name        varchar(100)         null comment '所属平台(利普芯、欧力特)',
    station_code       varchar(255)                   null comment '设备编号',
    station_id       varchar(255)                   null comment '设备ID',
    station_type       varchar(255)                   null comment '设备类型(与能量链里面的设备类型进行关联)',
    detail             text                 null comment '设备详情json字段',
    data_source        int                           null comment '数据来源(1系统创建,2第三方接口)',
    create_time        datetime                      null comment '创建时间',
    sync_time          datetime                      null comment '同步时间',
    update_time        datetime                      null comment '更新时间',
    del_flag           char           default '0'    not null comment '删除标志(0存在 2删除)'
)
    comment '能量链详情表' collate = utf8mb4_bin;