From e58a05b78d09bcd4c1a12e8610c5adfc316494e8 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期四, 18 十二月 2025 10:04:42 +0800
Subject: [PATCH] 2025.12.18

---
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedData.kt             |   19 --
 src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt      |   14 +
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt           |   15 -
 src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt       |   14 +
 src/main/resources/application-test.yml                                                 |   12 
 src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt |    2 
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt    |   88 +++++++++++
 src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedClue.kt             |   17 --
 src/main/kotlin/com/flightfeather/uav/model/epw/MutationDataPreprocess.kt               |  143 ++++++++++++++++++++
 src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt                 |    3 
 src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImpl.kt     |    2 
 src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt                   |   40 +++++
 src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt                         |    2 
 src/test/kotlin/com/flightfeather/uav/biz/mission/MissionUtilTest.kt                    |    9 
 14 files changed, 311 insertions(+), 69 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 0388a35..3d10232 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuous.kt
@@ -75,7 +75,7 @@
     }
 
     /**
-     * 鍒ゆ柇鏁版嵁閲忕骇鍦ㄥ紓甯稿垽鏂殑鑼冨洿鍐�
+     * 鍒ゆ柇鏁版嵁閲忕骇鏄惁鍦ㄥ紓甯稿垽鏂殑鑼冨洿鍐�
      * 榛樿鎵�鏈夐噺绾ч兘鍦ㄥ紓甯稿垽鏂殑鑼冨洿鍐�
      */
     open fun judgeDataScale(p: BaseRealTimeData?, n: BaseRealTimeData): MutableMap<FactorType, Boolean> {
@@ -126,9 +126,15 @@
         }
     }
 
+    /**
+     * 寮傚父鏁版嵁澶勭悊鏍稿績鍒ゆ柇閫昏緫
+     */
     override fun onNextData(data: BaseRealTimeData) {
+        // 1. 鍒ゆ柇鏂版暟鎹殑鏃堕棿杩炵画鎬�
         val isContinue = isContinuous(lastData, data)
+        // 2. 璁$畻鍚勪釜鐩戞祴鍥犲瓙鏄惁鍙戠敓寮傚父
         val hasException = judge(lastData, data)
+        // 3. 閬嶅巻閰嶇疆涓�変腑鐨勭洃娴嬪洜瀛愶紝鍒ゆ柇鏄惁鍙戠敓寮傚父
         config.factorFilter.selectedList.forEach { s ->
             val f = s.main
             // 鎺掗櫎姝ゅ紓甯哥被鍨嬩笉閫傜敤鐨勭洃娴嬪洜瀛�
@@ -151,12 +157,18 @@
                 it.addHistoryData(data)
             }
         }
+        // 4. 鏇存柊鏈�鏂版暟鎹�
         lastData = data
 
+        // 5. 淇濆瓨骞剁Щ闄ゅ崟鍥犲瓙寮傚父
         removeSingleFactor(data)
+        // 6. 妫�鏌ュ欢杩熷紓甯�
         val fittedComb = checkDelayedExceptions(data)
+        // 7. 鍚堝苟寮傚父缁撴灉
         mergeExceptionResult(data, fittedComb)
+        // 8. 瑙﹀彂鏂板紓甯哥粨鏋滀簨浠�
         onNewResult(result)
+        // 9. 娓呴櫎寮傚父璁板綍
         clearExceptions(data)
     }
 
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt
index 563c22b..4b334b8 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt
@@ -149,16 +149,32 @@
                                     town = if (address.address.contains(address.streetNumber)) {
                                         address.address
                                     } else {
-                                        address.address + "锛�" + address.street + address.streetNumber + "锛�"
+//                                        address.address + "锛�" + address.street + address.streetNumber + "锛�"
+                                        address.address
                                     }
                                 }
