traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
@@ -1403,24 +1403,29 @@
        return a;
    }
    /** 审核结果导出 Excel(含企业名/规则名/状态/审核意见) */
    /** 审核结果导出 Excel(两 Sheet,按业务模板):Sheet1 审核结果(逐条明细);Sheet2 企业合并(按企业汇总 + 问题列) */
    public byte[] exportResults(String reportPeriod, String reportType) throws Exception {
        return exportResults(reportPeriod, reportType, true, "all");
    }
    /** onlyVerify=true 时剔除“提醒型”规则(如 RULE_004 环比提醒),只保留核实型问题;status=all/pending/handled 与界面“状态”筛选一致 */
    public byte[] exportResults(String reportPeriod, String reportType, boolean onlyVerify, String status) throws Exception {
        List<AuditResult> results = queryResults(reportPeriod, reportType);
        java.util.List<AuditResult> filtered = new java.util.ArrayList<>();
        String st = status == null ? "all" : status.trim();
        for (AuditResult res : results) {
            if (onlyVerify && isReminderRule(res.getRuleCode())) continue;
            if ("pending".equals(st) && !"PENDING".equals(res.getStatus())) continue;
            if ("handled".equals(st) && (res.getStatus() == null || "PENDING".equals(res.getStatus()))) continue;
            filtered.add(res);
        }
        try (XSSFWorkbook wb = new XSSFWorkbook()) {
            // ---------- Sheet1 审核结果(逐条,列与业务模板一致) ----------
            Sheet sheet = wb.createSheet("审核结果");
            String[] headers = {"报表期", "企业/市州", "规则编码", "审核规则", "等级", "实际值", "阈值/基准", "偏差说明", "状态", "审核意见"};
            Row head = sheet.createRow(0);
            for (int i = 0; i < headers.length; i++) {
                Cell cell = head.createCell(i);
                cell.setCellValue(headers[i]);
                org.apache.poi.ss.usermodel.CellStyle style = wb.createCellStyle();
                org.apache.poi.ss.usermodel.Font font = wb.createFont();
                font.setBold(true);
                style.setFont(font);
                cell.setCellStyle(style);
            }
            writeAuditHeader(sheet, headers);
            int r = 1;
            for (AuditResult res : results) {
            for (AuditResult res : filtered) {
                Row row = sheet.createRow(r++);
                row.createCell(0).setCellValue(valueOf(res.getReportPeriod()));
                row.createCell(1).setCellValue(valueOf(res.getEnterpriseName()));
@@ -1437,12 +1442,97 @@
            for (int i = 0; i < widths.length; i++) {
                sheet.setColumnWidth(i, widths[i] * 256);
            }
            // ---------- Sheet2 企业合并(同一企业多条规则并排列出 + 问题汇总列) ----------
            Sheet ms = wb.createSheet("企业合并");
            String[] h2 = {"报表期", "企业/市州", "审核规则", "实际值", "阈值/基准", "偏差说明", "问题"};
            writeAuditHeader(ms, h2);
            java.util.LinkedHashMap<String, java.util.List<AuditResult>> groups = new java.util.LinkedHashMap<>();
            for (AuditResult res : filtered) {
                String key = valueOf(res.getEnterpriseName());
                if (key.isEmpty()) key = valueOf(res.getReportPeriod()) + "#" + valueOf(res.getActualValue());
                groups.computeIfAbsent(key, k -> new java.util.ArrayList<>()).add(res);
            }
            int rowCursor = 1;
            for (java.util.Map.Entry<String, java.util.List<AuditResult>> e : groups.entrySet()) {
                java.util.List<AuditResult> items = e.getValue();
                int groupStart = rowCursor;
                StringBuilder problems = new StringBuilder();
                int seq = 0;
                for (AuditResult it : items) {
                    Row row = ms.createRow(rowCursor++);
                    if (seq == 0) {
                        row.createCell(0).setCellValue(valueOf(it.getReportPeriod()));
                        row.createCell(1).setCellValue(valueOf(it.getEnterpriseName()).isEmpty() ? e.getKey() : valueOf(it.getEnterpriseName()));
                    }
                    row.createCell(2).setCellValue(valueOf(it.getRuleName()));
                    row.createCell(3).setCellValue(valueOf(it.getActualValue()));
                    row.createCell(4).setCellValue(valueOf(it.getThresholdValue()));
                    row.createCell(5).setCellValue(valueOf(it.getDeviation()));
                    seq++;
                    if (problems.length() > 0) problems.append("\n");
                    problems.append(seq).append("、").append(issueSentence(it));
                }
                if (rowCursor - groupStart > 1) {
                    ms.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(groupStart, rowCursor - 1, 1, 1));
                    ms.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(groupStart, rowCursor - 1, 6, 6));
                }
                Row first = ms.getRow(groupStart);
                if (first.getCell(6) == null) first.createCell(6);
                first.getCell(6).setCellValue(problems.toString());
            }
            int[] widths2 = {12, 30, 22, 34, 22, 34, 60};
            for (int i = 0; i < widths2.length; i++) {
                ms.setColumnWidth(i, widths2[i] * 256);
            }
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            wb.write(out);
            return out.toByteArray();
        }
    }
    private void writeAuditHeader(Sheet sheet, String[] headers) {
        Row head = sheet.createRow(0);
        for (int i = 0; i < headers.length; i++) {
            Cell cell = head.createCell(i);
            cell.setCellValue(headers[i]);
            org.apache.poi.ss.usermodel.CellStyle style = sheet.getWorkbook().createCellStyle();
            org.apache.poi.ss.usermodel.Font font = sheet.getWorkbook().createFont();
            font.setBold(true);
            style.setFont(font);
            cell.setCellStyle(style);
        }
    }
    /** 提醒型规则:仅提示变化、不需人工核实(如 RULE_004 货运类型环比提醒) */
    private boolean isReminderRule(String ruleCode) {
        return "RULE_004".equals(ruleCode);
    }
    /** 企业合并“问题”列话术:按规则类型生成可读的核实意见 */
    private String issueSentence(AuditResult res) {
        String code = res.getRuleCode();
        String dev = valueOf(res.getDeviation());
        String act = valueOf(res.getActualValue());
        String thr = valueOf(res.getThresholdValue());
        if ("RULE_001".equals(code) || "RULE_002".equals(code)) {
            return "企业上报车辆/吨位与运政数据对比存在差异(" + dev + "),请核实";
        }
        if ("RULE_003".equals(code)) {
            return "上报周转量与轨迹测算周转量差异明显(" + dev + "),请参考轨迹核实企业周转量";
        }
        if ("RULE_004".equals(code)) {
            return "货运类型环比发生变化,请核实";
        }
        if ("RULE_005".equals(code)) {
            return "无集装箱车但存在集装箱货运量,请核实车辆类型与货运量填报";
        }
        if ("RULE_006".equals(code)) {
            return "整车吨位超出 4~40 吨合理范围,请核实平均吨位填报";
        }
        return valueOf(res.getRuleName()) + "异常(实际:" + act + ";基准:" + thr + ";" + dev + "),请核实";
    }
    private String auditStatusCn(String status) {
        if ("CONFIRMED".equals(status)) return "已确认";
        if ("IGNORED".equals(status)) return "已忽略";