From e5f80b8f2604f0423128c0811f716919ae0a9cef Mon Sep 17 00:00:00 2001
From: liusheng <337615773@qq.com>
Date: 星期三, 23 九月 2026 10:05:27 +0800
Subject: [PATCH] 1.代码提交

---
 smartor/src/main/java/com/smartor/common/MydOptionRatioChartBuilder.java |  172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 172 insertions(+), 0 deletions(-)

diff --git a/smartor/src/main/java/com/smartor/common/MydOptionRatioChartBuilder.java b/smartor/src/main/java/com/smartor/common/MydOptionRatioChartBuilder.java
new file mode 100644
index 0000000..3c0d163
--- /dev/null
+++ b/smartor/src/main/java/com/smartor/common/MydOptionRatioChartBuilder.java
@@ -0,0 +1,172 @@
+package com.smartor.common;
+
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.smartor.domain.ServiceSubtaskDetailRatioExport;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xddf.usermodel.chart.AxisCrosses;
+import org.apache.poi.xddf.usermodel.chart.AxisPosition;
+import org.apache.poi.xddf.usermodel.chart.BarDirection;
+import org.apache.poi.xddf.usermodel.chart.ChartTypes;
+import org.apache.poi.xddf.usermodel.chart.LegendPosition;
+import org.apache.poi.xddf.usermodel.chart.XDDFBarChartData;
+import org.apache.poi.xddf.usermodel.chart.XDDFCategoryAxis;
+import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
+import org.apache.poi.xddf.usermodel.chart.XDDFChartLegend;
+import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
+import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
+import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource;
+import org.apache.poi.xddf.usermodel.chart.XDDFValueAxis;
+import org.apache.poi.xssf.usermodel.XSSFChart;
+import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
+import org.apache.poi.xssf.usermodel.XSSFDrawing;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 瀵煎嚭銆岄棶棰樿鎯呭崰姣斿浘琛ㄣ�峉heet 鐨勬瀯寤哄櫒
+ * <p>
+ * 鎸変换鍔″悕绉板垎缁勶細姣忎釜浠诲姟鍐欎竴鍧楁暟鎹紙琛�=棰樼洰锛屽垪=閫夐」锛屽��=鏁伴噺锛夛紝骞跺湪鏁版嵁鍙充晶鐢讳竴寮犳煴鐘跺浘銆�
+ *
+ * @author smartor
+ */
+public class MydOptionRatioChartBuilder implements ExcelUtil.ChartSheetBuilder {
+
+    /**
+     * 鍥捐〃 Sheet 鍚嶇О
+     */
+    public static final String SHEET_NAME = "闂璇︽儏鍗犳瘮鍥捐〃";
+
+    private final List<ServiceSubtaskDetailRatioExport> rows;
+
+    public MydOptionRatioChartBuilder(List<ServiceSubtaskDetailRatioExport> rows) {
+        this.rows = rows == null ? new ArrayList<>() : rows;
+    }
+
+    @Override
+    public void build(XSSFWorkbook workbook) {
+        if (rows.isEmpty()) {
+            return;
+        }
+        XSSFSheet sheet = workbook.createSheet(SHEET_NAME);
+
+        int startRow = 0;
+        for (Map.Entry<String, List<ServiceSubtaskDetailRatioExport>> entry : groupByTask().entrySet()) {
+            // 姣忓啓瀹屼竴鍧楁暟鎹珛鍒诲缓鍥撅細SXSSF 娴佸紡宸ヤ綔绨块噷鍒氬啓鐨勮杩樺湪鍐呭瓨涓紝鍥捐〃鎵嶈兘璇诲埌鍗曞厓鏍�
+            startRow = writeTaskBlockWithChart(sheet, entry.getKey(), entry.getValue(), startRow) + 2;
+        }
+    }
+
+    /**
+     * 鎸変换鍔″悕绉板垎缁勶紙淇濇寔鍒楄〃鍘熸湁椤哄簭锛屽嵆 SQL 鐨� task_name 鎺掑簭锛�
+     */
+    private Map<String, List<ServiceSubtaskDetailRatioExport>> groupByTask() {
+        Map<String, List<ServiceSubtaskDetailRatioExport>> grouped = new LinkedHashMap<>();
+        for (ServiceSubtaskDetailRatioExport row : rows) {
+            String taskName = row.getTaskname() == null ? "" : row.getTaskname();
+            grouped.computeIfAbsent(taskName, k -> new ArrayList<>()).add(row);
+        }
+        return grouped;
+    }
+
+    /**
+     * 鍐欎竴涓换鍔$殑鏁版嵁鍧楀苟鐢绘煴鐘跺浘锛岃繑鍥炰笅涓�涓潡鍙敤鐨勮捣濮嬭
+     */
+    private int writeTaskBlockWithChart(XSSFSheet sheet, String taskName,
+                                        List<ServiceSubtaskDetailRatioExport> taskRows, int startRow) {
+        // 棰樼洰 -> (閫夐」 -> 閫夎閫夐」鐨勪汉鏁�)锛岄�夐」鎸夊嚭鐜伴『搴忔敹闆嗘垚鏈换鍔$殑绯诲垪
+        Map<String, Map<String, Double>> questionOptions = new LinkedHashMap<>();
+        LinkedHashSet<String> optionSet = new LinkedHashSet<>();
+        String taskid = "";
+        for (ServiceSubtaskDetailRatioExport row : taskRows) {
+            String question = row.getQuestiontext() == null ? "" : row.getQuestiontext();
+            String option = row.getOptionresult() == null ? "" : row.getOptionresult();
+            questionOptions.computeIfAbsent(question, k -> new LinkedHashMap<>()).put(option, parseCount(row.getCount()));
+            optionSet.add(option);
+            if (taskid.isEmpty() && row.getTaskid() != null) {
+                taskid = row.getTaskid();
+            }
+        }
+        List<String> optionList = new ArrayList<>(optionSet);
+        List<String> questionList = new ArrayList<>(questionOptions.keySet());
+
+        int rowIdx = startRow;
+        // 鏍囬琛�
+        Row titleRow = sheet.createRow(rowIdx++);
+        titleRow.createCell(0).setCellValue("浠诲姟鍚嶇О锛�" + taskName + "锛堜换鍔$紪鍙� " + taskid + "锛�");
+
+        // 琛ㄥご琛岋細棰樼洰 + 鍚勯�夐」锛堝浘琛ㄧ郴鍒楃殑鏍囬灏辨槸杩欎竴琛岋級
+        Row headerRow = sheet.createRow(rowIdx++);
+        headerRow.createCell(0).setCellValue("棰樼洰");
+        for (int i = 0; i < optionList.size(); i++) {
+            headerRow.createCell(i + 1).setCellValue(optionList.get(i));
+        }
+
+        // 鏁版嵁琛岋細鍊� = 閫夎閫夐」鐨勪汉鏁�
+        int firstDataRow = rowIdx;
+        for (String question : questionList) {
+            Row dataRow = sheet.createRow(rowIdx++);
+            dataRow.createCell(0).setCellValue(question);
+            Map<String, Double> optionMap = questionOptions.get(question);
+            for (int i = 0; i < optionList.size(); i++) {
+                Double count = optionMap.get(optionList.get(i));
+                dataRow.createCell(i + 1).setCellValue(count == null ? 0D : count);
+            }
+        }
+        int lastDataRow = rowIdx - 1;
+
+        // 鏌辩姸鍥撅細鍒嗙被=棰樼洰锛岀郴鍒�=閫夐」锛屾暟鍊�=閫夎閫夐」鐨勪汉鏁�
+        int chartCol = optionList.size() + 3;
+        int chartHeight = Math.max(16, questionList.size() + 4);
+        XSSFDrawing drawing = sheet.createDrawingPatriarch();
+        XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0,
+                chartCol, firstDataRow - 1, chartCol + 10, firstDataRow - 1 + chartHeight);
+        XSSFChart chart = drawing.createChart(anchor);
+        chart.setTitleText(taskName);
+        chart.setTitleOverlay(false);
+
+        XDDFChartLegend legend = chart.getOrAddLegend();
+        legend.setPosition(LegendPosition.BOTTOM);
+
+        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
+        bottomAxis.setTitle("棰樼洰");
+        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
+        leftAxis.setTitle("鏁伴噺");
+        leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
+
+        XDDFDataSource<String> categories = XDDFDataSourcesFactory.fromStringCellRange(sheet,
+                new CellRangeAddress(firstDataRow, lastDataRow, 0, 0));
+        XDDFBarChartData barChart = (XDDFBarChartData) chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
+        for (int i = 0; i < optionList.size(); i++) {
+            XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(sheet,
+                    new CellRangeAddress(firstDataRow, lastDataRow, i + 1, i + 1));
+            XDDFChartData.Series series = barChart.addSeries(categories, values);
+            series.setTitle(optionList.get(i), null);
+        }
+        barChart.setBarDirection(BarDirection.COL);
+        // 鍏抽敭锛氫笉璋冪敤 plot 鍥捐〃涓嶄細鏄剧ず
+        chart.plot(barChart);
+
+        return rowIdx;
+    }
+
+    /**
+     * 鏁伴噺瀛楃涓� -> 鏁板瓧锛屽紓甯稿�兼寜 0 澶勭悊
+     */
+    private double parseCount(String count) {
+        if (count == null || count.isEmpty()) {
+            return 0D;
+        }
+        try {
+            return Double.parseDouble(count.trim());
+        } catch (NumberFormatException e) {
+            return 0D;
+        }
+    }
+}

--
Gitblit v1.9.3