-                                val polygon = listOf(
+                                var polygon = listOf(
                                     gf.cell.point1Lon.toDouble() to gf.cell.point1Lat.toDouble(),
                                     gf.cell.point2Lon.toDouble() to gf.cell.point2Lat.toDouble(),
                                     gf.cell.point3Lon.toDouble() to gf.cell.point3Lat.toDouble(),
                                     gf.cell.point4Lon.toDouble() to gf.cell.point4Lat.toDouble(),
                                 )
                                 bounds = MapUtil.calFourBoundaries(polygon)
+                                // 灏嗙綉鏍兼悳绱㈣寖鍥存墿澶т竴鍦堢綉鏍�(閽堝鍖楃含涓滅粡鐨勬儏鍐典笅)
+                                bounds?.let { bs->
+                                    val offsetLon = bs[1] - bs[0]
+                                    val offsetLat = bs[3] - bs[2]
+                                    polygon = listOf(
+                                        // 缃戞牸瑗垮寳瑙�
+                                        bs[0] - offsetLon to bs[3] + offsetLat,
+                                        // 缃戞牸涓滃寳瑙�
+                                        bs[1] + offsetLon to bs[3] + offsetLat,
+                                        // 缃戞牸涓滃崡瑙�
+                                        bs[1] + offsetLon to bs[2] - offsetLat,
+                                        // 缃戞牸瑗垮崡瑙�
+                                        bs[0] - offsetLon to bs[2] - offsetLat,
+                                    )
+                                }
                                 highRiskScenes =
                                     sceneInfoRep.findByPolygon(polygon, listOf(SceneType.TYPE19, SceneType.TYPE20, SceneType.TYPE21))
                             }
@@ -189,16 +205,32 @@
                                     town = if (address.address.contains(address.streetNumber)) {
                                         address.address
                                     } else {
-                                        address.address + address.street + address.streetNumber
+//                                        address.address + "锛�" + address.street + address.streetNumber + "锛�"
+                                        address.address
                                     }
                                 }
-                                val polygon = listOf(
+                                var polygon = listOf(
                                     gf.cell.point1Lon.toDouble() to gf.cell.point1Lat.toDouble(),
                                     gf.cell.point2Lon.toDouble() to gf.cell.point2Lat.toDouble(),
                                     gf.cell.point3Lon.toDouble() to gf.cell.point3Lat.toDouble(),
                                     gf.cell.point4Lon.toDouble() to gf.cell.point4Lat.toDouble(),
                                 )
                                 bounds = MapUtil.calFourBoundaries(polygon)
+                                // 灏嗙綉鏍兼悳绱㈣寖鍥存墿澶т竴鍦堢綉鏍�(閽堝鍖楃含涓滅粡鐨勬儏鍐典笅)
+                                bounds?.let { bs->
+                                    val offsetLon = bs[1] - bs[0]
+                                    val offsetLat = bs[3] - bs[2]
+                                    polygon = listOf(
+                                        // 缃戞牸瑗垮寳瑙�
+                                        bs[0] - offsetLon to bs[3] + offsetLat,
+                                        // 缃戞牸涓滃寳瑙�
+                                        bs[1] + offsetLon to bs[3] + offsetLat,
+                                        // 缃戞牸涓滃崡瑙�
+                                        bs[1] + offsetLon to bs[2] - offsetLat,
+                                        // 缃戞牸瑗垮崡瑙�
+                                        bs[0] - offsetLon to bs[2] - offsetLat,
+                                    )
+                                }
                                 highRiskScenes =
                                     sceneInfoRep.findByPolygon(polygon, listOf(SceneType.TYPE19, SceneType.TYPE20, SceneType.TYPE21))
                             })
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
index 9d69ca6..b51ab61 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/config/RTExcWindLevelConfig.kt
@@ -49,7 +49,7 @@
         .0 to 1.5,
         0.2 to 0.5,
         DistanceType.TYPE2,
-        1
+        2
     )
 
     // 2 - 4绾ч
@@ -95,6 +95,24 @@
             DistanceType.TYPE1,
             1
         ),
+        FactorType.NO to WindLevelCondition(
+            .0 to 1.5,
+            8.0 to Double.MAX_VALUE,
+            DistanceType.TYPE1,
+            4
+        ),
+        FactorType.NO2 to WindLevelCondition(
+            .0 to 1.5,
+            4.0 to Double.MAX_VALUE,
+            DistanceType.TYPE1,
+            4
+        ),
+        FactorType.CO to WindLevelCondition(
+            .0 to 1.5,
+            10.0 to Double.MAX_VALUE,
+            DistanceType.TYPE1,
+            5
+        ),
     )
     // 鍦ㄩ閫熷浜�1.6 - 7.9 m/s 涔嬮棿鏃�
     var changeRateUp2 = mutableMapOf(
@@ -119,6 +137,24 @@
             DistanceType.TYPE3,
             1
         ),
