实现java生成并下载数据质控报告的 Word 文档(Service)

 前面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. 关键技术点

  • 动态查询:通过deviceTypestationNum实现多设备、多站点的灵活统计。
  • 时间处理:使用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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值