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/biz/report/MissionSummary.kt |  160 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 152 insertions(+), 8 deletions(-)

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 beb76d5..ff1ab18 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt
@@ -1,16 +1,31 @@
 package com.flightfeather.uav.biz.report
 
-import org.springframework.stereotype.Component
+import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
+import com.flightfeather.uav.domain.entity.Mission
+import com.flightfeather.uav.lightshare.bean.AreaVo
+import com.flightfeather.uav.socket.eunm.FactorType
+import com.flightfeather.uav.socket.sender.MsgType
+import java.util.*
+import kotlin.math.round
 
 /**
  * 璧拌埅浠诲姟姹囨��
  * @date 2025/8/22
  * @author feiyu02
  */
-@Component
-class MissionSummary {
+class MissionSummary() {
 
-    inner class Summary(
+    companion object {
+        private const val FOCUS_AREA_COUNT = 2
+    }
+
+    data class Summary(
+        // 姹囨�诲懆鏈熷紑濮嬫椂闂�
+        val startTime: Date,
+        // 姹囨�诲懆鏈熺粨鏉熸椂闂�
+        val endTime: Date,
+        // 璧拌埅鍖哄煙淇℃伅
+        val area: AreaVo,
         // 璧拌埅娆℃暟
         val count: Int,
         // 鎬婚噷绋嬫暟锛堝叕閲岋級
@@ -20,14 +35,143 @@
         // 鍚勭瓑绾х┖姘旇川閲忚儗鏅蛋鑸鏁�,<绌烘皵璐ㄩ噺绛夌骇锛屾鏁帮紝鍗犳瘮>
         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>,
     )
 
-    fun execute() {
+    /**
+     * 鏍规嵁鏃堕棿鑼冨洿鏌ヨ璧拌埅浠诲姟骞剁敓鎴愮粺璁$粨鏋�
+     * @param startTime 缁熻寮�濮嬫椂闂�
+     * @param endTime 缁熻缁撴潫鏃堕棿
+     * @param missions 璧拌埅浠诲姟鍒楄〃锛堝閮ㄤ紶鍏ワ紝閬垮厤閲嶅鏌ヨ锛�
+     * @param clues 姹℃煋绾跨储鍒楄〃锛堢敤浜庨棶棰樼粺璁★級
+     * @return 璧拌埅浠诲姟缁熻缁撴灉Summary
+     */
+    fun execute(startTime: Date, endTime: Date, missions: List<Mission?>, clues: List<PollutedClue?>): Summary {
+        // 1. 鏌ヨ鎸囧畾鏃堕棿鑼冨洿鍐呯殑璧拌埅浠诲姟
+        if (missions.isEmpty()) {
+            return Summary(
+                startTime = startTime,
+                endTime = endTime,
+                area = AreaVo(), // 绌轰换鍔℃椂杩斿洖榛樿鍖哄煙淇℃伅
+                count = 0,
+                kilometres = 0.0,
+                regionList = emptyList(),
+                countByDegree = emptyList(),
+                probCount = 0,
+                highRiskSceneCount = 0,
+                probByFactor = emptyList(),
+                focusRegion = emptyList()
+            )
+        }
 
+        // 2. 鍩虹缁熻锛氭�讳换鍔℃暟銆佹�婚噷绋�
+        val totalCount = missions.size
+        val totalKilometres = missions.sumOf { it?.kilometres?.toDouble() ?: 0.0 }
+
+        // 3. 鍖哄煙淇℃伅锛氬彇棣栦釜浠诲姟鐨勮鏀垮尯鍒掍俊鎭紙濡傚瓨鍦ㄥ鍖哄煙鍙墿灞曚负鍚堝苟閫昏緫锛�
+        val firstMission = missions.first()
+        val area = AreaVo().apply {
+            provinceCode = firstMission?.provinceCode
+            provinceName = firstMission?.provinceName
+            cityCode = firstMission?.cityCode
+            cityName = firstMission?.cityName
+            districtCode = firstMission?.districtCode
+            districtName = firstMission?.districtName
+            townCode = firstMission?.townCode
+            townName = firstMission?.townName
+        }
+
+        // 4. 娑夊強鍖哄煙鍒楄〃锛氬幓閲嶆敹闆嗘墍鏈変换鍔$殑region瀛楁
+        val regionList = missions.mapNotNull { it?.region }.distinct()
+
+        // 5. 绌烘皵璐ㄩ噺绛夌骇鍒嗗竷锛氭寜pollutionDegree鍒嗙粍缁熻娆℃暟鍙婂崰姣�
+        val degreeGroups = missions
+            .filter { it?.pollutionDegree != null } // 杩囨护鏃犳晥绛夌骇
+            .groupBy { it?.pollutionDegree!! }
+            .mapValues { it.value.size }
+        val countByDegree = degreeGroups.map { (degree, count) ->
+            Triple(degree, count, count.toDouble() / totalCount)
+        }
+
+        // 6. 闂鐩稿叧缁熻
+        val clueRes = calClue(clues)
+        val probCount = clueRes.first // 闇�鍏宠仈闂琛ㄧ粺璁�
+        val highRiskSceneCount = clueRes.second // 闇�鍏宠仈鍦烘櫙琛ㄧ粺璁�
+        val probByFactor = clueRes.third
+
+        // 7. 浠庡紓甯告墍鍦ㄥ湴鍖哄拰婧簮鐨勫満鏅腑缁熻鑱氱劍鍖哄煙
+        val focusRegion = calFocusRegion(clues)
+
+        // 8. 鏋勫缓骞惰繑鍥炵粺璁$粨鏋�
+        return Summary(
+            startTime = startTime,
+            endTime = endTime,
+            area = area,
+            count = totalCount,
+            kilometres = totalKilometres,
+            regionList = regionList,
+            countByDegree = countByDegree,
+            probCount = probCount,
+            highRiskSceneCount = highRiskSceneCount,
+            probByFactor = probByFactor,
+            focusRegion = focusRegion
+        )
+    }
+
+    private fun calClue(clues: List<PollutedClue?>): Triple<Int, Int, List<Triple<String, Int, Double>>> {
+        var probCount = 0
+        var highRiskSceneCount = 0
+        val probByFactorMap = mutableMapOf<FactorType, Int>()
+        clues.forEach { c ->
+            if (c?.msgType == MsgType.PolClue.value) {
+                c.pollutedSource?.sceneList?.size?.let { s -> highRiskSceneCount += s }
+                c.pollutedData?.statisticMap?.keys?.forEach { k ->
+                    probCount++
+                    if (!probByFactorMap.containsKey(k)) {
+                        probByFactorMap[k] = 0
+                    }
+                    probByFactorMap[k] = probByFactorMap[k]!! + 1
+                }
+            }
+        }
+        val probByFactor = probByFactorMap.entries.map {
+            val per = if(probCount == 0) .0 else round(it.value.toDouble() / probCount * 100) / 100
+            Triple(it.key.des, it.value, per)
+        }
+        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

--
Gitblit v1.9.3