fix(holiday): 观测站速度/拥挤度走势图横轴改用系统配置假期天数
4个文件已修改
70 ■■■■■ 已修改文件
HANDOFF.md 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendChartService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayTrendChartServiceTest.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HANDOFF.md
@@ -4,7 +4,18 @@
> **每天结束时**:把当天进展、踩过的坑、新需求、未完成事项更新到本文件,然后可以放心开新对话。
> **协作约定(2026-08-31)**:说「提交」= 本地 commit + 推送到远端 git(origin main,Gitblit),不再只做本地 commit。
## 最新(2026-10-08 上班前关机快照)
## 最新(2026-10-08 走势图附录二观测站图天数修正)
### 10-08:观测站速度/拥挤度走势图改用系统配置假期天数(已修,待验收)
- **需求(用户)**:走势图最后两张图“普通国省道观测站平均机动车速度”“…平均拥挤度”只含今年数据,横轴天数不应是固定 8 天,而应为系统配置的假期天数。
- **改动**:`HolidayTrendChartService.lineChart` 新增可传 `dayLabels` 的重载(默认仍为 `DAY_LABELS` 8 天,其余 13 张图零改动);`HolidayTrendService.observationData` 改为按 `configService.findHolidayDays(year, holidayType)`(`day_index>=1`)的天数与日期生成数据点和横轴标签,最后两张图传入该标签。
- **配置依据**:`holiday_calendar` 2026 十一 `day_index 1..7`(7 天,`day_index 0` = 09-30 环比基期);2025 = 8 天、2024 = 7 天。
- **验证**:`mvn -o test` 16/16 通过(新增自定义横轴标签单元测试);真实云只读生成走势图,与用户已下载版逐图对比:image1~13 的 SHA-256 完全一致,仅 image14(速度)、image15(拥挤度)变化;OCR 确认新图横轴为 10月1日~10月7日、无 10月8日。
- **边界**:其余图仍固定 8 天口径,未改;未导入/覆盖/重导任何业务数据。
- **重启**:为让按钮下载用上新代码,已用 `start-dev.ps1` 重启后端(前端未动),重启时间 2026-10-08 10:38:53;可直接在系统里重新下载走势图验收。
## 历史(2026-10-08 上班前关机快照)
### 10-08:走势图 `(14)` 与已验收 `(12)` 比对(内容一致)
traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendChartService.java
@@ -103,6 +103,12 @@
    public byte[] lineChart(String yAxisLabel, List<ChartSeries> series, boolean percentAxis,
                            int valueDecimals, int width, int height) {
        return lineChart(yAxisLabel, series, percentAxis, valueDecimals, DAY_LABELS, width, height);
    }
    public byte[] lineChart(String yAxisLabel, List<ChartSeries> series, boolean percentAxis,
                            int valueDecimals, List<String> dayLabels, int width, int height) {
        List<String> axisLabels = (dayLabels == null || dayLabels.isEmpty()) ? DAY_LABELS : dayLabels;
        XYSeriesCollection dataset = new XYSeriesCollection();
        for (int seriesIndex = 0; seriesIndex < series.size(); seriesIndex++) {
            ChartSeries item = series.get(seriesIndex);
@@ -132,7 +138,7 @@
        plot.setRangeGridlinesVisible(false);
        plot.setDomainTickBandPaint(null);
        plot.setRangeTickBandPaint(null);
        SymbolAxis domainAxis = new SymbolAxis(null, DAY_LABELS.toArray(new String[0]));
        SymbolAxis domainAxis = new SymbolAxis(null, axisLabels.toArray(new String[0]));
        domainAxis.setGridBandPaint(new Color(0, 0, 0, 0));
        domainAxis.setGridBandAlternatePaint(new Color(0, 0, 0, 0));
        domainAxis.setLowerMargin(0.04);
traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendService.java
@@ -15,6 +15,7 @@
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -138,12 +139,14 @@
        blocks.add(new HolidayTrendWordService.ChartBlock(
                prefix + "普通国省道观测站平均机动车速度",
                chartService.lineChart("公里/小时",
                        Collections.singletonList(series("平均速度", observation.speed)), false, 1100, 590),
                        Collections.singletonList(series("平均速度", observation.speed)), false, 2,
                        observation.labels, 1100, 590),
                lineImageSize(5268595L, 2821305L)));
        blocks.add(new HolidayTrendWordService.ChartBlock(
                prefix + "普通国省道观测站平均拥挤度",
                chartService.lineChart("拥挤度",
                        Collections.singletonList(series("平均拥挤度", observation.congestion)), false, 1100, 590),
                        Collections.singletonList(series("平均拥挤度", observation.congestion)), false, 2,
                        observation.labels, 1100, 590),
                lineImageSize(5268595L, 2821305L)));
        File template = resolveTemplateFile();