+        FactorType.NO to WindLevelCondition(
+            1.6 to 7.9,
+            8.0 to Double.MAX_VALUE,
+            DistanceType.TYPE3,
+            4
+        ),
+        FactorType.NO2 to WindLevelCondition(
+            1.6 to 7.9,
+            4.0 to Double.MAX_VALUE,
+            DistanceType.TYPE3,
+            4
+        ),
+        FactorType.CO to WindLevelCondition(
+            1.6 to 7.9,
+            10.0 to Double.MAX_VALUE,
+            DistanceType.TYPE3,
+            5
+        ),
     )
     // 鍦ㄩ閫熷浜�8.0 - 13.8 m/s 涔嬮棿鏃�
     var changeRateUp3 = mutableMapOf(
@@ -142,6 +178,24 @@
             6.0 to Double.MAX_VALUE,
             DistanceType.TYPE4,
             1
+        ),
+        FactorType.NO to WindLevelCondition(
+            8.0 to 13.8,
+            8.0 to Double.MAX_VALUE,
+            DistanceType.TYPE4,
+            4
+        ),
+        FactorType.NO2 to WindLevelCondition(
+            8.0 to 13.8,
+            4.0 to Double.MAX_VALUE,
+            DistanceType.TYPE4,
+            4
+        ),
+        FactorType.CO to WindLevelCondition(
+            8.0 to 13.8,
+            10.0 to Double.MAX_VALUE,
+            DistanceType.TYPE4,
+            5
         ),
     )
 
@@ -168,6 +222,24 @@
             DistanceType.TYPE1,
             3
         ),
+        FactorType.NO to WindLevelCondition(
+            .0 to Double.MAX_VALUE,
+            -Double.MAX_VALUE to -6.0,
+            DistanceType.TYPE1,
+            4
+        ),
+        FactorType.NO2 to WindLevelCondition(
+            .0 to Double.MAX_VALUE,
+            -Double.MAX_VALUE to -2.0,
+            DistanceType.TYPE1,
+            4
+        ),
+        FactorType.CO to WindLevelCondition(
+            .0 to Double.MAX_VALUE,
+            -Double.MAX_VALUE to -10.0,
+            DistanceType.TYPE1,
+            5
+        ),
     )
 
     /****鏁版嵁鏈変笂鍗囪秼鍔挎彁閱�*****************************************************************************/
