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

345 lines
14 KiB
Java
Raw Normal View History

2025-12-29 17:13:09 +08:00
package com.gunshi.project.ss.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
2025-12-29 17:13:09 +08:00
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;
2025-12-29 17:13:09 +08:00
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;
2026-01-26 13:11:07 +08:00
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);
}
2025-12-02 15:18:17 +08:00
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;
}
2026-01-26 13:11:07 +08:00
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;
}
2026-01-26 13:11:07 +08:00
for (StFlowR stFlowR : list) {
StFlowRListVo stFlowRListVo = new StFlowRListVo();
stFlowRListVo.setStcd(stFlowR.getStcd());
stFlowRListVo.setQ(stFlowR.getQ());
stFlowRListVo.setTm(stFlowR.getTm());
res.add(stFlowRListVo);
}
2026-01-26 13:11:07 +08:00
// 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;
}
2026-01-26 13:11:07 +08:00
// 水量 = 流量 × 时间间隔m³/s × s = m³再除以10000转换为万m³
BigDecimal waterVolume = previousFlow
.multiply(BigDecimal.valueOf(seconds)) // m³
.divide(BigDecimal.valueOf(10000), 10, RoundingMode.HALF_UP); // 万m³
2026-01-26 13:11:07 +08:00
// 累计水量 = 上一个累计水量 + 当前时段水量
BigDecimal totalWater = previous.getTotalWater().add(waterVolume);
2026-01-26 13:11:07 +08:00
current.setTotalWater(totalWater);
}
2026-01-26 13:11:07 +08:00
// 3. 将结果按时间降序排列(最新的时间在前面)
res.sort(Comparator.comparing(StFlowRListVo::getTm).reversed());
return res;
}
2026-01-26 13:11:07 +08:00
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())
2026-01-26 13:11:07 +08:00
.orderByAsc(StFlowR::getTm)
.list();
if(list.isEmpty()){
return res;
}
2026-01-26 13:11:07 +08:00
// 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();
}
2026-01-26 13:11:07 +08:00
}
2026-01-26 13:11:07 +08:00
BigDecimal totalWater = BigDecimal.ZERO;
2026-01-26 13:11:07 +08:00
// 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);
}
2026-01-26 13:11:07 +08:00
// 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);
}
}
}