package com.gunshi.project.ss.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gunshi.project.ss.entity.so.GateHisPageSo; import com.gunshi.project.ss.entity.vo.AttResBaseVo; import com.gunshi.project.ss.entity.vo.GateStautsVo; import com.gunshi.project.ss.entity.vo.GateValveOplogVo; import com.gunshi.project.ss.mapper.GateValveRealMapper; import com.gunshi.project.ss.mapper.StWaterRRealMapper; import com.gunshi.project.ss.mapper.TyYearRainfallMapper; import com.gunshi.project.ss.model.*; import com.gunshi.project.ss.util.DataHandleUtil; import com.gunshi.project.ss.util.DateUtil; import com.gunshi.project.ss.util.ExcelUtil; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.util.*; import java.util.stream.Collectors; /** * 描述: 闸阀开关表 * author: xusan * date: 2024-07-08 17:30:37 */ @Service @Slf4j @Transactional(rollbackFor = Exception.class) public class GateValveRealService extends ServiceImpl { @Resource private com.gunshi.project.ss.model.GateValveKeyAutoDao gateValveKeyAutoDao; @Resource private com.gunshi.project.ss.model.GateValveOplogAutoDao gateValveOplogAutoDao; @Resource private ReservoirWaterService reservoirWaterService; @Resource private StWaterRRealMapper stWaterRRealMapper; @Resource private TyYearRainfallMapper tyYearRainfallMapper; public List gateStatusList() { List list = baseMapper.gateStatusList(); for(GateStautsVo vo : list){ if(vo.getTm() != null && DateUtil.hoursBetweenDate(vo.getTm(), new Date()) > 2){ vo.setFlag(1); } } return list; } public BigDecimal realQ(String valveCode) { return baseMapper.realQ(valveCode); } public String control(GateValveKey gateValveKey) { //先判断密码是否正确 String valveCode = gateValveKey.getValveCode(); String key = gateValveKey.getKey(); GateValveKey valveKey = gateValveKeyAutoDao.getById(valveCode); if(valveKey == null || !key.equals(valveKey.getKey())){ throw new IllegalArgumentException("密码不正确"); } //生成闸阀操作日志 GateValveOplog oplog = new GateValveOplog(); oplog.setId(IdWorker.getId()); oplog.setStatus(gateValveKey.getStatus()); oplog.setOpContent("设置闸阀开度为"+gateValveKey.getStatus()); oplog.setValveCode(valveCode); oplog.setTm(new Date()); oplog.setOpUserId(1L); oplog.setOpUserName("胡兵"); GateValveReal valveReal = this.getOne(new QueryWrapper().eq("valve_code", valveCode)); oplog.setBeforeStatus(valveReal == null ? "-" : valveReal.getStatus()); gateValveOplogAutoDao.save(oplog); //todo 给闸阀下发调节指令 // GateValveR gateValveR = new GateValveR(); // BeanUtils.copyProperties(gateValveKey,gateValveR); // gateValveR.setTm(new Date()); // gateValveRMapper.insert(gateValveR); // // GateValveReal real = new GateValveReal(); // BeanUtils.copyProperties(gateValveKey,real); // real.setTm(new Date()); // this.remove(new QueryWrapper().eq("valve_code",valveCode)); // this.save(real); return "调节闸阀成功"; } public Page logPage(GateHisPageSo so) { return baseMapper.logPage(so.getPageSo().toPage(),so); } public GateValveOplog loginfo(String code) { if(StringUtils.isBlank(code)){ return null; } LambdaQueryWrapper qw = new LambdaQueryWrapper(); qw.eq(GateValveOplog::getValveCode,code); qw.orderByDesc(GateValveOplog::getTm).last("LIMIT 1"); List list = gateValveOplogAutoDao.list(qw); return list.isEmpty() ? null : list.get(0); } public void logExport(GateHisPageSo so, HttpServletResponse response) { List logList = baseMapper.logList(so); ExcelUtil.exportExcel(logList,"闸阀操作日志",GateValveOplogVo.class,response,"闸阀操作日志"); } @Autowired private StEvpoService stEvpoService; @Autowired private StFlowRService stFlowRService; @Autowired private StWaterRRealService stWaterRRealService; public Map supplyTime(Integer year, Integer month) { Map result = new HashMap<>(); // 获取所有月份的日蒸发量数据 List allEvaporationData = stEvpoService.lambdaQuery().list(); Map evaporationMap = allEvaporationData.stream() .collect(Collectors.toMap(StEvpo::getMonth, StEvpo::getEvaporation)); // 获取水库基础数据 List reservoirList = reservoirWaterService.list(); if (CollectionUtils.isEmpty(reservoirList)) { return result; } AttResBaseVo reservoir = reservoirList.get(0); // 初始可供水量 = 实时库容 - 死库容 (万m³) BigDecimal nowCap = reservoir.getNowCap() == null ? BigDecimal.ZERO : reservoir.getNowCap(); BigDecimal deadCap = reservoir.getDeadCap() == null ? BigDecimal.ZERO : reservoir.getDeadCap(); BigDecimal availableWater = nowCap.subtract(deadCap); Date tm = reservoir.getTm(); Calendar now = Calendar.getInstance(); now.set(Calendar.MINUTE, 0); now.set(Calendar.SECOND, 0); now.set(Calendar.MILLISECOND, 0); Date currentHour = now.getTime(); if(tm.before(currentHour)){ result.put(BigDecimal.ZERO,"当前时间无最新水位数据"); return result; } // 如果初始可供水量就为0或负数,直接返回 if (availableWater.compareTo(BigDecimal.ZERO) <= 0) { result.put(BigDecimal.ZERO, "当前无可供水量"); return result; } // 获取当前流量数据 List waterFlowList = stWaterRRealService.listNewData(); BigDecimal totalFlow = waterFlowList.stream() .map(StWaterRReal::getQ) .reduce(BigDecimal.ZERO, BigDecimal::add); // 计算日供水量 (m³/d) 并转换为万m³/d BigDecimal dailySupply = totalFlow.multiply(new BigDecimal(3600 * 24)); BigDecimal dailySupplyInTenThousand = dailySupply.divide(new BigDecimal(10000), 10, RoundingMode.HALF_UP); // 从当前时间开始计算 Calendar calendar = Calendar.getInstance(); int currentYear = calendar.get(Calendar.YEAR); int currentMonth = calendar.get(Calendar.MONTH) + 1; // 计算当前月份剩余天数 calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); int daysInCurrentMonth = calendar.get(Calendar.DAY_OF_MONTH); int currentDay = Calendar.getInstance().get(Calendar.DAY_OF_MONTH); int remainingDaysInMonth = daysInCurrentMonth - currentDay + 1; BigDecimal totalSupplyDays = BigDecimal.ZERO; BigDecimal remainingWater = availableWater; // 判断是否计算预测水量 boolean calculatePredictWater = (year != null && month != null); // 逐月计算,直到可供水量耗尽 int calcYear = currentYear; int calcMonth = currentMonth; boolean waterExhausted = false; while (remainingWater.compareTo(BigDecimal.ZERO) > 0 && !waterExhausted) { // 获取当前计算月份的蒸发量 String monthKey = String.valueOf(calcMonth); BigDecimal monthlyEvaporation = evaporationMap.getOrDefault(Integer.valueOf(monthKey), BigDecimal.ZERO); // 计算当前月份的预测来水量(只有当传入年份和月份不为空,且当前月份在预测范围内时才计算) BigDecimal monthlyPredictWater = BigDecimal.ZERO; if (calculatePredictWater && isMonthInPredictRange(calcMonth, currentMonth, month)) { monthlyPredictWater = calcMonthlyPredictV(year, calcMonth, reservoir.getWatShedArea()); } // 当前月份总可用水量 = 剩余水量 + 预测来水量 BigDecimal monthlyTotalWater = remainingWater.add(monthlyPredictWater); // 当前月份日消耗量 = 日供水量 + 日蒸发量 (万m³/d) BigDecimal dailyConsumption = dailySupplyInTenThousand.add(monthlyEvaporation); if (dailyConsumption.compareTo(BigDecimal.ZERO) <= 0) { // 如果日消耗量为0或负数,说明可以无限供水 totalSupplyDays = new BigDecimal("9999"); // 表示无限期 break; } // 计算当前月份可供水天数 int daysInMonth = getDaysInMonth(calcYear, calcMonth); int daysToCalculate = (calcYear == currentYear && calcMonth == currentMonth) ? remainingDaysInMonth : daysInMonth; // 当前月份最大可供水量 = 日消耗量 * 当月剩余天数 BigDecimal monthlyMaxConsumption = dailyConsumption.multiply(new BigDecimal(daysToCalculate)); if (monthlyTotalWater.compareTo(monthlyMaxConsumption) >= 0) { // 当前月份水量充足,可以支撑整个月 totalSupplyDays = totalSupplyDays.add(new BigDecimal(daysToCalculate)); remainingWater = monthlyTotalWater.subtract(monthlyMaxConsumption); // 移动到下个月 calcMonth++; if (calcMonth > 12) { calcMonth = 1; calcYear++; } } else { // 当前月份水量不足,计算具体天数 BigDecimal remainingDays = monthlyTotalWater.divide(dailyConsumption, 1, RoundingMode.HALF_UP); totalSupplyDays = totalSupplyDays.add(remainingDays); waterExhausted = true; } } // 计算结束日期 long totalDays = DataHandleUtil.BigDecimalIntegerPart(totalSupplyDays); String endDate = DateUtil.getPlusDate(new Date(), totalDays); result.put(totalSupplyDays, endDate); return result; } /** * 判断当前计算月份是否在预测范围内 * 预测范围:从当前月份到传入的截至月份 */ private boolean isMonthInPredictRange(int calcMonth, int currentMonth, int endMonth) { if (endMonth >= currentMonth) { // 不跨年情况:当前月份 <= 计算月份 <= 截至月份 return calcMonth >= currentMonth && calcMonth <= endMonth; } else { // 跨年情况:计算月份 >= 当前月份 或者 计算月份 <= 截至月份 return calcMonth >= currentMonth || calcMonth <= endMonth; } } /** * 获取指定年份月份的天数 */ private int getDaysInMonth(int year, int month) { Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.YEAR, year); calendar.set(Calendar.MONTH, month - 1); return calendar.getActualMaximum(Calendar.DAY_OF_MONTH); } // public Map supplyTime(Integer year, Integer month) { // StEvpo stEvpo = stEvpoService.lambdaQuery().eq(StEvpo::getMonth, month.toString()).list().get(0); // BigDecimal evpo = BigDecimal.ZERO; //蒸发量 (万m³ // if(stEvpo != null){ // evpo = stEvpo.getEvaporation(); // } // Map map = new HashMap<>(); // /** // * 可供水量= 实时库容 - 死库容 // * 小时水量= (输水管流量 + 放水管流量)*3600 ->改为 (输水管流量 + 放水管流量)*3600 + 蒸发量 // * 可供水小时数 = 可供水量 * 10000/ 小时水量 ->改为 (可供水量 + 预测水量如果有) * 10000 / 小时水量 // * 可供水天数 = 可供水小时数换算为天数 // */ // List list = reservoirWaterService.list(); // if(CollectionUtils.isEmpty(list)){ // return map; // } // AttResBaseVo attResBaseVo = list.get(0); // BigDecimal nowCap = attResBaseVo.getNowCap() == null ? new BigDecimal(0) : attResBaseVo.getNowCap(); // BigDecimal deadCap = attResBaseVo.getDeadCap() == null ? new BigDecimal(0) : attResBaseVo.getDeadCap(); // // BigDecimal supplyV = nowCap.subtract(deadCap); // if(year != null){ // //计算预测来水量 // BigDecimal predictV = calcPredictV(year,month,attResBaseVo.getWatShedArea()); // supplyV = supplyV.add(predictV); // } // List water = stWaterRRealMapper.listRelated(); // //小时水量 // BigDecimal hourQ = water.stream().map(StWaterRReal::getQ).reduce(BigDecimal.ZERO, BigDecimal::add).multiply(new BigDecimal(3600)); // BigDecimal day = supplyV.multiply(new BigDecimal(10000)).divide(hourQ.multiply(new BigDecimal(24)),1, RoundingMode.HALF_UP); // long l = DataHandleUtil.BigDecimalIntegerPart(day); // String date = DateUtil.getPlusDate(new Date(), l); // map.put(day,date); // return map; // } /** * 新增方法:计算指定月份的预测来水量(独立月份计算) * 例子:10.14-10.30预测水量为40,11.01-11.30预测水量为60 */ private BigDecimal calcMonthlyPredictV(Integer year, Integer month, BigDecimal watShedArea) { LocalDate now = LocalDate.now(); Integer nowMonth = now.getMonthValue(); // 查询指定月份的降雨量数据 LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(TyYearRainfall::getYear, year) .eq(TyYearRainfall::getMonth, month) .eq(TyYearRainfall::getType, 2); TyYearRainfall rainfall = tyYearRainfallMapper.selectOne(queryWrapper); if (rainfall == null || rainfall.getDrp() == null) { return BigDecimal.ZERO; } BigDecimal monthlyRainfall = rainfall.getDrp(); // 如果是当前月份,需要按剩余天数比例计算 if (month.equals(nowMonth)) { int dayOfMonth = now.getDayOfMonth(); int totalDaysInMonth = now.lengthOfMonth(); int remainingDays = totalDaysInMonth - dayOfMonth + 1; // 按剩余天数比例计算当月有效降雨量 monthlyRainfall = new BigDecimal(remainingDays) .multiply(monthlyRainfall) .divide(new BigDecimal(totalDaysInMonth), 2, RoundingMode.HALF_UP); } // 如果不是当前月份,则使用整月的降雨量(不需要按比例计算) // 预测来水量 = 月降雨量 * 水库坝址控制流域面积 / 1000 BigDecimal sum = monthlyRainfall.divide(new BigDecimal(1000), 2, RoundingMode.HALF_UP); return sum.multiply(watShedArea); } private BigDecimal calcPredictV(Integer year, Integer month,BigDecimal watShedArea) { LocalDate now = LocalDate.now(); Integer nowMonth = now.getMonthValue(); LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); queryWrapper.eq(TyYearRainfall::getYear,year) .eq(TyYearRainfall::getType,2) .ge(TyYearRainfall::getMonth,nowMonth) .le(TyYearRainfall::getMonth,month); List list = tyYearRainfallMapper.selectList(queryWrapper); //和当前月份相同的降雨量 Optional first = list.stream().filter(o -> nowMonth == o.getMonth()).findFirst(); BigDecimal drp = first.isPresent() ? first.get().getDrp() : new BigDecimal(0); int dayOfMonth = now.getDayOfMonth(); int total = now.lengthOfMonth(); BigDecimal nowMonthDrp = new BigDecimal((total - dayOfMonth + 1)).multiply(drp).divide(new BigDecimal(total), 2, RoundingMode.HALF_UP); //当前月到预测月份的降雨量 BigDecimal value = list.stream().filter(o -> nowMonth != o.getMonth()).map(TyYearRainfall::getDrp).reduce(BigDecimal.ZERO, BigDecimal::add); //预测来水量 = 月降雨量和 * 水库坝址控制流域面积 BigDecimal sum = nowMonthDrp.add(value).divide(new BigDecimal(1000),2,RoundingMode.HALF_UP); return sum.multiply(watShedArea); } public BigDecimal predictWater(Integer year, Integer month) { List list = reservoirWaterService.list(); if(CollectionUtils.isEmpty(list)){ return new BigDecimal(0); } return calcPredictV(year,month,list.get(0).getWatShedArea()); } public BigDecimal predictWaterMonth(Integer year, Integer month) { List list = reservoirWaterService.list(); if(CollectionUtils.isEmpty(list)){ return new BigDecimal(0); } return calcMonthlyPredictV(year,month,list.get(0).getWatShedArea()); } }