gunshi-project-ss/src/main/java/com/gunshi/project/ss/service/StFlowRService.java

345 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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.StFlowRLatestVo;
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<StFlowRMapper, StFlowR> {
@Autowired
private StFlowRVoMapper voMapper;
@Autowired
private StStbprpBService stStbprpBService;
public StFlowR getNewDataByStcd(String stcd) {
LambdaQueryWrapper<StFlowR> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(StFlowR::getStcd, stcd);
queryWrapper.orderByDesc(StFlowR::getTm);
queryWrapper.last("limit 1");
return baseMapper.selectOne(queryWrapper);
}
public List<StFlowR> listNewData() {
return this.baseMapper.listNewData();
}
public List<String> getStcdList() {
return voMapper.listStcds();
}
public List<StFlowRVo> getDataByStcdAndTm(String stcd, Date tm) {
return voMapper.getDataByStcdAndTm(stcd, tm);
}
public List<StFlowRLatestVo> listLatestData() {
List<StFlowRLatestVo> res = new ArrayList<>();
List<StStbprpB> flowStations = stStbprpBService.getFlowStations();
LocalDateTime now = LocalDateTime.now();
LocalDateTime yesterday = now.minusDays(1);
for (StStbprpB flowStation : flowStations) {
StFlowRLatestVo vo = new StFlowRLatestVo();
vo.setStcd(flowStation.getStcd());
vo.setStnm(flowStation.getStnm());
List<StFlowR> list = lambdaQuery()
.eq(StFlowR::getStcd, vo.getStcd())
.ge(StFlowR::getTm, yesterday)
.le(StFlowR::getTm, now)
.orderByAsc(StFlowR::getTm)
.list();
if (!list.isEmpty()) {
// 初始化统计变量(每个站点独立)
BigDecimal total24V = BigDecimal.ZERO;
BigDecimal sum24Q = BigDecimal.ZERO;
BigDecimal min24Q = null;
BigDecimal max24Q = null;
int validCount = 0; // 有效数据点数
// 用于记录上一个有效数据点
StFlowR previousValid = null;
vo.setQ(list.getLast().getQ());
vo.setTm(list.getLast().getTm());
for (int i = 0; i < list.size(); i++) {
StFlowR current = list.get(i);
BigDecimal currentQ = current.getQ();
// 跳过流量为null的数据点
if (currentQ == null) {
continue;
}
// 统计有效数据
validCount++;
sum24Q = sum24Q.add(currentQ);
// 更新最小最大流量
if (min24Q == null || currentQ.compareTo(min24Q) < 0) {
min24Q = currentQ;
}
if (max24Q == null || currentQ.compareTo(max24Q) > 0) {
max24Q = currentQ;
}
// 计算水量(需要与前一个有效数据点计算)
if (previousValid != null && previousValid.getQ() != null) {
// 计算时间间隔(秒)
long seconds = ChronoUnit.SECONDS.between(previousValid.getTm(), current.getTm());
if (seconds > 0) { // 确保时间间隔为正
// 使用前一个时间的流量计算水量
BigDecimal previousFlow = previousValid.getQ();
// 水量 = 流量 × 时间间隔m³/s × s = m³再除以10000转换为万m³
BigDecimal waterVolume = previousFlow
.multiply(BigDecimal.valueOf(seconds)) // m³
.divide(BigDecimal.valueOf(10000), 10, RoundingMode.HALF_UP); // 万m³
total24V = total24V.add(waterVolume);
}
}
// 更新上一个有效数据点
previousValid = current;
}
// 设置统计结果
vo.setTotal24V(total24V.setScale(4, RoundingMode.HALF_UP));
// 计算24h平均流量
if (validCount > 0) {
BigDecimal avg24Q = sum24Q.divide(BigDecimal.valueOf(validCount), 4, RoundingMode.HALF_UP);
vo.setAvg24Q(avg24Q);
} else {
vo.setAvg24Q(BigDecimal.ZERO);
}
// 计算24h变幅流量最大流量 - 最小流量)
if (min24Q != null && max24Q != null) {
BigDecimal change24Q = max24Q.subtract(min24Q);
vo.setChange24Q(change24Q.setScale(4, RoundingMode.HALF_UP));
} else {
vo.setChange24Q(BigDecimal.ZERO);
}
}
res.add(vo);
}
return res;
}
public List<StFlowRListVo> upperDataCheck(StFlowDataCheckDto dto) {
List<StFlowRListVo> res = new ArrayList<>();
List<StFlowR> 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<StFlowR> 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<StFlowR> getByStcd(StFlowDataCheckDto dto) {
StStbprpB one = stStbprpBService.lambdaQuery()
.eq(StStbprpB::getStcd, dto.getStcd()).one();
if(one == null){
throw new IllegalArgumentException("对不起,该站点不存在");
}
LambdaQueryWrapper<StFlowR> 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<StFlowR> stFlowRS = this.baseMapper.selectList(queryWrapper);
stFlowRS.stream().forEach(o -> o.setStnm(one.getStnm()));
return stFlowRS;
}
public void export(StFlowDataCheckDto dto, HttpServletResponse response) {
// 1. 获取数据
List<StFlowR> 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);
}
}
}