From 4d065a305b997bfb66f41b33a31d59de63b1958d Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期四, 29 五月 2025 17:43:21 +0800
Subject: [PATCH] 1. 新增动态污染溯源新的判定逻辑(待完成)

---
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel6.kt                |   17 +
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt          |   55 ++--
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt                           |   67 +++++
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt                         |   38 +++
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt                     |   81 ++----
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1.kt                |   17 +
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel4.kt                |   17 +
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt                  |   55 ++++
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1_1.kt              |   17 +
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt |    3 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt  |    1 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt                           |   14 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt                           |   49 +++
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt             |  121 ++++++++++
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedArea.kt                           |  101 ++++++++
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt                        |   11 
 16 files changed, 573 insertions(+), 91 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
index bf4f1d1..658c621 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -19,42 +19,6 @@
         private const val OFFSET = 10
     }
 
-    inner class Tag {
-        // 璧峰鏁版嵁涓嬫爣
-        var sIndex = 0
-
-        // 璧峰鏁版嵁瀵硅薄
-        var startData: BaseRealTimeData? = null
-
-        // 鏈熬鏁版嵁涓嬫爣
-        var eIndex = -1
-
-        // 鏈熬鏁版嵁瀵硅薄
-        var endData: BaseRealTimeData? = null
-
-        // 寮傚父鏁版嵁娈�
-        var exceptionData = mutableListOf<BaseRealTimeData>()
-
-        // 鏄惁瀛樺湪寮傚父
-        var exceptionExisted = false
-
-        // 寮傚父缁撴灉鏄惁鍒涘缓
-        var exceptionCreated = false
-
-        fun addExceptionData(data: BaseRealTimeData) {
-            exceptionExisted = true
-            exceptionData.add(data)
-        }
-
-        fun refreshWithNextException(data: BaseRealTimeData) {
-            sIndex = eIndex
-            startData = data
-            exceptionData.clear()
-            exceptionExisted = false
-            exceptionCreated = false
-        }
-    }
-
     protected val tagMap = mutableMapOf<FactorType, T>()
 
     // 璧峰鏁版嵁涓庢湯灏炬暟鎹棿闅�
@@ -62,6 +26,20 @@
 
     // 鏈熬鏁版嵁瀵硅薄
     protected var lastData: BaseRealTimeData? = null
