package com.ruoyi.ems.handle;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.huashe.common.exception.Assert;
import com.huashe.common.exception.BusinessException;
import com.huashe.common.utils.DateUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.ems.config.BaCtlConfig;
import com.ruoyi.ems.core.BaCtlEnergyTemplate;
import com.ruoyi.ems.domain.ElecMeterH;
import com.ruoyi.ems.domain.ElecPgSupplyH;
import com.ruoyi.ems.domain.EmsDevice;
import com.ruoyi.ems.domain.EmsObjAttrValue;
import com.ruoyi.ems.domain.FdEnergyPriceConfig;
import com.ruoyi.ems.domain.MeterDevice;
import com.ruoyi.ems.domain.WaterMeterH;
import com.ruoyi.ems.enums.DevObjType;
import com.ruoyi.ems.model.AbilityPayload;
import com.ruoyi.ems.model.BoundaryObj;
import com.ruoyi.ems.model.CallData;
import com.ruoyi.ems.model.CallResponse;
import com.ruoyi.ems.model.Price;
import com.ruoyi.ems.model.QueryDevice;
import com.ruoyi.ems.model.idenergy.CodesVal;
import com.ruoyi.ems.model.idenergy.CodesValReq;
import com.ruoyi.ems.model.idenergy.CodesValSetReq;
import com.ruoyi.ems.service.IEmsObjAttrValueService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * BA楼控处理类
 * <功能详细描述>
 *
 * @author lvwenbin
 * @version [版本号, 2025/8/15]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
@Service
public class BaCtlHandler extends BaseMeterDevHandler {
    private static final Logger log = LoggerFactory.getLogger(BaCtlHandler.class);

    // 网关模型代码
    private static final String GATEWAY_MODEL = "M_W4_DEV_BA_GA";

    // 电表测点模型代码
    private static final String METER_MODEL_E = "M_W4_DEV_BA_METER_E";

    // 水表测点模型代码
    private static final String METER_MODEL_W = "M_W4_DEV_BA_METER_W";

    // 新风设备模型代码
    private static final String METER_MODEL_XF = "M_Z020_DEV_BA_XF";

    // 空调设备模型代码
    private static final String METER_MODEL_AHU = "M_Z020_DEV_BA_AHU";

    // 水箱设备模型代码
    private static final String METER_MODEL_WT = "M_Z020_DEV_BA_WT";

    // 水泵设备模型代码
    private static final String METER_MODEL_WP = "M_Z020_DEV_BA_WP";

    // 照明模型代码
    private static final String METER_MODEL_LIGHT = "M_Z020_DEV_BA_LIGHT";

    // 设备子系统代码
    private static final String SUBSYSTEM_CODE = "SYS_NHJC";

    // 单小时最大增长量（度）
    private static final double MAX_HOUR_INCREASE_RATE = 1000.0;

    // 允许的最小负增长（考虑精度误差）
    private static final double MIN_REASONABLE_DECREASE_RATE = -0.1;

    // 异常数据连续确认次数
    private static final int ABNORMAL_DATA_CONFIRM_COUNT = 3;

    @Resource
    private IEmsObjAttrValueService objAttrValueService;

    @Resource
    private BaCtlConfig config;

    @Autowired
    private RedisService redisService;

    @Override
    public List<MeterDevice> getMeterDeviceList() {
        QueryDevice queryDevice = new QueryDevice();
        queryDevice.setDeviceModel(METER_MODEL_E);
        queryDevice.setDeviceEnable(1);
        List<MeterDevice> list = new ArrayList<>(meterDeviceService.selectMeterDeviceList(queryDevice));

        QueryDevice queryDevice2 = new QueryDevice();
        queryDevice2.setDeviceModel(METER_MODEL_W);
        queryDevice2.setDeviceEnable(1);
        list.addAll(meterDeviceService.selectMeterDeviceList(queryDevice2));

        return list;
    }

    public List<MeterDevice> getMeterDeviceList(String modelCode) {
        QueryDevice queryDevice = new QueryDevice();
        queryDevice.setDeviceModel(modelCode);
        queryDevice.setDeviceEnable(1);
        return meterDeviceService.selectMeterDeviceList(queryDevice);
    }

    @Override
    public List<EmsDevice> getDeviceList() {
        throw new UnsupportedOperationException();
    }

    public List<EmsDevice> getDeviceList(String modelCode) {
        QueryDevice queryDevice = new QueryDevice();
        queryDevice.setDeviceModel(modelCode);
        return deviceService.selectList(queryDevice);
    }

