HANDOFF.md
@@ -6,6 +6,13 @@ ## 最新(2026-10-06) ### 10-06:折线图统一按显示标签精度绘制 - **口径(用户 2026-10-06 确认)**:同一张折线图中,同一个显示标签必须对应同一个点位高度;分析文字不得引用比该图显示精度更细的差异。 - **实现**:`HolidayTrendChartService.lineChart` 在所有原始统计计算完成后,于最终入图边界调用 `displayValue`;普通图按该图 `valueDecimals` 做 `HALF_UP` 舍入,百分比点标签按 1 位百分比精度(即原始小数 3 位)舍入。折线坐标和点标签共用同一舍入值,纵轴格式沿用该图精度。 - **边界**:只在最终图表显示层舍入,不改数据库、不改累计、快报、同比、环比或调整口径;该规则用于静态分析报告图,不能让文字引用图上无法分辨的微小差异。 - **验证**:`HolidayTrendChartServiceTest` 聚焦测试通过;后端全量 `mvn -q test -f traffic-audit-server\pom.xml` 通过。新增测试覆盖“两个不同原值按 2 位标签舍入后生成相同几何”和百分比精度换算。测试报告:`docs/功能测试报告/功能测试报告_折线图按标签精度绘制_2026-10-06.md`(本地留存,不入库)。 ### 10-06:观测站平均拥挤度走势图改为 3 位小数 - **问题**:用户导入 10-05 观测站数据后反馈,最后一张拥挤度图中 10-04、10-05 标签都显示 `0.49`,但点位高度不同。 traffic-audit-server/src/main/java/com/trafficaudit/holiday/service/HolidayTrendChartService.java
@@ -49,6 +49,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; @@ -76,6 +77,7 @@ private static final double RIGHT_LABEL_BAND = 74.0; private static final double ITEM_LABEL_GAP = 10.0; private static final float HALO_OUTLINE_WIDTH = 4.0f; private static final int PERCENT_ITEM_LABEL_DECIMALS = 1; private static final Color ITEM_LABEL_COLOR = new Color(55, 55, 55); private static final Color[] SERIES_COLORS = { new Color(192, 80, 77), @@ -107,7 +109,7 @@ for (int i = 0; i < item.getValues().size(); i++) { BigDecimal value = item.getValues().get(i); if (value != null) { xySeries.add(i, value.doubleValue()); xySeries.add(i, displayValue(value, percentAxis, valueDecimals).doubleValue()); } } dataset.addSeries(xySeries); @@ -1113,10 +1115,16 @@ return total; } static BigDecimal displayValue(BigDecimal value, boolean percentItemLabels, int valueDecimals) { if (value == null) return null; int scale = percentItemLabels ? PERCENT_ITEM_LABEL_DECIMALS + 2 : valueDecimals; return value.setScale(scale, RoundingMode.HALF_UP); } private String formatLineLabel(Number value, boolean percentItemLabels, int valueDecimals) { if (percentItemLabels) { NumberFormat formatter = NumberFormat.getPercentInstance(); formatter.setMaximumFractionDigits(1); formatter.setMaximumFractionDigits(PERCENT_ITEM_LABEL_DECIMALS); return formatter.format(value.doubleValue()); } DecimalFormat formatter = new DecimalFormat("#,##0"); traffic-audit-server/src/test/java/com/trafficaudit/holiday/service/HolidayTrendChartServiceTest.java
@@ -5,7 +5,10 @@ import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.math.BigDecimal; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -42,6 +45,40 @@ } @Test void roundsLineDataToTheSamePrecisionAsLabels() { assertEquals(new BigDecimal("0.49"), HolidayTrendChartService.displayValue(new BigDecimal("0.4904"), false, 2)); assertEquals(new BigDecimal("0.49"), HolidayTrendChartService.displayValue(new BigDecimal("0.4911"), false, 2)); assertEquals(new BigDecimal("0.491"), HolidayTrendChartService.displayValue(new BigDecimal("0.4911"), false, 3)); assertEquals(new BigDecimal("0.123"), HolidayTrendChartService.displayValue(new BigDecimal("0.1234"), true, 2)); } @Test void rendersSameGeometryWhenValuesRoundToSameLabel() { HolidayTrendChartService service = new HolidayTrendChartService(); HolidayTrendChartService.ChartSeries first = new HolidayTrendChartService.ChartSeries(); first.setName("平均拥挤度"); first.setValues(java.util.Arrays.asList( new BigDecimal("0.4904"), new BigDecimal("0.4923"), new BigDecimal("0.5100"))); HolidayTrendChartService.ChartSeries second = new HolidayTrendChartService.ChartSeries(); second.setName("平均拥挤度"); second.setValues(java.util.Arrays.asList( new BigDecimal("0.4911"), new BigDecimal("0.4934"), new BigDecimal("0.5100"))); byte[] firstChart = service.lineChart("拥挤度", java.util.Collections.singletonList(first), false, 2, 1100, 590); byte[] secondChart = service.lineChart("拥挤度", java.util.Collections.singletonList(second), false, 2, 1100, 590); assertPng(firstChart); assertPng(secondChart); assertArrayEquals(firstChart, secondChart); } @Test void rendersHigherPrecisionLineLabelsWhenRequested() { HolidayTrendChartService service = new HolidayTrendChartService(); HolidayTrendChartService.ChartSeries congestion = new HolidayTrendChartService.ChartSeries();