traffic-audit-server/src/main/java/com/trafficaudit/auditengine/service/AuditEngineService.java
@@ -1569,6 +1569,8 @@
            switch (rule.getCompareType()) {
                case "DIFF":
                    return checkDiff(rule, report, authMap);
                case "DIFF_FIELDS":
                    return checkDiffFields(rule, report, authMap);
                case "RATIO":
                    return checkRatio(rule, report, authMap, trackMap);
                case "MOM":
@@ -1611,6 +1613,86 @@
        return null;
    }
    /** RULE_001 分项运政对比:牵引车/挂车/整车车辆数与挂车吨位/整车吨位/总吨位分别与运政对比,任一分项超限即提醒 */
    private AuditResult checkDiffFields(AuditRule rule, H2032EnterpriseMonthly report,
                                        Map<String, TransportAuthVehicle> authMap) {
        TransportAuthVehicle auth = findAuth(authMap, report.getEnterpriseName());
        if (auth == null) return null;
        String[] fields = rule.getCheckField() == null ? new String[0] : rule.getCheckField().split(",");
        String[] limits = rule.getThreshold() == null ? new String[0] : rule.getThreshold().split("[,;|]");
        if (fields.length == 0 || limits.length != fields.length) return null;
        java.util.List<String> hits = new java.util.ArrayList<>();
        double maxAbs = 0;
        for (int i = 0; i < fields.length; i++) {
            String field = fields[i].trim();
            double limit;
            try {
                limit = Double.parseDouble(limits[i].trim());
            } catch (Exception e) {
                return null;
            }
            double actual;
            double reference;
            String unit;
            switch (field) {
                case "vehicleTractor":
                    actual = num(report.getVehicleTractor());
                    reference = num(auth.getTractorCount());
                    unit = "台";
                    break;
                case "vehicleTrailer":
                    actual = num(report.getVehicleTrailer());
                    reference = num(auth.getTrailerCount());
                    unit = "台";
                    break;
                case "tonsTrailer":
                    actual = nvl(report.getTonsTrailer());
                    reference = nvl(auth.getTrailerTons());
                    unit = "吨";
                    break;
                case "vehicleWhole":
                    actual = num(report.getVehicleWhole());
                    reference = num(auth.getOtherCount());
                    unit = "台";
                    break;
                case "tonsWhole":
                    actual = nvl(report.getTonsWhole());
                    reference = nvl(auth.getOtherTons());
                    unit = "吨";
                    break;
                case "tonsTotal":
                    actual = nvl(report.getTonsTotal());
                    reference = nvl(auth.getTrailerTons()) + nvl(auth.getOtherTons());
                    unit = "吨";
                    break;
                default:
                    return null;
            }
            double diff = Math.abs(actual - reference);
            maxAbs = Math.max(maxAbs, diff);
            if (diff > limit) {
                hits.add(h2032FieldLabel(field) + ":上报=" + fmt(actual) + " " + unit + ",运政=" + fmt(reference) + " " + unit
                    + ",差" + fmt(diff) + " " + unit + "(限" + fmt(limit) + " " + unit + ")");
            }
        }
        if (hits.isEmpty()) return null;
        StringBuilder detail = new StringBuilder();
        for (int i = 0; i < hits.size(); i++) {
            String one = hits.get(i);
            if (detail.length() == 0) {
                detail.append(one);
            } else if (detail.length() + one.length() + 1 <= 180) {
                detail.append(";").append(one);
            } else {
                detail.append(";等").append(hits.size() - i).append("项超限");
                break;
            }
        }
        return buildResult(rule, report, detail.toString(),
            "牵引车/挂车/整车车辆差≤5台;挂车吨位/整车吨位差≤50吨;总吨位差≤50吨",
            "超出项数=" + hits.size() + ",最大差=" + fmt(maxAbs));
    }
    /** 上报周转量与轨迹测算周转量对比,高出阈值比例核实 */
    private AuditResult checkRatio(AuditRule rule, H2032EnterpriseMonthly report,
                                   Map<String, TransportAuthVehicle> authMap,
@@ -1645,26 +1727,34 @@
        return null;
    }
    /** 货运类型环比:本月新增(上月为0、本月>0)需核实,少的不管 */
    /** 货运类型环比(RULE_004):上月无→本月新增 或 上月有→本月无,均提醒核实 */
    private AuditResult checkMom(AuditRule rule, H2032EnterpriseMonthly report,
                                 Map<String, H2032EnterpriseMonthly> lastMonthMap) {
        H2032EnterpriseMonthly lastMonth = lastMonthMap.get(key(report.getEnterpriseCode()));
        if (lastMonth == null) return null;
        List<String> hits = new ArrayList<>();
        List<String> addHits = new ArrayList<>();
        List<String> delHits = new ArrayList<>();
        for (String field : rule.getCheckField().split(",")) {
            double current = getH2032FieldValue(report, field);
            double last = getH2032FieldValue(lastMonth, field);
            if (current > 0 && last == 0) {
                hits.add(h2032FieldLabel(field) + "=" + fmt(current));
                addHits.add(h2032FieldLabel(field) + "=" + fmt(current));
            } else if (last > 0 && current == 0) {
                delHits.add(h2032FieldLabel(field) + "(上月=" + fmt(last) + ",本月无)");
            }
        }
        if (!hits.isEmpty()) {
            return buildResult(rule, report, "本月新增: " + String.join("、", hits),
                "上月该类型为0", "本月新增货运类型(上月无)");
        if (!addHits.isEmpty() || !delHits.isEmpty()) {
            StringBuilder msg = new StringBuilder();
            if (!addHits.isEmpty()) msg.append("本月新增: ").append(String.join("、", addHits));
            if (!delHits.isEmpty()) {
                if (msg.length() > 0) msg.append(";");
                msg.append("本月消失: ").append(String.join("、", delHits));
            }
            return buildResult(rule, report, msg.toString(),
                "上月无→本月新增 或 上月有→本月无", "货运类型环比新增/消失需核实");
        }
        return null;
    }
    /** 有货运量但对应车辆为0 需核实(如 有集装箱货运量无集装箱车辆) */
    private AuditResult checkNull(AuditRule rule, H2032EnterpriseMonthly report) {
        String[] fields = rule.getCheckField().split(",");
@@ -1796,6 +1886,10 @@
        labels.put("tonsWhole", "整车吨位");
        labels.put("turnoverTotal", "周转量");
        labels.put("vehicleWhole", "整车车辆数");
        labels.put("vehicleTractor", "牵引车数");
        labels.put("vehicleTrailer", "挂车数");
        labels.put("tonsTrailer", "挂车吨位");
        labels.put("tonsWhole", "整车吨位");
        String v = labels.get(field);
        return v == null ? field : v;
    }