    @Override
    public CallResponse<Void> call(AbilityPayload param) {
        CallResponse<Void> callResponse = null;

        try {
            if (DevObjType.SYSTEM.getCode() == param.getObjType()) {
                if (StringUtils.equals("MeterReadingTotal", param.getAbilityKey())) {
                    List<EmsDevice> deviceList = getDeviceList(GATEWAY_MODEL);

                    for (EmsDevice device : deviceList) {
                        meterReadingGw(device.getDeviceModel(), "MeterReadingGw", device.getDeviceCode());
                    }
                }
                else if (StringUtils.equals("SyncXfDevAttr", param.getAbilityKey())) {
                    xfCollect();
                }
                else if (StringUtils.equals("SyncAuhDevAttr", param.getAbilityKey())) {
                    ahuCollect();
                }
                else if (StringUtils.equals("SyncWtDevAttr", param.getAbilityKey())) {
                    wtCollect();
                }
                else if (StringUtils.equals("SyncWpDevAttr", param.getAbilityKey())) {
                    wpCollect();
                }
                else if (StringUtils.equals("SyncLightDevAttr", param.getAbilityKey())) {
                    lightCollect();
                }

                callResponse = new CallResponse<>(0, "成功");
            }
            else if (DevObjType.DEVC.getCode() == param.getObjType()) {
                if (StringUtils.equals("MeterReadingGw", param.getAbilityKey()) && StringUtils.equals(
                    param.getModelCode(), GATEWAY_MODEL)) {
                    meterReadingGw(param.getModelCode(), param.getAbilityKey(), param.getObjCode());
                }
                else if (StringUtils.equals("StartStopCtl", param.getAbilityKey()) && StringUtils.equals(
                    param.getModelCode(), METER_MODEL_XF)) {
                    Map<String, String> xfMapper = config.getXfMapper();
                    String pointId = xfMapper.get(String.format("%s.%s", param.getObjCode(), "setCtl-StartStop"));
                    Assert.notEmpty(pointId, -1, "该设备未配置控制测点pointId.");
                    devAbilityCall(METER_MODEL_XF, param.getObjCode(), pointId, param.getAbilityKey(),
                        param.getAbilityParam());
                }
                else if (StringUtils.equals("StartStopCtl", param.getAbilityKey()) && StringUtils.equals(
                    param.getModelCode(), METER_MODEL_AHU)) {
                    Map<String, String> ahuMapper = config.getAhuMapper();
                    String pointId = ahuMapper.get(String.format("%s.%s", param.getObjCode(), "setCtl-StartStop"));
                    Assert.notEmpty(pointId, -1, "该设备未配置控制测点pointId.");
                    devAbilityCall(METER_MODEL_AHU, param.getObjCode(), pointId, param.getAbilityKey(),
                        param.getAbilityParam());
                }
                else if (StringUtils.equals("OnOffCtl", param.getAbilityKey()) && StringUtils.equals(
                    param.getModelCode(), METER_MODEL_AHU)) {
                    Map<String, String> lightMapper = config.getLightMapper();
                    String pointId = lightMapper.get(String.format("%s.%s", param.getObjCode(), "setCtl-OnOff"));
                    Assert.notEmpty(pointId, -1, "该设备未配置控制测点pointId.");
                    devAbilityCall(METER_MODEL_LIGHT, param.getObjCode(), pointId, param.getAbilityKey(),
                        param.getAbilityParam());
                }

                callResponse = new CallResponse<>(0, "成功");
            }
            else {
                callResponse = new CallResponse<>(-1, "不支持的能力key：" + param.getAbilityKey());
            }
        }
        catch (BusinessException e) {
            callResponse = new CallResponse<>(e.getCode(), e.getMessage());
        }

        return callResponse;
    }

    @Override
    public void refreshOnline() {
    }

    public void devAbilityCall(String modelCode, String deviceId, String pointId, String abilityKey,
        String paramValue) {
        CodesVal codesVal = new CodesVal();
        codesVal.setPointId(pointId);
        codesVal.setValue(paramValue);

        BaCtlEnergyTemplate template = new BaCtlEnergyTemplate(config.getUrl());
        CallData<String> callData = template.setCodesVal(new CodesValSetReq(codesVal));

        saveCallLog(deviceId, modelCode, abilityKey, callData.getCallStatus(), callData.getCallPayload(),
            callData.getResPayload());
    }

    private void meterReadingGw(String modeCode, String abilityKey, String objCode) {
        // 查询当前设备的接口属性值
        EmsObjAttrValue subDevAttr = objAttrValueService.selectObjAttrValue(modeCode, objCode, "subDev");

        if (null != subDevAttr && StringUtils.isNotEmpty(subDevAttr.getAttrValue())) {
            JSONArray subDevs = JSON.parseArray(subDevAttr.getAttrValue());
            Set<String> pointIdSet = subDevs.stream().map(item -> ((JSONObject) item).getString("deviceCode"))
                .collect(Collectors.toSet());

            CodesValReq req = new CodesValReq(pointIdSet);

            // 调用能耗数据接口获取实时数据
            CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
            String callRes = callData.getResPayload();
            JSONObject resJson = JSONObject.parseObject(callRes);

            if (resJson.getInteger("code") == 200) {
                saveCallLog(objCode, modeCode, abilityKey, 0, JSON.toJSONString(req), callRes);

                String dataStr = resJson.getString("ResultPointObjArr");
                List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
                // 更新设备属性
                updateMeterDeviceAttrList(objCode, subDevs, retList);
            }
            else {
                saveCallLog(objCode, modeCode, abilityKey, 2, JSON.toJSONString(req), callRes);
                throw new BusinessException(-1, resJson.getString("error"));
            }
        }
    }

    /**
     * 采集室内能耗计量数据
     *
     * @return 采集条数
     */
    public int meterCollect() {
        int cnt = 0;

        try {
            List<EmsObjAttrValue> gwDeviceList = objAttrValueService.selectByAttrKey(GATEWAY_MODEL, "subDev");

            // 遍历每个网关设备
            if (CollectionUtils.isNotEmpty(gwDeviceList)) {
                // 调用能耗数据接口获取实时数据
                BaCtlEnergyTemplate template = new BaCtlEnergyTemplate(config.getUrl());

                for (EmsObjAttrValue gwDevice : gwDeviceList) {
                    cnt += meterDevCollect(template, gwDevice);
                }
            }
        }
        catch (Exception e) {
            log.error("能耗数据抄报异常", e);
        }

        return cnt;
    }

    public void xfCollect() {
        try {
            List<EmsDevice> deviceList = getDeviceList(METER_MODEL_XF);

            if (CollectionUtils.isNotEmpty(deviceList)) {
                for (EmsDevice device : deviceList) {
                    xfDeviceCollect(device);
                }
            }
        }
        catch (Exception e) {
            log.error("新风采集异常", e);
        }
    }

    public void ahuCollect() {
        try {
            List<EmsDevice> deviceList = getDeviceList(METER_MODEL_AHU);

            if (CollectionUtils.isNotEmpty(deviceList)) {
                for (EmsDevice device : deviceList) {
                    auhDeviceCollect(device);
                }
            }
        }
        catch (Exception e) {
            log.error("新风采集异常", e);
        }
    }

