package com.gunshi.project.hsz.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.gunshi.project.hsz.entity.so.DataQueryCommonSo; import com.gunshi.project.hsz.entity.vo.*; import com.gunshi.project.hsz.mapper.AttResBaseMapper; import com.gunshi.project.hsz.mapper.StZqRMapper; import com.gunshi.project.hsz.mapper.StZqrlBMapper; import com.gunshi.project.hsz.model.*; import com.gunshi.project.hsz.util.DataHandleUtil; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.*; import java.util.stream.Collectors; /** * Description: * Created by wanyan on 2024/2/21 * * @author wanyan * @version 1.0 */ @Service @Slf4j @Transactional(rollbackFor = Exception.class) public class RiverWaterService { @Resource private AttResBaseMapper attRvBaseMapper; @Resource private StZqrlBMapper stZqrlBMapper; @Resource private RainBasinDivisionService rainBasinDivisionService; @Resource private AlarmSetService alarmSetService; @Resource private StRiverRRealService stRiverRRealService; @Resource private StZqRMapper stZqRMapper;//水位-流量关系 public List list() { List attRvBaseVos = attRvBaseMapper.queryWaterListUpAndLow(); if(CollectionUtils.isEmpty(attRvBaseVos)){ return attRvBaseVos; } return attRvBaseVos; } public List get(String stcd) { List attRvBaseVos = attRvBaseMapper.queryWaterListByStcd(stcd); if(CollectionUtils.isEmpty(attRvBaseVos)){ return attRvBaseVos; } return attRvBaseVos; } public List zqrl(String stcd) { LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(StZqrlB::getStcd, stcd).orderByAsc(StZqrlB::getPtno); return stZqrlBMapper.selectList(queryWrapper); } public BigDecimal getQByRz(List zqr,BigDecimal rz){ // 1. 参数校验 if (zqr == null || zqr.isEmpty()) { throw new IllegalArgumentException("水位-流量关系列表不能为空"); } if (rz == null) { throw new IllegalArgumentException("水位值不能为空"); } // 2. 检查边界情况:如果目标水位低于最低水位或高于最高水位 BigDecimal minRz = zqr.get(0).getZ(); BigDecimal maxRz = zqr.get(zqr.size() - 1).getZ(); if (rz.compareTo(minRz) < 0) { // 低于最低水位,返回0流量 return BigDecimal.ZERO; } if (rz.compareTo(maxRz) > 0) { // 高于最高水位,返回0流量 return BigDecimal.ZERO; } // 3. 二分查找水位区间 int l = 0; int r = zqr.size() - 1; while (l <= r) { int mid = l + (r - l) / 2; BigDecimal midRz = zqr.get(mid).getZ(); int compareResult = midRz.compareTo(rz); if (compareResult == 0) { // 找到完全匹配的水位,直接返回对应的流量 return zqr.get(mid).getQ(); } else if (compareResult < 0) { l = mid + 1; } else { r = mid - 1; } } /** * 此时: * l 指向第一个大于rz的位置 * r 指向最后一个小于rz的位置 * 即:r为 (x1, y1) - 下限水位和流量 * l为 (x2, y2) - 上限水位和流量 */ if (l >= zqr.size() || r < 0) { return BigDecimal.ZERO; } // 获取前后相邻的两个点 StZqR lowerPoint = zqr.get(r); StZqR upperPoint = zqr.get(l); // 使用线性插值计算流量 return linearInterpolation( lowerPoint.getZ(), lowerPoint.getQ(), upperPoint.getZ(), upperPoint.getQ(), rz ).setScale(3, RoundingMode.HALF_UP); } /** * 线性插值辅助方法(可以复用之前的) * @param x1 已知点1的水位 * @param y1 已知点1的流量 * @param x2 已知点2的水位 * @param y2 已知点2的流量 * @param x 目标水位 * @return 目标水位对应的流量 */ private BigDecimal linearInterpolation(BigDecimal x1, BigDecimal y1, BigDecimal x2, BigDecimal y2, BigDecimal x) { // 使用公式: y = y1 + ( (y2 - y1) / (x2 - x1) ) * (x - x1) // 计算斜率: (y2 - y1) / (x2 - x1) BigDecimal slope = y2.subtract(y1) .divide(x2.subtract(x1), 10, RoundingMode.HALF_UP); // 计算: slope * (x - x1) BigDecimal xDiff = x.subtract(x1); BigDecimal product = slope.multiply(xDiff); // 计算最终结果: y1 + product return y1.add(product); } public List monitorData(DataQueryCommonSo dataQueryCommonSo) { String stcd = dataQueryCommonSo.getStcd(); //雨量数据 List drpData = attRvBaseMapper.drp(dataQueryCommonSo); //水位数据 List rzData = attRvBaseMapper.rz(dataQueryCommonSo); //获取水位流量关系,算出转换流量 //List zqrl = zqrl(stcd); List zqr = zqr(stcd); if(CollectionUtils.isNotEmpty(zqr)){ rzData.stream().forEach(o->{ o.setQ(getQByRz(zqr,o.getZ())); }); } //根据监测时间合并雨量和水位数据 return bindData(stcd,drpData,rzData); } private List bindData(String stcd, List drpData, List rzData) { HashSet strings = new HashSet<>(); drpData.stream().forEach(v1 -> strings.add(v1.getTm())); rzData.stream().forEach(v1 -> strings.add(v1.getTm())); ArrayList result = new ArrayList<>(); strings.stream().forEach(v1 -> result.add(AttRvMonitorVo.builder().stcd(stcd).tm(v1).build())); List list = result.stream().map(v1 -> { drpData.stream().filter(v2 -> v1.getTm().equals(v2.getTm())).forEach(v2 -> { v1.setDrp(v2.getDrp()); }); rzData.stream().filter(v2 -> v1.getTm().equals(v2.getTm())).forEach(v2 -> { v1.setZ(v2.getZ()); v1.setQ(v2.getQ()); v1.setTq(v2.getTq()); }); return v1; }).collect(Collectors.toList()); return list.stream().sorted(Comparator.comparing(AttRvMonitorVo::getTm).reversed()).collect(Collectors.toList()); } private void calcTqData(List rzData, List zqrl) { BigDecimal maxRz = zqrl.stream().max(Comparator.comparing(StZqrlB::getZ)).get().getZ(); BigDecimal minRz = zqrl.stream().min(Comparator.comparing(StZqrlB::getZ)).get().getZ(); Map map = zqrl.stream().collect(Collectors.toMap(StZqrlB::getZ, StZqrlB::getQ)); List list = zqrl.stream().map(StZqrlB::getZ).collect(Collectors.toList()); for(AttRvMonitorVo vo : rzData){ BigDecimal rz = vo.getZ(); if(rz.compareTo(minRz) < 0 || rz.compareTo(maxRz) > 0){ continue; } vo.setTq(DataHandleUtil.calcData(rz,map,list)); } } public AttRvMonitorDetailVo detail(String stcd) { AttRvMonitorDetailVo vo = new AttRvMonitorDetailVo(); StPptnDetailsVo stPptnDetailsVo = rainBasinDivisionService.queryStPptnDetailsByStcd(stcd); BeanUtils.copyProperties(stPptnDetailsVo,vo,AttRvMonitorDetailVo.class); //最新水位 AttRvMonitorVo monitorVo = attRvBaseMapper.newRz(stcd); if(monitorVo == null){ return vo; } Date tm = monitorVo.getTm(); BigDecimal rz = monitorVo.getZ(); //最新水位时间往前推24小时水位 LocalDateTime now = LocalDateTime.ofInstant(tm.toInstant(), ZoneId.systemDefault()); LocalDateTime dateTime = now.minusDays(1); BigDecimal oldRz = attRvBaseMapper.oldRz(stcd,1,Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant())); if(oldRz != null){ //设置水位涨跌情况 vo.setRzDiff(rz.subtract(oldRz)); } //本年最高水位 BigDecimal maxYearRz = queryYearRzByStcdAndTime(stcd); vo.setMaxRz(maxYearRz); return vo; } private BigDecimal queryYearRzByStcdAndTime(String stcd) { LocalDateTime now = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault()); LocalDateTime startTime; LocalDateTime endTime; if (now.getHour() >= 8 || (now.getHour() < 8 && now.getMonthValue() > 1) || (now.getHour() < 8 && now.getMonthValue() == 1 && now.getDayOfMonth() >= 1)){ startTime = LocalDateTime.of( now.getYear(), 1, 1, 8, 0, 0 ); endTime = now; return attRvBaseMapper.queryRiverMaxRz(stcd,Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()), Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant())); } return new BigDecimal(0); } public AttRiverNowDataVo nowData(String stcd) { AttRiverNowDataVo vo = new AttRiverNowDataVo(); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(AlarmSet::getStcd,stcd); AlarmSet alarmSet = alarmSetService.getBaseMapper().selectOne(queryWrapper);//查询该站点的警告水位配置信息 if(alarmSet == null){ vo.setWaterAlarm(BigDecimal.ZERO); vo.setWaterPromise(BigDecimal.ZERO); }else{ vo.setWaterAlarm(alarmSet.getWarnWaterLevel()); vo.setWaterPromise(alarmSet.getPromiseWaterLevel()); } StPptnRReal stPptnRReal = rainBasinDivisionService.queryStPptnRealByStcd(stcd);//查询该站点的实时降雨 BeanUtils.copyProperties(stPptnRReal,vo); //查询该站点实时水位 StRiverRReal stRiverRReal = stRiverRRealService.getBaseMapper().queryQByStcd(stcd); if(stRiverRReal == null){ return vo; } BigDecimal z = stRiverRReal.getZ() == null ? BigDecimal.ZERO : stRiverRReal.getZ(); vo.setRz(z); vo.setWaterAlarmGap(z.subtract(vo.getWaterAlarm())); vo.setWaterPromiseGap(z.subtract(vo.getWaterPromise())); return vo; } public List zqr(String stcd){ LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(StZqR::getStcd, stcd).orderByAsc(StZqR::getZ); return stZqRMapper.selectList(queryWrapper); } }