2025-12-29 17:13:09 +08:00
|
|
|
|
package com.gunshi.project.ss.service;
|
2025-11-03 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2026-01-20 14:29:38 +08:00
|
|
|
|
import com.gunshi.project.ss.common.model.StStbprpB;
|
|
|
|
|
|
import com.gunshi.project.ss.entity.dto.StFlowDataCheckDto;
|
|
|
|
|
|
import com.gunshi.project.ss.entity.vo.StFlowLowerDataCheckVo;
|
2026-02-02 16:48:43 +08:00
|
|
|
|
import com.gunshi.project.ss.entity.vo.StFlowRLatestVo;
|
2026-01-20 14:29:38 +08:00
|
|
|
|
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;
|
2026-01-20 14:29:38 +08:00
|
|
|
|
import jakarta.servlet.http.HttpServletResponse;
|
2025-11-03 16:47:35 +08:00
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
2026-01-20 14:29:38 +08:00
|
|
|
|
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;
|
2025-11-04 16:14:38 +08:00
|
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
2025-11-03 16:47:35 +08:00
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
2026-01-20 14:29:38 +08:00
|
|
|
|
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;
|
2026-01-20 14:29:38 +08:00
|
|
|
|
import java.util.*;
|
2025-11-03 16:47:35 +08:00
|
|
|
|
|
|
|
|
|
|
@Service
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
|
public class StFlowRService extends ServiceImpl<StFlowRMapper, StFlowR> {
|
|
|
|
|
|
|
2025-11-04 16:14:38 +08:00
|
|
|
|
@Autowired
|
|
|
|
|
|
private StFlowRVoMapper voMapper;
|
|
|
|
|
|
|
2026-01-20 14:29:38 +08:00
|
|
|
|
@Autowired
|
|
|
|
|
|
private StStbprpBService stStbprpBService;
|
|
|
|
|
|
|
2025-11-03 16:47:35 +08:00
|
|
|
|
public StFlowR getNewDataByStcd(String stcd) {
|
|
|
|
|
|
LambdaQueryWrapper<StFlowR> queryWrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
|
queryWrapper.eq(StFlowR::getStcd, stcd);
|
|
|
|
|
|
queryWrapper.orderByDesc(StFlowR::getTm);
|
|
|
|
|
|
queryWrapper.last("limit 1");
|
2025-11-04 16:14:38 +08:00
|
|
|
|
return baseMapper.selectOne(queryWrapper);
|
2025-11-03 16:47:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-02 15:18:17 +08:00
|
|
|
|
public List<StFlowR> listNewData() {
|
|
|
|
|
|
return this.baseMapper.listNewData();
|
2025-11-03 16:47:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public List<String> getStcdList() {
|
2025-11-04 16:14:38 +08:00
|
|
|
|
return voMapper.listStcds();
|
2025-11-03 16:47:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-04 16:14:38 +08:00
|
|
|
|
public List<StFlowRVo> getDataByStcdAndTm(String stcd, Date tm) {
|
|
|
|
|
|
return voMapper.getDataByStcdAndTm(stcd, tm);
|
2025-11-03 16:47:35 +08:00
|
|
|
|
}
|
2026-01-20 14:29:38 +08:00
|
|
|
|
|
2026-02-02 16:48:43 +08:00
|
|
|
|
public List<StFlowRLatestVo> listLatestData() {
|
|
|
|
|
|
List<StFlowRLatestVo> res = new ArrayList<>();
|
2026-01-20 14:29:38 +08:00
|
|
|
|
List<StStbprpB> flowStations = stStbprpBService.getFlowStations();
|
|
|
|
|
|
LocalDateTime now = LocalDateTime.now();
|
2026-02-02 16:48:43 +08:00
|
|
|
|
LocalDateTime yesterday = now.minusDays(1);
|
2026-01-20 14:29:38 +08:00
|
|
|
|
|
|
|
|
|
|
for (StStbprpB flowStation : flowStations) {
|
2026-02-02 16:48:43 +08:00
|
|
|
|
StFlowRLatestVo vo = new StFlowRLatestVo();
|
|
|
|
|
|
vo.setStcd(flowStation.getStcd());
|
|
|
|
|
|
vo.setStnm(flowStation.getStnm());
|
|
|
|
|
|
|
2026-01-20 14:29:38 +08:00
|
|
|
|
List<StFlowR> list = lambdaQuery()
|
2026-02-02 16:48:43 +08:00
|
|
|
|
.eq(StFlowR::getStcd, vo.getStcd())
|
|
|
|
|
|
.ge(StFlowR::getTm, yesterday)
|
2026-01-20 14:29:38 +08:00
|
|
|
|
.le(StFlowR::getTm, now)
|
2026-02-02 16:48:43 +08:00
|
|
|
|
.orderByAsc(StFlowR::getTm)
|
2026-01-20 14:29:38 +08:00
|
|
|
|
.list();
|
|
|
|
|
|
|
2026-02-02 16:48:43 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-01-20 14:29:38 +08:00
|
|
|
|
|
2026-01-22 17:21:05 +08:00
|
|
|
|
}
|
2026-01-20 14:29:38 +08:00
|
|
|
|
|
2026-02-02 16:48:43 +08:00
|
|
|
|
res.add(vo);
|
2026-01-20 14:29:38 +08:00
|
|
|
|
}
|
2026-02-02 16:48:43 +08:00
|
|
|
|
|
2026-01-20 14:29:38 +08:00
|
|
|
|
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-20 14:29:38 +08:00
|
|
|
|
|
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-20 14:29:38 +08:00
|
|
|
|
|
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-20 14:29:38 +08:00
|
|
|
|
|
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-20 14:29:38 +08:00
|
|
|
|
|
2026-01-26 13:11:07 +08:00
|
|
|
|
// 累计水量 = 上一个累计水量 + 当前时段水量
|
|
|
|
|
|
BigDecimal totalWater = previous.getTotalWater().add(waterVolume);
|
2026-01-20 14:29:38 +08:00
|
|
|
|
|
2026-01-26 13:11:07 +08:00
|
|
|
|
current.setTotalWater(totalWater);
|
2026-01-20 14:29:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-26 13:11:07 +08:00
|
|
|
|
// 3. 将结果按时间降序排列(最新的时间在前面)
|
|
|
|
|
|
res.sort(Comparator.comparing(StFlowRListVo::getTm).reversed());
|
|
|
|
|
|
return res;
|
2026-01-20 14:29:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-26 13:11:07 +08:00
|
|
|
|
public StFlowLowerDataCheckVo lowerDataCheckVoR(StFlowDataCheckDto dto) {
|
|
|
|
|
|
StFlowLowerDataCheckVo res = new StFlowLowerDataCheckVo();
|
2026-01-20 14:29:38 +08:00
|
|
|
|
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)
|
2026-01-20 14:29:38 +08:00
|
|
|
|
.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-20 14:29:38 +08:00
|
|
|
|
}
|
2026-01-26 13:11:07 +08:00
|
|
|
|
}
|
2026-01-20 14:29:38 +08:00
|
|
|
|
|
2026-01-26 13:11:07 +08:00
|
|
|
|
BigDecimal totalWater = BigDecimal.ZERO;
|
2026-01-20 14:29:38 +08:00
|
|
|
|
|
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-20 14:29:38 +08:00
|
|
|
|
}
|
2026-01-26 13:11:07 +08:00
|
|
|
|
// 3. 设置返回结果
|
|
|
|
|
|
res.setMaxQ(maxQ);
|
|
|
|
|
|
res.setMaxQTm(maxQTm);
|
|
|
|
|
|
res.setTotalWater(totalWater);
|
2026-01-20 14:29:38 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-03 16:47:35 +08:00
|
|
|
|
}
|