Эх сурвалжийг харах

[10023] 温度曲线页面优化: tsfcchart接口改为返回forecast/local时间点并集(时间升序, 缺数填0, 两侧stats等长下标对齐, 预测源同时间点多份重叠报告取首个即最新); 明细表与导出Excel均改为 时间/预测温度(°C)/观测温度(°C) 3列, 导出新增TsfcChartExportStat VO

taosj 6 өдөр өмнө
parent
commit
1c5fe1162d

+ 60 - 10
forecast-backend-server/src/main/java/com/ufo/project/forecast/shortterm/weather/controller/DataShortTermWeatherForecastRawController.java

@@ -30,6 +30,7 @@ import com.ufo.project.forecast.shortterm.weather.domain.vo.RoseChartResult;
 import com.ufo.project.forecast.shortterm.weather.domain.vo.TirrChartExportStat;
 import com.ufo.project.forecast.shortterm.weather.domain.vo.TirrChartResult;
 import com.ufo.project.forecast.shortterm.weather.domain.vo.TirrChartStat;
+import com.ufo.project.forecast.shortterm.weather.domain.vo.TsfcChartExportStat;
 import com.ufo.project.forecast.shortterm.weather.domain.vo.TsfcChartResult;
 import com.ufo.project.forecast.shortterm.weather.domain.vo.TsfcChartStat;
 import com.ufo.project.forecast.shortterm.weather.domain.vo.WsChartResult;
@@ -132,7 +133,7 @@ public class DataShortTermWeatherForecastRawController extends BaseController
     }
 
     /**
-     * 统计温度曲线数据(短期预报 + 本地实测)
+     * 统计温度曲线数据(短期预报 + 本地实测,两侧 stats 对齐到同一时间点并集,缺数填0)
      */
     @PreAuthorize("@ss.hasPermi('weather:weatherRaw:tsfcchart')")
     @GetMapping("/tsfcchart")
@@ -141,11 +142,12 @@ public class DataShortTermWeatherForecastRawController extends BaseController
     {
         TsfcChartResult forecast = dataShortTermWeatherForecastRawService.getTsfcChart(stationId, startTime, endTime);
         TsfcChartResult local = dataWeatherLocalService.getTsfcChart(stationId, startTime, endTime);
+        alignTsfcStats(forecast, local);
         return success(new ChartDataPair<TsfcChartResult>(forecast, local));
     }
 
     /**
-     * 导出温度曲线数据(短期预报 + 本地实测,合并导出)
+     * 导出温度曲线数据(短期预报 + 本地实测,按时间点并集对齐导出 时间/预测温度/观测温度)
      */
     @PreAuthorize("@ss.hasPermi('weather:weatherRaw:tsfcchart')")
     @Log(title = "温度曲线", businessType = BusinessType.EXPORT)
@@ -155,16 +157,16 @@ public class DataShortTermWeatherForecastRawController extends BaseController
     {
         TsfcChartResult forecast = dataShortTermWeatherForecastRawService.getTsfcChart(stationId, startTime, endTime);
         TsfcChartResult local = dataWeatherLocalService.getTsfcChart(stationId, startTime, endTime);
-        List<TsfcChartStat> stats = new ArrayList<TsfcChartStat>();
-        if (forecast.getStats() != null)
+        alignTsfcStats(forecast, local);
+        List<TsfcChartStat> forecastStats = forecast.getStats();
+        List<TsfcChartStat> localStats = local.getStats();
+        List<TsfcChartExportStat> stats = new ArrayList<TsfcChartExportStat>();
+        for (int i = 0; i < forecastStats.size(); i++)
         {
-            stats.addAll(forecast.getStats());
+            stats.add(new TsfcChartExportStat(forecastStats.get(i).getTime(),
+                    forecastStats.get(i).getTsfc(), localStats.get(i).getTsfc()));
         }
-        if (local.getStats() != null)
-        {
-            stats.addAll(local.getStats());
-        }
-        ExcelUtil<TsfcChartStat> util = new ExcelUtil<TsfcChartStat>(TsfcChartStat.class);
+        ExcelUtil<TsfcChartExportStat> util = new ExcelUtil<TsfcChartExportStat>(TsfcChartExportStat.class);
         util.exportExcel(response, stats, "温度曲线数据");
     }
 
@@ -328,6 +330,54 @@ public class DataShortTermWeatherForecastRawController extends BaseController
         return map;
     }
 
