diff --git a/service-datasync/src/main/java/com/gunshi/project/hsz/datasync/rainfall/SyncRainfallService.java b/service-datasync/src/main/java/com/gunshi/project/hsz/datasync/rainfall/SyncRainfallService.java index 998c8d1..25dbfac 100644 --- a/service-datasync/src/main/java/com/gunshi/project/hsz/datasync/rainfall/SyncRainfallService.java +++ b/service-datasync/src/main/java/com/gunshi/project/hsz/datasync/rainfall/SyncRainfallService.java @@ -107,7 +107,11 @@ public class SyncRainfallService implements SyncTableRService> report(@RequestBody ProjectSaveReportDto projectSaveReportDto){ + public R report(@RequestBody ProjectSaveReportDto projectSaveReportDto){ + ProjectSaveReportVo res = new ProjectSaveReportVo(); ForecastTask forecastTask = projectSaveReportDto.getForecastTask(); List voList = forecastResultsService.getHumanForecastResult(forecastTask); SyRegressionData syRegressionData = new SyRegressionData(); @@ -59,10 +70,33 @@ public class ProjectSafeAnalyseController { return R.ok(null); } //计算 - List res = forecastResultsService.calculateY(queryReg.get(0),voList); + List datas = forecastResultsService.calculateY(queryReg.get(0),voList); + res.setDatas(datas); + String formatEquation = RegressionEquationFormatter.formatEquation(queryReg.get(0).getRegressionEquation()); + res.setRegression(formatEquation); return R.ok(res); } + @Operation(summary = "预报-导出") + @PostMapping("/report/export") + public void reportExport(@RequestBody ProjectSaveReportDto projectSaveReportDto,HttpServletResponse response){ + ForecastTask forecastTask = projectSaveReportDto.getForecastTask(); + List voList = forecastResultsService.getHumanForecastResult(forecastTask); + SyRegressionData syRegressionData = new SyRegressionData(); + syRegressionData.setDvcd(projectSaveReportDto.getDvcd()); + syRegressionData.setOrder(projectSaveReportDto.getOrder()); + List queryReg = syRegressionDataService.queryData(syRegressionData);//获取方程组 + if(queryReg.isEmpty()){ + return; + } + //计算 + List res = forecastResultsService.calculateY(queryReg.get(0),voList); + String formatEquation = RegressionEquationFormatter.formatEquation(queryReg.get(0).getRegressionEquation()); + forecastResultsService.reportExport(res,formatEquation,projectSaveReportDto.getDvcd(),response); + } + + + @Operation(summary = "保存多线程回归方程") @PostMapping("/save") public R save(@RequestBody SyRegressionDataDto dto){ diff --git a/src/main/java/com/gunshi/project/hsz/entity/so/SyDataCheckRulePageSo.java b/src/main/java/com/gunshi/project/hsz/entity/so/SyDataCheckRulePageSo.java index 2cfa16e..0f2e1c3 100644 --- a/src/main/java/com/gunshi/project/hsz/entity/so/SyDataCheckRulePageSo.java +++ b/src/main/java/com/gunshi/project/hsz/entity/so/SyDataCheckRulePageSo.java @@ -15,4 +15,6 @@ public class SyDataCheckRulePageSo { private PageSo pageSo; private String dvcd; + + private String dm; } diff --git a/src/main/java/com/gunshi/project/hsz/entity/vo/ProjectSaveReportVo.java b/src/main/java/com/gunshi/project/hsz/entity/vo/ProjectSaveReportVo.java new file mode 100644 index 0000000..4a488fa --- /dev/null +++ b/src/main/java/com/gunshi/project/hsz/entity/vo/ProjectSaveReportVo.java @@ -0,0 +1,17 @@ +package com.gunshi.project.hsz.entity.vo; + + +import com.gunshi.project.hsz.common.model.vo.OsmoticPressDetailVo; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class ProjectSaveReportVo { + + private String regression; + + private List datas; + +} diff --git a/src/main/java/com/gunshi/project/hsz/model/SyDataCheckRule.java b/src/main/java/com/gunshi/project/hsz/model/SyDataCheckRule.java index febdb21..70546dd 100644 --- a/src/main/java/com/gunshi/project/hsz/model/SyDataCheckRule.java +++ b/src/main/java/com/gunshi/project/hsz/model/SyDataCheckRule.java @@ -67,4 +67,8 @@ public class SyDataCheckRule implements Serializable { @TableField(value = "is_available") @Schema(description = "是否启用 0:否 1:是") private Integer isAvailable; + + @TableField(value = "dm") + @Schema(description = "断面编码") + private String dm; } \ No newline at end of file diff --git a/src/main/java/com/gunshi/project/hsz/service/ForecastResultsService.java b/src/main/java/com/gunshi/project/hsz/service/ForecastResultsService.java index 0815b88..571ffed 100644 --- a/src/main/java/com/gunshi/project/hsz/service/ForecastResultsService.java +++ b/src/main/java/com/gunshi/project/hsz/service/ForecastResultsService.java @@ -19,13 +19,19 @@ import com.gunshi.project.hsz.grb.RainGrib2Layer; import com.gunshi.project.hsz.mapper.ForecastResultsMapper; import com.gunshi.project.hsz.model.*; import com.gunshi.project.hsz.util.DataHandleUtil; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +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.math.BigDecimal; +import java.math.MathContext; import java.math.RoundingMode; +import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDateTime; @@ -55,6 +61,8 @@ public class ForecastResultsService extends ServiceImpl> excuteForecastDebug(ForecastTask forecastTask) throws Exception { - List voList = new ArrayList<>(); - // 获取配置参数 - List paramList = forecastUseparamService.list(new QueryWrapper().isNotNull("param_code").isNotNull("param_value")); - if (CollectionUtils.isEmpty(paramList)) { - throw new IllegalArgumentException("温馨提示:当前洪水预报所依赖的数据尚不完整,请检查入参是否缺失。"); - } - // 获取系统当前的水库站编码、汛限水位 - AttResBase attResBase = attResBaseService.getOne(new QueryWrapper<>()); - // 多站点水库面雨量 - // List stbs = stStbprpBService.list(new QueryWrapper().eq("res_code", attResBase.getResCode())); - // 单站点 - List stbs = stStbprpBService.list(new QueryWrapper().eq("stcd", attResBase.getStcd())); - if (CollectionUtils.isEmpty(stbs)) { - return null; - } - Date nowHourTime = forecastTask.getNowTime(); - Date startTime = forecastTask.getStartTime(); - Date endTime = forecastTask.getEndTime(); - // 获取整个时间线的降雨数据 - List pptnRAllList = new ArrayList<>(); - List pptnRFutureList = new ArrayList<>(); - QueryWrapper qwExisted = new QueryWrapper<>(); - Boolean isHaveFuturePPtn = true; - // 檀树岗修改:实测降雨查询表数据,预测降雨使用geom参数。最后按时间算数平均 -// for(StStbprpB b : stbs){ -// String stcd = b.getStcd(); -// // 如果结束时间在当前时间之前,降雨序列从历史降雨表获取 -// if (endTime.compareTo(nowHourTime) <= 0) { -// qwExisted = new QueryWrapper().eq("stcd", stcd).ge("tm", startTime).le("tm", endTime).orderBy(true, true, "tm"); -// } else { -// qwExisted = new QueryWrapper().eq("stcd", stcd).ge("tm", startTime).le("tm", nowHourTime).orderBy(true, true, "tm"); -// try { -// // 获取预报数据 -// pptnRFutureList = getForecastDrpData(nowHourTime, stcd); -// } catch (IllegalArgumentException e) { -// if(stcd.equals(attResBase.getStcd())){ -// isHaveFuturePPtn = false; -// } -// log.error("该时间无预报数据"); -// } -// } -// List pptnRExistedList = stPptnRService.list(qwExisted); -// pptnRAllList.addAll(pptnRExistedList); -// pptnRAllList.addAll(pptnRFutureList); -// } - if (endTime.compareTo(nowHourTime) <= 0) { - qwExisted = new QueryWrapper().in("stcd", stbs.stream().map(StStbprpB::getStcd).toArray(String[]::new)).ge("tm", startTime).le("tm", endTime).orderBy(true, true, "tm"); - } else { - qwExisted = new QueryWrapper().in("stcd", stbs.stream().map(StStbprpB::getStcd).toArray(String[]::new)).ge("tm", startTime).le("tm", nowHourTime).orderBy(true, true, "tm"); - // 获取预报数据 - try { - pptnRFutureList = getForecastDrpData(nowHourTime, attResBase.getStcd()); - if(CollectionUtils.isEmpty(pptnRFutureList)) { - isHaveFuturePPtn = false; - log.error("该时间无预报数据"); - } - } catch (IllegalArgumentException e) { - isHaveFuturePPtn = false; - log.error("该时间无预报数据"); - } - } - List pptnRExistedList = stPptnRService.list(qwExisted); - pptnRAllList.addAll(pptnRExistedList); - pptnRAllList.addAll(pptnRFutureList); - - if (CollectionUtils.isEmpty(pptnRAllList)) { - return null; - } - // 多站点面雨量结果list - List polyPptnRList = pptnRAllList.stream() - .collect(Collectors.groupingBy( - StPptnR::getTm, - Collectors.mapping( - pptn -> Double.parseDouble(pptn.getDrp()), // 将String转换为double - Collectors.averagingDouble(d -> d) // 计算平均值 - ) - )) - .entrySet().stream() - .map(entry -> new StPptnRAverage(entry.getKey(), String.valueOf(entry.getValue()))) - .sorted(Comparator.comparing(StPptnRAverage::getTm)) - .collect(Collectors.toList()); - double dt = 0.0; - double Wm = 0.0; - double qOther = 0.0; - Map paramMap = paramList.stream().collect(Collectors.toMap(ForecastUseparam::getParamCode, ForecastUseparam::getParamValue, (existing, replacement) -> existing)); - if (paramMap.get("dt").isEmpty()) { - throw new IllegalArgumentException("温馨提示:当前洪水预报所依赖的数据尚不完整,请检查时间单元△T是否缺失。"); - } - if (paramMap.get("Im").isEmpty()) { - throw new IllegalArgumentException("温馨提示:当前洪水预报所依赖的数据尚不完整,请检查最大初损值Im是否缺失。"); - } - if (!paramMap.get("qOther").isEmpty()) { - qOther = Double.parseDouble(paramMap.get("qOther")); - } - dt = Double.parseDouble(paramMap.get("dt")); - // dt = Double.parseDouble("1.0"); - Wm = Double.parseDouble(paramMap.get("Im")); - List uList = forecastUService.list(); - if (CollectionUtils.isEmpty(uList)) { - throw new IllegalArgumentException("温馨提示:当前洪水预报所依赖的数据尚不完整,请检查U(I)单位线是否缺失。"); - } - // 每小时的单位径流量,单位m³/s - double[] u = uList.stream().mapToDouble(forecastU -> forecastU.getUValue().doubleValue()).toArray(); - // 根据开始结束时间查询pa - Calendar cal = Calendar.getInstance(); - cal.setTime(startTime); - // 将日期往前推一天 - cal.add(Calendar.DATE, -1); - List paList = forecastPaService.list(new QueryWrapper().eq("stcd", attResBase.getStcd()).ge("tm", sdfDay.format(cal.getTime())).le("tm", sdfDay.format(endTime))); - if (CollectionUtils.isEmpty(paList)) { - throw new IllegalArgumentException("温馨提示:当前洪水预报所依赖的数据尚不完整,请检查每日土壤含水量Pa是否缺失。"); - } - Map paMap = paList.stream().collect(Collectors.toMap(ForecastPa::getTm, entity -> entity, (existing, replacement) -> existing)); - // 获取预测开始时间前的最后水库水位 - double H1 = 0.0;// 初始水库水位,可以根据H1->V1,H1->q1得到初始的水库库容和下泄流量 - StRsvrR rsvrR = stRsvrRService.getOne(new QueryWrapper().eq("stcd", attResBase.getStcd()).le("tm", startTime).orderBy(true, false, "tm").last("limit 1")); - if (ObjectUtils.isEmpty(rsvrR)) { - return null; - } - H1 = Double.parseDouble(rsvrR.getRz()); - // 泄流量 - List stZqrlBList = stZqrlBService.list(new QueryWrapper().eq("stcd", attResBase.getStcd()).orderBy(true, true, "z")); - // 库容曲线 - List zvarlBS = stZvarlBService.list(new QueryWrapper().eq("stcd", attResBase.getStcd()).orderBy(true, true, "rz")); - // 水位历史数据 - List rsvrRRealList = stRsvrRService.list(new QueryWrapper().eq("stcd", attResBase.getStcd()).ge("tm", startTime).le("tm", endTime)); - List resultList = reorganizeRsvrRData(rsvrRRealList, dt); - List periods = splitByDay8To8(startTime, endTime); - // v:累计降雨 - BigDecimal vValue = BigDecimal.ZERO; - BigDecimal vSum = BigDecimal.ZERO; - // Rsum:累计径流深 - BigDecimal Rsum = BigDecimal.ZERO; - BigDecimal Psum = BigDecimal.ZERO; - List> res = new ArrayList<>(); - // 存储最后u的长度-1个r值 - for (int k = 0; k < periods.size(); k++) { - Date[] period = periods.get(k); - Calendar calNew = Calendar.getInstance(); - calNew.setTime(period[0]); - // 根据每段时间的开始时间,如果在08点前,则采用前一天的pa值计算 - if (isBeforeEightAM(period[0])) { - // 将日期往前推一天 - calNew.add(Calendar.DATE, -1); - } - ForecastPa forecastPa = paMap.get(sdfDay.format(calNew.getTime())); - if (ObjectUtils.isEmpty(forecastPa)) { - continue; -// throw new IllegalArgumentException("温馨提示:当前洪水预报所依赖的数据尚不完整,请检查每日土壤含水量Pa、K值是否缺失。"); - } - // 根据降雨数据,按照△t的颗粒度,均分 - // 筛选时间段内的降雨数据。不包前但包后 - List filterList = polyPptnRList.stream().filter(e -> e.getTm().compareTo(period[0]) >= 0).filter(e -> e.getTm().compareTo(period[1]) <= 0).collect(Collectors.toList()); - Map> retMap = new HashMap<>(); - if (CollectionUtils.isNotEmpty(filterList)) { - retMap = reorganizePptnRData(filterList, dt, period[0], period[1], nowHourTime, isHaveFuturePPtn); - } - if (ObjectUtils.isEmpty(retMap.get("listForForecast"))) { - continue; - } - List pForecastList = retMap.get("listForForecast").stream().map(s -> s.getDrp()).collect(Collectors.toList()); - if (pForecastList.size() == 0) { - continue; - } - if (ObjectUtils.isEmpty(retMap.get("listForReal"))) { - continue; - } - Map pRealMap = retMap.get("listForReal").stream().collect(Collectors.toMap(pptnR -> sdfMinute.format(pptnR.getTm()), StPptnR::getDrp, (existing, replacement) -> existing)); - double[] PList = pForecastList.stream().mapToDouble(Double::parseDouble).toArray(); - vSum = vSum.add(vValue); - vValue = BigDecimal.ZERO; - for (double value : PList) { - BigDecimal bd = BigDecimal.valueOf(value); - // 累加到总和中 - vValue = vValue.add(bd); - } - - // 存储上一次的径流深r,最开始默认全部为0 - List lastRArr = new ArrayList<>(); - for (int i = 0; i < u.length - 1; i++) { - lastRArr.add(0.0); // 填充初始值 - } - // 从后往前遍历不定长的实体列表 - for (int i = voList.size() - 1; i >= 0; i--) { - int index = lastRArr.size() - 1 - (voList.size() - 1 - i); - lastRArr.set(index, voList.get(i).getR().doubleValue()); - if(index <= 0){ - break; - } - } - - // 预测执行 - Map map = new HashMap<>(); - map.put("in period" + k, sdf.format(period[0])); - map.put("in K" + k, forecastPa.getK().doubleValue()); - map.put("in pa0" + k, forecastPa.getPa0().doubleValue()); - map.put("in Wm" + k, Wm); - map.put("in pt0" + k, forecastPa.getPt0().doubleValue()); - map.put("in H1" + k, H1); - map.put("in dt" + k, dt); - map.put("in pa" + k, forecastPa.getPa().doubleValue()); - map.put("in PList" + k, PList); - map.put("in u" + k, u); - map.put("in stcd" + k, attResBase.getStcd()); - map.put("in qOther" + k, qOther); - map.put("in vSum" + k, vSum.doubleValue()); - map.put("in Rsum" + k, Rsum.doubleValue()); - map.put("in lastRArr" + k, lastRArr); - List forecastVoList = RrainfallForecast.getData(sdf.format(period[0]), forecastPa.getK().doubleValue(), forecastPa.getPa0().doubleValue(), Wm, forecastPa.getPt0().doubleValue(), H1, dt, - forecastPa.getPa().doubleValue(), PList, u, attResBase.getStcd(), qOther, vSum.doubleValue(), Rsum.doubleValue(), lastRArr); - map.put("out res" + k, forecastVoList); - res.add(map); - if (CollectionUtils.isNotEmpty(forecastVoList)) { - // 筛选同时段的真实水位数据 - List realRsvrList = resultList.stream().filter(item -> item.getTm().compareTo(period[0]) >= 0 && item.getTm().compareTo(period[1]) <= 0).collect(Collectors.toList()); - Map realRsvrMap = realRsvrList.stream().collect(Collectors.toMap(rsvr -> sdfMinute.format(rsvr.getTm()), StRsvrR::getRz, (existing, replacement) -> existing)); - forecastVoList = forecastVoList.subList(0, PList.length + 1); - // 去除预测出来的最后一条与第二次实际的相同时间的数据 - if (voList.size() > 0 && voList.get(voList.size() - 1).getTm().equals(forecastVoList.get(0).getDateStr())) { - voList.remove(voList.size() - 1); - } - for (int j = 0; j < forecastVoList.size(); j++) { - FloodAlgorithemVo floodAlgorithemVo = forecastVoList.get(j); - // dt不同,预测结果的条数不同(跟new_q参数有关) - if (floodAlgorithemVo.getDateStr().substring(0, 15).compareTo(sdfMinute.format(period[1])) > 0) { - break; - } - String dateStr = floodAlgorithemVo.getDateStr(); - ForecastResultVo resultVo = new ForecastResultVo(); - resultVo.setTm(dateStr); - resultVo.setYcRkQValue(floodAlgorithemVo.getRq());// 预测入库流量 -// resultVo.setRealRkQValue();// 暂无真实入库流量 - resultVo.setYcCkQValue(floodAlgorithemVo.getCq());// 预测出库流量 - resultVo.setYcSwHValue(floodAlgorithemVo.getKh());// 预测水库水位 - H1 = resultVo.getYcSwHValue().doubleValue();// 以预测水位作为下一次预测段起始值 - String dateMinuteStr = dateStr.substring(0, dateStr.length() - 3);// 年月日 时分 - if (realRsvrMap.containsKey(dateMinuteStr)) { - BigDecimal realSwHValue = new BigDecimal(realRsvrMap.get(dateMinuteStr));// 根据时间取更准确 - resultVo.setRealSwHValue(realSwHValue);// 真实水库水位 - calculateCap(zvarlBS, resultVo, realSwHValue);// 有真实水位就用真实水位算(作为实测库容) - resultVo.setSwHDValue(resultVo.getYcSwHValue().subtract(resultVo.getRealSwHValue()));// 预测与真实水位差 - // 注释:修复跨8点时的陡升陡降 -// H1 = realSwHValue.doubleValue();// 如果有真实水位,将最后一条的真实水位作为下一次预测段的初始水位 - // 真实出库流量=真实水库水位与泄流量曲线差值法 - if (realSwHValue != null && CollectionUtils.isNotEmpty(stZqrlBList)) { - BigDecimal maxZ = stZqrlBList.stream().max(Comparator.comparing(StZqrlB::getZ)).get().getZ(); - BigDecimal minZ = stZqrlBList.stream().min(Comparator.comparing(StZqrlB::getZ)).get().getZ(); - if (realSwHValue.compareTo(minZ) < 0 || realSwHValue.compareTo(maxZ) > 0) { - resultVo.setRealCkQValue(BigDecimal.ZERO);// 真实出库流量 - } else { - Map stZvalMap = stZqrlBList.stream().collect(Collectors.toMap(StZqrlB::getZ, StZqrlB::getQ, (existing, replacement) -> existing)); - List list = stZqrlBList.stream().map(StZqrlB::getZ).collect(Collectors.toList()); - resultVo.setRealCkQValue(DataHandleUtil.calcData(realSwHValue, stZvalMap, list));// 真实出库流量 - } - } - } else { - // 没有真实水位就用预测水位算(作为预测库容) - calculateCap(zvarlBS, resultVo, resultVo.getYcSwHValue()); - } - - String pRealDrp = "0.0"; - if (pRealMap.containsKey(dateMinuteStr)) { - pRealDrp = pRealMap.get(dateMinuteStr); - } - BigDecimal drp = new BigDecimal(pRealDrp);// 根据时间取更准确 - if (drp.compareTo(BigDecimal.ZERO) < 0) { - resultVo.setDrp(BigDecimal.ZERO); - } else { - resultVo.setDrp(drp); - } - Psum = Psum.add(resultVo.getDrp()); - resultVo.setPSum(Psum); - Date dateTm = sdf.parse(floodAlgorithemVo.getDateStr()); - resultVo.setIspreDrp(dateTm.compareTo(nowHourTime) <= 0 ? "0" : "1");// 0:真实 1:预测 - resultVo.setR(floodAlgorithemVo.getR()); - if (resultVo.getR() != null) { - Rsum = Rsum.add(resultVo.getR()); - } else { - continue; - } - resultVo.setRSum(Rsum); - resultVo.setFlLowLimLev(attResBase.getFlLowLimLev()); - resultVo.setCurrentYdgdyjz(MapUtil.get(paramMap, "ydgdyjz", BigDecimal.class, BigDecimal.ZERO)); - resultVo.setPa(floodAlgorithemVo.getPa()); - voList.add(resultVo); - } - } - } - return res; - } - /** * @description: 根据水位计算库容 * @param zvarlBS 库容曲线 @@ -1122,23 +837,6 @@ public class ForecastResultsService extends ServiceImpl> getHumanForecastResultDebug(ForecastTask forecastTask) { - // 当前时间整点,作为获取雨量数据历史、预测分隔点 - Calendar calendar = Calendar.getInstance(); - calendar.set(Calendar.MINUTE, 0); - calendar.set(Calendar.SECOND, 0); - calendar.set(Calendar.MILLISECOND, 0); - Date nowHourTime = calendar.getTime(); - forecastTask.setNowTime(nowHourTime); - List> maps; - try { - maps = excuteForecastDebug(forecastTask); - } catch (Exception e) { - e.printStackTrace(); - throw new IllegalArgumentException(e.getMessage()); - } - return maps; - } public List calculateY(SyRegressionData syRegressionData, List voList) { //过滤出预测时间的数据 @@ -1188,7 +886,7 @@ public class ForecastResultsService extends ServiceImpl data, String regression, String dvcd, HttpServletResponse response) { + try { + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("UTF-8"); + String fileName = URLEncoder.encode("渗透压力预测报告", "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); + + // 创建Excel工作簿 + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("渗透压力预测报告"); + + // 创建单元格样式 - 支持换行 + CellStyle wrapStyle = workbook.createCellStyle(); + wrapStyle.setWrapText(true); + + // 设置行号 + int rowNum = 0; + + // 第一行:测点编号(不合并) + Row row1 = sheet.createRow(rowNum++); + Cell label1 = row1.createCell(0); + label1.setCellValue("测点编号:"); + Cell value1 = row1.createCell(1); + value1.setCellValue(dvcd); + + // 第二行:回归方程(从D列开始合并10个单元格) + Row row2 = sheet.createRow(rowNum++); + Cell label2 = row2.createCell(0); + label2.setCellValue("回归方程:"); + Cell value2 = row2.createCell(3); // D列(索引为3) + value2.setCellValue(regression != null ? regression : ""); + value2.setCellStyle(wrapStyle); + // 合并D列到M列(共10个单元格):从第3列到第12列 + sheet.addMergedRegion(new CellRangeAddress(1, 1, 3, 12)); + + // 空一行 + rowNum++; + + // 表头行 + Row headerRow = sheet.createRow(rowNum++); + String[] headers = {"预测时间", "预测值", "预测库水位"}; + for (int i = 0; i < headers.length; i++) { + Cell headerCell = headerRow.createCell(i); + headerCell.setCellValue(headers[i]); + } + + // 数据行 + if (data != null && !data.isEmpty()) { + for (OsmoticPressDetailVo detail : data) { + Row dataRow = sheet.createRow(rowNum++); + + // 预测时间 + Cell timeCell = dataRow.createCell(0); + timeCell.setCellValue(detail.getTm() != null ? detail.getTm() : ""); + + // 预测值 + Cell valueCell = dataRow.createCell(1); + if (detail.getValue() != null) { + valueCell.setCellValue(detail.getValue().doubleValue()); + } else { + valueCell.setCellValue(""); + } + + // 预测库水位 + Cell rzCell = dataRow.createCell(2); + if (detail.getRz() != null) { + rzCell.setCellValue(detail.getRz().doubleValue()); + } else { + rzCell.setCellValue(""); + } + } + } + + // 设置列宽 + sheet.setColumnWidth(0, 15 * 256); // A列:标签列,固定宽度 + sheet.setColumnWidth(1, 12 * 256); // B列:测点编号值,固定宽度 + sheet.setColumnWidth(2, 12 * 256); // C列:空列,固定宽度 + sheet.setColumnWidth(3, 50 * 256); // D列:方程列开始,固定宽度 + + // 自动调整数据表格的列宽 + sheet.autoSizeColumn(0); // 预测时间列 + sheet.autoSizeColumn(1); // 预测值列 + sheet.autoSizeColumn(2); // 预测库水位列 + + // 写入响应流 + workbook.write(response.getOutputStream()); + workbook.close(); + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("导出Excel失败"); + } + } } diff --git a/src/main/java/com/gunshi/project/hsz/service/SyDataCheckRuleService.java b/src/main/java/com/gunshi/project/hsz/service/SyDataCheckRuleService.java index d8853d5..c7a77cd 100644 --- a/src/main/java/com/gunshi/project/hsz/service/SyDataCheckRuleService.java +++ b/src/main/java/com/gunshi/project/hsz/service/SyDataCheckRuleService.java @@ -25,6 +25,9 @@ public class SyDataCheckRuleService extends ServiceImpl syDataCheckRulePage = this.baseMapper.selectPage(page.getPageSo().toPage(), queryWrapper); return syDataCheckRulePage; } diff --git a/src/main/java/com/gunshi/project/hsz/service/SyRegressionDataService.java b/src/main/java/com/gunshi/project/hsz/service/SyRegressionDataService.java index 405d1a1..a1f6401 100644 --- a/src/main/java/com/gunshi/project/hsz/service/SyRegressionDataService.java +++ b/src/main/java/com/gunshi/project/hsz/service/SyRegressionDataService.java @@ -3,15 +3,21 @@ package com.gunshi.project.hsz.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.gunshi.project.hsz.mapper.StZvarlBMapper; +import com.gunshi.project.hsz.common.model.vo.OsmoticPressDetailVo; +import com.gunshi.project.hsz.entity.vo.ProjectSafeCalculateVo; import com.gunshi.project.hsz.mapper.SyRegressionDataMapper; -import com.gunshi.project.hsz.model.StZvarlB; import com.gunshi.project.hsz.model.SyRegressionData; +import com.gunshi.project.hsz.util.RegressionEquationFormatter; +import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.net.URLEncoder; import java.util.List; @Service @@ -33,4 +39,133 @@ public class SyRegressionDataService extends ServiceImpl syRegressionData = this.baseMapper.selectList(queryWrapper); return syRegressionData; } + + public void export(String dvcd, ProjectSafeCalculateVo ans, HttpServletResponse response) { + try { + // 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("UTF-8"); + String fileName = URLEncoder.encode("渗透压力监测数据", "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); + + // 创建Excel工作簿 + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet("渗透压力监测数据"); + + // 创建单元格样式 - 支持换行 + CellStyle wrapStyle = workbook.createCellStyle(); + wrapStyle.setWrapText(true); // 设置自动换行 + + // 设置行号 + int rowNum = 0; + + // 第一行:测点编号(不合并) + Row row1 = sheet.createRow(rowNum++); + Cell label1 = row1.createCell(0); + label1.setCellValue("测点编号:"); + Cell value1 = row1.createCell(1); + value1.setCellValue(dvcd); + + // 第二行:一阶回归方程(从D列开始合并10个单元格) + Row row2 = sheet.createRow(rowNum++); + Cell label2 = row2.createCell(0); + label2.setCellValue("一阶回归方程:"); + Cell value2 = row2.createCell(3); // D列(索引为3) + String one = RegressionEquationFormatter.formatEquation(ans.getOne()); + value2.setCellValue(one != null ? one : ""); + value2.setCellStyle(wrapStyle); + // 合并D列到M列(共10个单元格):从第3列到第12列 + sheet.addMergedRegion(new CellRangeAddress(1, 1, 3, 12)); + + // 第三行:二阶回归方程(从D列开始合并10个单元格) + Row row3 = sheet.createRow(rowNum++); + Cell label3 = row3.createCell(0); + label3.setCellValue("二阶回归方程:"); + Cell value3 = row3.createCell(3); // D列(索引为3) + String tow = RegressionEquationFormatter.formatEquation(ans.getTwo()); + value3.setCellValue(tow != null ? tow : ""); + value3.setCellStyle(wrapStyle); + // 合并D列到M列(共10个单元格):从第3列到第12列 + sheet.addMergedRegion(new CellRangeAddress(2, 2, 3, 12)); + + // 第四行:三阶回归方程(从D列开始合并10个单元格) + Row row4 = sheet.createRow(rowNum++); + Cell label4 = row4.createCell(0); + label4.setCellValue("三阶回归方程:"); + Cell value4 = row4.createCell(3); // D列(索引为3) + String three = RegressionEquationFormatter.formatEquation(ans.getThree()); + value4.setCellValue(three != null ? three : ""); + value4.setCellStyle(wrapStyle); + // 合并D列到M列(共10个单元格):从第3列到第12列 + sheet.addMergedRegion(new CellRangeAddress(3, 3, 3, 12)); + + // 第五行:四阶回归方程(从D列开始合并10个单元格) + Row row5 = sheet.createRow(rowNum++); + Cell label5 = row5.createCell(0); + label5.setCellValue("四阶回归方程:"); + Cell value5 = row5.createCell(3); // D列(索引为3) + String four = RegressionEquationFormatter.formatEquation(ans.getFour()); + value5.setCellValue(four != null ? four : ""); + value5.setCellStyle(wrapStyle); + // 合并D列到M列(共10个单元格):从第3列到第12列 + sheet.addMergedRegion(new CellRangeAddress(4, 4, 3, 12)); + + // 空一行 + rowNum++; + + // 表头行 - 从第0列开始正常显示 + Row headerRow = sheet.createRow(rowNum++); + String[] headers = {"监测时间", "监测值", "库水位"}; + for (int i = 0; i < headers.length; i++) { + Cell headerCell = headerRow.createCell(i); + headerCell.setCellValue(headers[i]); + } + + // 数据行 + if (ans.getDatas() != null && !ans.getDatas().isEmpty()) { + for (OsmoticPressDetailVo data : ans.getDatas()) { + Row dataRow = sheet.createRow(rowNum++); + + // 监测时间 - 第0列 + Cell timeCell = dataRow.createCell(0); + timeCell.setCellValue(data.getTm() != null ? data.getTm() : ""); + + // 监测值 - 第1列 + Cell valueCell = dataRow.createCell(1); + if (data.getValue() != null) { + valueCell.setCellValue(data.getValue().doubleValue()); + } else { + valueCell.setCellValue(""); + } + + // 库水位 - 第2列 + Cell rzCell = dataRow.createCell(2); + if (data.getRz() != null) { + rzCell.setCellValue(data.getRz().doubleValue()); + } else { + rzCell.setCellValue(""); + } + } + } + + // 设置列宽 + sheet.setColumnWidth(0, 15 * 256); // A列:标签列,固定宽度 + sheet.setColumnWidth(1, 12 * 256); // B列:测点编号值,固定宽度 + sheet.setColumnWidth(2, 12 * 256); // C列:空列,固定宽度 + sheet.setColumnWidth(3, 50 * 256); // D列:方程列开始,固定宽度 + + // 自动调整数据表格的列宽(只对数据区域有效) + sheet.autoSizeColumn(0); // 监测时间列 + sheet.autoSizeColumn(1); // 监测值列 + sheet.autoSizeColumn(2); // 库水位列 + + // 写入响应流 + workbook.write(response.getOutputStream()); + workbook.close(); + + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("导出Excel失败"); + } + } } diff --git a/src/main/java/com/gunshi/project/hsz/util/RegressionEquationFormatter.java b/src/main/java/com/gunshi/project/hsz/util/RegressionEquationFormatter.java new file mode 100644 index 0000000..fe7e38d --- /dev/null +++ b/src/main/java/com/gunshi/project/hsz/util/RegressionEquationFormatter.java @@ -0,0 +1,144 @@ +package com.gunshi.project.hsz.util; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 回归方程格式化工具类 + */ +public class RegressionEquationFormatter { + + /** + * 格式化回归方程,保留小数点后指定有效小数位数(四舍五入) + * @param equation 原始方程字符串 + * @param decimalDigits 小数点后有效小数位数 + * @return 格式化后的方程字符串 + */ + public static String formatEquation(String equation, int decimalDigits) { + if (equation == null || equation.trim().isEmpty()) { + return equation; + } + + // 使用正则表达式匹配数字 + String regex = "-?\\d+\\.\\d+"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(equation); + + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + String numberStr = matcher.group(); + try { + // 截取小数点后指定位数,进行四舍五入 + String formatted = roundDecimal(numberStr, decimalDigits); + matcher.appendReplacement(result, formatted); + } catch (Exception e) { + // 如果处理失败,保留原数字 + matcher.appendReplacement(result, numberStr); + } + } + matcher.appendTail(result); + + return result.toString(); + } + + /** + * 截取小数点后指定位数,进行四舍五入 + */ + private static String roundDecimal(String numberStr, int decimalDigits) { + boolean isNegative = numberStr.startsWith("-"); + if (isNegative) { + numberStr = numberStr.substring(1); + } + + // 使用BigDecimal进行精确的四舍五入 + BigDecimal bd = new BigDecimal(numberStr); + bd = bd.setScale(decimalDigits, RoundingMode.HALF_UP); + + // 转换为字符串 + String result = bd.toPlainString(); + + // 确保显示所有小数位(即使末尾是0) + if (result.contains(".")) { + String[] parts = result.split("\\."); + String integerPart = parts[0]; + String decimalPart = parts[1]; + + // 如果小数部分长度不足,补0 + while (decimalPart.length() < decimalDigits) { + decimalPart += "0"; + } + result = integerPart + "." + decimalPart; + } + + return isNegative ? "-" + result : result; + } + + /** + * 格式化回归方程,默认保留小数点后4位有效小数(四舍五入) + * @param equation 原始方程字符串 + * @return 格式化后的方程字符串 + */ + public static String formatEquation(String equation) { + return formatEquation(equation, 4); + } + + /** + * 格式化单个数字,保留小数点后指定有效小数位数(四舍五入) + * @param number 原始数字 + * @param decimalDigits 小数点后有效小数位数 + * @return 格式化后的字符串 + */ + public static String formatNumber(double number, int decimalDigits) { + String numberStr = String.valueOf(number); + return roundDecimal(numberStr, decimalDigits); + } + + /** + * 格式化单个数字,默认保留小数点后4位有效小数(四舍五入) + * @param number 原始数字 + * @return 格式化后的字符串 + */ + public static String formatNumber(double number) { + return formatNumber(number, 4); + } + + /** + * 格式化单个数字,保留小数点后指定有效小数位数(四舍五入) + * @param number 原始数字 + * @param decimalDigits 小数点后有效小数位数 + * @return 格式化后的字符串 + */ + public static String formatNumber(BigDecimal number, int decimalDigits) { + if (number == null) { + return "0"; + } + number = number.setScale(decimalDigits, RoundingMode.HALF_UP); + String result = number.toPlainString(); + + // 确保显示所有小数位(即使末尾是0) + if (result.contains(".")) { + String[] parts = result.split("\\."); + String integerPart = parts[0]; + String decimalPart = parts[1]; + + // 如果小数部分长度不足,补0 + while (decimalPart.length() < decimalDigits) { + decimalPart += "0"; + } + result = integerPart + "." + decimalPart; + } + + return result; + } + + /** + * 格式化单个数字,默认保留小数点后4位有效小数(四舍五入) + * @param number 原始数字 + * @return 格式化后的字符串 + */ + public static String formatNumber(BigDecimal number) { + return formatNumber(number, 4); + } +} \ No newline at end of file