    public void wtCollect() {
        try {
            List<EmsDevice> deviceList = getDeviceList(METER_MODEL_WT);

            if (CollectionUtils.isNotEmpty(deviceList)) {
                for (EmsDevice device : deviceList) {
                    wtDeviceCollect(device);
                }
            }
        }
        catch (Exception e) {
            log.error("水箱采集异常", e);
        }
    }

    public void wpCollect() {
        try {
            List<EmsDevice> deviceList = getDeviceList(METER_MODEL_WP);

            if (CollectionUtils.isNotEmpty(deviceList)) {
                for (EmsDevice device : deviceList) {
                    wpDeviceCollect(device);
                }
            }
        }
        catch (Exception e) {
            log.error("水泵采集异常", e);
        }
    }

    public void lightCollect() {
        try {
            List<EmsDevice> deviceList = getDeviceList(METER_MODEL_LIGHT);

            if (CollectionUtils.isNotEmpty(deviceList)) {
                for (EmsDevice device : deviceList) {
                    lightDeviceCollect(device);
                }
            }
        }
        catch (Exception e) {
            log.error("照明采集异常", e);
        }
    }

    private int meterDevCollect(BaCtlEnergyTemplate template, EmsObjAttrValue subDevAttr) {
        int cnt = 0;

        try {

            if (null != subDevAttr && StringUtils.isNotEmpty(subDevAttr.getAttrValue())) {
                JSONArray subDevs = JSON.parseArray(subDevAttr.getAttrValue());
                Set<String> pointIdSet = subDevs.stream().map(item -> ((JSONObject) item).getString("deviceCode"))
                    .collect(Collectors.toSet());

                CodesValReq req = new CodesValReq(pointIdSet);

                // 调用能耗数据接口获取实时数据
                CallData<String> callData = template.getCodesVal(req);
                String callRes = callData.getResPayload();
                JSONObject resJson = JSONObject.parseObject(callRes);
                Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
                    resJson.getString("error"));
                int callStatus = StringUtils.equals(resJson.getString("code"), "200") ? 0 : 2;
                saveCallLog(subDevAttr.getObjCode(), GATEWAY_MODEL, "MeterReadingGw", callStatus,
                    JSON.toJSONString(req), callRes);

                String dataStr = resJson.getString("ResultPointObjArr");
                List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);

                // 更新设备属性
                updateMeterDeviceAttrList(subDevAttr.getObjCode(), subDevs, retList);
            }
        }
        catch (Exception e) {
            log.error("meterDevHourProd error! deviceCode:{}", subDevAttr.getObjCode(), e);
        }

        return cnt;
    }

    private void xfDeviceCollect(EmsDevice device) {
        String deviceCode = device.getDeviceCode();
        Map<String, String> paramKeys = config.getXfMapper();

        Set<String> pointIds = paramKeys.entrySet().stream().filter(
                entry -> StringUtils.startsWith(entry.getKey(), deviceCode) && !StringUtils.startsWith(entry.getKey(),
                    deviceCode + ".setCtl-")).flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ",")))
            .map(String::trim).collect(Collectors.toSet());

        if (CollectionUtils.isNotEmpty(pointIds)) {
            CodesValReq req = new CodesValReq(pointIds);

            // 调用能耗数据接口获取实时数据
            CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
            String callRes = callData.getResPayload();
            saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncXfDevAttr", 0, JSON.toJSONString(req), callRes);

            JSONObject resJson = JSONObject.parseObject(callRes);
            Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
                resJson.getString("error"));
            String dataStr = resJson.getString("ResultPointObjArr");
            List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
            Map<String, CodesVal> retMap = retList.stream()
                .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));

            List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_XF, deviceCode);
            Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
                .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));

            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfTemp", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "sfTemp", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "hfTemp", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfTemp", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "ppm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "wCv", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfCv", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfCv", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "lwDpAlarm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "fjUvAlarm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "afAlarm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfStatus", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfMA", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfFault", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfStatus", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfMA", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "pfFault", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "xfTempSetVal", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "LnSu", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "timeSetTag", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_XF, deviceCode, "manualTag", paramKeys, dbMap, retMap);
            baDevTimeStatueCheckAndUpdate(METER_MODEL_XF, deviceCode, "timeStatus", paramKeys, dbMap, retMap);
        }
    }

    private void auhDeviceCollect(EmsDevice device) {
        String deviceCode = device.getDeviceCode();
        Map<String, String> paramKeys = config.getAhuMapper();

        Set<String> pointIds = paramKeys.entrySet().stream().filter(
                entry -> StringUtils.startsWith(entry.getKey(), deviceCode) && !StringUtils.startsWith(entry.getKey(),
                    deviceCode + ".setCtl-")).flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ",")))
            .map(String::trim).collect(Collectors.toSet());

        if (CollectionUtils.isNotEmpty(pointIds)) {
            CodesValReq req = new CodesValReq(pointIds);

            // 调用能耗数据接口获取实时数据
            CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
            String callRes = callData.getResPayload();
            saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncAuhDevAttr", 0, JSON.toJSONString(req), callRes);
            JSONObject resJson = JSONObject.parseObject(callRes);
            Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
                resJson.getString("error"));
            String dataStr = resJson.getString("ResultPointObjArr");
            List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
            Map<String, CodesVal> retMap = retList.stream()
                .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));

            List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_AHU, deviceCode);
            Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
                .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));

            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfTemp", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "sfTemp", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "hfTemp", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "wCv", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfCv", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "lwDpAlarm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "fjUvAlarm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "afAlarm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfStatus", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfMA", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfFault", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "xfTempSetVal", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "LnSu", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "timeSetTag", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_AHU, deviceCode, "manualTag", paramKeys, dbMap, retMap);
            baDevTimeStatueCheckAndUpdate(METER_MODEL_AHU, deviceCode, "timeStatus", paramKeys, dbMap, retMap);
        }
    }

    private void wtDeviceCollect(EmsDevice device) {
        String deviceCode = device.getDeviceCode();
        Map<String, String> paramKeys = config.getWtMapper();

        Set<String> pointIds = paramKeys.entrySet().stream()
            .filter(entry -> StringUtils.startsWith(entry.getKey(), deviceCode))
            .flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ","))).map(String::trim)
            .collect(Collectors.toSet());

        if (CollectionUtils.isNotEmpty(pointIds)) {
            CodesValReq req = new CodesValReq(pointIds);

            // 调用能耗数据接口获取实时数据
            CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
            String callRes = callData.getResPayload();
            saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncWtDevAttr", 0, JSON.toJSONString(req), callRes);
            JSONObject resJson = JSONObject.parseObject(callRes);
            Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
                resJson.getString("error"));
            String dataStr = resJson.getString("ResultPointObjArr");
            List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
            Map<String, CodesVal> retMap = retList.stream()
                .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));

            List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_WT, deviceCode);
            Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
                .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));

            baDevAttrCheckAndUpdate(METER_MODEL_WT, deviceCode, "highLevelAlarm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_WT, deviceCode, "lowLevelAlarm", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_WT, deviceCode, "tankLevel", paramKeys, dbMap, retMap);
        }
    }

    private void wpDeviceCollect(EmsDevice device) {
        String deviceCode = device.getDeviceCode();
        Map<String, String> paramKeys = config.getWpMapper();

        Set<String> pointIds = paramKeys.entrySet().stream()
            .filter(entry -> StringUtils.startsWith(entry.getKey(), deviceCode))
            .flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ","))).map(String::trim)
            .collect(Collectors.toSet());

        if (CollectionUtils.isNotEmpty(pointIds)) {
            CodesValReq req = new CodesValReq(pointIds);

            // 调用能耗数据接口获取实时数据
            CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
            String callRes = callData.getResPayload();
            saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncWpDevAttr", 0, JSON.toJSONString(req), callRes);
            JSONObject resJson = JSONObject.parseObject(callRes);
            Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
                resJson.getString("error"));
            String dataStr = resJson.getString("ResultPointObjArr");
            List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
            Map<String, CodesVal> retMap = retList.stream()
                .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));

            List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_WP, deviceCode);
            Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
                .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));

            baDevAttrCheckAndUpdate(METER_MODEL_WP, deviceCode, "autoState", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_WP, deviceCode, "runningState", paramKeys, dbMap, retMap);
            baDevAttrCheckAndUpdate(METER_MODEL_WP, deviceCode, "faultState", paramKeys, dbMap, retMap);
        }
    }

    private void lightDeviceCollect(EmsDevice device) {
        String deviceCode = device.getDeviceCode();
        Map<String, String> paramKeys = config.getLightMapper();

        Set<String> pointIds = paramKeys.entrySet().stream().filter(
                entry -> StringUtils.startsWith(entry.getKey(), deviceCode) && !StringUtils.startsWith(entry.getKey(),
                    deviceCode + ".setCtl-")).flatMap(entry -> Arrays.stream(StringUtils.split(entry.getValue(), ",")))
            .map(String::trim).collect(Collectors.toSet());

        if (CollectionUtils.isNotEmpty(pointIds)) {
            CodesValReq req = new CodesValReq(pointIds);

            // 调用能耗数据接口获取实时数据
            CallData<String> callData = new BaCtlEnergyTemplate(config.getUrl()).getCodesVal(req);
            String callRes = callData.getResPayload();
            saveCallLog(device.getDeviceCode(), device.getDeviceModel(), "SyncLightDevAttr", 0, JSON.toJSONString(req), callRes);
            JSONObject resJson = JSONObject.parseObject(callRes);
            Assert.isTrue(StringUtils.equals(resJson.getString("code"), "200"), resJson.getInteger("code"),
                resJson.getString("error"));
            String dataStr = resJson.getString("ResultPointObjArr");
            List<CodesVal> retList = JSON.parseArray(dataStr, CodesVal.class);
            Map<String, CodesVal> retMap = retList.stream()
                .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));

            List<EmsObjAttrValue> dbAttrList = objAttrValueService.selectByObjCode(METER_MODEL_LIGHT, deviceCode);
            Map<String, EmsObjAttrValue> dbMap = dbAttrList.stream()
                .collect(Collectors.toMap(EmsObjAttrValue::getAttrKey, Function.identity()));

            baDevAttrCheckAndUpdate(METER_MODEL_LIGHT, deviceCode, "Switch", paramKeys, dbMap, retMap);
        }
    }

    private void baDevAttrCheckAndUpdate(String devModel, String deviceCode, String attrKey,
        Map<String, String> kvMap, Map<String, EmsObjAttrValue> dbAttrMap, Map<String, CodesVal> retMap) {
        EmsObjAttrValue dbAttr = dbAttrMap.get(attrKey);
        String pointId = kvMap.get(String.format("%s.%s", deviceCode, attrKey));

        if (null != dbAttr && retMap.containsKey(pointId)) {
            CodesVal codesVal = retMap.get(pointId);

            if (!StringUtils.equals(dbAttr.getAttrValue(), codesVal.getValue())) {
                objAttrValueService.updateObjAttrValue(devModel, deviceCode, attrKey, codesVal.getValue());
            }
        }
        else {
            if (null != pointId && retMap.containsKey(pointId)) {
                CodesVal codesVal = retMap.get(pointId);
                EmsObjAttrValue attr = new EmsObjAttrValue(deviceCode, devModel, attrKey, codesVal.getValue());
                objAttrValueService.mergeObjAttrValue(attr);
            }
        }
    }

    private void baDevTimeStatueCheckAndUpdate(String devModel, String deviceCode, String attrKey,
        Map<String, String> kvMap, Map<String, EmsObjAttrValue> dbAttrMap, Map<String, CodesVal> retMap) {
        EmsObjAttrValue dbAttr = dbAttrMap.get(attrKey);
        String pointIdStr = kvMap.get(String.format("%s.%s", deviceCode, attrKey));

        if (StringUtils.isNotEmpty(pointIdStr)) {
            String[] array = StringUtils.split(pointIdStr, ",");
            String value = "0";

            // 定时开-小时
            CodesVal codesVal = retMap.get(array[0]);

            if (null != codesVal && StringUtils.equals(codesVal.getValue(), "1")) {
                value = "160";
            }

            codesVal = retMap.get(array[1]);

            if (null != codesVal && StringUtils.equals(codesVal.getValue(), "1")) {
                value = "101";
            }

            codesVal = retMap.get(array[2]);

            if (null != codesVal && StringUtils.equals(codesVal.getValue(), "1")) {
                value = "260";
            }

            codesVal = retMap.get(array[3]);

            if (null != codesVal && StringUtils.equals(codesVal.getValue(), "1")) {
                value = "201";
            }

            if (null != dbAttr && !StringUtils.equals(dbAttr.getAttrValue(), value)) {
                objAttrValueService.updateObjAttrValue(devModel, deviceCode, attrKey, value);
            }
            else if (null == dbAttr) {
                EmsObjAttrValue attr = new EmsObjAttrValue(deviceCode, devModel, attrKey, value);
                objAttrValueService.mergeObjAttrValue(attr);
            }
        }
    }

    private void updateMeterDeviceAttrList(String gwDevieCode, JSONArray subDevs, List<CodesVal> retList) {
        try {
            // 将新采集数据转换为Map
            Map<String, CodesVal> codesValMap = retList.stream()
                .collect(Collectors.toMap(CodesVal::getPointId, Function.identity()));

            for (Object subDev : subDevs) {
                JSONObject subDevObj = (JSONObject) subDev;
                String pointId = subDevObj.getString("deviceCode");
                String modelCode = subDevObj.getString("modelCode");

                CodesVal syncCodesVal = codesValMap.get(pointId);

                if (null != syncCodesVal) {
                    EmsObjAttrValue dbAttr = objAttrValueService.selectObjAttrValue(modelCode, pointId, "value");

                    // 数据验证和处理
                    DataValidationResult validationResult = validateMeterData(gwDevieCode, pointId,
                        dbAttr.getAttrValue(), syncCodesVal.getValue());

                    if (validationResult.isValid()) {
                        // 更新缓存中的数据
                        String hKey = NEW_HOUR_READING + "-" + pointId;
                        redisService.setCacheMapValue(gwDevieCode, hKey, validationResult.getValidValue());

                        if (!StringUtils.equals(validationResult.getValidValue(), dbAttr.getAttrValue())) {
                            objAttrValueService.updateObjAttrValue(modelCode, pointId, "value",
                                validationResult.getValidValue());
                        }
                    }
                    else {
                        // 记录异常数据但不更新
                        log.warn("检测到异常抄表数据 - 设备:{}, 测点:{}, 旧值:{}, 新值:{}, 原因:{}",
                            dbAttr.getObjCode(), pointId, dbAttr.getAttrValue(), syncCodesVal.getValue(),
                            validationResult.getReason());
                    }
                }
            }
        }
        catch (Exception e) {
            log.error("更新设备属性异常", e);
        }
    }

    /**
     * 能耗数据抄报（扩展支持市电计量）
     */
    @Override
    public int meterHourProd() {
        int cnt = 0;

        try {
            // 计量设备-网关设备映射集合
            Map<String, EmsDevice> refParentMap = getRefParentMap();

            // 电表
            List<MeterDevice> meterDeviceList = getMeterDeviceList(METER_MODEL_E);

            // 计算电表小时数据
            if (CollectionUtils.isNotEmpty(meterDeviceList)) {
                Map<String, Price> priceMap = new ConcurrentHashMap<>();
                Date date = DateUtils.adjustHour(new Date(), -1);
                List<ElecMeterH> elecMeterHList = new ArrayList<>();

                for (MeterDevice meterDevice : meterDeviceList) {
                    EmsDevice gwDevice = refParentMap.get(meterDevice.getDeviceCode());
                    ElecMeterH elecMeterH = workElecMeterReading(gwDevice, meterDevice);

                    if (null != elecMeterH) {
                        Price price = priceMap.computeIfAbsent(gwDevice.getAreaCode(),
                            k -> priceService.getElecHourPrice(gwDevice.getAreaCode(), date));
                        completeElecPrice(elecMeterH, price);
                        elecMeterHList.add(elecMeterH);
                    }
                }

                if (CollectionUtils.isNotEmpty(elecMeterHList)) {
                    elecMeterHService.insertBatch(elecMeterHList);
                    cnt += elecMeterHList.size();
                    generatePgSupplyData(elecMeterHList);
                }
            }

            // 水表
            meterDeviceList = getMeterDeviceList(METER_MODEL_W);

            // 计算水表小时数据
            if (CollectionUtils.isNotEmpty(meterDeviceList)) {
                Map<String, FdEnergyPriceConfig> priceMap = new ConcurrentHashMap<>();
                List<WaterMeterH> waterMeterHList = new ArrayList<>();

                for (MeterDevice meterDevice : meterDeviceList) {
                    EmsDevice gwDevice = refParentMap.get(meterDevice.getDeviceCode());
                    WaterMeterH waterMeterH = workWaterMeterReading(gwDevice, meterDevice);

                    if (null != waterMeterH) {
                        FdEnergyPriceConfig price = priceMap.computeIfAbsent(gwDevice.getAreaCode(),
                            k -> fdEnergyPriceConfigService.selectByAreaCode(gwDevice.getAreaCode(), 70));
                        completeWaterPrice(waterMeterH, price);
                        waterMeterHList.add(waterMeterH);
                    }
                }

                if (CollectionUtils.isNotEmpty(waterMeterHList)) {
                    waterMeterHService.insertBatch(waterMeterHList);
                    cnt += waterMeterHList.size();

                }
            }
        }
        catch (Exception e) {
            log.error("能耗数据抄报异常", e);
        }

        return cnt;
    }

    private Map<String, EmsDevice> getRefParentMap() {
        Map<String, EmsDevice> refParentMap = new HashMap<>();

        List<EmsObjAttrValue> attrValues = objAttrValueService.selectByAttrKey(GATEWAY_MODEL, "subDev");
        Map<String, String> attrValueMap = attrValues.stream()
            .collect(Collectors.toMap(EmsObjAttrValue::getObjCode, EmsObjAttrValue::getAttrValue));

        // 网关设备列表
        List<EmsDevice> gwDevices = getDeviceList(GATEWAY_MODEL);

        if (CollectionUtils.isNotEmpty(gwDevices)) {
            for (EmsDevice gwDevice : gwDevices) {
                String subDevStr = attrValueMap.get(gwDevice.getDeviceCode());
                if (StringUtils.isNotBlank(subDevStr)) {
                    JSONArray jsonArray = JSON.parseArray(subDevStr);
                    jsonArray.forEach(obj -> {
                        JSONObject jsonObject = (JSONObject) obj;
                        refParentMap.put(jsonObject.getString("deviceCode"), gwDevice);
                    });
                }
            }
        }

        return refParentMap;
    }

    /**
     * 修改后的单设备计量产出方法，返回详细结果
     */

    private ElecMeterH workElecMeterReading(EmsDevice gwDevice, MeterDevice meterDevice) {
        ElecMeterH elecMeterH = null;

        // 读取最新抄表值
        String newMeterReading = redisService.getCacheMapValue(gwDevice.getDeviceCode(),
            NEW_HOUR_READING + "-" + meterDevice.getDeviceCode());

        if (null != newMeterReading) {
            String lastCacheKey = LAST_HOUR_READING + "-" + meterDevice.getDeviceCode();

            // 读取历史抄表
            String lastMeterReading = redisService.getCacheMapValue(gwDevice.getDeviceCode(), lastCacheKey);

            elecMeterH = getElecMeterH(gwDevice.getDeviceCode(), meterDevice, lastMeterReading, newMeterReading);
        }

        return elecMeterH;
    }

    @Override
    public ElecMeterH getElecMeterH(String deviceCode, MeterDevice mDevice, String lastMeterReading,
        String newMeterReading) {
        ElecMeterH elecMeterH = null;
        String cacheKey = LAST_HOUR_READING + "-" + mDevice.getDeviceCode();
        String timestampKey = LAST_HOUR_TIMESTAMP + "-" + mDevice.getDeviceCode();

        // 获取缓存时间戳
        String cachedTimestamp = redisService.getCacheMapValue(deviceCode, timestampKey);
        boolean shouldUseCache = false;

        if (StringUtils.isNotEmpty(lastMeterReading) && StringUtils.isNotEmpty(cachedTimestamp)) {
            try {
                long lastUpdateTime = Long.parseLong(cachedTimestamp);
                long currentTime = System.currentTimeMillis();
                // 只有当缓存数据在2小时内才使用
                shouldUseCache = (currentTime - lastUpdateTime) <= MAX_CACHE_AGE_MILLIS;
            }
            catch (NumberFormatException e) {
                log.warn("无效的时间戳缓存: {}", cachedTimestamp);
            }
        }

        if (shouldUseCache) {
            // 缓存有效，使用缓存计算
            elecMeterH = execElecHourMeter(mDevice, lastMeterReading, newMeterReading);
            // 更新缓存和时间戳
            updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
            redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
        }
        else {
            // 缓存过期或无效，查询数据库
            ElecMeterH dbElecMeterH = elecMeterHService.selectLatelyItem(mDevice.getDeviceCode());

            if (null != dbElecMeterH && null != dbElecMeterH.getMeterReading()) {
                // 验证数据库记录是否为上一小时
                Date currentHour = DateUtils.adjustHour(new Date(), -1);
                long timeDiff = currentHour.getTime() - dbElecMeterH.getRecordTime().getTime();

                // 只有时间差在1小时左右（容差30分钟）才计算
                if (Math.abs(timeDiff) <= 30 * 60 * 1000) {
                    elecMeterH = execElecHourMeter(mDevice, String.valueOf(dbElecMeterH.getMeterReading()),
                        newMeterReading);
                }
                else {
                    log.warn("数据时间差过大，跳过计算 - 设备:{}, 时间差:{}小时", mDevice.getDeviceCode(),
                        timeDiff / (60 * 60 * 1000));
                }

                // 无论是否计算，都更新缓存为当前值，作为下次计算基准
                updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
                redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
            }
            else {
                // 首次上报，仅更新缓存
                if (StringUtils.isNotEmpty(newMeterReading)) {
                    updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
                    redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
                }
            }
        }

        return elecMeterH;
    }

    private WaterMeterH workWaterMeterReading(EmsDevice gwDevice, MeterDevice meterDevice) {
        WaterMeterH waterMeterH = null;

        // 读取最新抄表值
        String newMeterReading = redisService.getCacheMapValue(gwDevice.getDeviceCode(),
            NEW_HOUR_READING + "-" + meterDevice.getDeviceCode());

        if (null != newMeterReading) {
            String lastCacheKey = LAST_HOUR_READING + "-" + meterDevice.getDeviceCode();

            // 读取历史抄表
            String lastMeterReading = redisService.getCacheMapValue(gwDevice.getDeviceCode(), lastCacheKey);

            // 组装水表抄报数据
            waterMeterH = getWaterMeterH(gwDevice.getDeviceCode(), meterDevice, lastMeterReading, newMeterReading);
        }

        return waterMeterH;
    }

    @Override
    public WaterMeterH getWaterMeterH(String deviceCode, MeterDevice mDevice, String lastMeterReading,
        String newMeterReading) {
        WaterMeterH waterMeterH = null;
        String cacheKey = LAST_HOUR_READING + "-" + mDevice.getDeviceCode();
        String timestampKey = LAST_HOUR_TIMESTAMP + "-" + mDevice.getDeviceCode();

        // 获取缓存时间戳
        String cachedTimestamp = redisService.getCacheMapValue(deviceCode, timestampKey);
        boolean shouldUseCache = false;

        if (StringUtils.isNotEmpty(lastMeterReading) && StringUtils.isNotEmpty(cachedTimestamp)) {
            try {
                long lastUpdateTime = Long.parseLong(cachedTimestamp);
                long currentTime = System.currentTimeMillis();
                // 只有当缓存数据在2小时内才使用
                shouldUseCache = (currentTime - lastUpdateTime) <= MAX_CACHE_AGE_MILLIS;

                if (!shouldUseCache) {
                    log.warn("水表缓存数据过期 - 设备:{}, 缓存时长:{}小时", mDevice.getDeviceCode(),
                        (currentTime - lastUpdateTime) / (60 * 60 * 1000));
                }
            }
            catch (NumberFormatException e) {
                log.warn("无效的时间戳缓存: {}", cachedTimestamp);
            }
        }

        if (shouldUseCache) {
            // 缓存有效，使用缓存计算
            waterMeterH = execWaterHourMeter(mDevice, lastMeterReading, newMeterReading);
            if (waterMeterH != null) {
                // 只有成功计算后才更新缓存
                updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
                redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
            }
        }
        else {
            // 缓存过期或无效，查询数据库
            WaterMeterH dbWaterMeterH = waterMeterHService.selectLatelyItem(mDevice.getDeviceCode());

            if (null != dbWaterMeterH && null != dbWaterMeterH.getMeterReading()) {
                // 验证数据库记录是否为上一小时
                Date currentHour = DateUtils.adjustHour(new Date(), -1);
                long timeDiff = currentHour.getTime() - dbWaterMeterH.getRecordTime().getTime();

                // 只有时间差在1小时左右（容差30分钟）才计算
                if (Math.abs(timeDiff) <= 30 * 60 * 1000) {
                    waterMeterH = execWaterHourMeter(mDevice, String.valueOf(dbWaterMeterH.getMeterReading()),
                        newMeterReading);
                }
                else {
                    log.warn("水表数据时间差过大，跳过计算 - 设备:{}, 时间差:{}小时", mDevice.getDeviceCode(),
                        timeDiff / (60 * 60 * 1000));
                }
            }

            // 无论是否计算成功，都更新缓存为当前值，作为下次计算基准
            if (StringUtils.isNotEmpty(newMeterReading)) {
                updateCacheAfterSuccess(deviceCode, cacheKey, newMeterReading);
                redisService.setCacheMapValue(deviceCode, timestampKey, String.valueOf(System.currentTimeMillis()));
            }
        }

        return waterMeterH;
    }

    /**
     * 产出市电供应计量数据（优化版）
     */
    private void generatePgSupplyData(List<ElecMeterH> allElecMeterHList) {
        try {
            // 生成市电供应数据
            List<ElecPgSupplyH> pgSupplyHList = new ArrayList<>();

            // 获取所有区域的市电设施及其绑定的计量设备
            Set<String> areaCodeSet = allElecMeterHList.stream().map(ElecMeterH::getAreaCode)
                .collect(Collectors.toSet());

            Map<String, Price> priceMap = new ConcurrentHashMap<>();
            Date recordTime = DateUtils.adjustHour(new Date(), -1);

            // 按区域查询市电设施
            for (String areaCode : areaCodeSet) {
                // 查询区域下市电的供应设施（同时获取了设施绑定的表计）
                List<BoundaryObj> facsMeterBinds = facsService.getFacsWithMeterDev(areaCode, "W", "W2", 45);

                if (CollectionUtils.isNotEmpty(facsMeterBinds)) {
                    for (BoundaryObj facsMeterBind : facsMeterBinds) {
                        // 过滤出当前设施绑定表计对应的小时计量数据
                        List<ElecMeterH> subElecMeterHs = filterBindElecMeterDevs(facsMeterBind, allElecMeterHList);

                        if (CollectionUtils.isNotEmpty(subElecMeterHs)) {
                            // 直接累加同一设施下的所有电表数据（同一小时电价类型相同）
                            double totalQuantity = subElecMeterHs.stream().mapToDouble(ElecMeterH::getElecQuantity)
                                .sum();

                            double totalCost = subElecMeterHs.stream().mapToDouble(ElecMeterH::getUseElecCost).sum();

                            // 精度控制：用量保留1位小数，金额保留2位小数
                            totalQuantity = formatToDecimalPlaces(totalQuantity, 1);
                            totalCost = formatToDecimalPlaces(totalCost, 2);

                            // 获取电价信息（同一小时所有表计的电价类型和单价都相同，取第一个即可）
                            ElecMeterH firstMeter = subElecMeterHs.get(0);
                            Integer meterType = firstMeter.getMeterType();
                            Double unitPrice = firstMeter.getMeterUnitPrice();

                            // 创建市电供应记录
                            ElecPgSupplyH pgSupplyH = new ElecPgSupplyH();
                            pgSupplyH.setAreaCode(areaCode);
                            pgSupplyH.setFacsCode(facsMeterBind.getObjCode());
                            pgSupplyH.setRecordTime(recordTime);
                            pgSupplyH.setDate(recordTime);
                            pgSupplyH.setTime(recordTime);
                            pgSupplyH.setTimeIndex(getHourIndex(recordTime));
                            pgSupplyH.setMeterType(meterType);
                            pgSupplyH.setMeterUnitPrice(unitPrice);
                            pgSupplyH.setUseElecQuantity(totalQuantity);
                            pgSupplyH.setUseElecCost(totalCost);
                            pgSupplyHList.add(pgSupplyH);

                        }
                        else {
                            Price price = priceMap.computeIfAbsent(areaCode,
                                k -> priceService.getElecHourPrice(areaCode, recordTime));

                            ElecPgSupplyH pgSupplyH = new ElecPgSupplyH();
                            pgSupplyH.setAreaCode(areaCode);
                            pgSupplyH.setFacsCode(facsMeterBind.getObjCode());
                            pgSupplyH.setRecordTime(recordTime);
                            pgSupplyH.setDate(recordTime);
                            pgSupplyH.setTime(recordTime);
                            pgSupplyH.setTimeIndex(getHourIndex(recordTime));
                            pgSupplyH.setMeterType(price.getMeterType());
                            pgSupplyH.setMeterUnitPrice(price.getPriceValue());
                            pgSupplyH.setUseElecQuantity(0.0);
                            pgSupplyH.setUseElecCost(0.0);
                            pgSupplyHList.add(pgSupplyH);
                        }
                    }
                }
                else {
                    log.warn("未找到市电设施配置，无法产出市电供应数据");
                }
            }

            if (CollectionUtils.isNotEmpty(pgSupplyHList)) {
                pgSupplyHService.insertBatch(pgSupplyHList);
            }
        }
        catch (Exception e) {
            log.error("产出市电供应计量数据异常", e);
        }
    }

    private List<ElecMeterH> filterBindElecMeterDevs(BoundaryObj facsMeterBind, List<ElecMeterH> allElecMeterHList) {
        Set<String> bindElecMeterDevs = facsMeterBind.getBindMeterDevs();
        if (CollectionUtils.isEmpty(bindElecMeterDevs)) {
            return new ArrayList<>();
        }
        return allElecMeterHList.stream().filter(elecMeterH -> bindElecMeterDevs.contains(elecMeterH.getDeviceCode()))
            .collect(Collectors.toList());
    }

    /**
     * 验证抄表数据的合理性
     */
    private DataValidationResult validateMeterData(String deviceCode, String pointId, String oldValue,
        String newValue) {
        try {
            // 新数据为空或无效
            if (StringUtils.isBlank(newValue)) {
                return DataValidationResult.invalid("新值为空");
            }

            double newVal = Double.parseDouble(newValue);

            // 首次数据，直接接受
            if (StringUtils.isBlank(oldValue)) {
                return DataValidationResult.valid(newValue);
            }

            double oldVal = Double.parseDouble(oldValue);
            double difference = newVal - oldVal;

            // 1. 检查是否为明显的向后跳变（新值比旧值小很多）
            if (difference < MIN_REASONABLE_DECREASE_RATE) {
                // 检查是否可能是表计重置（新值很小，旧值很大）
                if (newVal < 100 && oldVal > 10000) {
                    log.info("检测到可能的表计重置 - 设备:{}, 测点:{}, 旧值:{}, 新值:{}", deviceCode, pointId, oldVal,
                        newVal);
                    // 表计重置的情况，需要人工确认，暂时不更新
                    return DataValidationResult.invalid("疑似表计重置，需人工确认");
                }
                else {
                    return DataValidationResult.invalid(String.format("数据向后跳变，差值:%.2f", difference));
                }
            }

            // 2. 检查是否增长过快（可能是数据错误）
            if (difference > MAX_HOUR_INCREASE_RATE) {
                return DataValidationResult.invalid(String.format("增长过快，差值:%.2f", difference));
            }

            // 3. 连续异常数据确认机制
            if (Math.abs(difference) > MAX_HOUR_INCREASE_RATE * 0.1) { // 超过正常增长的10%认为需要关注
                String abnormalKey = "ABNORMAL_COUNT_" + pointId;
                String countStr = redisService.getCacheMapValue(deviceCode, abnormalKey);
                int abnormalCount = StringUtils.isNotBlank(countStr) ? Integer.parseInt(countStr) : 0;

                abnormalCount++;
                redisService.setCacheMapValue(deviceCode, abnormalKey, String.valueOf(abnormalCount));

                // 如果连续异常次数太多，需要告警但仍然更新数据
                if (abnormalCount >= ABNORMAL_DATA_CONFIRM_COUNT) {
                    log.warn("连续异常数据 - 设备:{}, 测点:{}, 连续{}次异常, 当前差值:{}", deviceCode, pointId,
                        abnormalCount, difference);
                    // 重置计数
                    redisService.setCacheMapValue(deviceCode, abnormalKey, "0");
                }
            }
            else {
                // 数据正常，清除异常计数
                String abnormalKey = "ABNORMAL_COUNT_" + pointId;
                redisService.setCacheMapValue(deviceCode, abnormalKey, "0");
            }

            return DataValidationResult.valid(newValue);

        }
        catch (NumberFormatException e) {
            return DataValidationResult.invalid("数据格式错误: " + e.getMessage());
        }
    }

    /**
     * 数据验证结果内部类
     */
    private static class DataValidationResult {
        private final boolean valid;

        private final String validValue;

        private final String reason;

        private DataValidationResult(boolean valid, String validValue, String reason) {
            this.valid = valid;
            this.validValue = validValue;
            this.reason = reason;
        }

        public static DataValidationResult valid(String value) {
            return new DataValidationResult(true, value, null);
        }

        public static DataValidationResult invalid(String reason) {
            return new DataValidationResult(false, null, reason);
        }

        public boolean isValid() {
            return valid;
        }

        public String getValidValue() {
            return validValue;
        }

        public String getReason() {
            return reason;
        }
    }
}