package com.njcn.cssystem.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.njcn.access.pojo.dto.NoticeUserDto;
import com.njcn.access.utils.SendMessageUtil;
import com.njcn.csdevice.api.*;
import com.njcn.csdevice.param.IcdBzParam;
import com.njcn.csdevice.pojo.dto.CsLineDTO;
import com.njcn.csdevice.pojo.param.UserDevParam;
import com.njcn.csdevice.pojo.po.CsDeviceUserPO;
import com.njcn.csdevice.pojo.po.RStatIntegrityD;
import com.njcn.csdevice.pojo.po.RStatOnlineRateD;
import com.njcn.csharmonic.api.*;
import com.njcn.csharmonic.param.CsEventUserQueryParam;
import com.njcn.csharmonic.pojo.param.RStatLimitQueryParam;
import com.njcn.csharmonic.pojo.po.*;
import com.njcn.cssystem.pojo.po.CsAlarmSet;
import com.njcn.cssystem.service.ICsAlarmSetService;
import com.njcn.cssystem.service.IDataTaskService;
import com.njcn.cssystem.utils.TimeUtil;
import com.njcn.influx.imapper.PqsCommunicateMapper;
import com.njcn.influx.pojo.po.PqsCommunicate;
import com.njcn.influx.query.InfluxQueryWrapper;
import com.njcn.system.api.DictTreeFeignClient;
import com.njcn.system.enums.DicDataEnum;
import com.njcn.system.pojo.vo.DictTreeVO;
import com.njcn.user.api.AppInfoSetFeignClient;
import com.njcn.user.api.AppUserFeignClient;
import com.njcn.user.api.UserFeignClient;
import com.njcn.user.pojo.po.User;
import com.njcn.user.pojo.po.app.AppInfoSet;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.hutool.core.date.DatePattern.NORM_DATE_PATTERN;


/**
 * @author xy
 */
@Service
@AllArgsConstructor
@Slf4j
public class DataTaskServiceImpl implements IDataTaskService {