@@ -182,16 +254,22 @@
         // PM10鍦ㄤ竴涓洃娴嬪懆鏈燂紙4绉掞級涓婂崌閲忕骇鍦�2 - 4渭g/m鲁涔嬮棿锛岃繛缁彂鐢�3娆�
         FactorType.PM10 to WindLevelCondition(
             .0 to Double.MAX_VALUE,
-            2.0 to 4.0,
+            4.0 to 8.0,
             DistanceType.TYPE1,
-            2
+            4
         ),
         // VOC鍦ㄤ竴涓洃娴嬪懆鏈燂紙4绉掞級涓婂崌閲忕骇鍦�3 - 6渭g/m鲁涔嬮棿锛岃繛缁彂鐢�2娆�
         FactorType.VOC to WindLevelCondition(
             .0 to Double.MAX_VALUE,
-            3.0 to 6.0,
+            2.0 to 4.0,
             DistanceType.TYPE1,
-            2
+            4
+        ),
+        FactorType.CO to WindLevelCondition(
+            .0 to Double.MAX_VALUE,
+            5.0 to 10.0,
+            DistanceType.TYPE1,
+            5
         ),
     )
 
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 6035ec5..306d4a7 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
@@ -20,18 +20,6 @@
  */
 class PollutedClue() : BaseExceptionResult() {
 
-//    constructor(
-//        tag: ExceptionTag, factor: FactorFilter.SelectedFactor, eType: ExceptionType, config: RTExcWindLevelConfig,
-//        windLevelCondition: RTExcWindLevelConfig.WindLevelCondition?,
-//    ) : this() {
-//        if (tag.exceptionData.isEmpty()) return
-//        deviceCode = tag.startData?.deviceCode
-//        pollutedData = PollutedData(
-//            tag.startData!!, tag.endData, factor, tag.exceptionData, tag.historyData, eType, windLevelCondition
-//        )
-//        pollutedArea = PollutedArea(tag.historyData, tag.exceptionData, config, windLevelCondition)
-//    }
-
     constructor(
         exceptions: List<Pair<FactorFilter.SelectedFactor, ExceptionTag>>,
         eType: ExceptionType,
@@ -97,11 +85,6 @@
         pollutedArea = PollutedArea(historyData, exceptionData, config, windLevelCondition)
 
     }
-
-
-    /**
-     * 6. 灞曠ず鏁版嵁鍙樺寲鎯呭喌锛屼笂鍗囬�熺巼绛夌瓑
-     */
 
     /**
      * @see [MsgType]
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 5dd3328..577b38b 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
@@ -34,15 +34,6 @@
         var max: Double? = null
     }
 
-    /**
-     * 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?,
@@ -59,8 +50,8 @@
         endTime = end?.dataTime
 //        startData = start.getByFactorType(factor.main)
 //        endData = end?.getByFactorType(factor.main) ?: startData
-        startData = start
-        endData = end
+//        startData = start
+//        endData = end
 
         windSpeed = exceptionData.first().windSpeed?.toDouble()
         times = windLevelCondition?.countLimit
@@ -100,8 +91,8 @@
     var startTime: Date? = null
     var endTime: Date? = null
 
-    var startData: BaseRealTimeData? = null
-    var endData: BaseRealTimeData? = null
+//    var startData: BaseRealTimeData? = null
+//    var endData: BaseRealTimeData? = null
 
     // 椋庨��
     var windSpeed: Double? = null
@@ -110,7 +101,7 @@
     var times: Int? = null
 
     var historyDataList = mutableListOf<DataVo>()
-    // 寮傚父鐩戞祴鏁版嵁锛屽寘鍚崟姝ゅ紓甯镐腑鎵�鏈夊彂鐢熶簡寮傚父鐨勬暟鎹�硷紙鍙兘涓嶆槸鏃堕棿杩炵画鐨勬暟鎹級
+    // 寮傚父鐩戞祴鏁版嵁锛屽寘鍚崟娆″紓甯镐腑鎵�鏈夊彂鐢熶簡寮傚父鐨勬暟鎹�硷紙鍙兘涓嶆槸鏃堕棿杩炵画鐨勬暟鎹級
     var dataList: MutableList<BaseRealTimeData> = mutableListOf()
     var dataVoList: MutableList<DataVo> = mutableListOf()
 
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 706622c..ab85953 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
@@ -46,12 +46,12 @@
         // Fixme 2025.5.14: 姹℃煋婧愮殑鍧愭爣鏄珮寰峰湴鍥惧潗鏍囩郴锛堢伀鏄熷潗鏍囩郴锛夛紝鑰岃蛋鑸暟鎹槸WGS84鍧愭爣绯�
         // 鎸夌収鍖哄煙妫�绱㈠唴閮ㄦ薄鏌撴簮淇℃伅
         var result = mutableListOf<SceneInfo>()
-        // 1. 棣栧厛鎸夌収鍥涜嚦鑼冨洿浠庢暟鎹簱鍒濇绛涢�夋薄鏌撴簮锛屾澶勭殑鍖哄煙鍧愭爣宸茶浆鎹负鐏槦鍧愭爣绯�
         val polygonTmp = pollutedArea.polygon
         this.sceneList = emptyList()
 
         if (polygonTmp != null) {
             val fb = MapUtil.calFourBoundaries(polygonTmp)
+            // 1. 棣栧厛鎸夌収鍥涜嚦鑼冨洿浠庢暟鎹簱鍒濇绛涢�夋薄鏌撴簮锛屾澶勭殑鍖哄煙鍧愭爣宸茶浆鎹负鐏槦鍧愭爣绯�
             val sceneList = sceneInfoRep.findByCoordinateRange(fb)
             // 2. 鍐嶇簿纭垽鏂槸鍚﹀湪鍙嶅悜婧簮鍖哄煙澶氳竟褰㈠唴閮�
             sceneList.forEach {
@@ -61,6 +61,7 @@
                 }
             }
 
+            // 3. 鍐嶇粺涓�妫�绱㈣繎璺濈姹℃煋鍦嗗舰鍖哄煙鍐呴儴鐨勬薄鏌撴簮
             val closePolygonTmp = pollutedArea.closePolygon!!
             val closeFb = MapUtil.calFourBoundaries(closePolygonTmp)
             val closeSceneList = sceneInfoRep.findByCoordinateRange(closeFb)
@@ -70,13 +71,12 @@
                     result.add(it)
                 }
             }
-            // 鍘婚噸
+            // 4. 鍘婚噸
             result = result.distinctBy { it.guid }.toMutableList()
 
-            // 鏍规嵁姹℃煋鍥犲瓙鐨勯噺绾э紝璁$畻涓昏鐨勬薄鏌撳満鏅被鍨嬶紝绛涢�夌粨鏋�
+            // 5. 鏍规嵁姹℃煋鍥犲瓙鐨勯噺绾э紝璁$畻涓昏鐨勬薄鏌撳満鏅被鍨嬶紝绛涢�夌粨鏋�
             val mainSceneType = calSceneType(pollutedData)
             if (mainSceneType != null) {
-//            this.conclusion = mainSceneType.first
                 result = result.filter {
                     val r = mainSceneType.second.find { s ->
                         s.value == it.typeId.toInt()
@@ -107,7 +107,7 @@
 //                    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.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
+                            listOf(SceneType.TYPE1, SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
                 }
 
                 FactorType.CO -> "" to listOf(SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
@@ -229,9 +229,6 @@
      * @return 婧簮鎻忚堪
      */
     private fun summaryTxt(pollutedData: PollutedData, sceneList: List<SceneInfoVo>): String {
-//        pollutedData.exception
-//        pollutedData.selectedFactor?.main
-
         val st = DateUtil.instance.getTime(pollutedData.startTime)
         val et = DateUtil.instance.getTime(pollutedData.endTime)
 
@@ -277,7 +274,7 @@
                         val curValue = pollutedData.dataList.last().getByFactorType(s.key)
                         if (preValue == null || curValue == null) return@forEach
                         val r = round((curValue - preValue) / preValue * 100)
-                        txt += "锛屼粠${preValue}渭g/m鲁蹇�熶笂鍗囪嚦${curValue}渭g/m鲁锛屽彉鍖栫巼涓�${r}%"
+                        txt += "锛�${s.key.getTxt()}浠�${preValue}渭g/m鲁蹇�熶笂鍗囪嚦${curValue}渭g/m鲁锛屽彉鍖栫巼涓�${r}%"
                     }
                 }
             }
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt b/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
index 1066c78..a525f6e 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
@@ -122,7 +122,7 @@
     }
 
     fun getByFactorType(type: FactorType?): Float? {
-        return when (type) {
+        val res =  when (type) {
             FactorType.NO2 -> no2
             FactorType.CO -> co
             FactorType.H2S -> h2s
@@ -144,6 +144,7 @@
             FactorType.NO -> no
             else -> null
         }
+        return if (res != null) round(res * 100) / 100 else null
     }
 
 }
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImpl.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImpl.kt
index c8ace2f..54484a9 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImpl.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImpl.kt
@@ -94,7 +94,7 @@
         val data = realTimeDataRep.fetchData(mission)
         mission.kilometres = MissionUtil.calKilometres(data).toFloat()
         // todo: 璁$畻璧拌埅浠诲姟鎵�鍦ㄤ腑蹇冨尯鍩�
