|
|
@ -15,26 +15,30 @@ import com.zc.business.utils.PoiUtil; |
|
|
|
import org.apache.poi.xssf.usermodel.XSSFSheet; |
|
|
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
|
|
|
import org.apache.poi.xwpf.usermodel.*; |
|
|
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*; |
|
|
|
import org.apache.poi.ss.usermodel.Cell; |
|
|
|
import org.apache.poi.ss.usermodel.CellType; |
|
|
|
import org.apache.poi.ss.usermodel.Row; |
|
|
|
import org.apache.poi.xssf.usermodel.XSSFCell; |
|
|
|
import org.apache.poi.xssf.usermodel.XSSFRow; |
|
|
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth; |
|
|
|
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
import org.springframework.web.multipart.MultipartFile; |
|
|
|
|
|
|
|
import javax.servlet.http.HttpServletResponse; |
|
|
|
import java.io.FileInputStream; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.InputStream; |
|
|
|
import java.lang.reflect.Array; |
|
|
|
import java.math.BigInteger; |
|
|
|
import java.text.SimpleDateFormat; |
|
|
|
import java.time.LocalDateTime; |
|
|
|
import java.time.format.DateTimeFormatter; |
|
|
|
import java.util.*; |
|
|
|
import java.time.ZoneId; |
|
|
|
import java.util.regex.Matcher; |
|
|
|
import java.util.regex.Pattern; |
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
import static com.zc.business.utils.PoiUtil.*; |
|
|
|
|
|
|
|
import static com.zc.business.utils.PoiUtil.addDescription; |
|
|
|
|
|
|
|
/** |
|
|
@ -223,6 +227,353 @@ public class DcSdhsEventServiceImpl implements IDcSdhsEventService { |
|
|
|
return AjaxResult.success("导入成功"); |
|
|
|
}*/ |
|
|
|
|
|
|
|
/** |
|
|
|
* 事故车型分析 导出 |
|
|
|
* @param doc |
|
|
|
* @param dcSdhsEventQuery |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
public void exportAccidentModelAnalysis(XWPFDocument doc,DcSdhsEventQuery dcSdhsEventQuery) { |
|
|
|
// 初始化一个空的结果映射
|
|
|
|
Map<String, Map<String, Integer>> result = accidentModelAnalysis(dcSdhsEventQuery); |
|
|
|
|
|
|
|
// 计算最大车辆类型数量
|
|
|
|
int maxVehicleTypes = 5; |
|
|
|
// 创建一个具有最大车辆类型数量的列的表格
|
|
|
|
XWPFTable table = doc.createTable(result.size() + 1, maxVehicleTypes ); |
|
|
|
|
|
|
|
// 提取所有不同的车辆类型作为列标题
|
|
|
|
Set<String> vehicleTypes = new HashSet<>(); |
|
|
|
vehicleTypes.add("货车"); |
|
|
|
vehicleTypes.add("小型车"); |
|
|
|
vehicleTypes.add("客车"); |
|
|
|
vehicleTypes.add("罐车"); |
|
|
|
|
|
|
|
// 设置表头
|
|
|
|
XWPFTableRow headerRow = table.getRow(0); |
|
|
|
setTableFonts(headerRow.getCell(0), ""); // 第一列是事件类型
|
|
|
|
int colIdx = 1; |
|
|
|
for (String vehicleType : vehicleTypes) { |
|
|
|
table.getRow(0).getCell(colIdx++).setText(vehicleType); |
|
|
|
} |
|
|
|
|
|
|
|
// 填充表格内容
|
|
|
|
int rowIdx = 1; |
|
|
|
for (Map.Entry<String, Map<String, Integer>> entry : result.entrySet()) { |
|
|
|
XWPFTableRow row = table.getRow(rowIdx++); |
|
|
|
setTableFonts(row.getCell(0), entry.getKey()); // 事件类型
|
|
|
|
|
|
|
|
int cellIdx = 1; |
|
|
|
for (String vehicleType : vehicleTypes) { |
|
|
|
Integer count = entry.getValue().get(vehicleType); |
|
|
|
XWPFTableCell cell = row.getCell(cellIdx++); |
|
|
|
XWPFRun run = cell.getParagraphs().get(0).createRun(); |
|
|
|
run.setText(String.valueOf(count != null ?count : 0)); // 如果该事件没有这种车辆类型,则设为0
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 创建柱状图
|
|
|
|
try { |
|
|
|
// 复制Word模板
|
|
|
|
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("wordTemplate/accidentModelAnalysis.docx"); |
|
|
|
XWPFDocument copiedTemplate = new XWPFDocument(inputStream); |
|
|
|
// 获取word中所有图表对象
|
|
|
|
List<XWPFChart> charts = copiedTemplate.getCharts(); |
|
|
|
|
|
|
|
XWPFChart chart = charts.get(0); |
|
|
|
XSSFWorkbook workbook = chart.getWorkbook(); |
|
|
|
XSSFSheet sheet = workbook.getSheetAt(0); |
|
|
|
// 系列信息
|
|
|
|
String[] singleBarSeriesNames = vehicleTypes.toArray(new String[0]); |
|
|
|
|
|
|
|
// 填充表格内容
|
|
|
|
int rowId = 1; // 开始填充数据的行号
|
|
|
|
for (Map.Entry<String, Map<String, Integer>> entry : result.entrySet()) { |
|
|
|
|
|
|
|
XSSFRow row = sheet.getRow(rowId); |
|
|
|
if (row == null) { |
|
|
|
row = sheet.createRow(rowId); // 如果行不存在,则创建新行
|
|
|
|
} |
|
|
|
|
|
|
|
// 获取当前行的第一个单元格
|
|
|
|
Cell cell1 = row.getCell(0); |
|
|
|
|
|
|
|
// 如果单元格为 null,则创建一个新的单元格
|
|
|
|
if (cell1 == null) { |
|
|
|
cell1 = row.createCell(0); |
|
|
|
} |
|
|
|
|
|
|
|
// 设置单元格的值
|
|
|
|
cell1.setCellValue(entry.getKey()); |
|
|
|
row.getCell(0).setCellValue(entry.getKey()); // 设置事件类型
|
|
|
|
int cellIdx = 1; |
|
|
|
for (String vehicleType : vehicleTypes) { |
|
|
|
Integer count = entry.getValue().get(vehicleType); |
|
|
|
XSSFCell cell = row.getCell(cellIdx); // 获取或创建单元格
|
|
|
|
if (cell == null) { |
|
|
|
cell = row.createCell(cellIdx); // 如果单元格不存在,则创建新单元格
|
|
|
|
} |
|
|
|
cell.setCellValue(count != null ? count : 0); // 设置单元格值
|
|
|
|
cellIdx++; |
|
|
|
} |
|
|
|
rowId++; // 移动到下一行
|
|
|
|
} |
|
|
|
for (int i=sheet.getLastRowNum();i> result.size();i--){ |
|
|
|
sheet.removeRow(sheet.getRow(i)); |
|
|
|
} |
|
|
|
|
|
|
|
// 创建柱状图
|
|
|
|
PoiUtil.wordExportChar(chart, "事故车型分析", singleBarSeriesNames, sheet); |
|
|
|
|
|
|
|
// 追加到同一个Word文档中
|
|
|
|
mergeChart(chart, doc); |
|
|
|
// 关闭复制的模板文档
|
|
|
|
copiedTemplate.close(); |
|
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) { |
|
|
|
e.printStackTrace(); |
|
|
|
} |
|
|
|
|
|
|
|
// 换行
|
|
|
|
createLineBreak(doc); |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 事故车型分析 查询 |
|
|
|
* @param dcSdhsEventQuery |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
public Map<String, Map<String, Integer>>accidentModelAnalysis(DcSdhsEventQuery dcSdhsEventQuery) { |
|
|
|
List<DcSdhsEvent> cacheList = redisCache.getCacheList(RedisKeyConstants.SDHS_EVENT); |
|
|
|
|
|
|
|
// 将查询开始时间和结束时间转换为 LocalDateTime
|
|
|
|
ZoneId zoneId = ZoneId.systemDefault(); // 获取系统默认时区
|
|
|
|
LocalDateTime queryStart = dcSdhsEventQuery.getStartTime().toInstant().atZone(zoneId).toLocalDateTime(); |
|
|
|
LocalDateTime queryEnd = dcSdhsEventQuery.getEndTime().toInstant().atZone(zoneId).toLocalDateTime(); |
|
|
|
|
|
|
|
// 在指定时间范围内的事件
|
|
|
|
String direction = dcSdhsEventQuery.getDirection(); // 获取方向查询参数
|
|
|
|
|
|
|
|
List<DcSdhsEvent> filteredEvents = cacheList.stream() |
|
|
|
.filter(event -> { |
|
|
|
LocalDateTime eventTime = event.getStartTime().toInstant().atZone(zoneId).toLocalDateTime(); |
|
|
|
boolean timeCondition = |
|
|
|
(eventTime.isAfter(queryStart) || eventTime.isEqual(queryStart)) && |
|
|
|
(eventTime.isBefore(queryEnd) || eventTime.isEqual(queryEnd)); |
|
|
|
|
|
|
|
// 如果 direction 不为空,则添加方向过滤条件
|
|
|
|
if (direction != null && !direction.isEmpty()) { |
|
|
|
return timeCondition && direction.equals(event.getDirection()); |
|
|
|
} else { |
|
|
|
return timeCondition; |
|
|
|
} |
|
|
|
}) |
|
|
|
.collect(Collectors.toList()); |
|
|
|
// 初始化一个空的结果映射
|
|
|
|
Map<String, Map<String, Integer>> result = new HashMap<>(); |
|
|
|
|
|
|
|
for (DcSdhsEvent event : filteredEvents) { |
|
|
|
String eventType = event.getEventType(); |
|
|
|
if ("交通事故".equals(eventType)) { |
|
|
|
String eventSubclass = event.getEventSubclass(); |
|
|
|
String carNums = event.getCarNum(); |
|
|
|
|
|
|
|
// 分割字符串,得到每种类型的车辆数量
|
|
|
|
List<String> carNumList = Arrays.asList(carNums.split(",")); |
|
|
|
|
|
|
|
// 如果结果映射中还没有这个事件子类,就初始化一个新的映射
|
|
|
|
if (!result.containsKey(eventSubclass)) { |
|
|
|
result.put(eventSubclass, new HashMap<>()); |
|
|
|
} |
|
|
|
|
|
|
|
// 统计每种类型的车辆数量
|
|
|
|
Map<String, Integer> carNumCount = result.get(eventSubclass); |
|
|
|
|
|
|
|
for (String carNum : carNumList) { |
|
|
|
// 检查字符串格式是否正确
|
|
|
|
int index = carNum.indexOf("辆"); |
|
|
|
if (index > 0) { // 确保 "辆" 的索引大于 0,表示字符串中包含 "辆" 且前面有字符
|
|
|
|
// 提取车辆类型和数量
|
|
|
|
String carType = carNum.substring(0, index-1); // 车辆类型
|
|
|
|
int carCount = Integer.parseInt(carNum.substring(index - 1, index)); // 数量
|
|
|
|
|
|
|
|
// 在映射中累加数量
|
|
|
|
carNumCount.merge(carType, carCount, Integer::sum); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 事故时间分析查询 |
|
|
|
* @param dcSdhsEventQuery |
|
|
|
* @return |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
public Map<Integer, Long> accidentTimeAnalysis(DcSdhsEventQuery dcSdhsEventQuery) { |
|
|
|
List<DcSdhsEvent> cacheList = redisCache.getCacheList(RedisKeyConstants.SDHS_EVENT); |
|
|
|
// 将查询开始时间和结束时间转换为 LocalDateTime
|
|
|
|
ZoneId zoneId = ZoneId.systemDefault(); // 获取系统默认时区
|
|
|
|
LocalDateTime queryStart = dcSdhsEventQuery.getStartTime().toInstant().atZone(zoneId).toLocalDateTime(); |
|
|
|
LocalDateTime queryEnd = dcSdhsEventQuery.getEndTime().toInstant().atZone(zoneId).toLocalDateTime(); |
|
|
|
|
|
|
|
// 在指定时间范围内的事件
|
|
|
|
String direction = dcSdhsEventQuery.getDirection(); // 获取方向查询参数
|
|
|
|
|
|
|
|
List<DcSdhsEvent> filteredEvents = cacheList.stream() |
|
|
|
.filter(event -> { |
|
|
|
LocalDateTime eventTime = event.getStartTime().toInstant().atZone(zoneId).toLocalDateTime(); |
|
|
|
boolean timeCondition = |
|
|
|
(eventTime.isAfter(queryStart) || eventTime.isEqual(queryStart)) && |
|
|
|
(eventTime.isBefore(queryEnd) || eventTime.isEqual(queryEnd)); |
|
|
|
|
|
|
|
// 如果 direction 不为空,则添加方向过滤条件
|
|
|
|
if (direction != null && !direction.isEmpty()) { |
|
|
|
return timeCondition && direction.equals(event.getDirection()); |
|
|
|
} else { |
|
|
|
return timeCondition; |
|
|
|
} |
|
|
|
}) |
|
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
|
|
// 按小时分组并统计每个小时内的事件数量
|
|
|
|
Map<Integer, Long> hourlyCounts = filteredEvents.stream() |
|
|
|
.collect( |
|
|
|
Collectors.groupingBy( |
|
|
|
event -> event.getStartTime().toInstant().atZone(zoneId).toLocalDateTime().getHour(), |
|
|
|
Collectors.counting() |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
// 小时都出现在结果中,如果不存在设为 0
|
|
|
|
for (int hour = 0; hour <= 23; hour++) { |
|
|
|
hourlyCounts.merge(hour, 0L, Long::sum); |
|
|
|
} |
|
|
|
|
|
|
|
return hourlyCounts; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 事故时间分析导出 |
|
|
|
* @param doc |
|
|
|
* @param dcSdhsEventQuery |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
public void exportAccidentTimeAnalysis(XWPFDocument doc,DcSdhsEventQuery dcSdhsEventQuery) { |
|
|
|
|
|
|
|
Map<Integer, Long> currentYearData = accidentTimeAnalysis(dcSdhsEventQuery); |
|
|
|
// 获取一年前的日期
|
|
|
|
Date startTime = dcSdhsEventQuery.getStartTime(); |
|
|
|
Date endTime = dcSdhsEventQuery.getEndTime(); |
|
|
|
// 使用 Calendar 计算一年前的时间
|
|
|
|
Calendar calendar = Calendar.getInstance(); |
|
|
|
calendar.setTime(startTime); // 设置当前时间为 startTime
|
|
|
|
calendar.add(Calendar.YEAR, -1); // 减去一年
|
|
|
|
Date oneYearAgoStart = calendar.getTime(); // 一年前的开始时间
|
|
|
|
calendar.setTime(endTime); // 设置当前时间为 endTime
|
|
|
|
calendar.add(Calendar.YEAR, -1); // 减去一年
|
|
|
|
Date oneYearAgoEnd = calendar.getTime(); // 一年前的结束时间
|
|
|
|
dcSdhsEventQuery.setStartTime(oneYearAgoStart); |
|
|
|
dcSdhsEventQuery.setEndTime(oneYearAgoEnd); |
|
|
|
Map<Integer, Long> lastYearData = accidentTimeAnalysis(dcSdhsEventQuery); |
|
|
|
|
|
|
|
// 创建表格
|
|
|
|
XWPFTable table = doc.createTable(25,3); |
|
|
|
CTTblWidth infoTableWidth = table.getCTTbl().addNewTblPr().addNewTblW(); |
|
|
|
infoTableWidth.setType(STTblWidth.DXA); |
|
|
|
infoTableWidth.setW(BigInteger.valueOf(UniversalEnum.NINE_THOUSAND_AND_SEVENTY_TWO.getNumber())); |
|
|
|
|
|
|
|
// 表头
|
|
|
|
setTableFonts(table.getRow(0).getCell(0), "时段"); |
|
|
|
setTableFonts(table.getRow(0).getCell(1), "今年同期"); |
|
|
|
setTableFonts(table.getRow(0).getCell(2), "去年同期"); |
|
|
|
|
|
|
|
// 填充表格数据
|
|
|
|
for (int i = 0; i < 24; i++) { |
|
|
|
setTableFonts(table.getRow(i + 1).getCell(0), (i ) + "点"); |
|
|
|
Long currentYearValue = currentYearData.getOrDefault(i, 0L); |
|
|
|
setTableFonts(table.getRow(i + 1).getCell(1), String.valueOf(currentYearValue)); |
|
|
|
Long lastYearValue = lastYearData.getOrDefault(i, 0L); |
|
|
|
setTableFonts(table.getRow(i + 1).getCell(2), String.valueOf(lastYearValue)); |
|
|
|
} |
|
|
|
|
|
|
|
// 图表部分
|
|
|
|
try { |
|
|
|
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("wordTemplate/accidentTimeAnalysis.docx"); |
|
|
|
XWPFDocument copiedTemplate = new XWPFDocument(inputStream); |
|
|
|
|
|
|
|
List<XWPFChart> charts = copiedTemplate.getCharts(); |
|
|
|
XWPFChart chart = charts.get(0); |
|
|
|
XSSFWorkbook workbook = chart.getWorkbook(); |
|
|
|
XSSFSheet sheet = workbook.getSheetAt(UniversalEnum.ZERO.getNumber()); |
|
|
|
// 更新图表数据
|
|
|
|
for (int i = UniversalEnum.ZERO.getNumber(); i < 24; i++){ |
|
|
|
sheet.getRow(i + 1).getCell(0).setCellValue((i) + "点"); |
|
|
|
int rowIndex = i + UniversalEnum.ONE.getNumber(); // 计算行索引
|
|
|
|
Row row = sheet.getRow(rowIndex); |
|
|
|
if (row == null) { |
|
|
|
// 如果行不存在,则创建新行
|
|
|
|
row = sheet.createRow(rowIndex); |
|
|
|
} |
|
|
|
|
|
|
|
// 现在可以安全地访问或创建单元格了
|
|
|
|
Cell cell = row.getCell(UniversalEnum.ONE.getNumber()); |
|
|
|
if (cell == null) { |
|
|
|
// 通常情况下,getCell 会创建新的单元格,但这里我们显式地检查以防万一
|
|
|
|
cell = row.createCell(UniversalEnum.ONE.getNumber(), CellType.NUMERIC); |
|
|
|
} |
|
|
|
|
|
|
|
// 设置单元格的值
|
|
|
|
sheet.getRow(i + 1).getCell(1).setCellValue(currentYearData.get(i)); |
|
|
|
int rowIndexTwo = i +1+ UniversalEnum.ONE.getNumber(); // 计算行索引
|
|
|
|
Row rowTwo = sheet.getRow(rowIndexTwo); |
|
|
|
if (rowTwo == null) { |
|
|
|
// 如果行不存在,则创建新行
|
|
|
|
rowTwo = sheet.createRow(rowIndexTwo); |
|
|
|
} |
|
|
|
|
|
|
|
// 现在可以安全地访问或创建单元格了
|
|
|
|
Cell cellTwo = row.getCell(UniversalEnum.TWO.getNumber()); |
|
|
|
if (cellTwo == null) { |
|
|
|
// 通常情况下,getCell 会创建新的单元格,但这里我们显式地检查以防万一
|
|
|
|
cell = row.createCell(UniversalEnum.TWO.getNumber(), CellType.NUMERIC); |
|
|
|
} |
|
|
|
sheet.getRow(i + 1).getCell(2).setCellValue(lastYearData.get(i)); |
|
|
|
//sheet.getRow(i+UniversalEnum.ONE.getNumber()).getCell(UniversalEnum.ONE.getNumber()).setCellValue(Long.parseLong(entries.get(i).getValue().toString()));
|
|
|
|
} |
|
|
|
/* for (int i = 0; i < 24; i++) { |
|
|
|
|
|
|
|
sheet.getRow(i + 1).getCell(0).setCellValue((i) + "点"); |
|
|
|
sheet.getRow(i + 1).getCell(1).setCellValue(currentYearData.get(i)); |
|
|
|
sheet.getRow(i + 1).getCell(2).setCellValue(lastYearData.get(i)); |
|
|
|
}*/ |
|
|
|
|
|
|
|
// 更新图表
|
|
|
|
PoiUtil.wordExportChar(chart, "事故时间分析", new String[]{"今年同期", "去年同期"}, sheet); |
|
|
|
|
|
|
|
// 合并图表到Word文档
|
|
|
|
mergeChart(chart, doc); |
|
|
|
|
|
|
|
// 关闭模板文档
|
|
|
|
copiedTemplate.close(); |
|
|
|
} catch (Exception e){ |
|
|
|
e.printStackTrace(); |
|
|
|
} |
|
|
|
//换行
|
|
|
|
createLineBreak(doc); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* @Description 提取文字中的数字 |
|
|
|
* |
|
|
|