| | |
| | | logger.info("【##】开始生成日报 ,时间:"+startTime); |
| | | OPCPackage opcPackage = null; |
| | | //加载文档 |
| | | logger.info("【##】获取模版并创建相关文件"); |
| | | XWPFDocument doc = null; |
| | | try { |
| | | ClassLoader classLoader = Application.class.getClassLoader(); |
| | |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | logger.info("【##】获取相关数据,并填充数据"); |
| | | Map<String, Object> map = dataMap(gasCategories); |
| | | List<XWPFParagraph> paragraphList = doc.getParagraphs(); |
| | | for (XWPFParagraph par : paragraphList) { |
| | |
| | | } |
| | | } |
| | | } |
| | | logger.info("【##】生成相关文件信息,保存相关文件"); |
| | | String format = now.format(reportNameFormatter); |
| | | String fileName = "富城能源气体监测日报"+format+".docx"; |
| | | String fileurl = reportFilePathConfig.getDcPath()+fileName; |
| | |
| | | List<GasConcentration> gasConcentrations = gasConcentrationService.listDatabyTimeSlot(LocalDateTime.of(now.minusDays(1).toLocalDate(), LocalTime.MIN) |
| | | , LocalDateTime.of(now.minusDays(1).toLocalDate(), LocalTime.MAX)); |
| | | // 绘制折线图 |
| | | logger.info("【##】 开始绘制折线图 "); |
| | | if (!CollectionUtils.isEmpty(gasConcentrations)) { |
| | | List<GasCategory> gasCategoryForReport = gasCategoryService.findGasCategoryForReport(); |
| | | for (int i = 0; i < gasCategoryForReport.size(); i++) { |
| | |
| | | } |
| | | //List<GasFlux> gasFluxes = gasFluxService.listYesterday(); |
| | | List<GasFlux> gasFluxes = gasFluxService.listYesterdayTenAmToSixPm(); |
| | | List<Integer> areaNum = gasFluxes.stream().map(GasFlux::getAreaId).distinct().collect(Collectors.toList()); |
| | | List<Integer> areaNum = gasFluxes.stream().map(GasFlux::getAreaId).distinct().sorted().collect(Collectors.toList()); |
| | | |
| | | List<Region> allRegion = regionService.findAll(); |
| | | if (CollectionUtils.isEmpty(allRegion)) |
| | |
| | | // drawBarChart(gasFluxesByArea, fileurl, regionMap.get(i), gasCategories.get(j).getMolecularFormula(), i, j + 1); |
| | | // } |
| | | // } |
| | | for (int i = 1; i <= areaNum.size(); i++) { |
| | | logger.info("【##】 开始绘制柱状图"); |
| | | for (int i = 0; i < areaNum.size(); i++) { |
| | | //for (int j = 0; j < 20; j++) { |
| | | int finalI = i; |
| | | int finalI = areaNum.get(i); |
| | | List<GasFlux> gasFluxesByArea = gasFluxes.stream().filter(gasFlux -> gasFlux.getAreaId() == finalI) |
| | | .collect(Collectors.toList()); |
| | | drawBarChart(gasFluxesByArea, fileurl, regionMap.get(i), gasCategories.get(0).getMolecularFormula(), i, 1); |
| | | drawBarChart(gasFluxesByArea, fileurl, regionMap.get(areaNum.get(i)), gasCategories.get(0).getMolecularFormula(), areaNum.get(i), 1); |
| | | } |
| | | } |
| | | logger.info("保存相关信息到数据库"); |
| | | String endTime = LocalDateTime.now().format(execformatter); |
| | | long execTime = ChronoUnit.SECONDS.between(now, LocalDateTime.now()); |
| | | MonitorDailyReport report = new MonitorDailyReport(); |
| | |
| | | } |
| | | |
| | | |
| | | // public void drawBarChartExec(XWPFChart chart, String[] series, String[] categories, |
| | | // List<Number[]> values, String chartTitle,String molecularFormula) { |
| | | // final List<XDDFChartData> data = chart.getChartSeries(); |
| | | // |
| | | // final XDDFBarChartData line = (XDDFBarChartData) data.get(0);//这里一般获取第一个,我们这里是折线图就是XDDFLineChartData |
| | | // line.getCategoryAxis().setTitle(chartTitle); |
| | | // line.getValueAxes().get(0).setTitle(molecularFormula+" Emission Rate (ug/m^2/s)"); |
| | | // final int numOfPoints = categories.length; |
| | | // |
| | | // final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0)); |
| | | // |
| | | // final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0); |
| | | // for (int i = 0; i < values.size(); i++) { |
| | | // final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, i + 1, i + 1)); |
| | | // Number[] value = values.get(i); |
| | | // final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(value, valuesDataRange, i + 1); |
| | | // XDDFChartData.Series ser;//图表中的系列 |
| | | // ser = line.getSeries().get(i); |
| | | // ser.replaceData(categoriesData, valuesData); |
| | | // CellReference cellReference = chart.setSheetTitle(series[i], 1);//修改系列标题 |
| | | // ser.setTitle(series[i], cellReference); |
| | | // } |
| | | // chart.plot(line); |
| | | // chart.setTitleText("");//折线图标题 |
| | | // chart.setTitleOverlay(false); |
| | | // } |
| | | |
| | | public void drawBarChartExec(XWPFChart chart, String[] series, String[] categories, |
| | | List<Number[]> values, String chartTitle,String molecularFormula) { |
| | | final List<XDDFChartData> data = chart.getChartSeries(); |
| | | |
| | | final XDDFBarChartData line = (XDDFBarChartData) data.get(0);//这里一般获取第一个,我们这里是折线图就是XDDFLineChartData |
| | | line.getCategoryAxis().setTitle(chartTitle); |
| | | line.getValueAxes().get(0).setTitle(molecularFormula+" Emission Rate (ug/m^2/s)"); |
| | | final int numOfPoints = categories.length; |
| | | |
| | | final String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0)); |
| | | |
| | | final XDDFDataSource<?> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0); |
| | | for (int i = 0; i < values.size(); i++) { |
| | | final String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, i + 1, i + 1)); |
| | | Number[] value = values.get(i); |
| | | final XDDFNumericalDataSource<? extends Number> valuesData = XDDFDataSourcesFactory.fromArray(value, valuesDataRange, i + 1); |
| | | XDDFChartData.Series ser;//图表中的系列 |
| | | ser = line.getSeries().get(i); |
| | | ser.replaceData(categoriesData, valuesData); |
| | | CellReference cellReference = chart.setSheetTitle(series[i], 1);//修改系列标题 |
| | | ser.setTitle(series[i], cellReference); |
| | | List<Number[]> values, String chartTitle, String molecularFormula) { |
| | | // 1. 基础参数非空校验 |
| | | if (chart == null) { |
| | | logger.error("绘制柱状图失败:图表对象(chart)为null"); |
| | | return; |
| | | } |
| | | chart.plot(line); |
| | | chart.setTitleText("");//折线图标题 |
| | | chart.setTitleOverlay(false); |
| | | if (series == null || series.length == 0) { |
| | | logger.error("绘制柱状图失败:系列名称(series)为空或长度为0"); |
| | | return; |
| | | } |
| | | if (categories == null || categories.length == 0) { |
| | | logger.error("绘制柱状图失败:分类轴数据(categories)为空或长度为0"); |
| | | return; |
| | | } |
| | | if (values == null || values.isEmpty()) { |
| | | logger.error("绘制柱状图失败:数值数据(values)为空或长度为0"); |
| | | return; |
| | | } |
| | | // 校验系列数与数值列表长度一致 |
| | | if (series.length != values.size()) { |
| | | logger.error("绘制柱状图失败:系列数({})与数值列表长度({})不匹配", series.length, values.size()); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | // 2. 获取图表数据系列(防御性处理) |
| | | List<XDDFChartData> data = chart.getChartSeries(); |
| | | if (data == null || data.isEmpty()) { |
| | | logger.error("绘制柱状图失败:图表中未找到数据系列(data is empty)"); |
| | | return; |
| | | } |
| | | // 强制转换为柱状图数据(若类型不匹配会抛出ClassCastException,后续捕获) |
| | | XDDFBarChartData barChartData = (XDDFBarChartData) data.get(0); |
| | | |
| | | // 3. 设置坐标轴标题(处理空字符串) |
| | | barChartData.getCategoryAxis().setTitle(Objects.toString(chartTitle, "分类轴")); // 空标题时用默认值 |
| | | String valueAxisTitle = (molecularFormula != null ? molecularFormula : "气体") + " Emission Rate (ug/m^2/s)"; |
| | | barChartData.getValueAxes().get(0).setTitle(valueAxisTitle); |
| | | |
| | | // 4. 校验分类轴数据长度(numOfPoints必须≥1,否则范围无效) |
| | | int numOfPoints = categories.length; |
| | | if (numOfPoints < 1) { |
| | | logger.error("绘制柱状图失败:分类轴数据长度(numOfPoints={})小于1,无法生成有效范围", numOfPoints); |
| | | return; |
| | | } |
| | | |
| | | // 5. 生成分类轴数据范围(避免起止行颠倒) |
| | | int startRow = 1; |
| | | int endRow = numOfPoints; |
| | | if (startRow > endRow) { |
| | | logger.warn("分类轴数据范围起止行颠倒({} > {}),自动修正", startRow, endRow); |
| | | // 交换起止行(避免CellRangeAddress抛出异常) |
| | | int temp = startRow; |
| | | startRow = endRow; |
| | | endRow = temp; |
| | | } |
| | | CellRangeAddress categoryRange = new CellRangeAddress(startRow, endRow, 0, 0); |
| | | String categoryDataRange; |
| | | try { |
| | | categoryDataRange = chart.formatRange(categoryRange); |
| | | } catch (Exception e) { |
| | | logger.error("格式化分类轴数据范围失败:startRow={}, endRow={}, columns=[0,0]", startRow, endRow, e); |
| | | return; |
| | | } |
| | | |
| | | // 6. 处理分类轴数据源 |
| | | XDDFDataSource<?> categoriesData; |
| | | try { |
| | | categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0); |
| | | } catch (Exception e) { |
| | | logger.error("创建分类轴数据源失败", e); |
| | | return; |
| | | } |
| | | |
| | | // 7. 循环处理每个系列的数值数据 |
| | | for (int i = 0; i < values.size(); i++) { |
| | | // 校验当前系列的数值数组非空 |
| | | Number[] valueArray = values.get(i); |
| | | if (valueArray == null) { |
| | | logger.warn("第{}个系列的数值数组为null,跳过处理", i); |
| | | continue; |
| | | } |
| | | // 校验数值数组长度与分类轴长度一致(否则图表可能显示异常) |
| | | if (valueArray.length != numOfPoints) { |
| | | logger.warn("第{}个系列的数值长度({})与分类轴长度({})不匹配,可能导致图表异常", |
| | | i, valueArray.length, numOfPoints); |
| | | } |
| | | |
| | | // 生成当前系列的数值范围 |
| | | CellRangeAddress valueRange = new CellRangeAddress(startRow, endRow, i + 1, i + 1); |
| | | String valuesDataRange; |
| | | try { |
| | | valuesDataRange = chart.formatRange(valueRange); |
| | | } catch (Exception e) { |
| | | logger.error("格式化第{}个系列的数值范围失败:startRow={}, endRow={}, columns=[{},{}]", |
| | | i, startRow, endRow, i + 1, i + 1, e); |
| | | continue; // 跳过当前系列,处理下一个 |
| | | } |
| | | |
| | | // 创建数值数据源 |
| | | XDDFNumericalDataSource<? extends Number> valuesData; |
| | | try { |
| | | valuesData = XDDFDataSourcesFactory.fromArray(valueArray, valuesDataRange, i + 1); |
| | | } catch (Exception e) { |
| | | logger.error("创建第{}个系列的数值数据源失败", i, e); |
| | | continue; |
| | | } |
| | | |
| | | // 更新系列数据(确保系列存在) |
| | | List<XDDFChartData.Series> chartSeries = barChartData.getSeries(); |
| | | if (i >= chartSeries.size()) { |
| | | logger.error("第{}个系列不存在(图表仅包含{}个系列),跳过处理", i, chartSeries.size()); |
| | | continue; |
| | | } |
| | | XDDFChartData.Series ser = chartSeries.get(i); |
| | | ser.replaceData(categoriesData, valuesData); |
| | | |
| | | // 设置系列标题 |
| | | String seriesName = series[i] != null ? series[i] : "系列" + i; // 处理空标题 |
| | | try { |
| | | CellReference cellReference = chart.setSheetTitle(seriesName, 1); |
| | | ser.setTitle(seriesName, cellReference); |
| | | } catch (Exception e) { |
| | | logger.error("设置第{}个系列标题({})失败", i, seriesName, e); |
| | | // 标题设置失败不影响数据,继续执行 |
| | | } |
| | | } |
| | | |
| | | // 8. 绘制图表并清除标题 |
| | | chart.plot(barChartData); |
| | | chart.setTitleText(""); // 清除图表标题 |
| | | chart.setTitleOverlay(false); |
| | | |
| | | logger.debug("柱状图绘制完成:标题={}, 系列数={}, 数据点数量={}", chartTitle, series.length, numOfPoints); |
| | | |
| | | } catch (ClassCastException e) { |
| | | // 图表数据类型不匹配(如预期是柱状图,实际是折线图) |
| | | logger.error("绘制柱状图失败:图表数据类型不匹配(预期XDDFBarChartData)", e); |
| | | } catch (IndexOutOfBoundsException e) { |
| | | // 坐标轴或系列索引越界(如getValueAxes()为空) |
| | | logger.error("绘制柱状图失败:索引越界(可能坐标轴未初始化)", e); |
| | | } catch (Exception e) { |
| | | // 捕获其他未预期异常 |
| | | logger.error("绘制柱状图时发生未知异常", e); |
| | | } |
| | | } |
| | | |
| | | public void drawLineChartExec(XWPFChart chart, String[] series, String[] categories, |
| | |
| | | } |
| | | |
| | | private Map<String, Object> dataMap(List<GasCategory> gasCategories){ |
| | | logger.info("【##】 生成相关数据"); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | String today = now.format(formatter); |
| | | String yesterday = LocalDateTime.now().minusDays(1).format(formatter); |