From 8eb584869b4fd4de0f51c93f2616f12e51df9193 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期四, 18 十二月 2025 17:48:29 +0800
Subject: [PATCH] 2025.12.18 1. 动态溯源相关分析逻辑调整; 2. 走航报告接口参数调整;

---
 src/main/kotlin/com/flightfeather/uav/lightshare/bean/AnalysisOption.kt                  |    8 
 src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt          |    1 
 src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt |    7 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt              |   53 ----
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt              |  265 ++++++++++++++++-----
 src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt       |   61 ----
 src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt                      |   11 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt            |  158 +++---------
 src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt           |   79 ++---
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt              |   26 -
 10 files changed, 300 insertions(+), 369 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt
index 3974903..e019c7c 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt
@@ -56,16 +56,11 @@
         
         pollutedClues.forEach { pollutedClue ->
             if (pollutedClue == null) return@forEach
-            val dataList = pollutedClue.pollutedData?.dataList ?: emptyList()
-            if (dataList.isEmpty()) return@forEach
-            
             // 璁$畻鍗曚釜PollutedClue鐨勫潎鍊肩粡绾害
-            val avgData = dataList.avg()
-            val wgs84Lng = avgData.longitude?.toDouble() ?: return@forEach
-            val wgs84Lat = avgData.latitude?.toDouble() ?: return@forEach
-            
+            val wgs84Center = pollutedClue.pollutedData?.getExceptionCenter() ?: return@forEach
+
             // 鍧愭爣杞崲
-            val gcj02Point = MapUtil.wgs84ToGcj02(wgs84Lng to wgs84Lat)
+            val gcj02Point = MapUtil.wgs84ToGcj02(wgs84Center)
             
             // 鏌ユ壘鏈�杩戝満鏅�
             var minDistance = Double.MAX_VALUE
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt
index f2f469a..127622c 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt
@@ -1,5 +1,7 @@
 package com.flightfeather.uav.biz.sourcetrace.model
 
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
 import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
 import com.flightfeather.uav.common.net.AMapService
 import com.flightfeather.uav.common.utils.MapUtil
@@ -20,15 +22,14 @@
      */
 
     constructor(
-        historyData: List<BaseRealTimeData>,
-        exceptionData: List<BaseRealTimeData>,
+        pollutedData: PollutedData,
         config: RTExcWindLevelConfig,
         windLevelCondition: RTExcWindLevelConfig.WindLevelCondition?,
-    ) : this() {
+    ) : this(){
         distanceType = windLevelCondition?.distanceType
         distanceRange = distanceType?.disRange
         distanceDes = distanceType?.des
-        windLevelCondition?.let { sourceTrace(historyData, exceptionData, config, it) }
+        windLevelCondition?.let { sourceTrace(pollutedData, config, it) }
     }
 
     // 鎵�灞炶闀�
@@ -64,19 +65,13 @@
      * 鍙嶅悜婧簮
      */
     private fun sourceTrace(
-        historyData: List<BaseRealTimeData>,
-        exceptionData: List<BaseRealTimeData>,
+        pollutedData: PollutedData,
         config: RTExcWindLevelConfig,
         windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
     ) {
-        val avgData = if (exceptionData.size == 1) {
-            exceptionData.first()
-        } else {
-            exceptionData.avg()
-        }
 
+        val avgData = pollutedData.getExceptionAvgData()
         val pair = avgData.longitude!!.toDouble() to avgData.latitude!!.toDouble()
-
         polygon = calSector(
             avgData.windDirection?.toDouble() ?: .0,
             pair,
@@ -87,7 +82,7 @@
             MapUtil.wgs84ToGcj02(it)
         }
 
-        closePolygon = closeSourceTrace(historyData, pair).map {
+        closePolygon = closeSourceTrace(pair).map {
             // 灏嗗潗鏍囪浆鎹负gcj02锛堢伀鏄熷潗鏍囩郴锛夛紝鍥犱负姹℃煋婧愬満鏅俊鎭兘涓烘鍧愭爣绯�
             MapUtil.wgs84ToGcj02(it)
         }
@@ -161,10 +156,7 @@
         return result
     }
 
-    private fun closeSourceTrace(
-        historyData: List<BaseRealTimeData>,
-        center: Pair<Double, Double>,
-    ): List<Pair<Double, Double>> {
+    private fun closeSourceTrace(center: Pair<Double, Double>): List<Pair<Double, Double>> {
         val result = mutableListOf<Pair<Double, Double>>()
         var startDeg = 0
         while (startDeg <= 360) {
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt
index 306d4a7..e8c29b7 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt
@@ -28,61 +28,12 @@
     ) : this() {
         if (exceptions.isEmpty() || exceptions[0].second.exceptionData.isEmpty()) return
         deviceCode = exceptions[0].second.startData?.deviceCode
-        var startData: BaseRealTimeData? = null
-        var endData: BaseRealTimeData? = null
-        var exceptionData = mutableListOf<BaseRealTimeData>()
-        var historyData = mutableListOf<BaseRealTimeData>()
-        exceptions.forEach { e ->
-            // 灏嗛噰鏍锋椂闂存渶鏃╃殑浣滀负寮�濮嬫暟鎹�
-            if (startData == null) {
-                startData = e.second.startData
-            } else {
-                if (e.second.startData?.dataTime!! < startData!!.dataTime) {
-                    startData = e.second.startData
-                }
-            }
 
-            // 灏嗛噰鏍锋椂闂存渶鏅氱殑浣滀负缁撴潫鏁版嵁
-            if (endData == null) {
-                endData = e.second.endData
-            } else {
-                if (e.second.endData?.dataTime!! > endData!!.dataTime) {
-                    endData = e.second.endData
-                }
-            }
-
-            // 灏嗘墍鏈夊紓甯告暟鎹幓閲嶅悎骞�
-            if (exceptionData.isEmpty()) {
-                exceptionData = e.second.exceptionData
-            } else {
-                e.second.exceptionData.forEach {
-                    if (exceptionData.find { d -> d.dataTime == it.dataTime } == null) {
-                        exceptionData.add(it)
-                    }
-                }
-            }
-
-            // 灏嗘墍鏈夊巻鍙叉暟鎹幓閲嶅悎骞�
-            if (historyData.isEmpty()) {
-                historyData = e.second.historyData
-            } else {
-                e.second.historyData.forEach {
-                    if (historyData.find { d -> d.dataTime == it.dataTime } == null) {
-                        historyData.add(it)
-                    }
-                }
-            }
-        }
-        // 鎸夌収閲囨牱鏃堕棿鍗囧簭鎺掑垪
-        exceptionData.sortBy { it.dataTime }
-        historyData.sortBy { it.dataTime }
 
         // 鑾峰彇鍘婚噸鍚庣殑鐩戞祴鍥犲瓙绫诲瀷
         val factorList = exceptions.map { it.first }.distinct()
-        pollutedData = PollutedData(
-            startData!!, endData, factorList, exceptionData, historyData, eType, windLevelCondition
-        )
-        pollutedArea = PollutedArea(historyData, exceptionData, config, windLevelCondition)
+        pollutedData = PollutedData(exceptions, eType)
+        pollutedArea = PollutedArea(pollutedData!!, config, windLevelCondition)
 
     }
 
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
index 577b38b..0483664 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt
@@ -1,10 +1,12 @@
 package com.flightfeather.uav.biz.sourcetrace.model
 
 import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.model.ExceptionTag
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
 import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
 import com.flightfeather.uav.common.utils.DateUtil
 import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.entity.avg
 import com.flightfeather.uav.lightshare.bean.DataVo
 import com.flightfeather.uav.socket.eunm.FactorType
 import java.util.Date
@@ -16,13 +18,78 @@
  * @author feiyu02
  */
 class PollutedData() {
+    
+    companion object {
+        // 榛樿鏁版嵁閲囨牱鏃堕棿闂撮殧锛屽崟浣嶏細绉�
+        const val DEFAULT_PERIOD = 4
+    }
 
+    /**
+     * 寮傚父鏁版嵁鍒嗙粍鎯呭喌缁熻
+     */
+    inner class ExcGroup{
+        constructor(dataIndexList: List<Int>, factorType: FactorType){
+            this.dataIndexList = dataIndexList
+            this.factorType = factorType
+            val first = getFirstDataValue()?.toDouble()
+            val last = getLastDataValue()?.toDouble()
+            if (first != null && last != null) {
+                per = round((last - first) / first * 100) / 100
+                rate = round((last - first) / DEFAULT_PERIOD * 100) / 100
+            }
+        }
+        var factorType: FactorType? = null
+        /**
+         * 寮傚父鏁版嵁瀵瑰簲鍘嗗彶鏁版嵁[historyDataList]涓殑绱㈠紩鍊�
+         */
+        var dataIndexList: List<Int>? = null
+        // 鍙樺寲骞呭害
+        var per: Double? = null
+        // 鍙樺寲閫熺巼
+        var rate: Double? = null
+
+        /**
+         * 鑾峰彇寮傚父鏁版嵁鐨勭涓�涓暟鎹�
+         * !!!!绗竴涓暟鎹叾瀹炴槸棣栦釜寮傚父鏁版嵁鐨勫墠涓�涓暟鎹��!!!!
+         */
+        fun getFirstData(): BaseRealTimeData? {
+            return dataIndexList?.firstOrNull()?.let {
+                val i = if (it > 0) it - 1 else it
+                historyDataList[i].toBaseRealTimeData(BaseRealTimeData::class.java)
+            }
+        }
+        fun getFirstDataValue(): Float? {
+            return getFirstData()?.getByFactorType(factorType)
+        }
+
+        /**
+         * 鑾峰彇寮傚父鏁版嵁鐨勬渶鍚庝竴涓暟鎹�
+         */
+        fun getLastData(): BaseRealTimeData? {
+            return dataIndexList?.lastOrNull()?.let {
+                historyDataList[it].toBaseRealTimeData(BaseRealTimeData::class.java)
+            }
+        }
+        fun getLastDataValue(): Float? {
+            return getLastData()?.getByFactorType(factorType)
+        }
+    }
+
+
+    /**
+     * 鍚勭洃娴嬪洜瀛愬紓甯哥粺璁′俊鎭�
+     */
     inner class Statistic(){
         var factorId: Int? = null
         var factorName: String? = null
         var subFactorId: List<Int>? = null
         var subFactorName: List<String>? = null
         var selectedFactor: FactorFilter.SelectedFactor? = null
+
+        /**
+         * 寮傚父鏁版嵁瀵瑰簲鍘嗗彶鏁版嵁[historyDataList]涓殑绱㈠紩鍊�
+         */
+        var dataIndexList: List<Int>? = null
 
         // 鍥犲瓙閲忕骇骞冲潎鍙樺寲骞呭害
         var avgPer: Double? = null
@@ -32,110 +99,176 @@
         var avg: Double? = null
         var min: Double? = null
         var max: Double? = null
+
+        var excGroup: List<ExcGroup>? = null
+
+        /**
+         * 鑾峰彇寮傚父鏁版嵁
+         */
+        fun getExceptionData(): List<BaseRealTimeData>? {
+            return dataIndexList?.map { historyDataList[it].toBaseRealTimeData(BaseRealTimeData::class.java) }
+        }
+
+        /**
+         * 鑾峰彇寮傚父鏁版嵁鍒嗘鎯呭喌
+         * 灏嗚繛缁殑寮傚父鏁版嵁鍒嗕负涓�缁�
+         */
+        fun getExceptionDataGroup(): List<List<Int>> {
+            val res = mutableListOf<MutableList<Int>>()
+            var curGroup = mutableListOf<Int>()
+            var lastIndex = -2
+            dataIndexList?.forEach {
+                if (curGroup.isEmpty()) {
+                    curGroup.add(it)
+                } else if (it - lastIndex == 1) {
+                    curGroup.add(it)
+                } else {
+                    res.add(curGroup)
+                    curGroup = mutableListOf(it)
+                }
+                lastIndex = it
+            }
+            if (curGroup.isNotEmpty()) {
+                res.add(curGroup)
+            }
+            return res
+        }
     }
 
-    constructor(
-        start: BaseRealTimeData,
-        end: BaseRealTimeData?,
-        factorList: List<FactorFilter.SelectedFactor>,
-        exceptionData: List<BaseRealTimeData>,
-        historyData: List<BaseRealTimeData>,
-        eType: ExceptionType,
-        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition?,
-    ) : this() {
+    constructor(exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>, eType: ExceptionType,) : this() {
+        // 閬嶅巻鎵�鏈夌殑鍥犲瓙鐨勫紓甯革紝鏁村悎缁熶竴鐨勫紓甯哥粨鏋滐紝鍏蜂綋濡備笅
+        var startData: BaseRealTimeData? = null
+        var endData: BaseRealTimeData? = null
+        var historyData = mutableListOf<BaseRealTimeData>()
+        var _times = 0
+        exceptions.forEach { e ->
+            // 灏嗛噰鏍锋椂闂存渶鏃╃殑鏁版嵁浣滀负寮�濮嬫暟鎹�
+            if (startData == null) {
+                startData = e.second.startData
+            } else {
+                if (e.second.startData?.dataTime!! < startData!!.dataTime) {
+                    startData = e.second.startData
+                }
+            }
+
+            // 灏嗛噰鏍锋椂闂存渶鏅氱殑浣滀负缁撴潫鏁版嵁
+            if (endData == null) {
+                endData = e.second.endData
+            } else {
+                if (e.second.endData?.dataTime!! > endData!!.dataTime) {
+                    endData = e.second.endData
+                }
+            }
+            // 灏嗘墍鏈夊巻鍙叉暟鎹幓閲嶅悎骞�
+            if (historyData.isEmpty()) {
+                historyData = e.second.historyData
+            } else {
+                e.second.historyData.forEach {
+                    if (historyData.find { d -> d.dataTime == it.dataTime } == null) {
+                        historyData.add(it)
+                    }
+                }
+            }
+
+            _times += e.second.exceptionData.size
+        }
+        // 鎸夌収閲囨牱鏃堕棿鍗囧簭鎺掑垪
+        historyData.sortBy { it.dataTime }
+
         exception = eType.des
         exceptionType = eType.value
-
-        startTime = start.dataTime
-        endTime = end?.dataTime
-//        startData = start.getByFactorType(factor.main)
-//        endData = end?.getByFactorType(factor.main) ?: startData
-//        startData = start
-//        endData = end
-
-        windSpeed = exceptionData.first().windSpeed?.toDouble()
-        times = windLevelCondition?.countLimit
-
-        dataList.add(start)
-        exceptionData.forEach {
-            dataList.add(it)
-        }
-        dataVoList.addAll(dataList.map { it.toDataVo() })
+        startTime = startData?.dataTime
+        endTime = endData?.dataTime
+        windSpeed = historyData.avg().windSpeed?.toDouble()
+        times = _times
         historyDataList.addAll(historyData.map { it.toDataVo() })
 
+        // 鍐嶆鏁村悎寮傚父鏁版嵁锛屽垎鍒绠楀悇鍥犲瓙鐨勫紓甯哥粨鏋滅粺璁�
+        exceptions.forEach {e ->
+            statisticMap[e.first.main] = Statistic().apply {
+                factorId = e.first.main.value
+                factorName = e.first.main.des
+                subFactorId = e.first.subs.map { it.value }
+                subFactorName = e.first.subs.map { it.des }
+                selectedFactor = e.first
+                dataIndexList = e.second.exceptionData.map {
+                    historyDataList.indexOfFirst { d ->
+                        d.time == DateUtil.instance.dateToString(it.dataTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
+                    }
+                }
 
-        factorList.forEach { f->
-            statisticMap[f.main] = Statistic().apply {
-                factorId = f.main.value
-                factorName = f.main.des
-                subFactorId = f.subs.map { it.value }
-                subFactorName = f.subs.map { it.des }
-                selectedFactor = f
-
-                avgPer = calPer(f.main)
-                avgRate = calRate(f.main)
-
-                val s = dataSummary(dataList, f.main)
+                val s = dataSummary(e.second.exceptionData, e.first.main)
                 avg = s.first
                 min = s.second
                 max = s.third
+
+                excGroup = getExceptionDataGroup().map { ExcGroup(it, e.first.main) }
+                avgPer = excGroup?.mapNotNull { it.per }?.average()
+                avgRate = excGroup?.mapNotNull { it.rate }?.average()
             }
         }
     }
 
     var deviceCode: String? = null
-
     var exception: String? = null
     var exceptionType: Int? = null
-
     var startTime: Date? = null
     var endTime: Date? = null
-
-//    var startData: BaseRealTimeData? = null
-//    var endData: BaseRealTimeData? = null
-
     // 椋庨��
     var windSpeed: Double? = null
-
     // 鍙戠敓娆℃暟
     var times: Int? = null
-
     var historyDataList = mutableListOf<DataVo>()
     // 寮傚父鐩戞祴鏁版嵁锛屽寘鍚崟娆″紓甯镐腑鎵�鏈夊彂鐢熶簡寮傚父鐨勬暟鎹�硷紙鍙兘涓嶆槸鏃堕棿杩炵画鐨勬暟鎹級
-    var dataList: MutableList<BaseRealTimeData> = mutableListOf()
-    var dataVoList: MutableList<DataVo> = mutableListOf()
-
+//    var dataList: MutableList<BaseRealTimeData> = mutableListOf()
+//    var dataVoList: MutableList<DataVo> = mutableListOf()
     var statisticMap = mutableMapOf<FactorType, Statistic>()
 
+    /**
+     * 鑾峰彇鎵�鏈夊紓甯稿洜瀛愬悕绉�
+     */
     fun toFactorNames(): String {
         val factors = statisticMap.entries.map { it.key }.sortedBy { it.value }.joinToString(";") { it.des }
         return factors
     }
 
-    private fun calPer(factorType: FactorType): Double? {
-        val list = dataList
-        if (list.size < 2) return null
-
-        var total = .0
-        for (i in 0 until list.size - 1) {
-            val p = list[i].getByFactorType(factorType) ?: .0f
-            val n = list[i + 1].getByFactorType(factorType) ?: .0f
-            total += (n - p) / p
-        }
-        return total / (list.size - 1)
+    fun getExceptionAvgData(): BaseRealTimeData {
+        val exceptionDataList = statisticMap.flatMap { it.value.getExceptionData() ?: emptyList() }
+        val avgData = exceptionDataList.avg()
+        return avgData
+    }
+    /**
+     * 鑾峰彇寮傚父鏁版嵁涓績鍧愭爣锛堝紓甯告暟鎹腑缁忓害绾害鐨勫钩鍧囧�硷級
+     */
+    fun getExceptionCenter(): Pair<Double, Double>? {
+        val avgData = getExceptionAvgData()
+        val wgs84Lng = avgData.longitude?.toDouble()
+        val wgs84Lat = avgData.latitude?.toDouble()
+        return if (wgs84Lng == null || wgs84Lat == null) null else Pair(wgs84Lng, wgs84Lat)
     }
 
-    private fun calRate(factorType: FactorType): Double? {
-        val list = dataList
-        if (list.size < 2) return null
+    private fun calPer(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Double? {
+        if (exceptionData.size < 2) return null
 
         var total = .0
-        for (i in 0 until list.size - 1) {
-            val p = list[i].getByFactorType(factorType) ?: .0f
-            val n = list[i + 1].getByFactorType(factorType) ?: .0f
+        for (i in 0 until exceptionData.size - 1) {
+            val p = exceptionData[i]?.getByFactorType(factorType) ?: .0f
+            val n = exceptionData[i + 1]?.getByFactorType(factorType) ?: .0f
+            total += (n - p) / p
+        }
+        return total / (exceptionData.size - 1)
+    }
+
+    private fun calRate(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Double? {
+        if (exceptionData.size < 2) return null
+
+        var total = .0
+        for (i in 0 until exceptionData.size - 1) {
+            val p = exceptionData[i]?.getByFactorType(factorType) ?: .0f
+            val n = exceptionData[i + 1]?.getByFactorType(factorType) ?: .0f
             total += (n - p) / 4
         }
-        return total / (list.size - 1)
+        return total / (exceptionData.size - 1)
     }
 
     private fun dataSummary(exceptionData: List<BaseRealTimeData?>, factorType: FactorType): Triple<Double, Double,
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt
index ab85953..a6e9311 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt
@@ -12,7 +12,6 @@
 import com.flightfeather.uav.socket.eunm.FactorType
 import org.springframework.beans.BeanUtils
 import org.springframework.web.context.ContextLoader
-import kotlin.math.round
 
 /**
  * 姹℃煋鏉ユ簮
@@ -76,14 +75,12 @@
 
             // 5. 鏍规嵁姹℃煋鍥犲瓙鐨勯噺绾э紝璁$畻涓昏鐨勬薄鏌撳満鏅被鍨嬶紝绛涢�夌粨鏋�
             val mainSceneType = calSceneType(pollutedData)
-            if (mainSceneType != null) {
-                result = result.filter {
-                    val r = mainSceneType.second.find { s ->
-                        s.value == it.typeId.toInt()
-                    }
-                    r != null
-                }.toMutableList()
-            }
+            result = result.filter {
+                val r = mainSceneType.find { s ->
+                    s.value == it.typeId.toInt()
+                }
+                r != null
+            }.toMutableList()
             this.sceneList = findClosestStation(sceneInfoRep, result)
         }
 
@@ -95,8 +92,7 @@
      * 璁$畻鍙兘鐨勭浉鍏虫薄鏌撳満鏅被鍨嬩互鍙婃帹鐞嗙粨璁�
      */
     @Throws(Exception::class)
-    private fun calSceneType(pollutedData: PollutedData): Pair<String, List<SceneType>>? {
-        var des: String? = null
+    private fun calSceneType(pollutedData: PollutedData): List<SceneType> {
         val sceneTypes = mutableListOf<SceneType>()
         pollutedData.statisticMap.entries.forEach { s ->
             val res = when (s.key) {
@@ -104,85 +100,35 @@
                 FactorType.NO,
                 FactorType.NO2,
                     -> {
-//                    val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
-                    val coAvg = round(pollutedData.statisticMap[FactorType.CO]?.avg ?: .0) / 1000
-                    "姘哀鍖栧悎鐗╁亸楂橈紝CO鐨勯噺绾т负${coAvg}mg/m鲁锛屼竴鑸敱浜庢満鍔ㄨ溅灏炬皵閫犳垚锛屾薄鏌撴簮浠ユ苯淇�佸姞娌圭珯涓轰富" to
-                            listOf(SceneType.TYPE1, SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
+                    listOf(SceneType.TYPE1, SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
                 }
 
-                FactorType.CO -> "" to listOf(SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
-
+                FactorType.CO -> listOf(SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
                 FactorType.H2S -> null
-
                 FactorType.SO2 -> null
-
                 FactorType.O3 -> null
-                // a) pm2.5銆乸m10鐗瑰埆楂橈紝涓よ�呭湪鍚勬儏鍐典笅鍚屾灞曠ず锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝姣旈噸瓒婇珮锛岃秺鏈夊彲鑳芥槸椁愰ギ
-                // b) pm10鐗瑰埆楂樸�乸m2.5杈冮珮锛屽ぇ棰楃矑鎵皹姹℃煋锛屽彧灞曠ずpm10锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝宸ュ湴涓轰富
                 FactorType.PM25,
                 FactorType.PM10,
                     -> {
-//                    val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
-//                    val pm10Avg = round(pollutedData.dataList.map { it.pm10!! }.average() * 10) / 10
-                    val pm25Avg = round((pollutedData.statisticMap[FactorType.PM25]?.avg ?: .0) * 10) / 10
-                    val pm10Avg = round((pollutedData.statisticMap[FactorType.PM10]?.avg ?: .0) * 10) / 10
-                    // 璁$畻寮傚父鏁版嵁鐨刾m2.5鍗爌m10姣旈噸鐨勫潎鍊�
-                    val percentageAvg = pollutedData.dataList.map {
-                        it.pm25!! / it.pm10!!
-                    }.average()
-                    val str =
-                        "PM2.5閲忕骇涓�${pm25Avg}渭g/m鲁锛孭M10閲忕骇涓�${pm10Avg}渭g/m鲁锛孭M2.5鍗燩M10鐨勬瘮閲嶄负${round(percentageAvg * 100)}%"
-                    if (percentageAvg > 0.666) {
-                        "${str}锛屾瘮閲嶈緝澶э紝姹℃煋婧愪互椁愰ギ涓轰富锛屽伐鍦版涔�" to
-                                listOf(
-                                    SceneType.TYPE1,
-                                    SceneType.TYPE2,
-                                    SceneType.TYPE3,
-                                    SceneType.TYPE14,
-                                    SceneType.TYPE5,
-                                    SceneType.TYPE18
-                                )
-                    } else if (percentageAvg < 0.333) {
-                        "${str}锛屾瘮閲嶈緝灏忥紝灞炰簬澶ч绮掓壃灏樻薄鏌擄紝姹℃煋婧愪互宸ュ湴涓轰富" to
-                                listOf(
-                                    SceneType.TYPE1,
-                                    SceneType.TYPE2,
-                                    SceneType.TYPE3,
-                                    SceneType.TYPE14,
-                                    SceneType.TYPE5,
-                                    SceneType.TYPE18
-                                )
-                    } else {
-                        "${str}锛屾薄鏌撴簮浠ラ楗�佸伐鍦颁负涓�" to
-                                listOf(
-                                    SceneType.TYPE1,
-                                    SceneType.TYPE2,
-                                    SceneType.TYPE3,
-                                    SceneType.TYPE14,
-                                    SceneType.TYPE5,
-                                    SceneType.TYPE18
-                                )
-                    }
+                    listOf(
+                        SceneType.TYPE1,
+                        SceneType.TYPE2,
+                        SceneType.TYPE3,
+                        SceneType.TYPE14,
+                        SceneType.TYPE5,
+                        SceneType.TYPE18
+                    )
                 }
-                // c) VOC杈冮珮锛屽悓姣旇绠梡m2.5鐨勯噺绾э紝鍙兘瀛樺湪鍚屾鍋忛珮锛堟苯淇�佸姞娌圭珯锛�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
-                // d) VOC杈冮珮锛屽浜庡姞娌圭珯锛堣溅杈嗘嫢鍫垫儏鍐碉級锛孋O涓�鑸緝楂�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
+
                 FactorType.VOC -> {
-//                    val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
-//                    val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
-//                    val o3Avg = round(pollutedData.dataList.map { it.o3!! }.average() * 10) / 10
-                    val pm25Avg = round((pollutedData.statisticMap[FactorType.PM25]?.avg ?: .0)) / 10
-                    val coAvg = round((pollutedData.statisticMap[FactorType.CO]?.avg ?: .0)) / 1000
-                    val o3Avg = round((pollutedData.statisticMap[FactorType.O3]?.avg ?: .0)) / 10
-                    "VOC鍋忛珮锛屽悓鏃禤M2.5閲忕骇涓�${pm25Avg}渭g/m鲁锛孋O閲忕骇涓�${coAvg}mg/m鲁锛孫3閲忕骇涓�${o3Avg}渭g/m鲁锛屾薄鏌撴簮浠ユ苯淇�佸姞娌圭珯涓轰富" to
-                            listOf(SceneType.TYPE5, SceneType.TYPE6, SceneType.TYPE17, SceneType.TYPE12, SceneType.TYPE18)
+                    listOf(SceneType.TYPE5, SceneType.TYPE6, SceneType.TYPE17, SceneType.TYPE12, SceneType.TYPE18)
                 }
 
                 else -> null
             }
-            des = res?.first
-            res?.second?.let { sceneTypes.addAll(it) }
+            res?.let { sceneTypes.addAll(it) }
         }
-        return (des ?: "") to sceneTypes
+        return sceneTypes.distinct()
     }
 
     /**
@@ -233,56 +179,32 @@
         val et = DateUtil.instance.getTime(pollutedData.endTime)
 
         // 1. 鎻忚堪寮傚父鍙戠敓鐨勬椂闂村拰寮傚父绫诲瀷
-        var txt = "鍦�${st}鑷�${et}涔嬮棿锛屽嚭鐜�${pollutedData.exception}"
+        var txt = "鍦�${st}鑷�${et}涔嬮棿锛屽嚭鐜�${pollutedData.exception}${pollutedData.times}娆�"
 
         // 2. 鎻忚堪寮傚父鏁版嵁鐨勫彉鍖栨儏鍐�
-        // 寮傚父鏁版嵁闀垮害搴旇澶т簬1锛岄涓�兼槸寮傚父寮�濮嬫暟鎹殑鍓嶄竴涓甯稿��,鍚庣画涓哄紓甯告暟鎹�硷紙浣嗕笉涓�瀹氭椂闂磋繛缁級
-        if (pollutedData.dataList.size > 1) {
-            val historyDataList = pollutedData.historyDataList.map { it.toBaseRealTimeData(BaseRealTimeData::class.java) }
-            when (pollutedData.exceptionType) {
-                // 閲忕骇绐佸彉
-                ExceptionType.TYPE4.value -> {
-                    val exceptionPair = mutableListOf<Pair<BaseRealTimeData, BaseRealTimeData>>()
-                    pollutedData.dataList.forEachIndexed { index, baseRealTimeData ->
-                        if (index == 0) return@forEachIndexed
-                        val preIndex = historyDataList.indexOfFirst {
-                            it.dataTime == baseRealTimeData.dataTime
-                        }
-                        exceptionPair.add(
-                            (if (preIndex - 1 < 0) historyDataList[0] else historyDataList[preIndex - 1])
-                                    to baseRealTimeData
-                        )
+        val statArr = mutableListOf<String>()
+        pollutedData.statisticMap.entries.forEach { s ->
+            val txtArr = mutableListOf<String>()
+            s.value.excGroup?.forEach exception@{ p ->
+                val preValue = p.getFirstDataValue()
+                val curValue = p.getLastDataValue()
+                val per = p.per?.times(100)
+                val rate = p.rate
+                if (preValue == null || curValue == null || per == null) return@exception
+                when (pollutedData.exceptionType) {
+                    // 閲忕骇绐佸彉
+                    ExceptionType.TYPE4.value -> {
+                        txtArr.add("浠�${preValue}渭g/m鲁绐佸彉鑷�${curValue}渭g/m鲁锛屽彉鍖栫巼涓�${per}%")
                     }
-                    val statArr = mutableListOf<String>()
-                    pollutedData.statisticMap.entries.forEach { s ->
-                        val txtArr = mutableListOf<String>()
-                        exceptionPair.forEach exception@{ p ->
-                            val preValue = p.first.getByFactorType(s.key)
-                            val curValue = p.second.getByFactorType(s.key)
-                            if (preValue == null || curValue == null) return@exception
-                            val r = round((curValue - preValue) / preValue * 100)
-                            txtArr.add("浠�${preValue}渭g/m鲁绐佸彉鑷�${curValue}渭g/m鲁锛屽彉鍖栫巼涓�${r}%")
-                        }
-                        statArr.add("${s.key.getTxt()}閲忕骇${txtArr.joinToString("锛�")}")
-                    }
-                    txt += "锛�${statArr.joinToString("锛�")}"
-                }
-                // 蹇�熶笂鍗�
-                ExceptionType.TYPE9.value -> {
-                    pollutedData.statisticMap.entries.forEach { s ->
-                        val preValue = pollutedData.dataList.first().getByFactorType(s.key)
-                        val curValue = pollutedData.dataList.last().getByFactorType(s.key)
-                        if (preValue == null || curValue == null) return@forEach
-                        val r = round((curValue - preValue) / preValue * 100)
-                        txt += "锛�${s.key.getTxt()}浠�${preValue}渭g/m鲁蹇�熶笂鍗囪嚦${curValue}渭g/m鲁锛屽彉鍖栫巼涓�${r}%"
+                    // 蹇�熶笂鍗�
+                    ExceptionType.TYPE9.value -> {
+                        txtArr.add("浠�${preValue}渭g/m鲁蹇�熶笂鍗囪嚦${curValue}渭g/m鲁锛屽彉鍖栭�熺巼涓�${rate}渭g/m鲁/绉掞紝鍙樺寲鐜囦负${per}%")
                     }
                 }
             }
-        } else {
-            pollutedData.statisticMap.entries.forEach { s ->
-                txt += "锛�${s.key.des}鏈�浣庡�间负${s.value.min}渭g/m鲁锛屾渶楂樺�间负${s.value.max}渭g/m鲁锛屽潎鍊间负${s.value.avg}渭g/m鲁"
-            }
+            statArr.add("${s.key.getTxt()}閲忕骇${txtArr.joinToString("锛�")}")
         }
+        txt += "锛�${statArr.joinToString("锛�")}"
 
         // 3. 鎻忚堪鍙戠幇鐨勯闄╂簮鎯呭喌
         if (sceneList.isEmpty()) {
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/AnalysisOption.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/AnalysisOption.kt
index ac79900..3f6e965 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/AnalysisOption.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/AnalysisOption.kt
@@ -3,7 +3,9 @@
 import com.fasterxml.jackson.annotation.JsonFormat
 import io.swagger.annotations.ApiModel
 import io.swagger.annotations.ApiModelProperty
+import io.swagger.annotations.ApiParam
 import org.springframework.format.annotation.DateTimeFormat
+import org.springframework.web.bind.annotation.RequestParam
 import java.time.LocalDateTime
 
 /**
@@ -36,4 +38,10 @@
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
     @ApiModelProperty(value = "缁撴潫鏃堕棿")
     var endTime: LocalDateTime? = null
+
+    @ApiModelProperty(value = "鏁版嵁棰楃矑搴�", allowableValues = "SECOND, MINUTE, HOUR")
+    var granularity: String? = null
+
+    @ApiParam("闇�瑕佺粺璁$殑鐩戞祴鍥犲瓙", example = "NO2, CO")
+    var factorTypes: List<String>? = null
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
index 1bb2999..b2a14af 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
@@ -32,6 +32,7 @@
      * @param missionCode 璧拌埅浠诲姟缂栧彿锛岀敤浜庡敮涓�鏍囪瘑鐗瑰畾鐨勮蛋鑸换鍔�
      * @return 寮傚父缁撴灉鍒楄〃锛屾瘡涓厓绱犲寘鍚紓甯哥被鍨嬨�佷綅缃拰璇︾粏淇℃伅
      */
+    @Deprecated("璇ユ帴鍙e凡搴熷純锛屼娇鐢�/report/missionSummary/one鎺ュ彛")
     fun pollutionTrace(missionCode: String): List<ExceptionResult>
 
     /**
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt
index bb19140..efb1451 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt
@@ -265,13 +265,6 @@
         if (removeNoPollutedSource) {
             clues.removeIf { it?.pollutedSource?.sceneList.isNullOrEmpty() }
         }
-//        val keyScenes = sceneInfoRep.findBySceneTypes(
-//            listOf(
-//                SceneType.TYPE19.value,
-//                SceneType.TYPE20.value,
-//                SceneType.TYPE21.value
-//            )
-//        )
         return MissionRiskArea().generateClueByRiskArea(clues)
     }
 
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt
index 07b27c3..8fd3212 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt
@@ -25,12 +25,11 @@
 @RequestMapping("air/analysis")
 class DataAnalysisController(private val dataAnalysisService: DataAnalysisService) {
 
+    @Deprecated("璇ユ帴鍙e凡搴熷純锛屼娇鐢�/report/missionSummary/one鎺ュ彛")
     @ApiOperation(value = "姹℃煋婧簮鍒嗘瀽")
     @GetMapping("/pollution/trace")
     fun pollutionTrace(
         @ApiParam("璧拌埅浠诲姟缂栧彿") @RequestParam missionCode: String,
-//        @RequestParam("page", required = false) page: Int?,
-//        @RequestParam("per_page", required = false) perPage: Int?,
     ) = resPack { dataAnalysisService.pollutionTrace(missionCode) }
 
     @ApiOperation(value = "鑾峰彇鍘嗗彶姹℃煋婧簮缁撴灉")
@@ -42,16 +41,14 @@
     @ApiOperation(value = "鐢熸垚璧拌埅浠诲姟姹囨�荤粺璁�")
     @PostMapping("/report/missionSummary")
     fun generateMissionSummary(
-        @ApiParam("寮�濮嬫椂闂�") @RequestParam
-        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-        startTime: LocalDateTime,
-        @ApiParam("缁撴潫鏃堕棿") @RequestParam
-        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-        endTime: LocalDateTime,
-        @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo,
+        @ApiParam("鍒嗘瀽閫夐」") @RequestBody analysisOption: AnalysisOption,
     ) = resPack {
+        if (analysisOption.startTime == null || analysisOption.endTime == null || analysisOption.area == null)
+            throw BizException("鍙傛暟閿欒, startTime, endTime, area涓嶈兘涓虹┖")
+        val startTime = analysisOption.startTime!!.atZone(ZoneId.systemDefault()).toInstant()
+        val endTime = analysisOption.endTime!!.atZone(ZoneId.systemDefault()).toInstant()
+        val areaVo = analysisOption.area!!
+
         dataAnalysisService.generateMissionSummary(
             Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()),
             Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()),
@@ -68,16 +65,14 @@
     @ApiOperation(value = "鐢熸垚璧拌埅浠诲姟娓呭崟")
     @PostMapping("/report/missionList")
     fun generateMissionList(
-        @ApiParam("寮�濮嬫椂闂�") @RequestParam
-        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-        startTime: LocalDateTime,
-        @ApiParam("缁撴潫鏃堕棿") @RequestParam
-        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-        endTime: LocalDateTime,
-        @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo,
+        @ApiParam("鍒嗘瀽閫夐」") @RequestBody analysisOption: AnalysisOption,
     ) = resPack {
+        if (analysisOption.startTime == null || analysisOption.endTime == null || analysisOption.area == null)
+            throw BizException("鍙傛暟閿欒, startTime, endTime, area涓嶈兘涓虹┖")
+        val startTime = analysisOption.startTime!!.atZone(ZoneId.systemDefault()).toInstant()
+        val endTime = analysisOption.endTime!!.atZone(ZoneId.systemDefault()).toInstant()
+        val areaVo = analysisOption.area!!
+
         dataAnalysisService.generateMissionList(
             Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()),
             Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()),
@@ -88,18 +83,15 @@
     @ApiOperation(value = "鐢熸垚璧拌埅浠诲姟璇︽儏")
     @PostMapping("/report/missionDetail")
     fun generateMissionDetail(
-        @ApiParam("寮�濮嬫椂闂�") @RequestParam
-        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-        startTime: LocalDateTime,
-        @ApiParam("缁撴潫鏃堕棿") @RequestParam
-        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-        endTime: LocalDateTime,
-        @ApiParam("鏁版嵁棰楃矑搴�", allowableValues = "SECOND, MINUTE, HOUR") @RequestParam(required = false)
-        granularity: String?,
-        @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo,
+        @ApiParam("鍒嗘瀽閫夐」") @RequestBody analysisOption: AnalysisOption,
     ) = resPack {
+        if (analysisOption.startTime == null || analysisOption.endTime == null || analysisOption.area == null)
+            throw BizException("鍙傛暟閿欒, startTime, endTime, area涓嶈兘涓虹┖")
+        val startTime = analysisOption.startTime!!.atZone(ZoneId.systemDefault()).toInstant()
+        val endTime = analysisOption.endTime!!.atZone(ZoneId.systemDefault()).toInstant()
+        val areaVo = analysisOption.area!!
+        val granularity = analysisOption.granularity
+
         dataAnalysisService.generateMissionDetail(
             Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()),
             Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()),
@@ -118,10 +110,10 @@
     @ApiOperation(value = "璧拌埅鍏稿瀷闅愭偅鍖哄煙缁熻")
     @PostMapping("/report/clueByRiskArea")
     fun generateClueByRiskArea(
-        @ApiParam("鍖哄煙") @RequestBody analysisOption: AnalysisOption,
+        @ApiParam("鍒嗘瀽閫夐」") @RequestBody analysisOption: AnalysisOption,
     ) = resPack {
         if (analysisOption.startTime == null || analysisOption.endTime == null || analysisOption.area == null)
-            throw BizException("鍙傛暟閿欒, startTime, endTime, areaVo涓嶈兘涓虹┖")
+            throw BizException("鍙傛暟閿欒, startTime, endTime, area涓嶈兘涓虹┖")
         val startTime = analysisOption.startTime!!.atZone(ZoneId.systemDefault()).toInstant()
         val endTime = analysisOption.endTime!!.atZone(ZoneId.systemDefault()).toInstant()
         val areaVo = analysisOption.area!!
@@ -143,20 +135,17 @@
     @ApiOperation(value = "鍙犲姞铻嶅悎鍒嗘瀽")
     @PostMapping("/report/gridFusion")
     fun generateGridFusion(
-        @ApiParam("寮�濮嬫椂闂�") @RequestParam
-        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-        startTime: LocalDateTime,
-        @ApiParam("缁撴潫鏃堕棿") @RequestParam
-        @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
-        endTime: LocalDateTime,
-        @ApiParam("闇�瑕佺粺璁$殑鐩戞祴鍥犲瓙", example = "NO2, CO") @RequestParam
-        factorTypes: String,
-        @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo,
+        @ApiParam("鍒嗘瀽閫夐」") @RequestBody analysisOption: AnalysisOption,
     ) = resPack {
+        if (analysisOption.startTime == null || analysisOption.endTime == null || analysisOption.area == null || analysisOption.factorTypes == null)
+            throw BizException("鍙傛暟閿欒, startTime, endTime, area, factorTypes涓嶈兘涓虹┖")
+        val startTime = analysisOption.startTime!!.atZone(ZoneId.systemDefault()).toInstant()
+        val endTime = analysisOption.endTime!!.atZone(ZoneId.systemDefault()).toInstant()
+        val areaVo = analysisOption.area!!
+        val factorTypes = analysisOption.factorTypes!!.map { FactorType.valueOf(it) }
+
         dataAnalysisService.generateGridFusion(
-            factorTypes.split(",").map { FactorType.valueOf(it) },
+            factorTypes,
             Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()),
             Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()),
             areaVo
diff --git a/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt b/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt
index 651ed15..7b99c9c 100644
--- a/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt
+++ b/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt
@@ -39,16 +39,8 @@
     @Test
     fun autoSourceTrace() {
         val sourceTraceController = SourceTraceController(sceneInfoRep, sourceTraceRep, true)
-//        val mCode = listOf(
-//            "SH-CN-20241227", "SH-CN-20241127", "SH-CN-20240906", "SH-CN-20240830(05)",
-//            "SH-CN-20240830(04)", "SH-CN-20240823",
-//            "SH-CN-20240723(02)",
-////            "SH-CN-20250723(01)"
-//        )
-//        val startTime = LocalDateTime.of(2025, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
-//        val endTime = LocalDateTime.of(2025, 9, 30, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
-        val startTime = LocalDateTime.of(2025, 11, 2, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
-        val endTime = LocalDateTime.of(2025, 11, 2, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
+        val startTime = LocalDateTime.of(2025, 12, 11, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
+        val endTime = LocalDateTime.of(2025, 12, 11, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
         val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
             createCriteria().andBetween("startTime", startTime, endTime)
         })
@@ -69,57 +61,12 @@
             }
             sourceTraceController.initTask()
         }
-//        val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
-//            createCriteria().andEqualTo("deviceType", "0a")
-//                .andLessThanOrEqualTo("startTime", "2024-11-07 15:00:00")
-//            orderBy("startTime").desc()
-//        })
-//        missions.forEach { m ->
-//            val rtData = realTimeDataService.getSecondData(
-//                m?.deviceType,
-//                m?.deviceCode,
-//                DateUtil.instance.dateToString(m?.startTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS),
-//                DateUtil.instance.dateToString(m?.endTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS),
-//                null,
-//                1,
-//                10000
-//            )
-//            rtData.data?.forEach { d ->
-//                val rtdVehicle = d.toBaseRealTimeData(RealTimeDataVehicle::class.java)
-////            Thread.sleep(500)
-//                sourceTraceController.addOneData(rtdVehicle)
-//            }
-//            sourceTraceController.initTask()
-//        }
-//        val rtData = realTimeDataService.getSecondData(
-//            "0a",
-//            "0a0000000001",
-//            "2024-08-23 12:30:23",
-//            "2024-08-23 15:12:56",
-//            null,
-//            1,
-//            10000
-//        )
-//        rtData.data?.forEach { d ->
-//            val rtdVehicle = d.toBaseRealTimeData(RealTimeDataVehicle::class.java)
-////            Thread.sleep(500)
-//            sourceTraceController.addOneData(rtdVehicle)
-//        }
-
     }
 
     @Test
     fun deleteSourceTrace() {
-//        val mCode = listOf(
-//            "SH-CN-20241227", "SH-CN-20241127", "SH-CN-20240906", "SH-CN-20240830(05)",
-//            "SH-CN-20240830(04)", "SH-CN-20240823",
-//            "SH-CN-20240723(02)",
-////            "SH-CN-20250723(01)"
-//        )
-//        val startTime = LocalDateTime.of(2025, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
-//        val endTime = LocalDateTime.of(2025, 9, 30, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
-        val startTime = LocalDateTime.of(2025, 11, 2, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
-        val endTime = LocalDateTime.of(2025, 11, 2, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
+        val startTime = LocalDateTime.of(2025, 12, 11, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
+        val endTime = LocalDateTime.of(2025, 12, 11, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
         val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
             createCriteria().andBetween("startTime", startTime, endTime)
         })

--
Gitblit v1.9.3