+
+    /**
+     * 鍚庣疆鍒ゆ柇锛氬綋鐩搁偦鏁版嵁鏃堕棿涓嶈繛缁椂锛屾垨鑰呮弧瓒宠嚜瀹氫箟鏉′欢鏃讹紝瀵逛箣鍓嶅凡鏈夌殑寮傚父杩涜璁板綍
+     */
+    open fun afterExcCheck(isContinue: Boolean, tag: T, hasException: Boolean?): Boolean {
+        return !isContinue || needCut(tag, hasException)
+    }
+
+    /**
+     * 绔嬪嵆鍒ゆ柇锛氬綋鍑虹幇寮傚父鏃讹紝缂撳瓨寮傚父鏁版嵁鐨勫悓鏃讹紝绔嬪嵆瀵瑰凡鏈夊紓甯歌繘琛屽垽鏂槸鍚︽弧瓒冲紓甯哥粨鏋滆姹�
+     */
+    open fun immeExcCheck(tag: T): Boolean {
+        return false
+    }
 
     /**
      * 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画
@@ -75,7 +53,7 @@
     }
 
     /**
-     * 鍒ゆ柇鏄惁婊¤冻寮傚父鏉′欢
+     * 鍒ゆ柇鍓嶅悗鏁版嵁鏄惁婊¤冻寮傚父鏉′欢
      */
     abstract fun judgeException(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean>
 
@@ -87,11 +65,11 @@
 
     /**
      * 寮傚父鏁版嵁鐨勬埅鍙栧垽鏂�
-     * 鏄惁闇�瑕侀檺鍒朵竴缁勫紓甯告暟鎹殑闀垮害
      * @return 榛樿涓嶉渶瑕佹埅鍙�
      */
-    open fun needCut(tag: T): Boolean {
-        return false
+    open fun needCut(tag: T, hasException: Boolean?): Boolean {
+        // 榛樿鍒ゆ柇鏉′欢涓� 褰撳紓甯镐笉鍐嶉噸澶嶅嚭鐜版椂锛屽舰鎴愬紓甯哥粨鏋�
+        return tag.exceptionExisted && hasException == false
     }
 
     override fun init() {
@@ -115,15 +93,19 @@
                 if (it.startData == null) {
                     it.refreshWithNextException(data)
                 }
-                // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画骞朵笖鏄惁婊¤冻寮傚父鍒ゆ柇
-                if (!isContinue || needCut(it)) {
-                    // 鏁版嵁涓嶈繛缁椂锛岃褰曞紓甯告儏鍐�
+
+                // 瀵逛簬寮傚父鐨勭敓鎴愬垎鍒墽琛屽悗缃垽鏂�佸拰绔嬪嵆鍒ゆ柇
+                // 1. 鍚庣疆鍒ゆ柇锛氬綋鐩搁偦鏁版嵁鏃堕棿涓嶈繛缁椂锛屾垨鑰呮弧瓒宠嚜瀹氫箟鏉′欢鏃讹紝瀵逛箣鍓嶅凡鏈夌殑寮傚父杩涜璁板綍锛屽舰鎴愬紓甯哥粨鏋�
+                if (afterExcCheck(isContinue, it, hasException[f])) {
+                    // 鏁版嵁涓嶈繛缁椂鎴栬�呮弧瓒充富鍔ㄦ埅鏂潯浠舵椂锛岃褰曞紓甯告儏鍐�
                     recordException(s, it, data)
-                } else {
-                    if (hasException[f] == true) {
-                        it.addExceptionData(data)
-                    } else {
-                        // 寮傚父涓嶅啀閲嶅鍑虹幇鏃讹紝璁板綍寮傚父鎯呭喌
+                }
+                // 2. 绔嬪嵆鍒ゆ柇锛氬綋鍑虹幇寮傚父鏃讹紝缂撳瓨寮傚父鏁版嵁鐨勫悓鏃讹紝绔嬪嵆瀵瑰凡鏈夊紓甯歌繘琛屽垽鏂槸鍚︽弧瓒冲紓甯哥粨鏋滆姹�
+                else if (hasException[f] == true) {
+                    // 鏈夊紓甯稿嚭鐜版椂锛岃褰曞紓甯告暟鎹�
+                    it.addExceptionData(data)
+                    // 褰撶珛鍗冲垽鏂�氳繃鏃讹紝褰㈡垚寮傚父缁撴灉
+                    if (immeExcCheck(it)) {
                         recordException(s, it, data)
                     }
                 }
@@ -138,12 +120,11 @@
 
     /**
      * 寮傚父缁撴潫锛岃褰曞紓甯�
+     * 鍒ゆ柇宸叉湁鐨勫紓甯告暟鎹槸鍚︽弧瓒冲紓甯告潯浠讹紝婊¤冻鍒欒褰曪紝涓嶆弧瓒冲垯鐣ヨ繃
      */
     fun recordException(factor: FactorFilter.SelectedFactor, tag: T, data: BaseRealTimeData) {
         checkResult(factor, ExceptionStatusType.Ended)
-//        if (tag.eIndex - tag.sIndex >= durationCount) {
         tag.refreshWithNextException(data)
-//        }
     }
 
     /**
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt
new file mode 100644
index 0000000..9ca9419
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt
@@ -0,0 +1,55 @@
+package com.flightfeather.uav.biz.sourcetrace.config
+
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseAnalysisConfig
+import com.flightfeather.uav.biz.sourcetrace.model.DistanceType
+
+/**
+ *
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevelConfig(factorFilter: FactorFilter): BaseAnalysisConfig(factorFilter) {
+
+    inner class WindLevelCondition(
+        val windSpeed: Pair<Double, Double>,
+        val mutationRate: Pair<Double, DistanceType>,
+        val countLimit: Int,
+    )
+
+    // 闄愬畾璺濈鍐咃紙鍗曚綅锛氱背锛�
+    var distanceLimit = 1000
+    // 闄愬畾鏃堕棿鍐咃紙鍗曚綅锛氬垎閽燂級
+    var timeLimit = 2
+
+    // 0 - 1绾ч
+    var windLevelCondition1 = WindLevelCondition(
+        .0 to 1.5,
+        0.5 to DistanceType.TYPE1,
+        1
+    )
+
+    // 0 - 1绾ч
+    var windLevelCondition1_1 = WindLevelCondition(
+        .0 to 1.5,
+        0.2 to DistanceType.TYPE2,
+        1
+    )
+
+    // 2 - 4绾ч
+    var windLevelCondition2 = WindLevelCondition(
+        1.6 to 7.9,
+        0.2 to DistanceType.TYPE3,
+        3
+    )
+
+    // 5 - 6绾ч
+    var windLevelCondition3 = WindLevelCondition(
+        8.0 to 13.8,
+        0.1 to DistanceType.TYPE4,
+        3
+    )
+
+    // 婧簮鎵╂暎鍋忕Щ瑙掑害锛堝崟浣嶏細搴︼級
+    var sourceTraceDegOffset = 120.0
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt
new file mode 100644
index 0000000..b1010a1
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRTExcWindLevel.kt
@@ -0,0 +1,121 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseExceptionContinuous
+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.biz.sourcetrace.model.PollutedClue
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.lightshare.eunm.ExceptionStatusType
+import com.flightfeather.uav.socket.eunm.FactorType
+import java.time.Duration
+import java.time.LocalDateTime
+import java.time.ZoneId
+
+
+// 寮傚父鏁版嵁鐢熸垚鍥炶皟绫�
+typealias NewPolluteClueCallback = (ex: PollutedClue) -> Unit
+/**
+ * 涓嶅悓椋庨�熶笅锛屾暟鎹獊鍙樺紓甯稿熀绫�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+abstract class BaseRTExcWindLevel(config: RTExcWindLevelConfig) :
+    BaseExceptionContinuous<ExceptionTag, RTExcWindLevelConfig, PollutedClue>(config, ExceptionTag::class.java) {
+
+    constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : this(config){
+        this.callback = callback
+    }
+
+    private var callback: NewPolluteClueCallback? = null
+
+    abstract var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition
+
+    override fun getExceptionType(): ExceptionType {
+        return ExceptionType.TYPE4
+    }
+
+    override fun judgeException(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> {
+        val res = mutableMapOf<FactorType, Boolean>()
+        config.factorFilter.mainList().forEach { f ->
+            if (p?.getByFactorType(f) == null || n.getByFactorType(f) == null || n.windSpeed == null) {
+                res[f] = (false)
+                return@forEach
+            }
+
+            val con = windLevelCondition
+
+            if (n.windSpeed!! in con.windSpeed.first..con.windSpeed.second) {
+                val pValue = p.getByFactorType(f)!!
+                val nValue = n.getByFactorType(f)!!
+                // 璁$畻鍚庝竴涓暟鎹浉姣斾簬鍓嶄竴涓暟鎹殑鍙樺寲鐜�
+                val r = (nValue - pValue) / pValue
+                val b1 = r >= con.mutationRate.first
+                res[f] = b1
+            } else {
+                res[f] = false
+            }
+        }
+
+        return res
+    }
+
+    override fun judgeExceptionCount(tag: ExceptionTag): Boolean {
+        return tag.exceptionData.size >= windLevelCondition.countLimit
+    }
+
+    override fun needCut(tag: ExceptionTag, hasException: Boolean?): Boolean {
+        // 鎸夌収鏃堕暱鍜岃窛绂婚檺鍒跺皢寮傚父鎴彇
+        if (tag.exceptionData.isEmpty()) return false
+
+        val se = tag.exceptionData.first()
+        val ee = tag.exceptionData.last()
+
+        val sTime = LocalDateTime.ofInstant(se.dataTime?.toInstant(), ZoneId.systemDefault())
+        val eTime = LocalDateTime.ofInstant(ee.dataTime?.toInstant(), ZoneId.systemDefault())
+        val duration = Duration.between(sTime, eTime).toMinutes()
+        // 鏁版嵁閲囨牱鐨勬椂闀胯秴杩囬檺鍒舵椂锛岄渶瑕佹埅鍙�
+        val b1 = duration > config.timeLimit
+
+        // 璧拌埅鏁版嵁鐨勮窛绂昏秴杩囬檺鍒舵椂锛岄渶瑕佹埅鍙�
+        val b2 = if (se.longitude == null || se.latitude == null || ee.longitude == null || ee.latitude == null) {
+            false
+        } else {
+            val distance = MapUtil.getDistance(
+                se.longitude!!.toDouble(), se.latitude!!.toDouble(), ee.longitude!!
+                    .toDouble(), ee.latitude!!.toDouble()
+            )
+            distance > config.distanceLimit
+        }
+
+        return b1 || b2
+    }
+
+    override fun immeExcCheck(tag: ExceptionTag): Boolean {
+        // 寮傚父鍑虹幇绛変簬闄愬畾娆℃暟鏃讹紝灏遍渶瑕佸舰鎴愭薄鏌撶嚎绱�
+        return tag.exceptionData.size == windLevelCondition.countLimit
+    }
+
+    override fun newResult(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+    ): PollutedClue {
+        return PollutedClue(start, end, factor, exceptionData, getExceptionType(), config, windLevelCondition)
+    }
+
+    override fun onNewException(
+        tag: ExceptionTag,
+        factor: FactorFilter.SelectedFactor,
+        exceptionStatus: ExceptionStatusType,
+    ) {
+        super.onNewException(tag, factor, exceptionStatus)
+        callback?.let { func ->
+            val exc = tag.exceptionResult.last()
+            func.invoke(exc as PollutedClue)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
index bfb6b9a..d4c0232 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/BaseRealTimeException.kt
@@ -17,6 +17,7 @@
  * @date 2025/5/13
  * @author feiyu02
  */
+@Deprecated("2025.5.29, 閫昏緫涓庝笟鍔′笉鍖归厤锛屽悗缁垹闄�")
 abstract class BaseRealTimeException<T : ExceptionTag>(config: RealTimeAnalysisConfig, tagClz: Class<T>) :
     BaseExceptionContinuous<T, RealTimeAnalysisConfig, RealTimeExceptionResult>(config, tagClz) {
 
@@ -26,33 +27,33 @@
 
     var callback: NewExceptionCallback? = null
 
-    override fun onNextData(data: BaseRealTimeData) {
-        val isContinue = isContinuous(lastData, data)
-        val hasException = judgeException(lastData, data)
-        config.factorFilter.selectedList.forEach { s ->
-            val f = s.main
-            tagMap[f]?.let {
-                it.eIndex++
-                // 璧峰鏁版嵁
-                it.endData = data
-                if (it.startData == null) {
-                    it.refreshWithNextException(data)
-                }
-                // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画鎴栬�呮槸鍚︽弧瓒宠嚜瀹氫箟鎴彇鏉′欢
-                if (!isContinue || needCut(it)) {
-                    // 璁板綍寮傚父锛岀粨鏉熷紓甯哥殑瀹炴椂鐘舵�佹挱鎶�
-                    recordException(s, it, data)
-                } else {
-                    // 绉婚櫎浜嗙埗绫诲師鏈夐�昏緫锛屾敼涓哄綋婊¤冻寮傚父鏉′欢鏃讹紝闇�瑕佸疄鏃舵帹閫佹挱鎶ュ紓甯哥殑鐘舵�佸彉鍖栵紝浣嗕笉鎴彇寮傚父
-                    if (hasException[f] == true) {
-                        it.addExceptionData(data)
-                        checkResult(s)
-                    }
-                }
-            }
-        }
-        lastData = data
-    }
+//    override fun onNextData(data: BaseRealTimeData) {
+//        val isContinue = isContinuous(lastData, data)
+//        val hasException = judgeException(lastData, data)
+//        config.factorFilter.selectedList.forEach { s ->
+//            val f = s.main
+//            tagMap[f]?.let {
+//                it.eIndex++
+//                // 璧峰鏁版嵁
+//                it.endData = data
+//                if (it.startData == null) {
+//                    it.refreshWithNextException(data)
+//                }
+//                // 鍒ゆ柇鐩搁偦鏁版嵁鏄惁杩炵画鎴栬�呮槸鍚︽弧瓒宠嚜瀹氫箟鎴彇鏉′欢
+//                if (!isContinue || needCut(it)) {
+//                    // 璁板綍寮傚父锛岀粨鏉熷紓甯哥殑瀹炴椂鐘舵�佹挱鎶�
+//                    recordException(s, it, data)
+//                } else {
+//                    // 绉婚櫎浜嗙埗绫诲師鏈夐�昏緫锛屾敼涓哄綋婊¤冻寮傚父鏉′欢鏃讹紝闇�瑕佸疄鏃舵帹閫佹挱鎶ュ紓甯哥殑鐘舵�佸彉鍖栵紝浣嗕笉鎴彇寮傚父
+//                    if (hasException[f] == true) {
+//                        it.addExceptionData(data)
+//                        checkResult(s)
+//                    }
+//                }
+//            }
+//        }
+//        lastData = data
+//    }
 
     override fun newResult(
         start: BaseRealTimeData,
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1.kt
new file mode 100644
index 0000000..63c48fc
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1.kt
@@ -0,0 +1,17 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+
+/**
+ * 0 - 1绾ч锛岃蒋椋庯紙椋庨��1.5m/s鍙婁互涓嬶級鐘舵�佷笅锛屾暟鎹獊鍙樺紓甯�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevel1 : BaseRTExcWindLevel {
+
+    constructor(config: RTExcWindLevelConfig) : super(config)
+
+    constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
+
+    override var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition = config.windLevelCondition1
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1_1.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1_1.kt
new file mode 100644
index 0000000..adc39e8
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel1_1.kt
@@ -0,0 +1,17 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+
+/**
+ * 0 - 1绾ч锛岃蒋椋庯紙椋庨��1.5m/s鍙婁互涓嬶級鐘舵�佷笅锛屾暟鎹獊鍙樺紓甯�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevel1_1 : BaseRTExcWindLevel {
+
+    constructor(config: RTExcWindLevelConfig) : super(config)
+
+    constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
+
+    override var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition = config.windLevelCondition1_1
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel4.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel4.kt
new file mode 100644
index 0000000..2f25b83
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel4.kt
@@ -0,0 +1,17 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+
+/**
+ * 2 - 4绾ч锛屽拰椋庯紙椋庨��1.6 - 7.9m/s鍙婁互涓嬶級鐘舵�佷笅锛屾暟鎹獊鍙樺紓甯�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevel4:BaseRTExcWindLevel {
+
+    constructor(config: RTExcWindLevelConfig) : super(config)
+
+    constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
+
+    override var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition = config.windLevelCondition2
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel6.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel6.kt
new file mode 100644
index 0000000..ef7f8eb
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RTExcWindLevel6.kt
@@ -0,0 +1,17 @@
+package com.flightfeather.uav.biz.sourcetrace.exceptiontype
+
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+
+/**
+ * 4 - 6绾ч锛屽己椋庯紙椋庨��8 - 13.8m/s锛夌姸鎬佷笅锛屾暟鎹獊鍙樺紓甯�
+ * @date 2025/5/29
+ * @author feiyu02
+ */
+class RTExcWindLevel6:BaseRTExcWindLevel {
+
+    constructor(config: RTExcWindLevelConfig) : super(config)
+
+    constructor(config: RTExcWindLevelConfig, callback: NewPolluteClueCallback) : super(config, callback)
+
+    override var windLevelCondition: RTExcWindLevelConfig.WindLevelCondition = config.windLevelCondition3
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt
index 8e1aba1..f471a79 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionSlideAverage.kt
@@ -19,6 +19,7 @@
  * @date 2025/5/13
  * @author feiyu02
  */
+@Deprecated("2025.5.29, 閫昏緫涓庝笟鍔′笉鍖归厤锛屽悗缁垹闄�")
 class RealTimeExceptionSlideAverage : BaseExceptionAnalysis<RealTimeAnalysisConfig, RealTimeExceptionResult> {
 
     constructor(config: RealTimeAnalysisConfig) : super(config)
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
index 5a97f66..81bc08a 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/exceptiontype/RealTimeExceptionValueMutation.kt
@@ -15,6 +15,7 @@
  * @date 2025/5/13
  * @author feiyu02
  */
+@Deprecated("2025.5.29, 閫昏緫涓庝笟鍔′笉鍖归厤锛屽悗缁垹闄�")
 class RealTimeExceptionValueMutation : BaseRealTimeException<ExceptionTag> {
 
     constructor(config: RealTimeAnalysisConfig) : super(config, ExceptionTag::class.java)
@@ -61,7 +62,7 @@
         return b1 || b2
     }
 
-    override fun needCut(tag: ExceptionTag): Boolean {
+    override fun needCut(tag: ExceptionTag, hasException: Boolean?): Boolean {
         // 鎸夌収鏃堕暱鍜岃窛绂婚檺鍒跺皢寮傚父鎴彇
         if (tag.exceptionData.isEmpty()) return false
 
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt
index 4f7af43..37c753a 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/DistanceType.kt
@@ -5,9 +5,13 @@
  * @date 2025/5/28
  * @author feiyu02
  */
-enum class DistanceType(val des: String) {
-    TYPE1("50绫�"),
-    TYPE2("50绫� - 500绫�"),
-    TYPE3("50绫� - 1鍏噷"),
-    TYPE4("50绫� - 2鍏噷")
+enum class DistanceType(val des: String, val disRange: Pair<Double, Double>) {
+    TYPE1("50绫�", .0 to 50.0),
+    TYPE2("50绫� - 500绫�", 50.0 to 500.0),
+    TYPE3("50绫� - 1鍏噷", 50.0 to 1000.0),
+    TYPE4("50绫� - 2鍏噷", 50.0 to 2000.0);
+
+    override fun toString(): String {
+        return this.des
+    }
 }
\ No newline at end of file
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 bcfe4e2..fac9ce7 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,18 +1,115 @@
 package com.flightfeather.uav.biz.sourcetrace.model
 
+import com.flightfeather.uav.biz.sourcetrace.config.RTExcWindLevelConfig
+import com.flightfeather.uav.common.net.AMapService
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.entity.avg
+import kotlin.math.PI
+
 /**
  * 鍔ㄦ�佹函婧愭薄鏌撳尯鍩�
  * 閫氳繃鍦板浘鍧愭爣鐐瑰舰鎴愬杈瑰舰鏉ユ弿杩颁竴鍧楁薄鏌撳尯鍩�
  * @date 2025/5/27
  * @author feiyu02
  */
-class PollutedArea {
+class PollutedArea() {
 
-    var name: String? = null
+    /**
+     * 婧簮瑙掑害鍙缃�
+     */
+
+    constructor(
+        exceptionData: List<BaseRealTimeData>,
+        config: RTExcWindLevelConfig,
+        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
+    ) : this() {
+        distanceType = windLevelCondition.mutationRate.second
+        sourceTrace(exceptionData, config, windLevelCondition)
+    }
+
+    var address: String? = null
 
     // 姹℃煋鑼冨洿鍖哄煙(缁忕含搴﹀杈瑰舰)
     var polygon: List<Pair<Double, Double>>? = null
 
     // 姹℃煋鍙兘鐨勫彂鐢熻窛绂�
     var distanceType: DistanceType? = null
+
+    /**
+     * 鍙嶅悜婧簮
+     */
+    private fun sourceTrace(
+        exceptionData: List<BaseRealTimeData>,
+        config: RTExcWindLevelConfig,
+        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
+    ) {
+        val avgData = if (exceptionData.size == 1) {
+            exceptionData.first()
+        } else {
+            exceptionData.avg()
+        }
+
+        val pair = avgData.longitude!!.toDouble() to avgData.latitude!!.toDouble()
+
+        polygon = calSector(
+            avgData.windSpeed!!.toDouble(),
+            pair,
+            windLevelCondition.mutationRate.second.disRange,
+            config.sourceTraceDegOffset
+        )
+
+        try {
+            val address = AMapService.reGeo(pair)
+            this.address = address.township + address.street
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+
+    }
+
+    /**
+     * 鏍规嵁涓績鐐瑰潗鏍囥�侀鍚戝拰婧簮璺濈锛屼互鍙婄粰瀹氱殑澶硅锛岃绠椾互涓績鐐规寜鐓ч鍚戝悜澶栨墿鏁e舰鎴愮殑鎵囧舰鐨勭偣鍧愭爣
+     * @param windDir 椋庡悜锛屽崟浣嶏細搴�
+     * @param center 涓績鐐瑰潗鏍囩粡绾害
+     * @param distanceRange 婧簮璺濈鑼冨洿锛屽崟浣嶏細绫�
+     * @param defaultDegOffset 鎵╂暎鍋忕Щ瑙掑害
+     * @return 澶氳竟褰㈤《鐐瑰潗鏍囬泦鍚�
+     */
+    private fun calSector(
+        windDir: Double,
+        center: Pair<Double, Double>,
+        distanceRange: Pair<Double, Double>,
+        defaultDegOffset: Double = 60.0,
+    ): List<Pair<Double, Double>> {
+
+        val sDeg = windDir - defaultDegOffset / 2
+        val eDeg = windDir + defaultDegOffset / 2
+
+        val result = mutableListOf<Pair<Double, Double>>()
+
+        if (distanceRange.first == .0) {
+            result.add(center)
+        } else {
+            // 浠庡紑濮嬭搴﹀惊鐜绠楀潗鏍囩偣鍊肩粨鏉熻搴︼紝姝ラ暱1掳
+            var startDeg = sDeg
+            while (startDeg <= eDeg) {
+                val p = MapUtil.getPointByLen(center, distanceRange.first, startDeg * PI / 180)
+                result.add(p)
+                startDeg++
+            }
+        }
+
+        if (distanceRange.second > .0) {
+            // 姝ゅ闇�瑕佷粠缁撴潫瑙掑害寮�濮嬪弽鍚戝惊鐜绠楄嚦寮�濮嬭搴︼紝姝ラ暱1掳锛屼娇寰椾袱缁勫潗鏍囩偣鎸夐『搴忔帓鍒楋紝鍙粯鍒跺搴旂殑澶氳竟褰�
+            var startDeg = eDeg
+            while (startDeg >= sDeg) {
+                val p = MapUtil.getPointByLen(center, distanceRange.second, startDeg * PI / 180)
+                result.add(p)
+                startDeg--
+            }
+        }
+
+        return result
+    }
 }
\ No newline at end of file
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 9893656..6775572 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
@@ -1,10 +1,55 @@
 package com.flightfeather.uav.biz.sourcetrace.model
 
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.biz.dataanalysis.BaseExceptionResult
+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.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.entity.SceneInfo
+import com.flightfeather.uav.domain.repository.SceneInfoRep
+
 /**
  * 姹℃煋绾跨储
- * 鏍规嵁
+ * 閫氳繃姹℃煋鏁版嵁[PollutedData],姹℃煋鍖哄煙[PollutedArea],姹℃煋鏉ユ簮[PollutedSource]锛屽舰鎴愪竴鏉℃薄鏌撴函婧愮嚎绱�
  * @date 2025/5/27
  * @author feiyu02
  */
-class PollutedClue {
+class PollutedClue() : BaseExceptionResult() {
+
+    constructor(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+        eType: ExceptionType,
+        config: RTExcWindLevelConfig,
+        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition
+    ) : this() {
+        this.factor = factor
+        if (exceptionData.isEmpty()) return
+        pollutedData = PollutedData(start, end, factor, exceptionData, eType, windLevelCondition)
+        pollutedArea = PollutedArea(exceptionData, config, windLevelCondition)
+    }
+
+    /**
+     * 6. 灞曠ず鏁版嵁鍙樺寲鎯呭喌锛屼笂鍗囬�熺巼绛夌瓑
+     */
+
+    var pollutedData: PollutedData? = null
+
+    var pollutedArea: PollutedArea? = null
+
+    var pollutedSource: PollutedSource? = null
+
+    private var factor: FactorFilter.SelectedFactor? = null
+
+    /**
+     * 鏌ユ壘绯荤粺鍐呴儴婧簮鑼冨洿鍐呯殑姹℃煋浼佷笟
+     */
+    fun searchScenes(sceneInfoRep: SceneInfoRep) {
+        if (pollutedArea == null || factor == null) return
+        pollutedSource = PollutedSource().also { it.searchScenes(pollutedArea!!, sceneInfoRep, factor!!) }
+    }
 }
\ No newline at end of file
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 8ae7734..aa722b8 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,5 +1,9 @@
 package com.flightfeather.uav.biz.sourcetrace.model
 
+import com.flightfeather.uav.biz.FactorFilter
+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.lightshare.bean.DataVo
 
@@ -8,7 +12,7 @@
  * @date 2025/5/27
  * @author feiyu02
  */
-class PollutedData {
+class PollutedData() {
 
     /**
      *
@@ -20,11 +24,68 @@
      * 3. 8 - 13.8 m/s 浠ヤ笂锛屽墠鍚庡�间笂鍗囧箙搴﹀湪10%浠ヤ笂3娆★紝璁や负鏄繙璺濈鍙戠敓(50绫� - 2鍏噷)
      */
 
+    /**
+     * 9. 鍏宠仈鍥犲瓙
+     * 	a) pm2.5銆乸m10鐗瑰埆楂橈紝涓よ�呭湪鍚勬儏鍐典笅鍚屾灞曠ず锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝姣旈噸瓒婇珮锛岃秺鏈夊彲鑳芥槸椁愰ギ
+     * 	b) pm10鐗瑰埆楂樸�乸m2.5杈冮珮锛屽ぇ棰楃矑鎵皹姹℃煋锛屽彧灞曠ずpm10锛宲m2.5鍗爌m10鐨勬瘮閲嶅彉鍖栵紝宸ュ湴涓轰富
+     * 	c) VOC杈冮珮锛屽悓姣旇绠梡m2.5鐨勯噺绾э紝鍙兘瀛樺湪鍚屾鍋忛珮锛堟苯淇�佸姞娌圭珯锛�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
+     * 	d) VOC杈冮珮锛屽浜庡姞娌圭珯锛堣溅杈嗘嫢鍫垫儏鍐碉級锛孋O涓�鑸緝楂�, 鍚屾璁$畻O3鏄惁鏈夐珮鍊�
+     * 	e) 姘哀鍖栧悎鐗╋紝涓�鑸敱浜庢満鍔ㄨ溅灏炬皵锛屽悓姝ヨ绠桟O
+     */
+
+    constructor(
+        start: BaseRealTimeData,
+        end: BaseRealTimeData?,
+        factor: FactorFilter.SelectedFactor,
+        exceptionData: List<BaseRealTimeData>,
+        eType: ExceptionType,
+        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition,
+    ) : this() {
+        exception = eType.des
+        exceptionType = eType.value
+        factorId = factor.main.value
+        factorName = factor.main.des
+        subFactorId = factor.subs.map { it.value }
+        subFactorName = factor.subs.map { it.des }
+        selectedFactor = factor
+
+        startTime = DateUtil.instance.dateToString(start.dataTime, DateUtil.DateStyle.HH_MM_SS)
+        endTime = DateUtil.instance.dateToString(end?.dataTime, DateUtil.DateStyle.HH_MM_SS) ?: startTime
+        startData = start.getByFactorType(factor.main)
+        endData = end?.getByFactorType(factor.main) ?: startData
+
+        windSpeed = exceptionData.first().windSpeed?.toDouble()
+        percentage = windLevelCondition.mutationRate.first
+        times = windLevelCondition.countLimit
+
+        exceptionData.forEach {
+            dataList.add(it)
+            dataVoList.add(it.toDataVo())
+        }
+    }
+
+    var deviceCode: String? = null
+
+    var exception: String? = null
+    var exceptionType: Int? = null
+
+    var factorId: Int? = null
+    var factorName: String? = null
+    var subFactorId: List<Int>? = null
+    var subFactorName: List<String>? = null
+    var selectedFactor: FactorFilter.SelectedFactor? = null
+
+    var startTime: String? = null
+    var endTime: String? = null
+
+    var startData: Float? = null
+    var endData: Float? = null
+
     // 椋庨��
-    var windSpeed: Float? = null
+    var windSpeed: Double? = null
 
     // 鍥犲瓙閲忕骇鍙樺寲骞呭害
-    var percentage: Float? = null
+    var percentage: Double? = null
 
     // 鍙戠敓娆℃暟
     var times: Int? = null
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 d0e3fd0..de0dc62 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
@@ -1,5 +1,10 @@
 package com.flightfeather.uav.biz.sourcetrace.model
 
+import com.flightfeather.uav.biz.FactorFilter
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.SceneInfo
+import com.flightfeather.uav.domain.repository.SceneInfoRep
+
 /**
  * 姹℃煋鏉ユ簮
  * 绯荤粺鍐呴儴鐨勬薄鏌撳満鏅�佺數瀛愬湴鍥炬悳绱㈠緱鍒扮殑瀹為檯璺璺彛绛夋爣蹇椾俊鎭�
@@ -8,5 +13,38 @@
  */
 class PollutedSource {
 
+    /**
+     * 婧簮娓呭崟鏄剧ず涓庝复杩戠洃娴嬬珯鐐圭殑璺濈锛堝浗鎺с�佸競鎺с�佺綉鏍煎寲鐩戞祴鐐癸級
+     *
+     */
 
+    // 婧簮浼佷笟
+    var sceneList:List<SceneInfo?>? = null
+
+    /**
+     * 鏌ユ壘绯荤粺鍐呴儴婧簮鑼冨洿鍐呯殑姹℃煋浼佷笟
+     */
+    fun searchScenes(pollutedArea: PollutedArea, sceneInfoRep: SceneInfoRep, factor: FactorFilter.SelectedFactor) {
+        // Fixme 2025.5.14: 姹℃煋婧愮殑鍧愭爣鏄珮寰峰湴鍥惧潗鏍囩郴锛堢伀鏄熷潗鏍囩郴锛夛紝鑰岃蛋鑸暟鎹槸WGS84鍧愭爣绯�
+        // 鎸夌収鍖哄煙妫�绱㈠唴閮ㄦ薄鏌撴簮淇℃伅
+        // 1. 棣栧厛鎸夌収鍥涜嚦鑼冨洿浠庢暟鎹簱鍒濇绛涢�夋薄鏌撴簮锛岄渶瑕佸厛灏嗗潗鏍囪浆鎹负gcj02锛堢伀鏄熷潗鏍囩郴锛夛紝鍥犱负姹℃煋婧愬満鏅俊鎭兘涓烘鍧愭爣绯�
+        val polygonTmp = pollutedArea.polygon!!.map {
+            MapUtil.gcj02ToWgs84(it)
+        }
+        val fb = MapUtil.calFourBoundaries(polygonTmp)
+        val sceneList = sceneInfoRep.findByCoordinateRange(fb)
+        // 2. 鍐嶇簿纭垽鏂槸鍚﹀湪鍙嶅悜婧簮鍖哄煙澶氳竟褰㈠唴閮�
+        val result = mutableListOf<SceneInfo>()
+        sceneList.forEach {
+            val point = it!!.longitude.toDouble() to it.latitude.toDouble()
+            if (MapUtil.isPointInPolygon(point, polygonTmp)) {
+                result.add(it)
+            }
+        }
+
+        this.sceneList = result
+
+        TODO("鎸夌収鎵�閫夌洃娴嬪洜瀛愮被鍨嬶紝鍖哄垎姹℃煋婧愮被鍨�")
+
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt
index e4eed40..91983a8 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSummary.kt
@@ -2,9 +2,18 @@
 
 /**
  * 姹℃煋鎯呭喌姹囨��
- * 閽堝鍗曟璧拌埅锛屽畾鏃剁粺璁″凡鏈夋薄鏌撶嚎绱紝鎸夌収绛栫暐缁欏嚭璧拌埅寤鸿
+ * 閽堝鍗曟璧拌埅锛屽畾鏃剁粺璁″凡鏈夋薄鏌撶嚎绱PollutedClue]锛屾寜鐓х瓥鐣ョ粰鍑鸿蛋鑸缓璁�
  * @date 2025/5/27
  * @author feiyu02
  */
 class PollutedSummary {
+
+
+    /**
+     * 5. 姹℃煋婧愮殑琚壂鎻忔鏁�
+     * 姣忎竴鍒婚挓瀵瑰巻鍙茬嚎绱㈣繘琛岀粺璁★紝鎻愬嚭浼氬晢寤鸿锛堢姹℃煋婧愯緝杩溿�佹薄鏌撴簮鏁伴噺銆佸嚭鐜版鏁帮級銆佽蛋鑸矾绾胯皟鏁村缓璁紙绂绘薄鏌撴簮杈冭繎銆佽蛋鑸建杩规湭鎺ヨ繎婧簮鍦烘櫙锛�
+     */
+
+    // 姹℃煋绾跨储
+    var clueList = mutableListOf<PollutedClue>()
 }
\ No newline at end of file

--
Gitblit v1.9.3