package com.gkhy.fourierSpecialGasMonitor.schedule;
|
|
import com.gkhy.fourierSpecialGasMonitor.Application;
|
import com.gkhy.fourierSpecialGasMonitor.commons.enums.ResultCode;
|
import com.gkhy.fourierSpecialGasMonitor.commons.enums.SystemCacheKeyEnum;
|
import com.gkhy.fourierSpecialGasMonitor.commons.exception.BusinessException;
|
import com.gkhy.fourierSpecialGasMonitor.config.file.ReportFilePathConfig;
|
import com.gkhy.fourierSpecialGasMonitor.entity.*;
|
import com.gkhy.fourierSpecialGasMonitor.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.Month;
|
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 2 * * ?") //每天两点执行一次
|
// @Scheduled(cron = "0 0/1 * * * ? ") // 分钟
|
@Async(value = "SocketTaskExecutor")
|
public void generateDailyReport() {
|
LocalDateTime now = LocalDateTime.now();
|
String startTime = now.format(execformatter);
|
RBucket<List<GasCategory>> bucket = redissonClient.getBucket(SystemCacheKeyEnum.KEY_GAS_CATEGORY.getKey());
|
List<GasCategory> 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<String, Object> map = dataMap(gasCategories);
|
List<XWPFParagraph> paragraphList = doc.getParagraphs();
|
for (XWPFParagraph par : paragraphList) {
|
//获取段落的文本对象
|
List<XWPFRun> runs = par.getRuns();
|
for (XWPFRun run : runs) {
|
//获取文本的值
|
String text = run.getText(0);
|
//遍历map
|
for (Map.Entry<String, Object> 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<GasConcentration> gasConcentrations = gasConcentrationService.listDatabyTimeSlot(LocalDateTime.of(now.minusDays(1).toLocalDate(), LocalTime.MIN)
|
, LocalDateTime.of(now.minusDays(1).toLocalDate(), LocalTime.MAX));
|
// 绘制折线图
|
if (!CollectionUtils.isEmpty(gasConcentrations)) {
|
List<GasCategory> 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<GasFlux> gasFluxes = gasFluxService.listYesterday();
|
List<GasFlux> gasFluxes = gasFluxService.listYesterdayTenAmToSixPm();
|
List<Integer> areaNum = gasFluxes.stream().map(GasFlux::getAreaId).distinct().collect(Collectors.toList());
|
|
List<Region> allRegion = regionService.findAll();
|
if (CollectionUtils.isEmpty(allRegion))
|
throw new BusinessException(this.getClass(), ResultCode.SYSTEM_ERROR_DATABASE_FAIL.getCode(),"区域信息为空");
|
Map<Integer, String> 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<GasFlux> 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<GasFlux> 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<GasFlux> gasFluxes,String fileurl,String series,String molecularFormula,Integer i,Integer j) {
|
List<String> 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<Number[]> value2 = new ArrayList<>();//每一条折现图,第二个表1条
|
value2.add(n);
|
|
List<XWPFChart> 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()));
|
// 获取根元素 <a:r>
|
Element root = document.getDocumentElement();
|
// 获取包含文本的 <a:t> 元素
|
Element aT = (Element) root.getElementsByTagName("a:t").item(0);
|
// 获取 <a:t> 元素的文本内容 获取标题具体内容
|
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<GasConcentration> gasConcentrations,String fileurl,String series,String title,String molecularFormula,Integer i) {
|
List<String> 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<Number[]> value2 = new ArrayList<>();
|
value2.add(n);
|
|
//
|
List<XWPFChart> 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()));
|
// 获取根元素 <a:r>
|
Element root = document.getDocumentElement();
|
// 获取包含文本的 <a:t> 元素
|
Element aT = (Element) root.getElementsByTagName("a:t").item(0);
|
// 获取 <a:t> 元素的文本内容 获取标题具体内容
|
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<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 drawLineChartExec(XWPFChart chart, String[] series, String[] categories,
|
List<Number[]> values, String chartTitle) {
|
final List<XDDFChartData> data = chart.getChartSeries();
|
|
final XDDFLineChartData line = (XDDFLineChartData) data.get(0);//这里一般获取第一个,我们这里是折线图就是XDDFLineChartData
|
|
final int numOfPoints = categories.length;
|
|
//List<String> 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<? 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(chartTitle);//折线图标题
|
chart.setTitleOverlay(false);
|
}
|
|
private Map<String, Object> dataMap(List<GasCategory> gasCategories){
|
LocalDateTime now = LocalDateTime.now();
|
String today = now.format(formatter);
|
String yesterday = LocalDateTime.now().minusDays(1).format(formatter);
|
//要替换的map,key为占位符,value为要被替换的值
|
Map<String, Object> map = new HashMap<>();
|
map.put("${today}", today);
|
map.put("${yesterday}", yesterday);
|
map.put("${generalradius}","1km");
|
map.put("${generalradiu}","300m");
|
|
List<GasWarnLog> 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;
|
}
|
}
|