From 61871594dfa0a5ac2c4d895d9ec4034feba57094 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期五, 12 九月 2025 17:20:53 +0800
Subject: [PATCH] 2025.9.5 1. 新增走航任务统计功能

---
 src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt          |    7 +
 src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt |   36 ++++++-
 src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt                    |    6 
 src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt                     |  161 +++++++++++++++++++++++++------
 src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt                       |   53 +++++++++-
 5 files changed, 213 insertions(+), 50 deletions(-)

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 4bebc3f..a274fae 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionGridFusion.kt
@@ -23,7 +23,7 @@
     class GridFusionByAQI {
         var pollutionDegree: String? = null
         var gridLen:Int? = null
-        var missionList: MutableList<MissionInventory.MissionDetail> = mutableListOf()
+        var missionList: MutableList<MissionInventory.MissionInfo> = mutableListOf()
         var gridFusionList: MutableList<FusionGrid> = mutableListOf()
         var highRiskGridList: MutableList<HighRiskGridByFactor> = mutableListOf()
     }
@@ -70,7 +70,7 @@
         factorTypes: List<FactorType>,
         gridLen: Int,
         gridCells: List<GridCell>,
-        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>,
+        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionInfo>, List<GridDataDetailMixVo>>>,
     ): List<GridFusionByAQI> {
         return dataList.map {
             GridFusionByAQI().apply {
@@ -85,7 +85,7 @@
                 highRiskGridList.addAll(factorTypes.map { f->
                     HighRiskGridByFactor().apply {
                         factorType = f
-                        highRiskGrid = gridFusionList.sortedBy { gf->gf.data.rank }.firstOrNull()
+                        highRiskGrid = gridFusionList.maxByOrNull { gf->gf.data.getByFactorType(f) ?: 0f }
                         if (highRiskGrid != null) {
                             factorValue = highRiskGrid!!.data.getByFactorType(f)
                             if (highRiskGrid!!.cell.longitude != null && highRiskGrid!!.cell.latitude != null) {
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt
index cd8f696..f6700d7 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt
@@ -26,13 +26,21 @@
 
         // 婧簮闂鍦烘櫙鏁�
         var sceneCount: Int = 0
+
+        // 婧簮闂鍦烘櫙
+        var scenes: List<SceneInfo>? = null
+
+        // 璧拌埅娑夊強鍖哄煙
+        var keyScene: List<SceneInfo>? = null
+
+        var exceptionCount: Int = 0
     }
 
     // 璧拌埅璇︽儏淇℃伅
     class MissionDetail : Mission() {
-        var keyScene: List<SceneInfo>? = null
+//        var keyScene: List<SceneInfo>? = null
         var dataStatistics: List<FactorStatistics>? = null
-        var exceptionCount: Int = 0
+//        var exceptionCount: Int = 0
     }
 
     /**
@@ -48,7 +56,7 @@
             var sceneCount = 0
             clue.forEach {
                 if (it?.msgType == MsgType.PolClue.value) {
-                    it.pollutedData?.statisticMap?.keys?.forEach { k->
+                    it.pollutedData?.statisticMap?.keys?.forEach { k ->
                         // 璁$畻姣忎釜璧拌埅浠诲姟鐨勬墍鏈夊紓甯稿洜瀛�
                         if (!abnormalFactors.contains(k)) {
                             abnormalFactors.add(k)
@@ -74,6 +82,87 @@
         return result
     }
 
+    fun generateMissionInfo(
+        keyScenes: List<SceneInfo?>,
+        mission: Mission,
+        pollutedClues: List<PollutedClue?>,
+        data: List<BaseRealTimeData>,
+        minDis: Double = 100.0,
+    ): MissionInfo {
+        val factorMap = mutableMapOf<FactorType, Int>()
+        val abnormalFactors = mutableListOf<FactorType>()
+        var sceneCount = 0
+        val scenes = mutableListOf<SceneInfo>()
+        // 鎻愬彇閫斿緞鍏抽敭鍦烘櫙淇℃伅锛堣绠楄蛋鑸矾绾挎槸鍚︿笌鍏抽敭鍦烘櫙璺濈杈冭繎锛�
+        val relatedScenes = mutableListOf<SceneInfo>()
+        data.forEach { d ->
+            // 璺宠繃缂哄皯缁忕含搴︾殑鏁版嵁鐐�
+            if (d.longitude == null || d.latitude == null) {
+                return@forEach
+            }
+            // 杞崲涓篏CJ02鍧愭爣绯�
+            val point = MapUtil.wgs84ToGcj02(d.longitude!!.toDouble() to d.latitude!!.toDouble())
+            keyScenes.forEach ks@{ k ->
+                // 璺宠繃缂哄皯缁忕含搴︾殑鍦烘櫙
+                if (k?.longitude == null || k.latitude == null) {
+                    return@ks
+                }
+                // 妫�鏌ユ槸鍚︽湭娣诲姞杩�
+                if (!relatedScenes.contains(k)) {
+                    // 璁$畻璺濈
+                    val distance = MapUtil.getDistance(
+                        k.longitude!!.toDouble(),
+                        k.latitude!!.toDouble(),
+                        point.first,
+                        point.second
+                    )
+                    // 妫�鏌ユ槸鍚﹁窛绂诲皬浜庨槇鍊�
+                    if (distance < minDis) {
+                        relatedScenes.add(k)
+                    }
+                }
+            }
+        }
+        pollutedClues.forEach {
+            if (it?.msgType == MsgType.PolClue.value) {
+                it.pollutedData?.statisticMap?.keys?.forEach { k ->
+                    // 璁$畻姣忎釜璧拌埅浠诲姟鐨勬墍鏈夊紓甯稿洜瀛�
+                    if (!abnormalFactors.contains(k)) {
+                        abnormalFactors.add(k)
+                    }
+                    // 璁$畻姣忎釜璧拌埅浠诲姟鐨勯瑕佹薄鏌撶墿
+                    if (!factorMap.containsKey(k)) {
+                        factorMap[k] = 0
+                    }
+                    factorMap[k] = factorMap[k]!! + 1
+                }
+                // 璁$畻姣忎釜璧拌埅浠诲姟鐨勬函婧愬満鏅暟閲�
+                sceneCount += it.pollutedSource?.sceneList?.size ?: 0
+                it.pollutedSource?.sceneList?.forEach { s->
+                    if (scenes.find { s1 -> s1.guid == s.guid } == null) {
+                        scenes.add(s)
+                    }
+                }
+            }
+        }
+
+        // 寮傚父鏁版嵁鐐规暟閲忕粺璁�
+        val clues = pollutedClues.filter { it?.msgType == MsgType.PolClue.value }
+
+        val missionInfo = MissionInfo()
+        BeanUtils.copyProperties(mission, missionInfo)
+        missionInfo.apply {
+            mainFactor = factorMap.maxByOrNull { it.value }?.key?.name
+            this.abnormalFactors = abnormalFactors
+            this.sceneCount = sceneCount
+            this.scenes = scenes
+            keyScene = relatedScenes
+            exceptionCount = clues.size
+        }
+
+        return missionInfo
+    }
+
     /**
      * 鐢熸垚璧拌埅浠诲姟璇︾粏淇℃伅
      * 鏁村悎璧拌埅浠诲姟鍩烘湰淇℃伅銆佸叧閿満鏅�佹暟鎹粺璁″拰寮傚父鏁伴噺锛岀敓鎴愬畬鏁寸殑浠诲姟璇︽儏鎶ュ憡
@@ -93,44 +182,52 @@
         mission: Mission,
         pollutedClues: List<PollutedClue?>,
         data: List<BaseRealTimeData>,
-        minDis:Double = 100.0
+        minDis: Double = 100.0,
     ): MissionDetail {
         // 鍒涘缓浠诲姟璇︽儏瀵硅薄骞跺鍒跺熀鏈俊鎭�
         val missionDetail = MissionDetail()
         BeanUtils.copyProperties(mission, missionDetail)
-    
+
         // 鎻愬彇閫斿緞鍏抽敭鍦烘櫙淇℃伅锛堣绠楄蛋鑸矾绾挎槸鍚︿笌鍏抽敭鍦烘櫙璺濈杈冭繎锛�
-        val relatedScenes = mutableListOf<SceneInfo>()
-        data.forEach { d->
-            // 璺宠繃缂哄皯缁忕含搴︾殑鏁版嵁鐐�
-            if (d.longitude == null || d.latitude == null) {
-                return@forEach
-            }
-            // 杞崲涓篏CJ02鍧愭爣绯�
-            val point = MapUtil.wgs84ToGcj02(d.longitude!!.toDouble() to d.latitude!!.toDouble())
-            keyScenes.forEach ks@ { k->
-                // 璺宠繃缂哄皯缁忕含搴︾殑鍦烘櫙
-                if (k?.longitude == null || k.latitude == null) {
-                    return@ks
-                }
-                // 璁$畻璺濈
-                val distance = MapUtil.getDistance(k.longitude!!.toDouble(), k.latitude!!.toDouble(), point.first, point.second)
-                // 妫�鏌ユ槸鍚﹁窛绂诲皬浜庨槇鍊间笖鏈坊鍔犺繃
-                if (distance < minDis && !relatedScenes.contains(k)) {
-                    relatedScenes.add(k)
-                }
-            }
-        }
+//        val relatedScenes = mutableListOf<SceneInfo>()
+//        data.forEach { d ->
+//            // 璺宠繃缂哄皯缁忕含搴︾殑鏁版嵁鐐�
+//            if (d.longitude == null || d.latitude == null) {
+//                return@forEach
+//            }
+//            // 杞崲涓篏CJ02鍧愭爣绯�
+//            val point = MapUtil.wgs84ToGcj02(d.longitude!!.toDouble() to d.latitude!!.toDouble())
+//            keyScenes.forEach ks@{ k ->
+//                // 璺宠繃缂哄皯缁忕含搴︾殑鍦烘櫙
+//                if (k?.longitude == null || k.latitude == null) {
+//                    return@ks
+//                }
+//                // 妫�鏌ユ槸鍚︽湭娣诲姞杩�
+//                if (!relatedScenes.contains(k)) {
+//                    // 璁$畻璺濈
+//                    val distance = MapUtil.getDistance(
+//                        k.longitude!!.toDouble(),
+//                        k.latitude!!.toDouble(),
+//                        point.first,
+//                        point.second
+//                    )
+//                    // 妫�鏌ユ槸鍚﹁窛绂诲皬浜庨槇鍊�
+//                    if (distance < minDis) {
+//                        relatedScenes.add(k)
+//                    }
+//                }
+//            }
+//        }
         // 瀛樺偍涓庝换鍔$浉鍏宠仈鐨勫叧閿満鏅俊鎭�
-        missionDetail.keyScene = relatedScenes
-    
+//        missionDetail.keyScene = relatedScenes
+
         // 璁$畻鐜鍥犲瓙缁熻鏁版嵁锛堝钩鍧囧�笺�佹渶灏忓�笺�佹渶澶у�硷級
         missionDetail.dataStatistics = data.calDataStatistics()
-    
+
         // 寮傚父鏁版嵁鐐规暟閲忕粺璁�
-        val clues = pollutedClues.filter { it?.msgType == MsgType.PolClue.value }
-        missionDetail.exceptionCount = clues.size
-    
+//        val clues = pollutedClues.filter { it?.msgType == MsgType.PolClue.value }
+//        missionDetail.exceptionCount = clues.size
+
         return missionDetail
     }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt
index f82bf96..ff1ab18 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt
@@ -2,11 +2,9 @@
 
 import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
 import com.flightfeather.uav.domain.entity.Mission
-import com.flightfeather.uav.domain.repository.MissionRep
 import com.flightfeather.uav.lightshare.bean.AreaVo
 import com.flightfeather.uav.socket.eunm.FactorType
 import com.flightfeather.uav.socket.sender.MsgType
-import org.springframework.stereotype.Component
 import java.util.*
 import kotlin.math.round
 
@@ -16,6 +14,10 @@
  * @author feiyu02
  */
 class MissionSummary() {
+
+    companion object {
+        private const val FOCUS_AREA_COUNT = 2
+    }
 
     data class Summary(
         // 姹囨�诲懆鏈熷紑濮嬫椂闂�
@@ -33,11 +35,13 @@
         // 鍚勭瓑绾х┖姘旇川閲忚儗鏅蛋鑸鏁�,<绌烘皵璐ㄩ噺绛夌骇锛屾鏁帮紝鍗犳瘮>
         val countByDegree: List<Triple<String, Int, Double>>,
         // 闂鎬绘暟
-        val probCount:Int,
+        val probCount: Int,
         // 楂橀闄╁満鏅�绘暟
-        val highRiskSceneCount:Int,
+        val highRiskSceneCount: Int,
         // 闂鎸夌洃娴嬪洜瀛愮被鍨嬪垎甯冩儏鍐�, <鍥犲瓙绫诲瀷锛屾鏁帮紝鍗犳瘮>
-        val probByFactor:List<Triple<String, Int, Double>>
+        val probByFactor: List<Triple<String, Int, Double>>,
+        // 鑱氱劍鍖哄煙鎴栧満鏅�
+        val focusRegion: List<String>,
     )
 
     /**
@@ -61,7 +65,8 @@
                 countByDegree = emptyList(),
                 probCount = 0,
                 highRiskSceneCount = 0,
-                probByFactor = emptyList()
+                probByFactor = emptyList(),
+                focusRegion = emptyList()
             )
         }
 
@@ -100,7 +105,10 @@
         val highRiskSceneCount = clueRes.second // 闇�鍏宠仈鍦烘櫙琛ㄧ粺璁�
         val probByFactor = clueRes.third
 
-        // 7. 鏋勫缓骞惰繑鍥炵粺璁$粨鏋�
+        // 7. 浠庡紓甯告墍鍦ㄥ湴鍖哄拰婧簮鐨勫満鏅腑缁熻鑱氱劍鍖哄煙
+        val focusRegion = calFocusRegion(clues)
+
+        // 8. 鏋勫缓骞惰繑鍥炵粺璁$粨鏋�
         return Summary(
             startTime = startTime,
             endTime = endTime,
@@ -111,7 +119,8 @@
             countByDegree = countByDegree,
             probCount = probCount,
             highRiskSceneCount = highRiskSceneCount,
-            probByFactor = probByFactor
+            probByFactor = probByFactor,
+            focusRegion = focusRegion
         )
     }
 
@@ -137,4 +146,32 @@
         }
         return Triple(probCount, highRiskSceneCount, probByFactor)
     }
+
+    private fun calFocusRegion(clues: List<PollutedClue?>): List<String> {
+        // 缁熻姣忎釜鍖哄煙鎴栧満鏅嚭鐜扮殑娆℃暟
+        val focusArea = mutableMapOf<String, Int>()
+        val focusScene = mutableMapOf<String, Int>()
+        clues.forEach { c->
+            if (c?.msgType == MsgType.PolClue.value) {
+                if (!c.pollutedArea?.address.isNullOrBlank()) {
+                    if (focusArea.containsKey(c.pollutedArea?.address)) {
+                        focusArea[c.pollutedArea?.address!!] = focusArea[c.pollutedArea?.address]!! + 1
+                    } else {
+                        focusArea[c.pollutedArea?.address!!] = 1
+                    }
+                }
+                c.pollutedSource?.sceneList?.forEach { s->
+                    if (s.name != null) {
+                        if (focusScene.containsKey(s.name!!)) {
+                            focusScene[s.name!!] = focusScene[s.name!!]!! + 1
+                        } else {
+                            focusScene[s.name!!] = 1
+                        }
+                    }
+                }
+            }
+        }
+        return focusArea.entries.sortedByDescending { it.value }.map { it.key }.take(FOCUS_AREA_COUNT) +
+                focusScene.entries.sortedByDescending { it.value }.map { it.key }.take(FOCUS_AREA_COUNT)
+    }
 }
\ 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 5bd59a6..2fa37ca 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
@@ -70,6 +70,11 @@
      */
     fun generateMissionList(missionClues: List<Pair<Mission, List<PollutedClue?>>>): List<MissionInventory.MissionInfo>
 
+    fun generateMissionInfo(
+        keyScenes: List<SceneInfo?>,
+        missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>,
+    ): List<MissionInventory.MissionInfo>
+
     /**
      * 鑾峰彇璧拌埅浠诲姟璇︽儏锛堟寜鏃堕棿鍜屽尯鍩熺瓫閫夛級
      * 鏍规嵁鏃堕棿鑼冨洿鍜屽尯鍩熸煡璇㈠苟鐢熸垚璇︾粏鐨勪换鍔℃姤鍛婏紝鍖呭惈鍏抽敭鍦烘櫙鍜屾暟鎹粺璁�
@@ -100,6 +105,6 @@
         factorTypes: List<FactorType>,
         gridLen: Int,
         gridCells: List<GridCell>,
-        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>,
+        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionInfo>, List<GridDataDetailMixVo>>>,
     ): List<MissionGridFusion.GridFusionByAQI>
 }
\ No newline at end of file
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 0dcbb4a..3e20de7 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
@@ -119,10 +119,25 @@
      * @see generateMissionList 閲嶈浇鏂规硶锛屽鐞嗗凡鍏宠仈鐨勬暟鎹
      */
     override fun generateMissionList(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionInventory.MissionInfo> {
-        val missionClues = missionRep.findByAreaAndTime(areaVo, startTime, endTime).filterNotNull().map {
-            it to sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?>
+//        val missionClues = missionRep.findByAreaAndTime(areaVo, startTime, endTime).filterNotNull().map {
+//            it to sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?>
+//        }
+//        return generateMissionList(missionClues)
+        val missionCluesData = missionRep.findByAreaAndTime(areaVo, startTime, endTime).filterNotNull().map {
+            Triple(
+                it,
+                sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?>,
+                realTimeDataRep.fetchData(it)
+            )
         }
-        return generateMissionList(missionClues)
+        val keyScenes = sceneInfoRep.findBySceneTypes(
+            listOf(
+                SceneType.TYPE19.value,
+                SceneType.TYPE20.value,
+                SceneType.TYPE21.value
+            )
+        )
+        return generateMissionInfo(keyScenes, missionCluesData)
     }
 
     /**
@@ -134,6 +149,15 @@
      */
     override fun generateMissionList(missionClues: List<Pair<Mission, List<PollutedClue?>>>): List<MissionInventory.MissionInfo> {
         return MissionInventory().generateMissionList(missionClues)
+    }
+
+    override fun generateMissionInfo(
+        keyScenes: List<SceneInfo?>,
+        missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>,
+    ): List<MissionInventory.MissionInfo> {
+        return missionCluesData.map {
+            MissionInventory().generateMissionInfo(keyScenes, it.first, it.second, it.third)
+        }
     }
 
     /**
@@ -256,9 +280,9 @@
                     SceneType.TYPE21.value
                 )
             )
-            val missionDetails = generateMissionDetail(keyScenes, missionCluesData)
+            val missionInfos = generateMissionInfo(keyScenes, missionCluesData)
 
-            return@mapNotNull Triple(degree, missionDetails, gridDataDetailMixVos)
+            return@mapNotNull Triple(degree, missionInfos, gridDataDetailMixVos)
         }.filter { it.second.isNotEmpty() }
 
         return generateGridFusion(factorTypes, gridLen, gridCells, gridDataDetailList)
@@ -268,7 +292,7 @@
         factorTypes: List<FactorType>,
         gridLen: Int,
         gridCells: List<GridCell>,
-        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionDetail>, List<GridDataDetailMixVo>>>,
+        dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionInfo>, List<GridDataDetailMixVo>>>,
     ): List<MissionGridFusion.GridFusionByAQI> {
         return MissionGridFusion(sceneInfoRep).generateGridFusion(factorTypes, gridLen, gridCells, dataList)
     }

--
Gitblit v1.9.3