    private final CsLineFeignClient csLineFeignClient;
    private final CsDeviceUserFeignClient csDeviceUserFeignClient;
    private final AppUserFeignClient appUserFeignClient;
    private final RStatLimitRateDFeignClient rStatLimitRateDClient;
    private final EventUserFeignClient eventUserFeignClient;
    private final CsHarmonicFeignClient csHarmonicFeignClient;
    @Resource
    private PqsCommunicateMapper pqsCommunicateMapper;
    private final EventFeignClient eventFeignClient;
    private final CsAlarmFeignClient csAlarmFeignClient;
    private final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault());
    private final DictTreeFeignClient dictTreeFeignClient;
    private final UserFeignClient userFeignClient;
    private final AppInfoSetFeignClient appInfoSetFeignClient;
    private final SendMessageUtil sendMessageUtil;
    private final CsCommTerminalFeignClient csCommTerminalFeignClient;
    private final CsHarmonicPlanFeignClient csHarmonicPlanFeignClient;
    private final CsHarmonicPlanLineFeignClient csHarmonicPlanLineFeignClient;
    private final ICsAlarmSetService csAlarmSetService;
    private final OnlineRateFeignClient onlineRateFeignClient;
    private final IntegrityFeignClient integrityClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    //fixme 这边事务没生效，可能后面需要研究下分布式事务
    public void channelHarmonic(String time) {
        List<CsHarmonic> list1 = new ArrayList<>();
        List<CsEventUserPO> list2 = new ArrayList<>();
        //获取所有监测点
        List<CsLineDTO> allLine = csLineFeignClient.getAllLineDetail().getData();
        if (CollUtil.isNotEmpty(allLine)) {
            //过滤物联、在线监测的点
            DictTreeVO portable = dictTreeFeignClient.queryByCode(DicDataEnum.PORTABLE.getCode()).getData();
            if (ObjectUtil.isNotNull(portable)) {
                allLine = allLine.stream().filter(item -> !item.getDeviceType().equals(portable.getId())).collect(Collectors.toList());
            }
            //监测点id集合
            List<String> lineList = allLine.stream().map(CsLineDTO::getLineId).distinct().collect(Collectors.toList());
            //设备id集合
            List<String> devList = allLine.stream().map(CsLineDTO::getDeviceId).distinct().collect(Collectors.toList());
            //获取监测点和设备关系
            Map<String,String> lineDevMap = allLine.stream().collect(Collectors.toMap(CsLineDTO::getLineId,CsLineDTO::getDeviceId));

            //获取设备和用户关系
            UserDevParam param = new UserDevParam();
            param.setList(devList);
            List<CsDeviceUserPO> userList = csDeviceUserFeignClient.getList(param).getData();
            //根据设备id分组 组是subUserId的集合
            Map<String, List<String>> userDevMap = userList.stream().collect(Collectors.groupingBy(
                    CsDeviceUserPO::getDeviceId,
                    Collectors.mapping(
                            CsDeviceUserPO::getSubUserId,
                            Collectors.toList()
                    )
            ));
            //获取管理员信息
            List<User> adminList = appUserFeignClient.getAdminInfo().getData();
            //获取用户id集合
            List<String> userIdList = adminList.stream().map(User::getId).collect(Collectors.toList());

            //获取越限数据
            String date;
            if (StringUtils.isNotBlank(time)) {
                date = time;
            } else {
                date = DateUtil.yesterday().toString(NORM_DATE_PATTERN);
            }
            RStatLimitQueryParam rStatLimitQueryParam = RStatLimitQueryParam.builder().ids(lineList).date(date).endDate(date).build();
            List<RStatLimitRateDPO> limitRates =  rStatLimitRateDClient.monitorIdsGetLimitRateInfo(rStatLimitQueryParam).getData();

            //获取监测点信息
            Map<String, CsLineDTO> lineMap;
            if (CollUtil.isNotEmpty(allLine)) {
                lineMap = allLine.stream().collect(Collectors.toMap(CsLineDTO::getLineId, item -> item));
            } else {
                lineMap = Collections.emptyMap();
            }
            if (CollUtil.isNotEmpty(limitRates)) {
                limitRates.forEach(item->{
                    //新增cs_harmonic数据
                    String id = IdUtil.fastSimpleUUID();
                    CsHarmonic csharmonic = new CsHarmonic();
                    csharmonic.setId(id);
                    csharmonic.setLineId(item.getLineId());
                    csharmonic.setTime(LocalDate.parse(date));
                    //统计间隔
                    Integer lineInterval = 1;
                    if (!Objects.isNull(lineMap.get(item.getLineId())) && lineMap.get(item.getLineId()).getLineInterval() != 0) {
                        lineInterval = lineMap.get(item.getLineId()).getLineInterval();
                    }
                    String tag = buildOverLimitTag(item,lineInterval);
                    if (StringUtils.isNotBlank(tag)) {
                        csharmonic.setTag(buildOverLimitTag(item,lineInterval));
                        list1.add(csharmonic);

                        //根据监测点id获取设备
                        String deviceId = lineDevMap.get(item.getLineId());
                        //根据设备获取用户
                        List<String> userIds = userDevMap.get(deviceId);
                        //添加管理员用户
                        List<String> result = Stream.concat(userIdList.stream(), userIds.stream())
                                .distinct()
                                .collect(Collectors.toList());
                        result.forEach(userId->{
                            //新增cs_event_user数据
                            CsEventUserPO po = new CsEventUserPO();
                            po.setUserId(userId);
                            po.setEventId(id);
                            po.setStatus(0);
                            list2.add(po);
                        });
                    }
                });
            }
        }
        if (CollUtil.isNotEmpty(list1)) {
            //先删除 再录入
            List<CsHarmonic> oldList = csHarmonicFeignClient.queryListByTime(time).getData();
            if (CollUtil.isNotEmpty(oldList)) {
                List<String> ids = oldList.stream().map(CsHarmonic::getId).collect(Collectors.toList());
                csHarmonicFeignClient.deleteListByTime(time);
                eventUserFeignClient.deleteByIds(ids);
            }
            csHarmonicFeignClient.addList(list1);
            eventUserFeignClient.addUserEventList(list2);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    //fixme 这边事务没生效，可能后面需要研究下分布式事务
    public void channelRunAlarm(String time) {
        CsAlarm alarm = new CsAlarm();
        List<CsEventUserPO> list2 = new ArrayList<>();
        //获取所有监测点
        List<CsLineDTO> allLine = csLineFeignClient.getAllLineDetail().getData();
        if (CollUtil.isNotEmpty(allLine)) {
            //过滤物联、在线监测的点
            DictTreeVO portable = dictTreeFeignClient.queryByCode(DicDataEnum.PORTABLE.getCode()).getData();
            if (ObjectUtil.isNotNull(portable)) {
                allLine = allLine.stream().filter(item -> !item.getDeviceType().equals(portable.getId())).collect(Collectors.toList());
            }
            //设备id集合
            List<String> devList = allLine.stream().map(CsLineDTO::getDeviceId).distinct().collect(Collectors.toList());

            //获取设备和用户关系
            UserDevParam param = new UserDevParam();
            param.setList(devList);
            List<CsDeviceUserPO> userList = csDeviceUserFeignClient.getList(param).getData();
            //根据设备id分组 组是subUserId的集合
            Map<String, List<String>> userDevMap = userList.stream().collect(Collectors.groupingBy(
                    CsDeviceUserPO::getDeviceId,
                    Collectors.mapping(
                            CsDeviceUserPO::getSubUserId,
                            Collectors.toList()
                    )
            ));
            //获取管理员信息
            List<User> adminList = appUserFeignClient.getAdminInfo().getData();
            //获取用户id集合
            List<String> userIdList = adminList.stream().map(User::getId).collect(Collectors.toList());

            //获取通讯数据
            String date;
            if (StringUtils.isNotBlank(time)) {
                date = time;
            } else {
                date = DateUtil.yesterday().toString(NORM_DATE_PATTERN);
            }
            Map<String, List<String>> map = getDevCommunicate(devList, date + " 00:00:00", date+ " 23:59:59");

            //获取告警数据
            CsEventUserQueryParam queryParam = new CsEventUserQueryParam();
            queryParam.setTarget(devList);
            queryParam.setStartTime(date + " 00:00:00");
            queryParam.setEndTime(date + " 23:59:59");
            List<CsEventPO> alarmList = eventFeignClient.getDevAlarmList(queryParam).getData();
            Map<String, List<CsEventPO>> alarmMap = alarmList.stream().collect(Collectors.groupingBy(CsEventPO::getDeviceId));

            List<String> l0 = new ArrayList<>();
            List<List<String>> l1 = new ArrayList<>();
            List<List<String>> l2 = new ArrayList<>();
            //step1:构造告警数据
            String id = IdUtil.fastSimpleUUID();
            alarm.setId(id);
            alarm.setTime(LocalDate.parse(date));

            devList.forEach(item->{
                //写入中断数据
                List<String> data1 = map.get(item);
                if (CollUtil.isEmpty(data1)) {
                    //获取最新的一条数据
                    List<PqsCommunicate> latest = getRawDataLatest(item);
                    if (CollUtil.isNotEmpty(latest)) {
                        PqsCommunicate one = latest.get(0);
                        if (one.getType() == 0) {
                            data1 = Collections.singletonList(date + " 00:00:00" + " ~ " + date + " 23:59:59");
                        }
                    }
                }
                //写入告警数据
                List<CsEventPO> data2 = alarmMap.get(item);
                List<String> alarmIds = null;
                if (CollUtil.isNotEmpty(data2)) {
                    alarmIds = data2.stream().map(CsEventPO::getId).collect(Collectors.toList());
                }

                List<String> filteredData1 = new ArrayList<>();
                if (CollUtil.isNotEmpty(data1)) {
                    filteredData1 = filterTimeIntervals(data1, 6);
                }
                //只要有一个不为空，就需要统计
                if (CollUtil.isNotEmpty(filteredData1) || CollUtil.isNotEmpty(alarmIds)) {
                    // 对中断时间数据进行过滤，只保留间隔超过 6 小时的时间段
                    l0.add(item);
                    l1.add(filteredData1 != null && !filteredData1.isEmpty() ? Collections.singletonList(String.join("，", filteredData1)) : null);
                    l2.add(alarmIds);
                }
            });
            alarm.setDevList(String.join(",", l0));
            alarm.setInterruptEvent(new Gson().toJson(l1));
            alarm.setAlarmEvent(new Gson().toJson(l2));
            //step2:用户告警通知
            if (CollUtil.isNotEmpty(l0)) {
                Set<String> userIds = new HashSet<>();
                l0.forEach(item->{
                    List<String> ids = userDevMap.get(item);
                    if (CollUtil.isNotEmpty(ids)) {
                        userIds.addAll(ids);
                    }
                });
                //添加管理员用户
                List<String> result = Stream.concat(userIdList.stream(), userIds.stream())
                        .distinct()
                        .collect(Collectors.toList());
                result.forEach(userId->{
                    //新增cs_event_user数据
                    CsEventUserPO po = new CsEventUserPO();
                    po.setUserId(userId);
                    po.setEventId(id);
                    po.setStatus(0);
                    list2.add(po);
                });
            }
        }
        if (ObjectUtil.isNotNull(alarm)) {
            //先删除 再录入
            List<CsAlarm> oldList = csAlarmFeignClient.queryListByTime(time).getData();
            if (CollUtil.isNotEmpty(oldList)) {
                List<String> ids = oldList.stream().map(CsAlarm::getId).collect(Collectors.toList());
                csAlarmFeignClient.deleteListByTime(time);
                eventUserFeignClient.deleteByIds(ids);
            }
            csAlarmFeignClient.addList(Collections.singletonList(alarm));
            eventUserFeignClient.addUserEventList(list2);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void channelRunDataAlarm(String time) {
        CsAlarm alarm = new CsAlarm();
        List<CsEventUserPO> list2 = new ArrayList<>();
        //获取所有监测点
        List<CsLineDTO> allLine = csLineFeignClient.getAllLineDetail().getData();
        if (CollUtil.isNotEmpty(allLine)) {
            //过滤物联、在线监测的点
            DictTreeVO portable = dictTreeFeignClient.queryByCode(DicDataEnum.PORTABLE.getCode()).getData();
            if (ObjectUtil.isNotNull(portable)) {
                allLine = allLine.stream().filter(item -> !item.getDeviceType().equals(portable.getId())).collect(Collectors.toList());
            }
            //设备id集合
            List<String> devList = allLine.stream().map(CsLineDTO::getDeviceId).distinct().collect(Collectors.toList());

            //获取设备和用户关系
            UserDevParam param = new UserDevParam();
            param.setList(devList);
            List<CsDeviceUserPO> userList = csDeviceUserFeignClient.getList(param).getData();
            //根据设备id分组 组是subUserId的集合
            Map<String, List<String>> userDevMap = userList.stream().collect(Collectors.groupingBy(
                    CsDeviceUserPO::getDeviceId,
                    Collectors.mapping(
                            CsDeviceUserPO::getSubUserId,
                            Collectors.toList()
                    )
            ));
            //获取管理员信息
            List<User> adminList = appUserFeignClient.getAdminInfo().getData();
            //获取用户id集合
            List<String> userIdList = adminList.stream().map(User::getId).collect(Collectors.toList());

            //获取时间
            String date;
            if (StringUtils.isNotBlank(time)) {
                date = time;
            } else {
                date = DateUtil.yesterday().toString(NORM_DATE_PATTERN);
            }
            //获取在线率、完整性阈值
            CsAlarmSet csAlarmSet = csAlarmSetService.getEnabledConfig();
            //获取当日的在线率数据
            IcdBzParam param1 = new IcdBzParam();
            param1.setStartTime(date);
            param1.setEndTime(date);
            param1.setLineList(devList);
            List<RStatOnlineRateD> onlineRateList = onlineRateFeignClient.list(param1).getData();
            //获取当日的完整性数据
            List<String> lineList = allLine.stream().map(CsLineDTO::getLineId).distinct().collect(Collectors.toList());
            param1.setLineList(lineList);
            List<RStatIntegrityD> integrityList = integrityClient.list(param1).getData();

            // 构建告警数据
            List<CsAlarmData> alarmDataList = buildAlarmData(devList, onlineRateList, integrityList, allLine, csAlarmSet);
            Map<String,CsAlarmData> alarmDataMap = alarmDataList.stream().collect(Collectors.toMap(CsAlarmData::getDeviceId, item -> item));

            //获取告警数据
            CsEventUserQueryParam queryParam = new CsEventUserQueryParam();
            queryParam.setTarget(devList);
            queryParam.setStartTime(date + " 00:00:00");
            queryParam.setEndTime(date + " 23:59:59");
            List<CsEventPO> alarmList = eventFeignClient.getDevAlarmList(queryParam).getData();
            Map<String, List<CsEventPO>> alarmMap = alarmList.stream().collect(Collectors.groupingBy(CsEventPO::getDeviceId));

            List<String> l0 = new ArrayList<>();
            List<CsAlarmData> l1 = new ArrayList<>();
            List<List<String>> l2 = new ArrayList<>();
            //step1:构造告警数据
            String id = IdUtil.fastSimpleUUID();
            alarm.setId(id);
            alarm.setTime(LocalDate.parse(date));

            devList.forEach(item->{
                //写入中断数据
                CsAlarmData data1 = alarmDataMap.get(item);
                //写入告警数据
                List<CsEventPO> data2 = alarmMap.get(item);
                List<String> alarmIds = null;
                if (CollUtil.isNotEmpty(data2)) {
                    alarmIds = data2.stream().map(CsEventPO::getId).collect(Collectors.toList());
                }
                //只要有一个不为空，就需要统计
                if (Objects.nonNull(data1) || CollUtil.isNotEmpty(alarmIds)) {
                    // 对中断时间数据进行过滤，只保留间隔超过 6 小时的时间段
                    l0.add(item);
                    l1.add(data1);
                    l2.add(alarmIds);
                }
            });
            alarm.setDevList(String.join(",", l0));
            alarm.setInterruptEvent(new Gson().toJson(l1));
            alarm.setAlarmEvent(new Gson().toJson(l2));
            //step2:用户告警通知
            if (CollUtil.isNotEmpty(l0)) {
                Set<String> userIds = new HashSet<>();
                l0.forEach(item->{
                    List<String> ids = userDevMap.get(item);
                    if (CollUtil.isNotEmpty(ids)) {
                        userIds.addAll(ids);
                    }
                });
                //添加管理员用户
                List<String> result = Stream.concat(userIdList.stream(), userIds.stream())
                        .distinct()
                        .collect(Collectors.toList());
                result.forEach(userId->{
                    //新增cs_event_user数据
                    CsEventUserPO po = new CsEventUserPO();
                    po.setUserId(userId);
                    po.setEventId(id);
                    po.setStatus(0);
                    list2.add(po);
                });
            }
        }
        if (ObjectUtil.isNotNull(alarm)) {
            //先删除 再录入
            List<CsAlarm> oldList = csAlarmFeignClient.queryListByTime(time).getData();
            if (CollUtil.isNotEmpty(oldList)) {
                List<String> ids = oldList.stream().map(CsAlarm::getId).collect(Collectors.toList());
                csAlarmFeignClient.deleteListByTime(time);
                eventUserFeignClient.deleteByIds(ids);
            }
            csAlarmFeignClient.addList(Collections.singletonList(alarm));
            eventUserFeignClient.addUserEventList(list2);
        }
    }

    @Override
    public void pushAppMsg(String time) {
        //查询所有用户-设备关系
        List<CsDeviceUserPO> relationList = csDeviceUserFeignClient.getList(new UserDevParam()).getData();
        if (CollectionUtil.isNotEmpty(relationList)) {
            //根据时间获取稳态事件
            List<CsHarmonic> harmonicList = csHarmonicFeignClient.queryListByTime(time).getData();
            //根据时间获取告警事件
            List<CsAlarm> alarmList = csAlarmFeignClient.queryListByTime(time).getData();

            //note 先处理管理员，管理员推送所有设备告警
            List<User> adminAllUser = appUserFeignClient.getAdminInfo().getData();
            List<User> adminUser = adminAllUser.stream()
                    .filter(item->item.getDevCode() != null)
                    .filter(item-> !Objects.equals(item.getDevCode(), ""))
                    .collect(Collectors.toList());
            //获取管理员消息推送打开的情况
            if (CollectionUtil.isNotEmpty(adminUser)) {
                List<AppInfoSet> appInfoSetList = appInfoSetFeignClient.getListById(
                        adminUser.stream().map(User::getId).collect(Collectors.toList())).getData();
                if (CollectionUtil.isNotEmpty(appInfoSetList)) {
                    //需要稳态提示的用户
                    List<String> user1 = appInfoSetList.stream()
                            .filter(item->Objects.equals(item.getHarmonicInfo(), 1))
                            .map(AppInfoSet::getUserId)
                            .distinct()
                            .collect(Collectors.toList());
                    //需要暂态提示的用户
                    List<String> user2 = appInfoSetList.stream()
                            .filter(item->Objects.equals(item.getAlarmInfo(), 1))
                            .map(AppInfoSet::getUserId)
                            .distinct()
                            .collect(Collectors.toList());
                    // 在 adminUser 中找到对应的用户，提取 devCode
                    List<String> harmonicDevCodes = adminUser.stream()
                            .filter(user -> user1.contains(user.getId()))
                            .map(User::getDevCode)
                            .distinct()
                            .collect(Collectors.toList());
                    List<String> alarmDevCodes = adminUser.stream()
                            .filter(user -> user2.contains(user.getId()))
                            .map(User::getDevCode)
                            .distinct()
                            .collect(Collectors.toList());

                    //管理员用户不为空，推送稳态消息
                    NoticeUserDto noticeUserDto = new NoticeUserDto();
                    if (CollectionUtil.isNotEmpty(harmonicDevCodes) && CollectionUtil.isNotEmpty(harmonicList)) {
                        NoticeUserDto.Payload payload = new NoticeUserDto.Payload();
                        noticeUserDto.setPushClientId(harmonicDevCodes);
                        noticeUserDto.setTitle("稳态事件");
                        noticeUserDto.setContent(time + "发生稳态事件，点击查看详情");
                        payload.setType(1);
                        payload.setPath("/pages/index/message1?type="+payload.getType());
                        noticeUserDto.setPayload(payload);
                        sendMessageUtil.sendEventToUser(noticeUserDto);
                    }
                    //管理员用户不为空，推送告警消息
                    if (CollectionUtil.isNotEmpty(alarmDevCodes) && CollectionUtil.isNotEmpty(alarmList)) {
                        NoticeUserDto.Payload payload = new NoticeUserDto.Payload();
                        noticeUserDto.setPushClientId(alarmDevCodes);
                        noticeUserDto.setTitle("告警事件");
                        noticeUserDto.setContent(time + "发生告警事件，点击查看详情");
                        payload.setType(2);
                        payload.setPath("/pages/index/message1?type="+payload.getType());
                        noticeUserDto.setPayload(payload);
                        sendMessageUtil.sendEventToUser(noticeUserDto);
                    }
                }
            }

            //note 再处理普通用户，普通用户根据关系来确定哪些设备
            List<String> userIdList = relationList.stream().map(CsDeviceUserPO::getSubUserId).distinct().collect(Collectors.toList());
            List<User> userList = userFeignClient.getUserListByIds(userIdList).getData();
            //获取普通用户 并且 有设备码的用户
            List<User> normalUser = userList.stream()
                    .filter(item->item.getDevCode() != null)
                    .filter(item-> !Objects.equals(item.getDevCode(), ""))
                    .filter(item->item.getType() == 3)
                    .collect(Collectors.toList());
            //获取普通用户消息推送打开的情况
            if (CollectionUtil.isNotEmpty(normalUser)) {
                List<AppInfoSet> appInfoSetList = appInfoSetFeignClient.getListById(
                        normalUser.stream().map(User::getId).collect(Collectors.toList())).getData();
                if (CollectionUtil.isNotEmpty(appInfoSetList)) {
                    //需要稳态提示的用户
                    List<String> user1 = appInfoSetList.stream()
                            .filter(item->Objects.equals(item.getHarmonicInfo(), 1))
                            .map(AppInfoSet::getUserId)
                            .distinct()
                            .collect(Collectors.toList());
                    //需要暂态提示的用户
                    List<String> user2 = appInfoSetList.stream()
                            .filter(item->Objects.equals(item.getAlarmInfo(), 1))
                            .map(AppInfoSet::getUserId)
                            .distinct()
                            .collect(Collectors.toList());
                    // 在 normalUser 中找到对应的用户
                    List<User> harmonicUser = normalUser.stream()
                            .filter(user -> user1.contains(user.getId()))
                            .distinct()
                            .collect(Collectors.toList());
                    List<User> alarmUser = normalUser.stream()
                            .filter(user -> user2.contains(user.getId()))
                            .distinct()
                            .collect(Collectors.toList());

                    //普通用户不为空，推送稳态消息
                    if (CollectionUtil.isNotEmpty(harmonicUser) && CollectionUtil.isNotEmpty(harmonicList)) {
                        harmonicUser.forEach(item->{
                            //根据用户获取监测点
                            List<String> lineIds = csCommTerminalFeignClient.getLineIdsByUser(item.getId()).getData();
                            List<CsHarmonic> harmonicData = harmonicList.stream()
                                    .filter(line -> lineIds.contains(line.getLineId()))
                                    .distinct()
                                    .collect(Collectors.toList());
                            if (CollectionUtil.isNotEmpty(harmonicData)) {
                                NoticeUserDto noticeUserDto = new NoticeUserDto();
                                NoticeUserDto.Payload payload = new NoticeUserDto.Payload();
                                noticeUserDto.setPushClientId(Collections.singletonList(item.getDevCode()));
                                noticeUserDto.setTitle("稳态事件");
                                noticeUserDto.setContent(time + "发生稳态事件，点击查看详情");
                                payload.setType(1);
                                payload.setPath("/pages/index/message1?type="+payload.getType());
                                noticeUserDto.setPayload(payload);
                                sendMessageUtil.sendEventToUser(noticeUserDto);
                            }
                        });
                    }
                    //普通用户不为空，推送告警消息
                    if (CollectionUtil.isNotEmpty(alarmUser) && CollectionUtil.isNotEmpty(alarmList)) {
                        alarmUser.forEach(item->{
                            //根据用户获取设备
                            List<String> devIds = csCommTerminalFeignClient.getDevIdsByUser(item.getId()).getData();
                            List<String> alarmDevIds = Arrays.asList(alarmList.get(0).getDevList().split(","));
                            //是否存在交集
                            boolean hasMatch = !Collections.disjoint(devIds, alarmDevIds);
                            if (hasMatch) {
                                NoticeUserDto noticeUserDto = new NoticeUserDto();
                                NoticeUserDto.Payload payload = new NoticeUserDto.Payload();
                                noticeUserDto.setPushClientId(Collections.singletonList(item.getDevCode()));
                                noticeUserDto.setTitle("告警事件");
                                noticeUserDto.setContent(time + "发生告警事件，点击查看详情");
                                payload.setType(2);
                                payload.setPath("/pages/index/message1?type="+payload.getType());
                                noticeUserDto.setPayload(payload);
                                sendMessageUtil.sendEventToUser(noticeUserDto);
                            }
                        });
                    }
                }
            }
        }
    }

    /**
     * 构建告警数据
     */
    private List<CsAlarmData> buildAlarmData(List<String> devList,
                                             List<RStatOnlineRateD> onlineRateList,
                                             List<RStatIntegrityD> integrityList,
                                             List<CsLineDTO> allLine,
                                             CsAlarmSet csAlarmSet) {
        List<CsAlarmData> result = new ArrayList<>();

        if (CollUtil.isEmpty(devList)) {
            return result;
        }

        // 获取在线率阈值和完整性阈值
        double onlineRateThreshold = csAlarmSet.getOnlineRateLimit() != null ? csAlarmSet.getOnlineRateLimit().doubleValue() : 60.0;
        double integrityThreshold = csAlarmSet.getIntegrityLimit() != null ? csAlarmSet.getIntegrityLimit().doubleValue() : 90.0;

        // 构建设备ID到监测点列表的映射
        Map<String, List<CsLineDTO>> deviceToLinesMap = allLine.stream()
                .collect(Collectors.groupingBy(CsLineDTO::getDeviceId));

        // 构建监测点ID到监测点信息的映射
        Map<String, CsLineDTO> lineInfoMap = allLine.stream()
                .collect(Collectors.toMap(CsLineDTO::getLineId, line -> line, (k1, k2) -> k1));

        // 将列表转换为Map便于查找（处理null情况）
        Map<String, RStatOnlineRateD> onlineRateMap = CollUtil.isNotEmpty(onlineRateList)
                ? onlineRateList.stream().collect(Collectors.toMap(RStatOnlineRateD::getDevIndex, item -> item, (k1, k2) -> k1))
                : new HashMap<>();

        Map<String, RStatIntegrityD> integrityDataMap = CollUtil.isNotEmpty(integrityList)
                ? integrityList.stream().collect(Collectors.toMap(RStatIntegrityD::getLineIndex, item -> item, (k1, k2) -> k1))
                : new HashMap<>();

        // 遍历每个设备
        for (String deviceId : devList) {
            // 处理在线率数据
            RStatOnlineRateD onlineRateData = onlineRateMap.get(deviceId);

            // 如果为空，默认值为0
            int onlineMin = onlineRateData != null ? onlineRateData.getOnlineMin() : 0;
            double onlineRateValue = Math.round(onlineMin * 100.0 / 1440 * 100.0) / 100.0;

            CsAlarmData.OnlineRateAlarm onlineRateAlarm = new CsAlarmData.OnlineRateAlarm();
            onlineRateAlarm.setValue(onlineRateValue);
            onlineRateAlarm.setThreshold(onlineRateThreshold);
            // value < threshold 时为 true（低于阈值为异常）
            onlineRateAlarm.setIsAbnormal(onlineRateValue < onlineRateThreshold);

            // 处理完整性数据 - 按设备分组
            List<CsLineDTO> deviceLines = deviceToLinesMap.get(deviceId);
            boolean hasIntegrityAbnormal = false;

            // 构建完整性数据结构
            CsAlarmData.IntegrityAlarm integrityAlarm = new CsAlarmData.IntegrityAlarm();
            integrityAlarm.setThreshold(integrityThreshold);

            List<CsAlarmData.IntegrityAlarm.MonitorPointIntegrity> monitorPoints = new ArrayList<>();

            if (CollUtil.isNotEmpty(deviceLines)) {
                // 为该设备下的每个监测点创建完整性数据
                for (CsLineDTO line : deviceLines) {
                    String lineId = line.getLineId();
                    RStatIntegrityD integrityData = integrityDataMap.get(lineId);

                    // 如果为空，默认值为0
                    int dueTime = integrityData != null && integrityData.getDueTime() != null ? integrityData.getDueTime() : 0;
                    int realTime = integrityData != null && integrityData.getRealTime() != null ? integrityData.getRealTime() : 0;
                    double integrityValue = dueTime > 0 ? Math.round(realTime * 100.0 / dueTime * 100.0) / 100.0 : 0.0;

                    CsAlarmData.IntegrityAlarm.MonitorPointIntegrity monitorPoint = new CsAlarmData.IntegrityAlarm.MonitorPointIntegrity();
                    monitorPoint.setMonitorPointId(lineId);
                    monitorPoint.setMonitorName(line.getName());
                    monitorPoint.setValue(integrityValue);
                    // value < threshold 时为 true（低于阈值为异常）
                    Boolean isAbnormal = integrityValue < integrityThreshold;
                    monitorPoint.setIsAbnormal(isAbnormal);

                    if (isAbnormal) {
                        hasIntegrityAbnormal = true;
                    }

                    monitorPoints.add(monitorPoint);
                }
            }

            integrityAlarm.setMonitorPoints(monitorPoints);

            // 只要在线率异常 或 完整性有任意一个监测点异常，就记录该设备的完整数据
            if (onlineRateAlarm.getIsAbnormal() || hasIntegrityAbnormal) {
                CsAlarmData alarmData = new CsAlarmData();
                alarmData.setDeviceId(deviceId);
                alarmData.setOnlineRate(onlineRateAlarm);
                alarmData.setIntegrity(integrityAlarm);
                result.add(alarmData);
            }
        }

        return result;
    }


    /**
     * 过滤时间间隔，只保留超过指定小时数的时间段
     *
     * @param timePeriods 时间段列表，格式如 ["2026-03-18 08:26:24 ~ 2026-03-18 08:27:45", "2026-03-18 08:29:46 ~ 2026-03-18 08:31:07"]
     * @param hours 最小间隔小时数
     * @return 过滤后的时间段列表
     */
    private List<String> filterTimeIntervals(List<String> timePeriods, int hours) {
        if (CollUtil.isEmpty(timePeriods)) {
            return timePeriods;
        }

        List<String> result = new ArrayList<>();

        // 将所有时间段展开为独立的字符串
        for (String periodStr : timePeriods) {
            if (periodStr == null || periodStr.trim().isEmpty()) {
                continue;
            }

            // 分割多个时间段（中文逗号分隔）
            String[] periods = periodStr.split(",");

            for (String period : periods) {
                period = period.trim();
                if (period.isEmpty()) {
                    continue;
                }

                // 解析时间段，格式："2026-03-18 08:26:24 ~ 2026-03-18 08:27:45"
                String[] parts = period.split("~");
                if (parts.length != 2) {
                    continue;
                }

                try {
                    String startTimeStr = parts[0].trim();
                    String endTimeStr = parts[1].trim();

                    LocalDateTime startTime = LocalDateTime.parse(startTimeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                    LocalDateTime endTime = LocalDateTime.parse(endTimeStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

                    // 计算时间间隔（小时）
                    long hoursBetween = ChronoUnit.HOURS.between(startTime, endTime);

                    // 如果间隔超过指定的小时数，则添加到结果中
                    if (hoursBetween >= hours) {
                        result.add(period);
                    }
                } catch (Exception e) {
                    log.warn("解析时间段失败：{}", period, e);
                }
            }
        }

        return result;
    }

    //获取设备最新的一条数据
    public List<PqsCommunicate> getRawDataLatest(String devId) {
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.regular(PqsCommunicate::getDevId, devId)
                .select(PqsCommunicate::getTime)
                .select(PqsCommunicate::getDevId)
                .select(PqsCommunicate::getDescription)
                .select(PqsCommunicate::getType)
                .timeDesc()
                .limit(1);
        return pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
    }


    //获取设备的通讯记录
    public Map<String, List<String>> getDevCommunicate(List<String> devList, String startTime, String endTime) {
        InfluxQueryWrapper influxQueryWrapper = new InfluxQueryWrapper(PqsCommunicate.class);
        influxQueryWrapper.regular(PqsCommunicate::getDevId, devList)
                .select(PqsCommunicate::getTime)
                .select(PqsCommunicate::getDevId)
                .select(PqsCommunicate::getType)
                .between(PqsCommunicate::getTime, startTime, endTime);
        List<PqsCommunicate> pqsCommunicates = pqsCommunicateMapper.selectByQueryWrapper(influxQueryWrapper);
        Map<String, List<String>> result = new HashMap<>();

        if (CollUtil.isNotEmpty(pqsCommunicates)) {
            //这边需求是根据 pqsCommunicates 的数据 按设备来统计设备的掉线时间段，type = 1 是上线 type = 0 是掉线。因为这是按日来统计的，所以需要考虑 0 点 和 24 点的时间段
            //还有个情况就是 如果只有 1 条数据 需要根据是掉线还是上线，来统计掉线时间段；如果是多条数据需要考虑 0 点 和 24 点的时间段

            // 按设备 ID 分组
            Map<String, List<PqsCommunicate>> deviceCommMap = pqsCommunicates.stream()
                    .collect(Collectors.groupingBy(PqsCommunicate::getDevId));
            deviceCommMap.forEach((devId, commList) -> {
                // 按时间排序
                commList.sort(Comparator.comparing(PqsCommunicate::getTime));

                List<String> offlinePeriods = new ArrayList<>();
                String offlineStartTime = null;

                if (commList.size() == 1) {
                    // 只有一条数据的情况
                    PqsCommunicate singleRecord = commList.get(0);
                    if (singleRecord.getType() == 0) {
                        // 掉线：从掉线时间到 24 点
                        String startTime1 = DATE_TIME_FORMATTER.format(singleRecord.getTime());
                        String endTime1 = DATE_TIME_FORMATTER.format(singleRecord.getTime()).substring(0, 10) + " 23:59:59";
                        offlinePeriods.add(startTime1 + " ~ " + endTime1);
                    } else {
                        // 上线：从 0 点到上线时间
                        String startTime1 = DATE_TIME_FORMATTER.format(singleRecord.getTime()).substring(0, 10) + " 00:00:00";
                        String endTime1 = DATE_TIME_FORMATTER.format(singleRecord.getTime());
                        offlinePeriods.add(startTime1 + " ~ " + endTime1);
                    }
                } else {
                    // 多条数据的情况：遍历每相邻两条记录
                    for (int i = 0; i < commList.size(); i++) {
                        PqsCommunicate current = commList.get(i);

                        if (current.getType() == 0) {
                            // 掉线记录：开始计时
                            if (offlineStartTime == null) {
                                offlineStartTime = DATE_TIME_FORMATTER.format(current.getTime());
                            }
                        } else {
                            // 上线记录：结算离线时段
                            if (offlineStartTime != null) {
                                // 有未结算的离线时段，进行结算
                                String offlineEndTime = DATE_TIME_FORMATTER.format(current.getTime());
                                offlinePeriods.add(offlineStartTime + " ~ " + offlineEndTime);
                                offlineStartTime = null;
                            } else {
                                // 没有对应的掉线记录，说明是从 0 点开始掉线到当前上线时间
                                String startTime1 = DATE_TIME_FORMATTER.format(current.getTime()).substring(0, 10) + " 00:00:00";
                                String endTime1 = DATE_TIME_FORMATTER.format(current.getTime());
                                offlinePeriods.add(startTime1 + " ~ " + endTime1);
                            }
                        }

                        // 如果是最后一条记录且仍在离线状态，结算到 24 点
                        if (i == commList.size() - 1 && offlineStartTime != null) {
                            String endTime1 = DATE_TIME_FORMATTER.format(current.getTime()).substring(0, 10) + " 23:59:59";
                            offlinePeriods.add(offlineStartTime + " ~ " + endTime1);
                            offlineStartTime = null;
                        }
                    }
                }
                if (CollUtil.isNotEmpty(offlinePeriods)) {
                    result.put(devId, offlinePeriods);
                }
            });
        }
        return result;
    }


    /**
     * 保存设备掉线时间段
     *
     * @param devId 设备 ID
     * @param startTime 掉线开始时间
     * @param endTime 掉线结束时间
     */
    private void saveOfflinePeriod(String devId, String startTime, String endTime) {
        // TODO: 将掉线时间段保存到数据库或其他存储介质
        log.info("设备 {} 掉线时间段：{} - {}", devId, startTime, endTime);
    }


    /**
     * 构建越限标签字符串
     *
     * @param item 越限统计数据
     * @return 越限标签
     */
    private String buildOverLimitTag(RStatLimitRateDPO item, Integer lineInterval) {
        StringBuilder tagBuilder = new StringBuilder();

        String planId = csHarmonicPlanLineFeignClient.getPlanIdByLineId(item.getLineId()).getData();
        Set<String> targetSet = null;

        if (planId != null) {
            CsHarmonicPlan csHarmonicPlan = csHarmonicPlanFeignClient.getById(planId).getData();
            if (csHarmonicPlan != null) {
                String harmonicTarget = csHarmonicPlan.getHarmonicTarget();
                if (StrUtil.isNotBlank(harmonicTarget)) {
                    targetSet = new HashSet<>(Arrays.asList(harmonicTarget.split(",")));
                }
            }
        }

        if (targetSet != null) {
            Integer freqDevOvertime = item.getFreqDevOvertime();
            if (freqDevOvertime != null && freqDevOvertime > 0 && targetSet.contains("频率偏差")) {
                tagBuilder.append("频率偏差越限").append(TimeUtil.convertMinutes(Math.min(freqDevOvertime * lineInterval,1440))).append("，");
            }

            Integer voltageDevOvertime = item.getVoltageDevOvertime();
            if (voltageDevOvertime != null && voltageDevOvertime > 0 && targetSet.contains("电压偏差")) {
                tagBuilder.append("电压偏差越限").append(TimeUtil.convertMinutes(Math.min(voltageDevOvertime * lineInterval,1440))).append("，");
            }

            Integer ubalanceOvertime = item.getUbalanceOvertime();
            if (ubalanceOvertime != null && ubalanceOvertime > 0 && targetSet.contains("三相电压不平衡度")) {
                tagBuilder.append("三相电压不平衡度越限").append(TimeUtil.convertMinutes(Math.min(ubalanceOvertime * lineInterval,1440))).append("，");
            }

            Integer flickerOvertime = item.getFlickerOvertime();
            if (flickerOvertime != null && flickerOvertime > 0 && targetSet.contains("闪变")) {
                tagBuilder.append("闪变越限").append(TimeUtil.convertMinutes(Math.min(flickerOvertime * 120,1440))).append("，");
            }

            Integer uaberranceOvertime = item.getUaberranceOvertime();
            if (uaberranceOvertime != null && uaberranceOvertime > 0 && targetSet.contains("电压总谐波畸变率")) {
                tagBuilder.append("电压总谐波畸变率越限").append(TimeUtil.convertMinutes(Math.min(uaberranceOvertime * lineInterval,1440))).append("，");
            }

            Integer iNegOvertime = item.getINegOvertime();
            if (iNegOvertime != null && iNegOvertime > 0 && targetSet.contains("负序电流")) {
                tagBuilder.append("负序电流越限").append(TimeUtil.convertMinutes(Math.min(iNegOvertime * lineInterval,1440))).append("，");
            }

            for (int i = 2; i <= 25; i++) {
                String fieldName = "uharm" + i + "Overtime";
                Integer value = (Integer) ReflectUtil.getFieldValue(item, fieldName);
                if (value != null && value > 0) {
                    String targetName = i + "次谐波电压含有率";
                    if (targetSet.contains(targetName)) {
                        tagBuilder.append(i).append("次谐波电压含有率越限").append(TimeUtil.convertMinutes(Math.min(value * lineInterval,1440))).append("，");
                    }
                }
            }

            for (int i = 2; i <= 25; i++) {
                String fieldName = "iharm" + i + "Overtime";
                Integer value = (Integer) ReflectUtil.getFieldValue(item, fieldName);
                if (value != null && value > 0) {
                    String targetName = i + "次谐波电流有效值";
                    if (targetSet.contains(targetName)) {
                        tagBuilder.append(i).append("次谐波电流有效值越限").append(TimeUtil.convertMinutes(Math.min(value * lineInterval,1440))).append("，");
                    }
                }
            }

            for (int i = 1; i <= 16; i++) {
                String fieldName = "inuharm" + i + "Overtime";
                Integer value = (Integer) ReflectUtil.getFieldValue(item, fieldName);
                if (value != null && value > 0) {
                    double harmonicOrder = i * 1.0 - 0.5;
                    String targetName = harmonicOrder + "次间谐波电压含有率";
                    if (targetSet.contains(targetName)) {
                        tagBuilder.append(harmonicOrder).append("次间谐波电压含有率越限").append(TimeUtil.convertMinutes(Math.min(value * lineInterval,1440))).append("，");
                    }
                }
            }
        } else {
            Integer freqDevOvertime = item.getFreqDevOvertime();
            if (freqDevOvertime != null && freqDevOvertime > 0) {
                tagBuilder.append("频率偏差越限").append(TimeUtil.convertMinutes(Math.min(freqDevOvertime * lineInterval,1440))).append("，");
            }

            Integer voltageDevOvertime = item.getVoltageDevOvertime();
            if (voltageDevOvertime != null && voltageDevOvertime > 0) {
                tagBuilder.append("电压偏差越限").append(TimeUtil.convertMinutes(Math.min(voltageDevOvertime * lineInterval,1440))).append("，");
            }

            Integer ubalanceOvertime = item.getUbalanceOvertime();
            if (ubalanceOvertime != null && ubalanceOvertime > 0) {
                tagBuilder.append("三相电压不平衡度越限").append(TimeUtil.convertMinutes(Math.min(ubalanceOvertime * lineInterval,1440))).append("，");
            }

            Integer flickerOvertime = item.getFlickerOvertime();
            if (flickerOvertime != null && flickerOvertime > 0) {
                tagBuilder.append("闪变越限").append(TimeUtil.convertMinutes(Math.min(flickerOvertime * 120,1440))).append("分钟，");
            }

            Integer uaberranceOvertime = item.getUaberranceOvertime();
            if (uaberranceOvertime != null && uaberranceOvertime > 0) {
                tagBuilder.append("电压总谐波畸变率越限").append(TimeUtil.convertMinutes(Math.min(uaberranceOvertime * lineInterval,1440))).append("，");
            }

            Integer iNegOvertime = item.getINegOvertime();
            if (iNegOvertime != null && iNegOvertime > 0) {
                tagBuilder.append("负序电流越限").append(TimeUtil.convertMinutes(Math.min(iNegOvertime * lineInterval,1440))).append("，");
            }

            String harmonicVoltageTag = buildHarmonicVoltageTags(item, "", lineInterval);
            if (StrUtil.isNotBlank(harmonicVoltageTag)) {
                tagBuilder.append(harmonicVoltageTag);
            }

            String harmonicCurrentTag = buildHarmonicCurrentTags(item, "", lineInterval);
            if (StrUtil.isNotBlank(harmonicCurrentTag)) {
                tagBuilder.append(harmonicCurrentTag);
            }

            String interharmonicTag = buildInterharmonicVoltageTags(item, "", lineInterval);
            if (StrUtil.isNotBlank(interharmonicTag)) {
                tagBuilder.append(interharmonicTag);
            }
        }

        return trimTrailingComma(tagBuilder.toString());
    }

    /**
     * 批量构建谐波电压含有率 tag（2-25 次）
     *
     * @param item        数据对象
     * @param originalTag 原始 tag
     * @return 组装后的 tag
     */
    public static String buildHarmonicVoltageTags(Object item, String originalTag, Integer lineInterval) {
        StringBuilder tag = new StringBuilder(originalTag);
        for (int i = 2; i <= 25; i++) {
            String fieldName = "uharm" + i + "Overtime";
            Integer value = (Integer) ReflectUtil.getFieldValue(item, fieldName);
            if (value != null && value > 0) {
                tag.append(i).append("次谐波电压含有率越限").append(TimeUtil.convertMinutes(Math.min(value * lineInterval,1440))).append("，");
            }
        }
        return tag.toString();
    }

    /**
     * 批量构建谐波电流有效值 tag（2-25 次）
     *
     * @param item        数据对象
     * @param originalTag 原始 tag
     * @return 组装后的 tag
     */
    public static String buildHarmonicCurrentTags(Object item, String originalTag, Integer lineInterval) {
        StringBuilder tag = new StringBuilder(originalTag);
        for (int i = 2; i <= 25; i++) {
            String fieldName = "iharm" + i + "Overtime";
            Integer value = (Integer) ReflectUtil.getFieldValue(item, fieldName);
            if (value != null && value > 0) {
                tag.append(i).append("次谐波电流有效值越限").append(TimeUtil.convertMinutes(Math.min(value * lineInterval,1440))).append("，");
            }
        }
        return tag.toString();
    }

    /**
     * 批量构建间谐波电压含有率 tag（0.5-15.5 次）
     *
     * @param item        数据对象
     * @param originalTag 原始 tag
     * @return 组装后的 tag
     */
    public static String buildInterharmonicVoltageTags(Object item, String originalTag, Integer lineInterval) {
        StringBuilder tag = new StringBuilder(originalTag);
        for (int i = 1; i <= 16; i++) {
            String fieldName = "inuharm" + i + "Overtime";
            Integer value = (Integer) ReflectUtil.getFieldValue(item, fieldName);
            if (value != null && value > 0) {
                double harmonicOrder = i * 1.0 - 0.5;
                tag.append(harmonicOrder).append("次间谐波电压含有率越限").append(TimeUtil.convertMinutes(Math.min(value * lineInterval,1440))).append("，");
            }
        }
        return tag.toString();
    }

    /**
     * 去除字符串末尾的逗号
     *
     * @param str 原字符串
     * @return 处理后的字符串
     */
    public static String trimTrailingComma(String str) {
        return StrUtil.endWith(str, "，") ? StrUtil.subPre(str, str.length() - 1) : str;
    }

}
