package com.gunshi.project.ss.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gunshi.project.ss.common.mapper.StFlowRMapper; import com.gunshi.project.ss.common.model.StFlowR; import com.gunshi.project.ss.common.model.StStbprpB; import com.gunshi.project.ss.entity.dto.StFlowDataCheckDto; import com.gunshi.project.ss.entity.vo.StFlowLowerDataCheckVo; import com.gunshi.project.ss.entity.vo.StFlowRListVo; import com.gunshi.project.ss.entity.vo.StFlowRVo; import com.gunshi.project.ss.mapper.StFlowRVoMapper; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.URLEncoder; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; @Service @Slf4j @Transactional(rollbackFor = Exception.class) public class StFlowRService extends ServiceImpl { @Autowired private StFlowRVoMapper voMapper; @Autowired private StStbprpBService stStbprpBService; public StFlowR getNewDataByStcd(String stcd) { LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(StFlowR::getStcd, stcd); queryWrapper.orderByDesc(StFlowR::getTm); queryWrapper.last("limit 1"); return baseMapper.selectOne(queryWrapper); } public List listNewData() { return this.baseMapper.listNewData(); } public List getStcdList() { return voMapper.listStcds(); } public List getDataByStcdAndTm(String stcd, Date tm) { return voMapper.getDataByStcdAndTm(stcd, tm); } public List listLatestData() { List res = new ArrayList<>(); List flowStations = stStbprpBService.getFlowStations(); LocalDateTime now = LocalDateTime.now(); LocalDateTime lastDay = now.minusDays(1); for (StStbprpB flowStation : flowStations) { StFlowRListVo stFlowR = new StFlowRListVo(); stFlowR.setStcd(flowStation.getStcd()); stFlowR.setStnm(flowStation.getStnm()); stFlowR.setLgtd(flowStation.getLgtd()); stFlowR.setLttd(flowStation.getLttd()); // 获取24小时内的所有流量数据 List list = lambdaQuery() .eq(StFlowR::getStcd, flowStation.getStcd()) .ge(StFlowR::getTm, lastDay) .le(StFlowR::getTm, now) .orderByAsc(StFlowR::getTm) // 按时间升序排列,方便计算 .list(); if(list.isEmpty()){ res.add(stFlowR); continue; } // 设置最新时间点的数据 StFlowR latestData = list.get(list.size() - 1); stFlowR.setTm(latestData.getTm()); stFlowR.setQ(latestData.getQ()); //判断状态 LocalDateTime tm = latestData.getTm(); Duration between = Duration.between(tm, now); if(between.toHours() > 24){ stFlowR.setStatus(0); }else{ stFlowR.setStatus(1); } // 计算统计值 res.add(stFlowR); } return res; } public List upperDataCheck(StFlowDataCheckDto dto) { List res = new ArrayList<>(); List list = lambdaQuery() .eq(StFlowR::getStcd, dto.getStcd()) .ge(StFlowR::getTm, dto.getDateTimeRangeSo().getStart()) .le(StFlowR::getTm, dto.getDateTimeRangeSo().getEnd()) .orderByAsc(StFlowR::getTm) .list(); if(list.isEmpty()){ return res; } for (StFlowR stFlowR : list) { StFlowRListVo stFlowRListVo = new StFlowRListVo(); stFlowRListVo.setStcd(stFlowR.getStcd()); stFlowRListVo.setQ(stFlowR.getQ()); stFlowRListVo.setTm(stFlowR.getTm()); res.add(stFlowRListVo); } // 3. 计算累计水量 // 设置第一个时间点的累计水量为0 res.get(0).setTotalWater(BigDecimal.ZERO); // 从第二个开始计算累计水量 for (int i = 1; i < res.size(); i++) { StFlowRListVo current = res.get(i); StFlowRListVo previous = res.get(i - 1); // 计算时间间隔(秒) long seconds = ChronoUnit.SECONDS.between(previous.getTm(), current.getTm()); // 使用前一个时间的流量计算水量 BigDecimal previousFlow = previous.getQ(); if (previousFlow == null) { previousFlow = BigDecimal.ZERO; } // 水量 = 流量 × 时间间隔(m³/s × s = m³,再除以10000转换为万m³) BigDecimal waterVolume = previousFlow .multiply(BigDecimal.valueOf(seconds)) // m³ .divide(BigDecimal.valueOf(10000), 10, RoundingMode.HALF_UP); // 万m³ // 累计水量 = 上一个累计水量 + 当前时段水量 BigDecimal totalWater = previous.getTotalWater().add(waterVolume); current.setTotalWater(totalWater); } // 3. 将结果按时间降序排列(最新的时间在前面) res.sort(Comparator.comparing(StFlowRListVo::getTm).reversed()); return res; } public StFlowLowerDataCheckVo lowerDataCheckVoR(StFlowDataCheckDto dto) { StFlowLowerDataCheckVo res = new StFlowLowerDataCheckVo(); List list = lambdaQuery() .eq(StFlowR::getStcd, dto.getStcd()) .ge(StFlowR::getTm, dto.getDateTimeRangeSo().getStart()) .le(StFlowR::getTm, dto.getDateTimeRangeSo().getEnd()) .orderByAsc(StFlowR::getTm) .list(); if(list.isEmpty()){ return res; } // 1. 找出最大瞬时流量及其时间(此时res是按时间升序的) BigDecimal maxQ = list.get(0).getQ(); LocalDateTime maxQTm = list.get(0).getTm(); for (StFlowR vo : list) { if (vo.getQ() != null && vo.getQ().compareTo(maxQ) > 0) { maxQ = vo.getQ(); maxQTm = vo.getTm(); } } BigDecimal totalWater = BigDecimal.ZERO; // 2. 计算累计水量 // 第一个时间点累计水量为0 // 从第二个开始计算:上一个累计水量 + 流量 × 时间间隔 for (int i = 1; i < list.size(); i++) { StFlowR current = list.get(i); StFlowR previous = list.get(i - 1); // 计算时间间隔(秒) long seconds = ChronoUnit.SECONDS.between(previous.getTm(), current.getTm()); // 使用当前时间点的流量(矩形法) // 注意:这里使用的是上一个时间点的流量,不是当前时间点的 // 也可以根据需求改为使用 current.getQ() BigDecimal previousFlow = previous.getQ() != null ? previous.getQ() : BigDecimal.ZERO; // 计算当前时段的水量:流量 × 时间间隔 // 注意单位转换:流量 m³/s,时间 s,结果 m³,再转万m³ BigDecimal waterVolume = previousFlow .multiply(BigDecimal.valueOf(seconds)) // m³ .divide(BigDecimal.valueOf(10000), 10, RoundingMode.HALF_UP); // 万m³ // 累加到总水量 totalWater = totalWater.add(waterVolume); } // 3. 设置返回结果 res.setMaxQ(maxQ); res.setMaxQTm(maxQTm); res.setTotalWater(totalWater); return res; } public List getByStcd(StFlowDataCheckDto dto) { StStbprpB one = stStbprpBService.lambdaQuery() .eq(StStbprpB::getStcd, dto.getStcd()).one(); if(one == null){ throw new IllegalArgumentException("对不起,该站点不存在"); } LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(StFlowR::getStcd, dto.getStcd()); queryWrapper.ge(StFlowR::getTm, dto.getDateTimeRangeSo().getStart()); queryWrapper.le(StFlowR::getTm, dto.getDateTimeRangeSo().getEnd()); queryWrapper.orderByDesc(StFlowR::getTm); List stFlowRS = this.baseMapper.selectList(queryWrapper); stFlowRS.stream().forEach(o -> o.setStnm(one.getStnm())); return stFlowRS; } public void export(StFlowDataCheckDto dto, HttpServletResponse response) { // 1. 获取数据 List dataList = getByStcd(dto); // 2. 创建Excel工作簿 try (Workbook workbook = new XSSFWorkbook()) { Sheet sheet = workbook.createSheet("流量数据"); // 3. 创建标题行 Row headerRow = sheet.createRow(0); headerRow.createCell(0).setCellValue("站点名称"); headerRow.createCell(1).setCellValue("时间"); headerRow.createCell(2).setCellValue("瞬时流量(m³/s)"); // 4. 填充数据 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); for (int i = 0; i < dataList.size(); i++) { StFlowR flowData = dataList.get(i); Row row = sheet.createRow(i + 1); // 站点名称 row.createCell(0).setCellValue(flowData.getStnm() != null ? flowData.getStnm() : ""); // 时间 String timeStr = ""; if (flowData.getTm() != null) { timeStr = flowData.getTm().format(formatter); } row.createCell(1).setCellValue(timeStr); // 流量 if (flowData.getQ() != null) { row.createCell(2).setCellValue(flowData.getQ().doubleValue()); } else { row.createCell(2).setCellValue(""); } } // 5. 自动调整列宽 for (int i = 0; i < 3; i++) { sheet.autoSizeColumn(i); } // 6. 设置响应头 String fileName = "流量数据_" + System.currentTimeMillis() + ".xlsx"; response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); // 7. 写入响应流 workbook.write(response.getOutputStream()); } catch (IOException e) { throw new RuntimeException("导出Excel失败", e); } } }