前面Controller层传入了相关参数,现在调用service方法进行
@GetMapping("/getEquipmentWord")
public ResponseEntity<byte[]> getReportWord(
@RequestParam LocalDate startTime
, @RequestParam LocalDate endTime
, @RequestParam String stationNum
, @RequestParam String deviceType
/* @RequestParam Integer type*/) throws Exception {
String fileName =startTime +"-"+endTime + "数据质控报告.docx";
//生成并导出设备相关Word文档的Java方法,主要功能是将特定时间段、类型及站点的设备数据填充到模板中并输出为文档。
qcService.exportEquipmentWord(Const.STATE_XLSX_TEMP_PATH+ fileName, startTime,endTime,deviceType,stationNum);
public void exportEquipmentWord(String outPath, LocalDate startTime, LocalDate endTime, String type,String stationNum ) throws Exception {
EquipmentTableDto equipmentTableDto = getEquipmentData(startTime, endTime,type,stationNum);
Configure config = Configure.newBuilder().customPolicy(ApiConst.EQPMT_TABLE, new ReportTablePolicy()).build();
String path = ApiConst.EQUIPMENT_DOC_TEMP_PATH_OTHER;
equipmentTableDto.setEquipmentName(collectionToDeviceNameMap.get(type));
export(config, path, outPath, equipmentTableDto);
}
1. 方法功能概述
该方法名为exportEquipmentWord,核心作用是根据输入的参数生成设备报表的Word文档并导出到指定路径。结合代码中的命名和调用逻辑,可推测其业务场景可能涉及设备管理系统的数据导出功能,例如生成设备运行报告、维护记录等。
2. 参数解析
**outPath**: 输出文件的保存路径。
**startTime/endTime**: 指定数据查询的时间范围(LocalDate类型)。
**type**: 设备类型标识,用于筛选数据。
**stationNum**: 站点编号,进一步限定设备范围。
3. 代码执行流程
步骤1:获取设备数据
java
EquipmentTableDto equipmentTableDto = getEquipmentData(startTime, endTime, type, stationNum);
调用getEquipmentData()方法,从数据库或其他数据源中查询符合时间、类型和站点条件的设备数据,并封装为EquipmentTableDto对象。该DTO可能包含表格字段、设备名称等属性。
步骤2:配置文档生成策略
java
Configure config = Configure.newBuilder()
.customPolicy(ApiConst.EQPMT_TABLE, new ReportTablePolicy())
.build();
创建文档生成的配置对象Configure,并通过customPolicy()指定自定义策略ReportTablePolicy,用于处理设备表格(ApiConst.EQPMT_TABLE)的渲染逻辑。这表明文档中可能包含动态表格,需按特定规则填充数据。
步骤3:设置模板路径与设备名称
java
String path = ApiConst.EQUIPMENT_DOC_TEMP_PATH_OTHER;
equipmentTableDto.setEquipmentName(collectionToDeviceNameMap.get(type));
模板路径:从常量ApiConst获取预定义的Word模板路径(如/templates/equipment.docx)。
设备名称映射:根据type参数从collectionToDeviceNameMap获取设备名称并设置到DTO中。这可能是将内部编码转换为用户友好的显示名称。
步骤4:调用导出方法
java
export(config, path, outPath, equipmentTableDto);
将配置、模板路径、输出路径及数据对象传递给export()方法,完成Word文档的生成与导出。此方法可能使用模板引擎(如Apache POI、Freemarker)将数据填充到模板的指定位置。
4. 技术实现细节
模板引擎机制:代码中隐含使用模板化文档生成,可能通过占位符替换或动态表格插入实现数据绑定。例如,模板中可能包含类似{{equipmentName}}的标记,由ReportTablePolicy处理复杂表格结构。
异常处理:方法声明抛出Exception,说明可能涉及文件IO操作、模板加载或数据查询的潜在错误。
扩展性设计:通过customPolicy支持自定义策略,便于未来扩展其他文档元素(如图表、多级列表)的生成逻辑。
5. 关联业务场景
设备管理报表:如生成设备运行状态报告、维护记录或巡检清单。
标准化输出:通过预定义模板确保文档格式统一,符合企业或行业规范(类似网页5提到的CT操作手册生成流程)。
数据驱动:基于时间范围和设备类型动态筛选数据,支持按需导出不同维度的统计结果。
逻辑:设置一个查询参数,去mongdb查数据
public class QcStatisticDomain {
@ApiModelProperty(value = "开始时间", required = true, example = "2023-01-01 00:00:00")
private String beginTime;
@ApiModelProperty(value = "结束时间", required = true, example = "2023-01-02 00:00:00")
private String endTime;
@ApiModelProperty(value = "设备Id", required = false, example = "")
private Integer deviceId;
@ApiModelProperty(value = "设备类型", required = true, example = "QC_wpr")
private String deviceType;
@ApiModelProperty(value = "站号", required = true, example = "59134")
private String stationNum;
将前端传来的时间参数格式化为字符串,然后计算他们之间相差的天数
// 将开始时间和结束时间转换为字符串
String startDateStr = startTime.toString();
String endDateStr = endTime.toString();
// 使用指定格式解析日期字符串
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate startDate = LocalDate.parse(startDateStr, formatter);
LocalDate endDate = LocalDate.parse(endDateStr, formatter);
// 计算开始日期和结束日期之间的天数
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
创建LocalDate对象
LocalDate类代表了一个不含时区信息的日期,只包含年、月、日三个部分。要计算两个日期之间的天数差,我们需要为这两个日期创建LocalDate对象。以下是如何创建表示特定日期的LocalDate实例:LocalDate startDate = LocalDate.of(2022, 6, 1); // 创建起始日期(年份、月份、日期)
LocalDate endDate = LocalDate.of(2022, 7, 31); // 创建结束日期(年份、月份、日期)
在这里,我们创建了两个LocalDate对象:
startDate表示2022年6月1日,endDate表示2022年7月31日。
使用ChronoUnit计算天数差
Java 8的java.time.temporal.ChronoUnit枚举类提供了一系列预定义的时间单位,包括DAYS。我们可以使用between()方法,传入两个LocalDate对象,轻松计算它们之间的天数差:
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
上述代码中,ChronoUnit.DAYS.between(startDate, endDate)返回的是一个long类型的整数,表示startDate和endDate之间相隔的完整天数。在这个示例中,它将计算2022年6月1日至2022年7月31日之间的天数。
拿到天数之后,我们就可以弄一个循环遍历,将每天的异常报告数据查询一遍,然后添加到集合中
创建 EquipmentTableDto 对象,设置当前时间、开始时间和结束时间。
创建 DetailDto 对象,用于存储后续生成的表格数据。
初始化一个空的 RowRenderData 列表,用于存放每日的统计数据。
// 创建并初始化设备数据的DTO对象
EquipmentTableDto equipmentTableDto = new EquipmentTableDto();
equipmentTableDto.setIseTime(LocalDate.now().toString());
equipmentTableDto.setStartTime(startTime.toString());
equipmentTableDto.setEndTime(endTime.toString());
// 创建详情对象和行数据列表
DetailDto detailDto = new DetailDto();
List<RowRenderData> rowDataList = new ArrayList<>();
日期计算:通过startDate.plusDays(i)生成当前循环的日期,并将其转换为字符串格式。
设置时间范围:将生成的日期字符串设置为qcStatisticDomain的开始时间和结束时间,表示一天的时间范围。
调用统计方法:调用qcBarStatisticA方法,传入qcStatisticDomain对象,获取该天的统计数据,返回一个BarVO对象。
startDate.plusDays(i):基于startDate,增加i天,生成一个新的LocalDate对象。
变量date存储了计算后的日期,用于后续统计每日的数据。
第一天传入qcbarstat中
for (int i = 0; i < daysBetween; i++) {
LocalDate date = startDate.plusDays(i);
String dateStr = date.toString();
qcStatisticDomain.setBeginTime(dateStr);
qcStatisticDomain.setEndTime(dateStr);
BarVO barVO = qcBarStatisticA(qcStatisticDomain);
步骤1:参数解析与时间范围设定
调用 DateUtil.offsetDay 方法,传入 endTime 和偏移量 -1。
偏移量 -1 表示将 endTime 减去一天,得到前一天的时间,然后赋值给begintime
String collectionName = qcStatisticDomain.getDeviceType(); // 根据设备类型确定MongoDB集合名
DateTime endTime = DateUtil.parse(qcStatisticDomain.getEndTime()); // 解析结束时间
DateTime beginTime = DateUtil.offsetDay(endTime, -1); // 计算开始时间(结束时间的前一天)
- 集合动态选择:通过设备类型
deviceType
动态确定MongoDB集合名称,支持多设备类型数据隔离。 - 时间范围:设定统计范围为从结束时间前24小时至结束时间(例如:若结束时间为2025-03-28,则统计2025-03-27至2025-03-28的数据)。
步骤2:构建MongoDB查询条件
Query query;
if (!qcStatisticDomain.getStationNum().equals("stations")) {
// 按指定站点过滤
query = new Query(Criteria.where("stationNum").is(qcStatisticDomain.getStationNum())
.and("dataTime").gte(beginTime).lte(endTime));
} else {
// 全站点统计
query = new Query(Criteria.where("dataTime").gte(beginTime).lte(endTime));
}
- 条件分支:若
stationNum
不是默认值"stations",则添加站点过滤条件;否则统计所有站点数据。 - 时间过滤:通过
gte
(大于等于)和lte
(小于等于)限定时间范围。
步骤3:执行数据库查询
List<QcWpr> results = mongoTemplate.find(query, QcWpr.class, collectionName);
- 数据获取:使用Spring Data MongoDB的
mongoTemplate
执行查询,返回QcWpr
实体列表。QcWpr
类应包含qcFlag
(状态标识)和dataTime
(数据时间)等字段。
步骤4:按日期分类统计状态数量
Map<String, int[]> statisticsMap = new HashMap<>(); // Key: 日期,Value: [正常数, 可疑数, 错误数]
for (QcWpr item : results) {
String qfFlag = item.getQcFlag();
String formattedDate = DateUtil.format(item.getDataTime(), "yyyy-MM-dd");
// 初始化或更新统计数组
statisticsMap.putIfAbsent(formattedDate, new int[3]);
if ("0".equals(qfFlag)) {
statisticsMap.get(formattedDate)[0]++;
} else if ("1".equals(qfFlag)) {
statisticsMap.get(formattedDate)[1]++;
} else if ("2".equals(qfFlag)) {
statisticsMap.get(formattedDate)[2]++;
}
}
- 状态映射:
qcFlag
的"0"、"1"、"2"分别对应正常、可疑、错误状态。 - 日期聚合:将同一日期的数据合并统计,使用
HashMap
实现快速查找与更新。
步骤5:封装结果到VO对象
BarVO barVO = new BarVO();
barVO.setDateList(dateList); // 日期列表(如["2025-03-27", "2025-03-28"])
barVO.setNormalList(normalList); // 每日正常数量
barVO.setSuspiciousList(suspiciousList); // 每日可疑数量
barVO.setErrorList(errorList); // 每日错误数量
- 数据结构:
BarVO
对象包含四个列表,可直接用于前端图表库(如ECharts)渲染柱状图。
3. 关键技术点
- 动态查询:通过
deviceType
和stationNum
实现多设备、多站点的灵活统计。 - 时间处理:使用
DateUtil
(可能来自Hutool库)简化日期计算与格式化操作。 - 聚合统计:利用Map按日期分组,避免多次遍历数据,时间复杂度为O(n)。
- 状态编码:硬编码的
qcFlag
值(0/1/2)需与业务定义严格一致,否则会导致统计错误。
查询完数据后,先进行数值为空处理,如果为空,直接编辑百分比为0.因为这时的数据渲染会倒序,反转一下
使用String.format("%.2f", error * 100)将错误率error乘以100并保留两位小数。
将结果转换为百分比形式,并拼接%符号。
调用RowRenderData.build方法,将日期和三个百分比值(错误率、可疑率、正常率)组合成一行数据。数据为一行。
将生成的数据行添加到rowDataList列表中。
再将rowDataList列表添加到
detailDto.setRecords(rowDataList);中
再将
equipmentTableDto.setDetail(detailDto);
这样层层封装会得到一个数据DTO对象,将数据DTO对象返回
public void exportEquipmentWord(String outPath, LocalDate startTime, LocalDate endTime, String type,String stationNum ) throws Exception {
// 获取指定条件下的设备数据
EquipmentTableDto equipmentTableDto = getEquipmentData(startTime, endTime,type,stationNum);
// 配置自定义策略,用于指定API的设备表格
Configure config = Configure.newBuilder().customPolicy(ApiConst.EQPMT_TABLE, new ReportTablePolicy()).build();
// 设备报告模板路径
String path = ApiConst.EQUIPMENT_DOC_TEMP_PATH_OTHER;
// 根据设备类型代码获取设备名称,并设置到设备数据对象中
equipmentTableDto.setEquipmentName(collectionToDeviceNameMap.get(type));
// 执行导出操作,将设备数据导出为Word文档
export(config, path, outPath, equipmentTableDto);
}
有一个生成图标策略
/**
* 重写render方法以定制表格的渲染逻辑
* 该方法将给定的数据对象转换为表格形式的文档部分
*
* @param table 要填充数据的表格对象
* @param data 包含表格数据的源对象,预期为DetailDto类型
*/
@Override
public void render(XWPFTable table, Object data) {
// 如果数据为空,则直接返回,不进行渲染
if (null == data){
return;
}
// 将数据对象显式转换为DetailDto类型
com.smart.mcp.entity.vo.DetailDto reportTableData = (com.smart.mcp.entity.vo.DetailDto) data;
// 获取数据记录列表
List<RowRenderData> records = reportTableData.getRecords();
// 如果记录列表不为空,则移除表格中开始行,准备插入新数据
if (null != records) {
table.removeRow(recordsStartRow);
// 遍历记录列表,为每条记录插入新行并填充单元格
for (int i = 0; i < records.size(); i++) {
XWPFTableRow insertNewTableRow = table.insertNewTableRow(recordsStartRow);
// 为当前行创建与记录中单元格数量相等的单元格
for (int j = 0; j < records.get(i).size(); j++){
insertNewTableRow.createCell();
}
// 使用特定策略渲染当前行
MiniTableRenderPolicy.Helper.renderRow(table, recordsStartRow, records.get(i));
}
}
}
特定策略:
/**
* 根据提供的行渲染数据渲染表格中指定行的单元格
* 此方法主要用于根据RowRenderData对象中的数据,更新表格中指定行的样式和内容
* 它通过遍历行中的每个单元格,并应用相应的渲染逻辑
*
* @param table 表格对象,代表一个Word文档中的表格
* @param row 需要渲染的行号,行号从0开始计数
* @param rowData 包含行渲染所需数据的对象,包括单元格数据和行样式
*/
public static void renderRow(XWPFTable table, int row, RowRenderData rowData) {
// 检查rowData是否非空且包含至少一个单元格数据,否则不执行渲染
if (null != rowData && rowData.size() > 0) {
// 获取表格中指定行号的行对象
XWPFTableRow tableRow = table.getRow(row);
// 确保行对象存在,如果不存在则抛出异常
ObjectUtils.requireNonNull(tableRow, "Row " + row + " do not exist in the table");
// 获取行的样式数据
TableStyle rowStyle = rowData.getRowStyle();
// 获取单元格渲染数据列表
List<CellRenderData> cellList = rowData.getCellDatas();
// 定义一个单元格对象用于后续渲染
XWPFTableCell cell = null;
// 遍历单元格渲染数据列表,按索引更新表格中的单元格
for(int i = 0; i < cellList.size(); ++i) {
// 获取当前行的指定索引单元格
cell = tableRow.getCell(i);
// 如果单元格不存在,记录警告信息并中断渲染流程
if (null == cell) {
RenderPolicy.logger.warn("Extra cell data at row {}, but no extra cell: col {}", row, cell);
break;
}
// 渲染当前单元格,应用单元格渲染数据和行样式
renderCell(cell, (CellRenderData)cellList.get(i), rowStyle);
}
}
}
接着
// 设备报告模板路径
String path = ApiConst.EQUIPMENT_DOC_TEMP_PATH_OTHER;
// 根据设备类型代码获取设备名称,并设置到设备数据对象中
equipmentTableDto.setEquipmentName(collectionToDeviceNameMap.get(type));
// 执行导出操作,将设备数据导出为Word文档
export(config, path, outPath, equipmentTableDto);
/**
* 将数据导出为Word文档
* 该方法使用模板处理数据,并将填充后的文档保存到指定路径
*
* @param config 配置对象,用于模板的配置
* @param tempPath 模板文件路径
* @param outPath 输出文件路径
* @param data 要填充到模板的数据
*/
public static void export(Configure config, String tempPath, String outPath, Object data) {
// 编译模板并渲染数据
XWPFTemplate template = XWPFTemplate.compile(tempPath, config).render(data);
// 尝试将填充后的模板内容写入到输出文件中
try {
template.writeToFile(outPath);
} catch (Exception e) {
// 如果在写入文件过程中发生异常,则打印异常信息
e.printStackTrace();
}
// 尝试关闭模板资源
try {
template.close();
} catch (Exception e) {
// 如果在关闭模板过程中发生异常,则打印异常信息
e.printStackTrace();
}
}
// 调用服务方法生成Word文档
qcService.exportEquipmentWord(Const.STATE_XLSX_TEMP_PATH+ fileName, startTime,endTime,deviceType,stationNum);
// 创建File对象以访问生成的Word文档
File docFile = new File(Const.STATE_XLSX_TEMP_PATH + fileName);
// 初始化文档内容数组
byte[] body = null;
// 打开文件输入流以读取文档内容
InputStream is = new FileInputStream(docFile);
// 将文档内容读入字节数组
body = new byte[is.available()];
is.read(body);
// 创建HTTP响应头对象
HttpHeaders headers = new HttpHeaders();
// 对文件名进行编码以适应HTTP响应头的规范
fileName = new String(fileName.getBytes("Gb2312"), "ISO-8859-1");
// 设置响应头,指示客户端将响应内容视为附件,并指定文件名
headers.add("Content-Disposition", "attchement;filename=" + fileName);
// 设置HTTP响应状态码为200 OK
HttpStatus statusCode = HttpStatus.OK;
// 创建并返回包含文档内容、响应头和状态码的ResponseEntity对象
ResponseEntity<byte[]> entity = new ResponseEntity<>(body, headers, statusCode);
return entity;
rowDataList列表的数据类型是
RowRenderData
这段代码定义了一个名为 RowRenderData 的类,用于表示表格中的一行数据及其样式。主要功能如下:
包含两个核心属性:cellDatas(单元格数据列表)和 rowStyle(行样式)。
提供多个构造方法和静态方法 build,用于创建 RowRenderData 实例。
支持设置和获取单元格数据及行样式。
提供 size 方法返回单元格数据的数量。
cellDatas(单元格数据列表)的数据类型是
CellRenderData 该代码定义了一个名为CellRenderData的类,用于存储单元格的渲染数据和样式信息。主要功能如下: 包含两个成员变量:renderData(文本渲染数据)和cellStyle(单元格样式)。 提供了三个构造方法,分别支持无参、仅传入renderData、同时传入renderData和cellStyle的初始化方式。 提供了对renderData和cellStyle的getter和setter方法,用于访问和修改这些属性。
public class RowRenderData implements RenderData {
private List<CellRenderData> cellDatas;
private TableStyle rowStyle;
public RowRenderData() {
}
public RowRenderData(List<CellRenderData> cellDatas) {
this.cellDatas = cellDatas;
}
public static RowRenderData build(String... cellStr) {
List<TextRenderData> cellDatas = new ArrayList();
if (null != cellStr) {
String[] var2 = cellStr;
int var3 = cellStr.length;
for(int var4 = 0; var4 < var3; ++var4) {
String col = var2[var4];
cellDatas.add(new TextRenderData(col));
}
}
return new RowRenderData(cellDatas, (String)null);
}
public static RowRenderData build(TextRenderData... cellData) {
return new RowRenderData(null == cellData ? null : Arrays.asList(cellData), (String)null);
}
public RowRenderData(List<TextRenderData> rowData, String backgroundColor) {
this.cellDatas = new ArrayList();
if (null != rowData) {
Iterator var3 = rowData.iterator();
while(var3.hasNext()) {
TextRenderData data = (TextRenderData)var3.next();
this.cellDatas.add(new CellRenderData(data));
}
}
TableStyle style = new TableStyle();
style.setBackgroundColor(backgroundColor);
this.rowStyle = style;
}
public int size() {
return null == this.cellDatas ? 0 : this.cellDatas.size();
}
public List<CellRenderData> getCellDatas() {
return this.cellDatas;
}
public void setCellDatas(List<CellRenderData> cellDatas) {
this.cellDatas = cellDatas;
}
public TableStyle getRowStyle() {
return this.rowStyle;
}
public void setRowStyle(TableStyle style) {
this.rowStyle = style;
}
}
/**
* 获取设备数据
*
* @param startTime 开始时间
* @param endTime 结束时间
* @param type 设备类型
* @param stationNum 站点编号
* @return 设备数据的DTO对象
* @throws ParseException 解析异常
*/
public EquipmentTableDto getEquipmentData(LocalDate startTime, LocalDate endTime, String type, String stationNum ) throws ParseException {
// 创建质量统计领域对象
QcStatisticDomain qcStatisticDomain = new QcStatisticDomain();
qcStatisticDomain.setStationNum(stationNum);
qcStatisticDomain.setDeviceType(String.valueOf(type));
// 将开始时间和结束时间转换为字符串
String startDateStr = startTime.toString();
String endDateStr = endTime.toString();
// 使用指定格式解析日期字符串
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate startDate = LocalDate.parse(startDateStr, formatter);
LocalDate endDate = LocalDate.parse(endDateStr, formatter);
// 计算开始日期和结束日期之间的天数
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
// 创建一个二维数组来存储数据
String[][] valueList = new String[(int) daysBetween][3];
// 创建并初始化设备数据的DTO对象
EquipmentTableDto equipmentTableDto = new EquipmentTableDto();
equipmentTableDto.setIseTime(LocalDate.now().toString());
equipmentTableDto.setStartTime(startTime.toString());
equipmentTableDto.setEndTime(endTime.toString());
// 创建详情对象和行数据列表
DetailDto detailDto = new DetailDto();
List<RowRenderData> rowDataList = new ArrayList<>();
// 遍历每一天,统计设备数据
for (int i = 0; i < daysBetween; i++) {
LocalDate date = startDate.plusDays(i);
String dateStr = date.toString();
qcStatisticDomain.setBeginTime(dateStr);
qcStatisticDomain.setEndTime(dateStr);
// 调用方法获取条形图数据
BarVO barVO = qcBarStatisticA(qcStatisticDomain);
// 检查列表是否为空或没有足够的元素
List<Integer> errorList = barVO.getErrorList();
List<Integer> suspiciousList = barVO.getSuspiciousList();
List<Integer> normalList = barVO.getNormalList();
if (errorList.isEmpty() || suspiciousList.isEmpty() || normalList.isEmpty()) {
rowDataList.add(RowRenderData.build(dateStr, "0.00%", "0.00%", "0.00%"));
continue;
}
// 计算总数和各状态的比例
double total = errorList.get(0) + suspiciousList.get(0) + normalList.get(0);
double error = Double.valueOf((errorList.get(0))) / Double.valueOf(total);
double suspicious = Double.valueOf((suspiciousList.get(0))) / Double.valueOf(total);
double normal = Double.valueOf((normalList.get(0))) / Double.valueOf(total);
// 将计算结果添加到行数据列表中
rowDataList.add(RowRenderData.build(dateStr, String.format("%.2f", error * 100)+'%', String.format("%.2f", suspicious * 100)+'%', String.format("%.2f", normal * 100)+'%'));
}
// 反转行数据列表,以便按照降序显示
Collections.reverse(rowDataList);
// 设置详情对象的记录,并将其设置到设备数据DTO中
detailDto.setRecords(rowDataList);
equipmentTableDto.setDetail(detailDto);
// 返回设备数据DTO对象
return equipmentTableDto;
}
/**
* 根据给定的条件统计质量控制数据
*
* @param qcStatisticDomain 质量控制统计的领域对象,包含统计所需的条件
* @return 返回一个包含统计结果的BarVO对象
*/
public BarVO qcBarStatisticA(QcStatisticDomain qcStatisticDomain) {
// 获取集合名称
String collectionName = qcStatisticDomain.getDeviceType();
// 解析结束时间
DateTime endTime = DateUtil.parse(qcStatisticDomain.getEndTime());
// 计算开始时间,即结束时间的前一天
DateTime beginTime = DateUtil.offsetDay(endTime, -1);
Query query;
// 根据站点编号是否为"stations"来构建不同的查询条件
if(!qcStatisticDomain.getStationNum().equals("stations")) {
query = new Query(Criteria.where("stationNum").is(qcStatisticDomain.getStationNum()).and("dataTime").gte(beginTime).lte(endTime));
}else{
query = new Query(Criteria.where("dataTime").gte(beginTime).lte(endTime));
}
// 执行查询,获取结果列表
List<QcWpr> results = mongoTemplate.find(query, QcWpr.class,collectionName);
// 初始化用于存储统计结果的列表
List<String> dateList = new ArrayList<>();
List<Integer> normalList = new ArrayList<>();
List<Integer> suspiciousList = new ArrayList<>();
List<Integer> errorList = new ArrayList<>();
// 用于按日期统计的 Map
Map<String, int[]> statisticsMap = new HashMap<>();
// 遍历查询结果,进行统计
for (QcWpr item : results) {
// 获取 qfFlag 的值
String qfFlag = item.getQcFlag();
// 获取日期
String formattedDate = DateUtil.format(item.getDataTime(),"yyy-MM-dd");
// 更新统计数据
statisticsMap.putIfAbsent(formattedDate, new int[3]); // 0: normal, 1: suspicious, 2: error
if ("0".equals(qfFlag)) {
statisticsMap.get(formattedDate)[0]++;
} else if ("1".equals(qfFlag)) {
statisticsMap.get(formattedDate)[1]++;
} else if ("2".equals(qfFlag)) {
statisticsMap.get(formattedDate)[2]++;
}
}
// 将统计数据填充到列表中
for (Map.Entry<String, int[]> entry : statisticsMap.entrySet()) {
dateList.add(entry.getKey());
normalList.add(entry.getValue()[0]);
suspiciousList.add(entry.getValue()[1]);
errorList.add(entry.getValue()[2]);
}
// 创建 BarVO 对象并设置统计结果
BarVO barVO = new BarVO();
barVO.setDateList(dateList);
barVO.setNormalList(normalList);
barVO.setSuspiciousList(suspiciousList);
barVO.setErrorList(errorList);
// 返回包含统计结果的 BarVO 对象
return barVO;
}