package com.gkhy.testFourierSpecialGasMonitor.schedule; import com.gkhy.testFourierSpecialGasMonitor.Application; import com.gkhy.testFourierSpecialGasMonitor.commons.enums.ResultCode; import com.gkhy.testFourierSpecialGasMonitor.commons.enums.SystemCacheKeyEnum; import com.gkhy.testFourierSpecialGasMonitor.commons.exception.BusinessException; import com.gkhy.testFourierSpecialGasMonitor.config.file.ReportFilePathConfig; import com.gkhy.testFourierSpecialGasMonitor.entity.*; import com.gkhy.testFourierSpecialGasMonitor.service.*; import org.apache.commons.compress.utils.IOUtils; import org.apache.poi.ooxml.POIXMLDocument; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xddf.usermodel.chart.*; import org.apache.poi.xddf.usermodel.text.XDDFTextBody; import org.apache.poi.xwpf.usermodel.*; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; import org.redisson.api.RBucket; import org.redisson.api.RedissonClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import org.w3c.dom.Element; import org.xml.sax.SAXException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import java.io.*; import java.lang.reflect.Field; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.temporal.ChronoUnit; import java.util.*; import java.util.stream.Collectors; /** * @author Mr.huang * @decription * @date 2023/8/11 13:25 */ @Component public class DailyReportSchedule { private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日"); private static final DateTimeFormatter reportNameFormatter = DateTimeFormatter.ofPattern("yyyy_MM_dd"); private static final DateTimeFormatter execformatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter lineChartXDataDisplay = DateTimeFormatter.ofPattern("HH:mm"); private final Logger logger = LoggerFactory.getLogger(this.getClass()); private static int serialNumber = 4; @Autowired private GasWarnLogService gasWarnLogService; @Autowired private GasConcentrationService gasConcentrationService; @Autowired private RedissonClient redissonClient; @Autowired private RegionService regionService; @Autowired private ReportFilePathConfig reportFilePathConfig; @Autowired private GasFluxService gasFluxService; @Autowired private MonitorDailyReportService monitorDailyReportService; @Autowired private GasCategoryService gasCategoryService; @Scheduled(cron = "0 0 9 * * ?") //每天九点执行一次 //@Scheduled(cron = "0 0/1 * * * ? ") // 分钟 @Async(value = "SocketTaskExecutor") public void generateDailyReport() { LocalDateTime now = LocalDateTime.now(); String startTime = now.format(execformatter); RBucket> bucket = redissonClient.getBucket(SystemCacheKeyEnum.KEY_GAS_CATEGORY.getKey()); List gasCategories = bucket.get(); serialNumber = 4; logger.info("【##】开始生成日报 ,时间:"+startTime); OPCPackage opcPackage = null; //加载文档 XWPFDocument doc = null; try { ClassLoader classLoader = Application.class.getClassLoader(); InputStream originalFileInputStream = classLoader.getResourceAsStream("template/dailyReportTemplate.docx"); String copyFileUrl = reportFilePathConfig.getDcPath()+"temp/dailyReportTemplate.docx"; File file = new File(copyFileUrl); if (!file.exists()){ try { // 获取文件所在的文件夹路径 String folderPath = file.getParent(); // 创建文件夹(包括父目录) File folder = new File(folderPath); if (!folder.exists()){ folder.mkdirs(); } file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } FileOutputStream copyFileOutputStream = new FileOutputStream(copyFileUrl); IOUtils.copy(originalFileInputStream, copyFileOutputStream); opcPackage = POIXMLDocument.openPackage(copyFileUrl); //FileInputStream originalFileInputStream = new FileInputStream("src/main/resources/template/dailyReportTemplate.docx"); //FileOutputStream copyFileOutputStream = new FileOutputStream("src/main/resources/temp/dailyReportTemplate.docx"); //// 创建副本文件 //IOUtils.copy(originalFileInputStream, copyFileOutputStream); //opcPackage = POIXMLDocument.openPackage("src/main/resources/temp/dailyReportTemplate.docx"); doc = new XWPFDocument(opcPackage); } catch (IOException e) { e.printStackTrace(); } Map map = dataMap(gasCategories); List paragraphList = doc.getParagraphs(); for (XWPFParagraph par : paragraphList) { //获取段落的文本对象 List runs = par.getRuns(); for (XWPFRun run : runs) { //获取文本的值 String text = run.getText(0); //遍历map for (Map.Entry entry : map.entrySet()) { //获取map的key String key = entry.getKey(); //判断文本的值和map的key,文本中是否有和key一样的占位符 if (text != null && text.indexOf(key) != -1) { //获取对应key的value Object value = entry.getValue(); //把文本的内容,key替换为value text = text.replace(key, value.toString()); //把替换好的文本内容,保存到当前这个文本对象 run.setText(text, 0); } } } } String format = now.format(reportNameFormatter); String fileName = "华泰重化工气体监测日报"+format+".docx"; String fileurl = reportFilePathConfig.getDcPath()+fileName; File file = new File(fileurl); if (!file.exists()){ try { // 获取文件所在的文件夹路径 String folderPath = file.getParent(); // 创建文件夹(包括父目录) File folder = new File(folderPath); if (!folder.exists()){ folder.mkdirs(); } file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { doc.write(fileOutputStream); fileOutputStream.close(); opcPackage.close(); } catch (IOException e) { e.printStackTrace(); } List gasConcentrations = gasConcentrationService.listDatabyTimeSlot(LocalDateTime.of(now.minusDays(1).toLocalDate(), LocalTime.MIN) , LocalDateTime.of(now.minusDays(1).toLocalDate(), LocalTime.MAX)); // 绘制折线图 if (!CollectionUtils.isEmpty(gasConcentrations)) { List gasCategoryForReport = gasCategoryService.findGasCategoryForReport(); for (int i = 0; i < gasCategoryForReport.size(); i++) { String series = gasCategories.get(i).getMolecularFormula() + "浓度观测结果"; String title = gasCategories.get(i).getMolecularFormula(); drawLineChart(gasConcentrations, fileurl, series, title, gasCategories.get(i).getMolecularFormula(), i); } } //List gasFluxes = gasFluxService.listYesterday(); List gasFluxes = gasFluxService.listYesterdayTenAmToSixPm(); List areaNum = gasFluxes.stream().map(GasFlux::getAreaId).distinct().collect(Collectors.toList()); List allRegion = regionService.findAll(); if (CollectionUtils.isEmpty(allRegion)) throw new BusinessException(this.getClass(), ResultCode.SYSTEM_ERROR_DATABASE_FAIL.getCode(),"区域信息为空"); Map regionMap = allRegion.stream() .collect(Collectors.toMap(Region::getId, Region::getName)); // 绘制柱状图 if (!CollectionUtils.isEmpty(gasFluxes)) { // for (int j = 0; j < gasCategories.size(); j++) { // for (int i = 1; i <= areaNum.size(); i++) { // //for (int j = 0; j < 20; j++) { // int finalI = i; // List gasFluxesByArea = gasFluxes.stream().filter(gasFlux -> gasFlux.getAreaId() == finalI) // .collect(Collectors.toList()); // drawBarChart(gasFluxesByArea, fileurl, regionMap.get(i), gasCategories.get(j).getMolecularFormula(), i, j + 1); // } // } for (int i = 1; i <= areaNum.size(); i++) { //for (int j = 0; j < 20; j++) { int finalI = i; List gasFluxesByArea = gasFluxes.stream().filter(gasFlux -> gasFlux.getAreaId() == finalI) .collect(Collectors.toList()); drawBarChart(gasFluxesByArea, fileurl, regionMap.get(i), gasCategories.get(0).getMolecularFormula(), i, 1); } } String endTime = LocalDateTime.now().format(execformatter); long execTime = ChronoUnit.SECONDS.between(now, LocalDateTime.now()); MonitorDailyReport report = new MonitorDailyReport(); report.setName(fileName); report.setGmtCreate(now); report.setEndTime(LocalDateTime.now()); report.setFileUrl(reportFilePathConfig.getUrlRootPath()+fileName); MonitorDailyReport save = monitorDailyReportService.save(report); if (save == null) throw new BusinessException(this.getClass(), ResultCode.SYSTEM_ERROR_DATABASE_FAIL,"日常报表保存失败"); logger.info("【##】日报生成成功!!! ,时间:"+endTime+",所耗时间: "+execTime+"s"); } /** * @decription 画柱形图 * @author Mr.huang */ public void drawBarChart(List gasFluxes,String fileurl,String series,String molecularFormula,Integer i,Integer j) { List collect = gasFluxes.stream() .map(gasFlux -> gasFlux.getTime().format(lineChartXDataDisplay)) .collect(Collectors.toList()); String[] x2 = collect.toArray(new String[collect.size()]); String fieldName = "gasValue"; if (j<10){ fieldName = fieldName + "0" + j; }else { fieldName = fieldName + j; } final String fieldTempName = fieldName; Double[] n = gasFluxes.stream().map(obj -> { try { Field field = obj.getClass().getDeclaredField(fieldTempName); field.setAccessible(true); return field.get(obj); } catch (NoSuchFieldException | IllegalAccessException e) { logger.info("柱状图反射获取字段值异常"); return null; } }).collect(Collectors.toList()).toArray(new Double[gasFluxes.size()]); String templatePath = fileurl; XWPFDocument doc=null; InputStream is = null; try{ is = new FileInputStream(new File(templatePath)); doc = new XWPFDocument(is); } catch (Exception e){ System.out.println(e); } //区域名称 String[] series2 = {series};// //y轴 List value2 = new ArrayList<>();//每一条折现图,第二个表1条 value2.add(n); List charts = doc.getCharts(); serialNumber++; for (int k = 0; k < charts.size(); k++) { XWPFChart xwpfChart = charts.get(k); XDDFTitle xddfTitletitle = xwpfChart.getTitle(); if (xddfTitletitle != null) { XDDFTextBody body = xddfTitletitle.getBody(); CTTextBody xmlObject = body.getXmlObject(); String tt = xmlObject.toString(); //图表的标题 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = factory.newDocumentBuilder(); // 解析 XML 字符串 org.w3c.dom.Document document = builder.parse(new ByteArrayInputStream(tt.getBytes())); // 获取根元素 Element root = document.getDocumentElement(); // 获取包含文本的 元素 Element aT = (Element) root.getElementsByTagName("a:t").item(0); // 获取 元素的文本内容 获取标题具体内容 String value = aT.getTextContent(); String chartTitle = "图"+serialNumber+" "+i+"号厂区排放"+molecularFormula+"通量反演结果"; if ((j+"-"+i).equals(value)) { drawBarChartExec(charts.get(k), series2, x2, value2, chartTitle,molecularFormula); } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } } try (FileOutputStream fos = new FileOutputStream(templatePath)) { doc.write(fos); doc.close(); is.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public void drawLineChart(List gasConcentrations,String fileurl,String series,String title,String molecularFormula,Integer i) { List collect = gasConcentrations.stream() .map(gasConcentration -> gasConcentration.getTime().format(lineChartXDataDisplay)) .collect(Collectors.toList()); String[] x2 = collect.toArray(new String[collect.size()]); String fieldName = "gasValue"; i = i+1; if (i<10){ fieldName = fieldName + "0" + i; }else { fieldName = fieldName + i; } final String fieldTempName = fieldName; Double[] n = gasConcentrations.stream().map(obj -> { try { Field field = obj.getClass().getDeclaredField(fieldTempName); field.setAccessible(true); return field.get(obj); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); return null; } }).collect(Collectors.toList()).toArray(new Double[gasConcentrations.size()]); String templatePath = fileurl; XWPFDocument doc=null; InputStream is = null; try{ is = new FileInputStream(new File(templatePath)); doc = new XWPFDocument(is); } catch (Exception e){ System.out.println(e); } String[] series2 = {"图" + (i)+" "+series};// String title2=title; //y轴 List value2 = new ArrayList<>(); value2.add(n); // List charts = doc.getCharts(); for (int j = 0; j < charts.size(); j++) { XWPFChart xwpfChart = charts.get(j); XDDFTitle xddfTitletitle = xwpfChart.getTitle(); if (xddfTitletitle != null) { XDDFTextBody body = xddfTitletitle.getBody(); CTTextBody xmlObject = body.getXmlObject(); String tt = xmlObject.toString(); //图表的标题 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder builder = factory.newDocumentBuilder(); // 解析 XML 字符串 org.w3c.dom.Document document = builder.parse(new ByteArrayInputStream(tt.getBytes())); // 获取根元素 Element root = document.getDocumentElement(); // 获取包含文本的 元素 Element aT = (Element) root.getElementsByTagName("a:t").item(0); // 获取 元素的文本内容 获取标题具体内容 String value = aT.getTextContent(); if (molecularFormula.equals(value)) { drawLineChartExec(charts.get(j), series2, x2, value2, title2); } } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } } } try (FileOutputStream fos = new FileOutputStream(templatePath)) { doc.write(fos); doc.close(); is.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public void drawBarChartExec(XWPFChart chart, String[] series, String[] categories, List values, String chartTitle,String molecularFormula) { final List 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 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 drawLineChartExec(XWPFChart chart, String[] series, String[] categories, List values, String chartTitle) { final List data = chart.getChartSeries(); final XDDFLineChartData line = (XDDFLineChartData) data.get(0);//这里一般获取第一个,我们这里是折线图就是XDDFLineChartData final int numOfPoints = categories.length; //List lineChartXDataDisplayDefault = Arrays.asList("00:00","01:00","02:00","03:00","04:00","05:00","06:00" // ,"07:00","08:00","09:00", // "10:00","11:00","12:00","13:00","19:00"); //for (int i = 0; i < numOfPoints; i++) { // if (!lineChartXDataDisplayDefault.contains(categories[i])) { // categories[i] = ""; // 设置为空字符串,隐藏原有的标签 // } //} 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 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(chartTitle);//折线图标题 chart.setTitleOverlay(false); } private Map dataMap(List gasCategories){ LocalDateTime now = LocalDateTime.now(); String today = now.format(formatter); String yesterday = LocalDateTime.now().minusDays(1).format(formatter); //要替换的map,key为占位符,value为要被替换的值 Map map = new HashMap<>(); map.put("${today}", today); map.put("${yesterday}", yesterday); map.put("${generalradius}","1km"); map.put("${generalradiu}","300m"); List gasWarnLogs = gasWarnLogService.listYesterday(); if (!CollectionUtils.isEmpty(gasWarnLogs)){ String warnInfo = "根据以上气体浓度变化曲线,各物种的浓度有如下特征:"; for (int i = 0; i < gasWarnLogs.size(); i++) { LocalDateTime warnTime = gasWarnLogs.get(i).getWarnTime(); String time = warnTime.format(execformatter); warnInfo = warnInfo + gasWarnLogs.get(i).getGasMolecularFormula() +"("+gasWarnLogs.get(i).getGasName()+")浓度在"+time+" 出现"+gasWarnLogs.get(i).getGasThresholdName() + "浓度达到:"+gasWarnLogs.get(i).getGasConcentration()+gasWarnLogs.get(i).getGasUnit() + "; "; } map.put("${warn}",warnInfo); }else { map.put("${warn}",""); } String fiveGas = ""; String sixGas = ""; for (int i = 0; i < 3; i++) { String name = gasCategories.get(i).getMolecularFormula()+"("+gasCategories.get(i).getName()+")"; name = name + "、"; if (i < 3){ fiveGas = fiveGas + name; if (i == 2){ int index = fiveGas.lastIndexOf("、"); fiveGas = fiveGas.substring(0, index)+"共三种"; } } if (i < 3){ sixGas = sixGas + name; if (i == 2){ int index = sixGas.lastIndexOf("、"); sixGas = sixGas.substring(0, index)+"共三种"; } } } map.put("${fiveGas}",fiveGas); map.put("${sixGas}",sixGas); return map; } }