@@ -263,23 +266,27 @@
    private ObservationData observationData(int year, String holidayType) {
        List<HolidayCalendar> days = configService.findHolidayDays(year, holidayType);
        if (days.isEmpty()) {
            return new ObservationData(nulls(), nulls());
            return new ObservationData(nulls(), nulls(), HolidayTrendChartService.DAY_LABELS);
        }
        LocalDate start = days.get(0).getHolidayDate();
        LocalDate end = start.plusDays(HolidayTrendChartService.DAY_LABELS.size() - 1L);
        LocalDate end = days.get(days.size() - 1).getHolidayDate();
        List<ObservationDailyOperation> rows = observationStationFlowMapper.selectDailyOperations(start, end);
        Map<LocalDate, ObservationDailyOperation> byDate = new LinkedHashMap<>();
        for (ObservationDailyOperation row : rows) {
            byDate.put(row.getObsDate(), row);
        }
        DateTimeFormatter labelFormat = DateTimeFormatter.ofPattern("M月d日");
        List<BigDecimal> speed = new ArrayList<>();
        List<BigDecimal> congestion = new ArrayList<>();
        for (int i = 0; i < HolidayTrendChartService.DAY_LABELS.size(); i++) {
            ObservationDailyOperation row = byDate.get(start.plusDays(i));
        List<String> labels = new ArrayList<>();
        for (HolidayCalendar day : days) {
            LocalDate date = day.getHolidayDate();
            ObservationDailyOperation row = byDate.get(date);
            speed.add(row == null ? null : row.getAverageSpeed());
            congestion.add(row == null ? null : row.getAverageCongestion());
            labels.add(date.format(labelFormat));
        }
        return new ObservationData(speed, congestion);
        return new ObservationData(speed, congestion, labels);
    }
    private HolidayTrendChartService.ChartSeries series(String name, List<BigDecimal> values) {
@@ -441,10 +448,12 @@
    private static class ObservationData {
        private final List<BigDecimal> speed;
        private final List<BigDecimal> congestion;
        private final List<String> labels;
        private ObservationData(List<BigDecimal> speed, List<BigDecimal> congestion) {
        private ObservationData(List<BigDecimal> speed, List<BigDecimal> congestion, List<String> labels) {
            this.speed = speed;
            this.congestion = congestion;
            this.labels = labels;
        }
    }
}
traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayTrendChartServiceTest.java
@@ -114,6 +114,30 @@
        assertFalse(java.util.Arrays.equals(twoDecimals, threeDecimals));
    }
    @Test
    void rendersLineChartWithConfiguredDayLabels() {
        HolidayTrendChartService service = new HolidayTrendChartService();
        HolidayTrendChartService.ChartSeries congestion = new HolidayTrendChartService.ChartSeries();
        congestion.setName("\u5e73\u5747\u62e5\u6324\u5ea6");
        congestion.setValues(java.util.Arrays.asList(
                new BigDecimal("0.50"), new BigDecimal("0.48"), new BigDecimal("0.47"),
                new BigDecimal("0.49"), new BigDecimal("0.49"), new BigDecimal("0.49"),
                new BigDecimal("0.47")));
        java.util.List<String> sevenDays = java.util.Arrays.asList(
                "10\u67081\u65e5", "10\u67082\u65e5", "10\u67083\u65e5", "10\u67084\u65e5",
                "10\u67085\u65e5", "10\u67086\u65e5", "10\u67087\u65e5");
        byte[] custom = service.lineChart("\u62e5\u6324\u5ea6",
                java.util.Collections.singletonList(congestion), false, 2, sevenDays, 1100, 590);
        byte[] defaults = service.lineChart("\u62e5\u6324\u5ea6",
                java.util.Collections.singletonList(congestion), false, 2, 1100, 590);
        assertPng(custom);
        assertPng(defaults);
        assertFalse(java.util.Arrays.equals(custom, defaults),
                "custom day labels should change the rendered axis");
    }
    private void assertNoLargeLightGrayBackground(byte[] data) throws Exception {
        BufferedImage image = ImageIO.read(new ByteArrayInputStream(data));
        int matches = 0;