-        mission.region = MissionUtil.calRegion(data)
+//        mission.region = MissionUtil.calRegion(data)
         return updateMission(mission)
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/model/epw/MutationDataPreprocess.kt b/src/main/kotlin/com/flightfeather/uav/model/epw/MutationDataPreprocess.kt
new file mode 100644
index 0000000..cb35e11
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/model/epw/MutationDataPreprocess.kt
@@ -0,0 +1,143 @@
+package com.flightfeather.uav.model.epw
+
+import com.flightfeather.uav.common.utils.DateUtil
+import com.flightfeather.uav.lightshare.bean.DataVo
+import com.flightfeather.uav.socket.bean.AirData
+import com.flightfeather.uav.socket.eunm.FactorType
+
+/**
+ * 绐佸彉鏁版嵁棰勫鐞�
+ * 1. 閽堝鍗曚釜鏁版嵁绐侀珮鎴栫獊浣庯紝鍓嶅悗涓や釜鏁版嵁鐨勯噺绾ф帴杩戠殑鎯呭喌锛岃繘琛屽鐞�
+ * 2. 閽堝鏈夊叧鑱斿叧绯荤殑鐩戞祴鍥犲瓙锛屾暟鎹噺绾у嚭鐜版槑鏄鹃敊璇殑鎯呭喌锛岃繘琛屽鐞�
+ * @date 2025/11/20
+ * @author feiyu02
+ */
+class MutationDataPreprocess {
+
+    // 淇濆瓨鏁版嵁鏈�澶ц褰曟暟
+    private val MAX_COUNT = 15
+
+    private val lastData = mutableListOf<DataVo>()
+
+    // 閲忕骇鍙樺寲闃堝�煎�嶆暟锛堢敤浜庡垽鏂槸鍚︿负绐佸彉锛�
+    private val MUTATION_THRESHOLD = 5.0
+    // 鍓嶅悗鏁版嵁鎺ヨ繎闃堝�煎�嶆暟锛堢敤浜庡垽鏂墠鍚庢暟鎹槸鍚﹂噺绾ф帴杩戯級
+    private val SIMILARITY_THRESHOLD = 1.5
+
+    /**
+     * 鏁版嵁骞虫粦
+     * 瑙e喅鍗曚釜鏁版嵁鐩告瘮鍓嶅悗涓や釜鏁版嵁鐨勯噺绾ц繃澶ф垨杩囧皬鐨勯棶棰�
+     * @param data 鍘熷鏁版嵁
+     * @return 棰勫鐞嗗悗鐨勬暟鎹�
+     */
+    fun preprocess(data: List<DataVo>): List<DataVo> {
+        // 褰撴柊鏁版嵁涓庢棫鏁版嵁閲囨牱鏃堕棿宸秴杩�1鍒嗛挓鏃讹紝璁や负涓ょ粍鏁版嵁宸叉棤鍏宠仈鎬э紝娓呯┖鏃ф暟鎹�
+        if (lastData.isNotEmpty() && data.isNotEmpty()) {
+            val lastTime = DateUtil.instance.StringToDate(lastData.last().time)
+            val thisTime = DateUtil.instance.StringToDate(data.first().time)
+            if ((thisTime?.time?.minus(lastTime?.time ?: 0) ?: 0) >= (60 * 1000)) {
+                lastData.clear()
+            }
+        }
+        lastData.addAll(data)
+        detectAndReplaceMutation(lastData)
+        saveHistory(lastData)
+        return lastData
+    }
+
+    /**
+     * 妫�娴嬪苟鏇挎崲寮傚父绐佸彉鏁版嵁
+     * @param data 鍘熷鏁版嵁鍒楄〃
+     * @return 澶勭悊鍚庣殑鏁版嵁鍒楄〃
+     */
+    fun detectAndReplaceMutation(data: MutableList<DataVo>){
+        if (data.size < 3) return // 鏁版嵁閲忎笉瓒筹紝鏃犳硶杩涜鍓嶅悗瀵规瘮
+
+        // 閬嶅巻鏁版嵁锛屼粠绗簩涓紑濮嬪埌鍊掓暟绗簩涓粨鏉�
+        for (i in 1 until data.size - 1) {
+            val currentData = data[i]
+            val prevData = data[i - 1]
+            val nextData = data[i + 1]
+
+            // 妫�鏌ユ瘡涓洃娴嬪洜瀛�
+            if (currentData.values != null && prevData.values != null && nextData.values != null) {
+                processEachFactor(currentData, prevData, nextData)
+            }
+        }
+
+    }
+
+    /**
+     * 澶勭悊姣忎釜鐩戞祴鍥犲瓙鐨勬暟鎹獊鍙�
+     */
+    private fun processEachFactor(currentData: DataVo, prevData: DataVo, nextData: DataVo) {
+        val currentFactors = mutableMapOf<String, AirData>()
+        val prevFactors = mutableMapOf<String, AirData>()
+        val nextFactors = mutableMapOf<String, AirData>()
+
+        // 鏋勫缓鍥犲瓙鍚嶇О鍒癆irData鐨勬槧灏�
+        currentData.values?.forEach { currentFactors[it.factorName ?: ""] = it }
+        prevData.values?.forEach { prevFactors[it.factorName ?: ""] = it }
+        nextData.values?.forEach { nextFactors[it.factorName ?: ""] = it }
+
+        // 閬嶅巻褰撳墠鏁版嵁涓殑鎵�鏈夊洜瀛�
+        currentFactors.forEach { (factorName, currentFactor) ->
+            val prevFactor = prevFactors[factorName]
+            val nextFactor = nextFactors[factorName]
+
+            // 纭繚涓変釜鏁版嵁鐐归兘鏈夎鍥犲瓙鐨勬暟鎹�
+            if (prevFactor != null && nextFactor != null &&
+                currentFactor.factorData != null && prevFactor.factorData != null && nextFactor.factorData != null) {
+
+                val currentValue = currentFactor.factorData!!
+                val prevValue = prevFactor.factorData!!
+                val nextValue = nextFactor.factorData!!
+
+                // 璺宠繃0鍊兼垨璐熷�硷紝閬垮厤闄ら浂閿欒
+                if (prevValue <= 0 || nextValue <= 0) return@forEach
+
+                // 妫�鏌ユ槸鍚︿负寮傚父绐佸彉鏁版嵁
+                if (isMutationData(currentValue, prevValue, nextValue)) {
+                    // 鏇挎崲涓哄墠涓�涓暟鎹殑鍊�
+                    currentFactor.factorData = prevValue
+                }
+            }
+        }
+    }
+
+    /**
+     * 鍒ゆ柇褰撳墠鏁版嵁鏄惁涓虹獊鍙樻暟鎹�
+     * 鏉′欢锛�1. 褰撳墠鏁版嵁涓庡墠涓�涓暟鎹殑閲忕骇鍙樺寲瓒呰繃闃堝��
+     *       2. 鍓嶄竴涓暟鎹笌鍚庝竴涓暟鎹殑閲忕骇鎺ヨ繎
+     */
+    private fun isMutationData(currentValue: Double, prevValue: Double, nextValue: Double): Boolean {
+        // 璁$畻鍙樺寲鐜�
+        val currentToPrevRatio = Math.max(currentValue, prevValue) / Math.min(currentValue, prevValue)
+        val prevToNextRatio = Math.max(prevValue, nextValue) / Math.min(prevValue, nextValue)
+
+        // 鍒ゆ柇鏄惁婊¤冻绐佸彉鏉′欢
+        return currentToPrevRatio > MUTATION_THRESHOLD && prevToNextRatio <= SIMILARITY_THRESHOLD
+    }
+
+    fun saveHistory(data: MutableList<DataVo>) {
+//        // 灏嗘柊鏁版嵁鐨勮嚦澶氭渶鍚�15涓繚瀛樹笅鏉ワ紙宸茬粡杩囬澶勭悊锛夛紝鐢ㄤ簬涓嬩竴娆$殑鍒ゆ柇
+//        val newList = mutableListOf<DataVo>()
+//        val s = if ((data.lastIndex - MAX_COUNT + 1) < 0) 0 else data.lastIndex - MAX_COUNT + 1
+//        data.subList(s, data.lastIndex + 1).forEach {
+//            newList.add(it.copy())
+//        }
+//        // 褰撴柊鏁版嵁涓庢棫鏁版嵁閲囨牱鏃堕棿宸秴杩�1鍒嗛挓鏃讹紝璁や负涓ょ粍鏁版嵁宸叉棤鍏宠仈鎬э紝娓呯┖鏃ф暟鎹�
+//        if (lastData.isNotEmpty() && newList.isNotEmpty()) {
+//            val lastTime = DateUtil.instance.StringToDate(lastData.last().time)
+//            val thisTime = DateUtil.instance.StringToDate(newList.first().time)
+//            if ((thisTime?.time?.minus(lastTime?.time ?: 0) ?: 0) >= (60 * 1000)) {
+//                lastData.clear()
+//            }
+//        }
+//        lastData.addAll(newList)
+        // 纭繚淇濆瓨鐨勬暟鎹渶澶氬彧鏈夋渶鏂扮殑15涓�
+        while (data.size > MAX_COUNT) {
+            data.removeAt(0)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt b/src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt
index e99a0b7..62af905 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt
@@ -157,7 +157,7 @@
         }
 
         /**
-         * 涓嶅鐞嗕綆浜庢鍊肩殑鍊�
+         * 涓嶅鐞嗕綆浜庢鍊肩殑鏁版嵁
          */
         fun getVMin(type: FactorType): Double = when (type) {
             NO -> 1.0
diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml
index 75ca738..02d8369 100644
--- a/src/main/resources/application-test.yml
+++ b/src/main/resources/application-test.yml
@@ -7,13 +7,13 @@
     #    password: cn.FLIGHTFEATHER
 
     #   杩滅▼鏈嶅姟鍣�
-#    url: jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
-#    username: remoteU1
-#    password: eSoF8DnzfGTlhAjE
-
-    url: jdbc:mysql://114.215.109.124:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
+    url: jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
     username: remoteU1
-    password: feiyu2024
+    password: eSoF8DnzfGTlhAjE
+
+#    url: jdbc:mysql://114.215.109.124:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
+#    username: remoteU1
+#    password: feiyu2024
 
 springfox:
   documentation:
diff --git a/src/test/kotlin/com/flightfeather/uav/biz/mission/MissionUtilTest.kt b/src/test/kotlin/com/flightfeather/uav/biz/mission/MissionUtilTest.kt
index ce27a1a..7550519 100644
--- a/src/test/kotlin/com/flightfeather/uav/biz/mission/MissionUtilTest.kt
+++ b/src/test/kotlin/com/flightfeather/uav/biz/mission/MissionUtilTest.kt
@@ -22,9 +22,12 @@
 
     @Test
     fun calKilometres() {
-        val m = missionRep.findOne("20250819") ?: return
-        val data = realTimeDataRep.fetchData(m)
-        MissionUtil.calKilometres(data)
+        val mcodeList = listOf("20250427", "20250526", "20250530")
+        mcodeList.forEach { mcode ->
+            val m = missionRep.findOne(mcode) ?: return@forEach
+            val data = realTimeDataRep.fetchData(m)
+            MissionUtil.calKilometres(data)
+        }
 //        val d = MapUtil.getDistance(121.425187, 31.225907, 121.425196, 31.225892)
 //        println(d)
 //        val d1 = MapUtil.getDistance(121.425196, 31.225892, 121.425187, 31.225907)
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 05d4e09..651ed15 100644
--- a/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt
+++ b/src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt
@@ -45,10 +45,10 @@
 //            "SH-CN-20240723(02)",
 ////            "SH-CN-20250723(01)"
 //        )
-//        val startTime = LocalDateTime.of(2024, 12, 31, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
-        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 endTime = LocalDateTime.of(2025, 7, 31, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
+//        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 missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
             createCriteria().andBetween("startTime", startTime, endTime)
         })
@@ -116,8 +116,10 @@
 //            "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, 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 missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
             createCriteria().andBetween("startTime", startTime, endTime)
         })
diff --git a/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt b/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt
index d66b52b..f5f17d3 100644
--- a/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt
+++ b/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt
@@ -34,7 +34,7 @@
     @Test
     fun calMissionInfo() {
         missionMapper.selectByExample(Example(Mission::class.java).apply {
-            createCriteria().andGreaterThanOrEqualTo("startTime", "2025-07-08 08:30:00")
+            createCriteria().andBetween("startTime", "2025-12-05 00:00:00", "2025-12-31 23:59:59")
         }).forEach {mission ->
             mission?.let { missionService.calMissionInfo(it.missionCode) }
             Thread.sleep(1000)

--
Gitblit v1.9.3