+    /**
+     * 将温度曲线的短期预报与本地实测对齐到同一时间点并集(时间从小到大)。
+     * 某侧缺失的时间点 tsfc 填 0;预测源同一时间点可能含多份重叠报告,保留列表中首个(最新报告)。
+     * 合并后两侧 stats 等长、下标一一对应。
+     */
+    private void alignTsfcStats(TsfcChartResult forecast, TsfcChartResult local)
+    {
+        Map<String, Double> forecastMap = buildTsfcTimeMap(forecast.getStats());
+        Map<String, Double> localMap = buildTsfcTimeMap(local.getStats());
+        TreeSet<String> times = new TreeSet<String>();
+        times.addAll(forecastMap.keySet());
+        times.addAll(localMap.keySet());
+        List<TsfcChartStat> forecastStats = new ArrayList<TsfcChartStat>();
+        List<TsfcChartStat> localStats = new ArrayList<TsfcChartStat>();
+        for (String time : times)
+        {
+            TsfcChartStat forecastStat = new TsfcChartStat(time, forecastMap.getOrDefault(time, 0d));
+            forecastStat.setSource("预测");
+            forecastStats.add(forecastStat);
+            TsfcChartStat localStat = new TsfcChartStat(time, localMap.getOrDefault(time, 0d));
+            localStat.setSource("本地实测");
+            localStats.add(localStat);
+        }
+        forecast.setStats(forecastStats);
+        local.setStats(localStats);
+    }
+
+    /**
+     * 按时间建立 时间->温度 映射(LinkedHashMap 保留列表序,putIfAbsent 去重取首个)
+     */
+    private Map<String, Double> buildTsfcTimeMap(List<TsfcChartStat> stats)
+    {
+        Map<String, Double> map = new LinkedHashMap<String, Double>();
+        if (stats == null)
+        {
+            return map;
+        }
+        for (TsfcChartStat stat : stats)
+        {
+            if (stat == null || stat.getTime() == null)
+            {
+                continue;
+            }
+            map.putIfAbsent(stat.getTime(), stat.getTsfc() == null ? 0d : stat.getTsfc());
+        }
+        return map;
+    }
+
     /**
      * 导出短期气象原始数据列表
      */

+ 65 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/shortterm/weather/domain/vo/TsfcChartExportStat.java

@@ -0,0 +1,65 @@
+package com.ufo.project.forecast.shortterm.weather.domain.vo;
+
+import com.ufo.base.framework.aspectj.lang.annotation.Excel;
+
+/**
+ * 温度曲线导出行数据(预测与本地实测按时间点并集对齐,缺数填0)
+ *
+ * @author ufo
+ * @date 2026-10-29
+ */
+public class TsfcChartExportStat
+{
+    /** 时间(yyyy-MM-dd HH:mm:ss) */
+    @Excel(name = "时间")
+    private String time;
+
+    /** 预测温度(°C) */
+    @Excel(name = "预测温度(°C)")
+    private Double forecastTsfc;
+
+    /** 观测温度(°C) */
+    @Excel(name = "观测温度(°C)")
+    private Double localTsfc;
+
+    public TsfcChartExportStat()
+    {
+    }
+
+    public TsfcChartExportStat(String time, Double forecastTsfc, Double localTsfc)
+    {
+        this.time = time;
+        this.forecastTsfc = forecastTsfc;
+        this.localTsfc = localTsfc;
+    }
+
+    public String getTime()
+    {
+        return time;
+    }
+
+    public void setTime(String time)
+    {
+        this.time = time;
+    }
+
+    public Double getForecastTsfc()
+    {
+        return forecastTsfc;
+    }
+
+    public void setForecastTsfc(Double forecastTsfc)
+    {
+        this.forecastTsfc = forecastTsfc;
+    }
+
+    public Double getLocalTsfc()
+    {
+        return localTsfc;
+    }
+
+    public void setLocalTsfc(Double localTsfc)
+    {
+        this.localTsfc = localTsfc;
+    }
+}

+ 12 - 16
forecast-backend-vue/src/views/ufo/electricity/weather/TemperatureCurve.vue

@@ -84,8 +84,8 @@ const hasData = computed(() => !!summary.value && (summary.value.totalRecords >
 
 const columns = [
   {title: '时间', dataIndex: 'time', width: 180, align: 'center', fixed: 'left'},
-  {title: '来源', dataIndex: 'source', width: 100, align: 'center'},
-  {title: '温度(°C)', dataIndex: 'tsfc', width: 150, align: 'right'}
+  {title: '预测温度(°C)', dataIndex: 'forecastTsfc', width: 150, align: 'right'},
+  {title: '观测温度(°C)', dataIndex: 'localTsfc', width: 150, align: 'right'}
 ];
 
 const pagination = {
@@ -191,20 +191,16 @@ const handleQuery = async () => {
       localTotalRecords: local.totalRecords || 0,
       localValidRecords: local.validRecords || 0
     };
-    detailRows.value = [
-      ...(forecast.stats || []).map((item, index) => ({
-        key: `forecast-${index}`,
-        time: item.time,
-        source: item.source || '预测',
-        tsfc: toNumber(item.tsfc, 0)
-      })),
-      ...(local.stats || []).map((item, index) => ({
-        key: `local-${index}`,
-        time: item.time,
-        source: item.source || '观测',
-        tsfc: toNumber(item.tsfc, 0)
-      }))
-    ];
+    // 后端已将 forecast/local 的 stats 对齐到同一时间点并集(缺数填0),按下标对齐取行
+    const forecastStats = forecast.stats || [];
+    const localStats = local.stats || [];
+    const rowCount = Math.max(forecastStats.length, localStats.length);
+    detailRows.value = Array.from({ length: rowCount }, (_, index) => ({
+      key: `row-${index}`,
+      time: forecastStats[index]?.time || localStats[index]?.time,
+      forecastTsfc: toNumber(forecastStats[index]?.tsfc, 0),
+      localTsfc: toNumber(localStats[index]?.tsfc, 0)
+    }));
     renderChart(forecast.stats || [], local.stats || []);
   } finally {
     loading.value = false;