From 29383149f7040d89ae00ad48dc48bbcf46587946 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期一, 01 九月 2025 17:32:53 +0800
Subject: [PATCH] 2025.9.1 1. 新增走航任务统计功能(待完成)

---
 src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt     |  160 ++++++++++-
 src/main/resources/mapper/MissionMapper.xml                                                  |    3 
 src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt                          |   86 ++++++
 src/main/kotlin/com/flightfeather/uav/domain/repository/SceneInfoRep.kt                      |    9 
 src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt                           |    1 
 src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt                         |  105 ++++++-
 src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt                              |    4 
 src/main/kotlin/com/flightfeather/uav/domain/entity/Mission.java                             |   16 +
 /dev/null                                                                                    |   15 -
 src/main/kotlin/com/flightfeather/uav/domain/repository/MissionRep.kt                        |   22 +
 src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt                    |   15 
 src/main/resources/generator/generatorConfig.xml                                             |    4 
 src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImplTest.kt |   30 ++
 src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt              |   74 +++++
 src/main/kotlin/com/flightfeather/uav/lightshare/bean/FactorStatistics.kt                    |   23 +
 src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt                      |   69 +++++
 src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt                             |    7 
 src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt               |   58 ++++
 src/test/kotlin/com/flightfeather/uav/biz/report/MissionSummaryTest.kt                       |    6 
 src/main/kotlin/com/flightfeather/uav/socket/eunm/AggregatedFactorType.kt                    |   24 +
 src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt                              |    2 
 21 files changed, 649 insertions(+), 84 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionClue.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionClue.kt
deleted file mode 100644
index f61020e..0000000
--- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionClue.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.flightfeather.uav.biz.report
-
-import com.flightfeather.uav.socket.eunm.FactorType
-
-/**
- * @date 2025/8/25 14:18
- * @author feiyu
- */
-class MissionClue {
-
-    class Clue{
-        var factor:FactorType?=null
-        var riskRegion:String?=null
-    }
-}
\ No newline at end of file
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 26a597a..cd8f696 100644
--- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt
+++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt
@@ -1,11 +1,12 @@
 package com.flightfeather.uav.biz.report
 
 import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
-import com.flightfeather.uav.domain.entity.Mission
-import com.flightfeather.uav.domain.entity.SceneInfo
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.*
 import com.flightfeather.uav.lightshare.bean.FactorStatistics
 import com.flightfeather.uav.socket.eunm.FactorType
 import com.flightfeather.uav.socket.sender.MsgType
+import org.springframework.beans.BeanUtils
 
 /**
  * 璧拌埅婧簮娓呭崟
@@ -31,37 +32,105 @@
     class MissionDetail : Mission() {
         var keyScene: List<SceneInfo>? = null
         var dataStatistics: List<FactorStatistics>? = null
+        var exceptionCount: Int = 0
     }
 
     /**
-     * 杈撳嚭璧拌埅娓呭崟
+     * 鐢熸垚璧拌埅浠诲姟娓呭崟
+     * 澶勭悊璧拌埅浠诲姟涓庢薄鏌撶嚎绱㈡暟鎹紝缁熻姣忎釜浠诲姟鐨勫紓甯稿洜瀛愩�侀瑕佹薄鏌撶墿鍜屽満鏅暟閲�
+     * @param missionClues 鍖呭惈璧拌埅浠诲姟鍜屽搴旀薄鏌撶嚎绱㈢殑Pair鍒楄〃
+     * @return 鍖呭惈缁熻淇℃伅鐨凪issionInfo鍒楄〃锛屾瘡涓厓绱犲寘鍚换鍔″熀鏈俊鎭強缁熻鏁版嵁
      */
-    fun missionList(missionClues: List<Pair<Mission?, List<PollutedClue?>>>): List<MissionInfo> {
-
-        val missionMap = mutableMapOf<String, MissionInfo>()
-        missionClues.forEach { (mission, clue) ->
-            mission ?: return@forEach
-            clue ?: return@forEach
-            val missionInfo = missionMap[mission.missionCode] ?: MissionInfo().apply {
-                missionMap[mission.missionCode] = this
-            }
+    fun generateMissionList(missionClues: List<Pair<Mission, List<PollutedClue?>>>): List<MissionInfo> {
+        val result = missionClues.map { (mission, clue) ->
+            val factorMap = mutableMapOf<FactorType, Int>()
             val abnormalFactors = mutableListOf<FactorType>()
             var sceneCount = 0
             clue.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
                 }
             }
-            // 璁$畻姣忎釜璧拌埅浠诲姟鐨勬墍鏈夊紓甯稿洜瀛�
-
-            // 璁$畻姣忎釜璧拌埅浠诲姟鐨勯瑕佹薄鏌撶墿
-
-            // 璁$畻姣忎釜璧拌埅浠诲姟鐨勬函婧愬満鏅暟閲�
+            val missionInfo = MissionInfo()
+            BeanUtils.copyProperties(mission, missionInfo)
+            missionInfo.apply {
+                mainFactor = factorMap.maxByOrNull { it.value }?.key?.name
+                this.abnormalFactors = abnormalFactors
+                this.sceneCount = sceneCount
+            }
         }
-        return mutableListOf()
+        return result
+    }
+
+    /**
+     * 鐢熸垚璧拌埅浠诲姟璇︾粏淇℃伅
+     * 鏁村悎璧拌埅浠诲姟鍩烘湰淇℃伅銆佸叧閿満鏅�佹暟鎹粺璁″拰寮傚父鏁伴噺锛岀敓鎴愬畬鏁寸殑浠诲姟璇︽儏鎶ュ憡
+     * @param keyScenes 鍏抽敭鍦烘櫙鍒楄〃锛岀敤浜庡垎鏋愯蛋鑸槸鍚︾粡杩囪鍖哄煙
+     * @param mission 璧拌埅浠诲姟鍩烘湰淇℃伅瀵硅薄锛屽寘鍚换鍔D銆佸悕绉般�佹椂闂寸瓑鍏冩暟鎹�
+     * @param pollutedClues 姹℃煋绾跨储鍒楄〃锛岀敤浜庢彁鍙栧叧閿満鏅俊鎭�
+     * @param data 瀹炴椂鐩戞祴鏁版嵁鍒楄〃锛岀敤浜庤绠楃幆澧冨洜瀛愮粺璁′俊鎭�
+     * @param minDis 鏈�灏忚窛绂伙紝鐢ㄤ簬鍒ゆ柇璧拌埅鏄惁缁忚繃鍏抽敭鍦烘櫙
+     * @return 鍖呭惈璇︾粏淇℃伅鐨凪issionDetail瀵硅薄锛屽寘鎷細
+     *         - 浠诲姟鍩烘湰淇℃伅锛堢户鎵胯嚜Mission绫伙級
+     *         - 鍏抽敭鍦烘櫙鍒楄〃锛圱YPE19鍜孴YPE20绫诲瀷鐨勫満鏅級
+     *         - 鐜鍥犲瓙缁熻鏁版嵁锛堝钩鍧囧�笺�佹渶灏忓�笺�佹渶澶у�硷級
+     *         - 寮傚父鏁版嵁鐐规暟閲�
+     */
+    fun generateMissionDetail(
+        keyScenes: List<SceneInfo?>,
+        mission: Mission,
+        pollutedClues: List<PollutedClue?>,
+        data: List<BaseRealTimeData>,
+        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)
+                }
+            }
+        }
+        // 瀛樺偍涓庝换鍔$浉鍏宠仈鐨勫叧閿満鏅俊鎭�
+        missionDetail.keyScene = relatedScenes
+    
+        // 璁$畻鐜鍥犲瓙缁熻鏁版嵁锛堝钩鍧囧�笺�佹渶灏忓�笺�佹渶澶у�硷級
+        missionDetail.dataStatistics = data.calDataStatistics()
+    
+        // 寮傚父鏁版嵁鐐规暟閲忕粺璁�
+        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/MissionRiskArea.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt
new file mode 100644
index 0000000..38eef5b
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt
@@ -0,0 +1,86 @@
+package com.flightfeather.uav.biz.report
+
+import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
+import com.flightfeather.uav.common.utils.MapUtil
+import com.flightfeather.uav.domain.entity.SceneInfo
+import com.flightfeather.uav.domain.entity.avg
+import com.flightfeather.uav.socket.eunm.AggregatedFactorType
+
+/**
+ * 璧拌埅鍏稿瀷闅愭偅鍖哄煙
+ * @date 2025/8/25 14:18
+ * @author feiyu
+ */
+class MissionRiskArea {
+
+    class ClueByArea {
+        var sceneInfo: SceneInfo? = null
+        var clueByFactorList: MutableList<ClueByFactor>? = null
+    }
+
+    class ClueByFactor {
+        var factor: String? = null
+        var clues: MutableList<PollutedClue>? = null
+    }
+
+    fun generateClueByRiskArea(keyScenes: List<SceneInfo?>, pollutedClues: List<PollutedClue?>): List<ClueByArea> {
+        val result = mutableListOf<ClueByArea>()
+        
+        pollutedClues.forEach { pollutedClue ->
+            if (pollutedClue == null) return@forEach
+            val dataList = pollutedClue.pollutedData?.dataList ?: emptyList()
+            if (dataList.isEmpty()) return@forEach
+            
+            // 璁$畻鍗曚釜PollutedClue鐨勫潎鍊肩粡绾害
+            val avgData = dataList.avg()
+            val wgs84Lng = avgData.longitude?.toDouble() ?: return@forEach
+            val wgs84Lat = avgData.latitude?.toDouble() ?: return@forEach
+            
+            // 鍧愭爣杞崲
+            val gcj02Point = MapUtil.wgs84ToGcj02(wgs84Lng to wgs84Lat)
+            
+            // 鏌ユ壘鏈�杩戝満鏅�
+            var minDistance = Double.MAX_VALUE
+            var closestScene: SceneInfo? = null
+            keyScenes.forEach { scene ->
+                scene?.let { s ->
+                    val sceneLng = s.longitude?.toDouble() ?: return@let
+                    val sceneLat = s.latitude?.toDouble() ?: return@let
+                    val distance = MapUtil.getDistance(gcj02Point.first, gcj02Point.second, sceneLng, sceneLat)
+                    if (distance < minDistance) {
+                        minDistance = distance
+                        closestScene = s
+                    }
+                }
+            }
+            
+            // 鎸夊満鏅拰鍥犲瓙鍒嗙粍绾跨储
+            closestScene?.let { scene ->
+                var clueByArea = result.find { it.sceneInfo?.guid == scene.guid }
+                if (clueByArea == null) {
+                    clueByArea = ClueByArea().apply {
+                        this.sceneInfo = scene
+                        this.clueByFactorList = mutableListOf()
+                    }
+                    result.add(clueByArea)
+                }
+
+                val firstFactorType = pollutedClue.pollutedData?.statisticMap?.keys?.first()
+                val afType =  AggregatedFactorType.getAFType(firstFactorType)
+                val factorName = afType?.des ?: firstFactorType?.des
+                var clueByFactor = clueByArea.clueByFactorList?.find { it.factor == factorName }
+                if (clueByFactor == null) {
+                    clueByFactor = ClueByFactor().apply {
+                        this.factor = factorName
+                        this.clues = mutableListOf()
+                    }
+                    clueByArea.clueByFactorList?.add(clueByFactor)
+                }
+                
+                clueByFactor.clues?.add(pollutedClue)
+            }
+        }
+        
+        return result
+    }
+}
\ No newline at end of file
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 f33ac6f..53989b5 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
@@ -3,6 +3,7 @@
 import com.flightfeather.uav.biz.dataprocess.AvgPair
 import com.flightfeather.uav.common.utils.DateUtil
 import com.flightfeather.uav.lightshare.bean.DataVo
+import com.flightfeather.uav.lightshare.bean.FactorStatistics
 import com.flightfeather.uav.socket.bean.AirData
 import com.flightfeather.uav.socket.eunm.FactorType
 import java.io.Serializable
@@ -146,7 +147,7 @@
 
 }
 
-fun List<BaseRealTimeData>.avg(): BaseRealTimeData {
+fun List<BaseRealTimeData>.avg(onEach: (BaseRealTimeData) -> Unit = {  }): BaseRealTimeData {
     if (isEmpty()) {
         return BaseRealTimeData()
     }
@@ -162,8 +163,9 @@
     }
 
     forEach {
+        onEach(it)
         //椋庡悜
-        it.windDirection?.let {w ->
+        it.windDirection?.let { w ->
             val r = Math.toRadians(w.toDouble())
             u += sin(r)
             v += cos(r)
@@ -325,4 +327,67 @@
             windDirection = round(a.toFloat())
         }
     }
+}
+
+/**
+ * 璁$畻瀹炴椂鐩戞祴鏁版嵁鍒楄〃鐨勭粺璁′俊鎭�
+ * 涓烘瘡绉嶇幆澧冨洜瀛愯绠楁渶灏忓�笺�佹渶澶у�煎拰骞冲潎鍊�
+ *
+ * @return 鍖呭惈鍚勭幆澧冨洜瀛愮粺璁′俊鎭殑FactorStatistics鍒楄〃
+ *         姣忎釜FactorStatistics瀵硅薄鍖呭惈鍥犲瓙绫诲瀷銆佹渶灏忓�笺�佹渶澶у�煎拰骞冲潎鍊�
+ */
+fun List<BaseRealTimeData>.calDataStatistics(): List<FactorStatistics> {
+
+    // 鍒濆鍖栧悇鐜鍥犲瓙鐨勭粺璁″璞″垪琛�
+    val statistics = mutableListOf<FactorStatistics>()
+    listOf(
+        FactorType.NO2,
+        FactorType.CO,
+        FactorType.H2S,
+        FactorType.SO2,
+        FactorType.O3,
+        FactorType.PM25,
+        FactorType.PM10,
+        FactorType.VOC,
+        FactorType.NOI,
+        FactorType.VELOCITY,
+        FactorType.WIND_SPEED,
+        FactorType.HEIGHT,
+        FactorType.NO
+    ).forEach { statistics.add(FactorStatistics(it)) }
+
+    // 璁$畻骞冲潎鍊煎苟鍚屾椂鏇存柊鍚勫洜瀛愮殑鏈�灏忓�煎拰鏈�澶у��
+    val avgData = avg { item ->
+        // 鏇存柊姣忎釜鍥犲瓙鐨勬渶灏忓拰鏈�澶у��
+        statistics[0].updateMinAndMaxValue(item.no2)
+        statistics[1].updateMinAndMaxValue(item.co)
+        statistics[2].updateMinAndMaxValue(item.h2s)
+        statistics[3].updateMinAndMaxValue(item.so2)
+        statistics[4].updateMinAndMaxValue(item.o3)
+        statistics[5].updateMinAndMaxValue(item.pm25)
+        statistics[6].updateMinAndMaxValue(item.pm10)
+        statistics[7].updateMinAndMaxValue(item.voc)
+        statistics[8].updateMinAndMaxValue(item.noi)
+        statistics[9].updateMinAndMaxValue(item.velocity)
+        statistics[10].updateMinAndMaxValue(item.windSpeed)
+        statistics[11].updateMinAndMaxValue(item.height)
+        statistics[12].updateMinAndMaxValue(item.no)
+    }
+
+    // 灏嗚绠楀緱鍒扮殑骞冲潎鍊艰缃埌瀵瑰簲鐨勭粺璁″璞′腑
+    statistics[0].avgValue = avgData.no2 ?: 0f
+    statistics[1].avgValue = avgData.co ?: 0f
+    statistics[2].avgValue = avgData.h2s ?: 0f
+    statistics[3].avgValue = avgData.so2 ?: 0f
+    statistics[4].avgValue = avgData.o3 ?: 0f
+    statistics[5].avgValue = avgData.pm25 ?: 0f
+    statistics[6].avgValue = avgData.pm10 ?: 0f
+    statistics[7].avgValue = avgData.voc ?: 0f
+    statistics[8].avgValue = avgData.noi ?: 0f
+    statistics[9].avgValue = avgData.velocity ?: 0f
+    statistics[10].avgValue = avgData.windSpeed ?: 0f
+    statistics[11].avgValue = avgData.height ?: 0f
+    statistics[12].avgValue = avgData.no ?: 0f
+
+    return statistics
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt b/src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt
index 15b6b78..e0f3799 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/entity/ExpandFun.kt
@@ -7,9 +7,6 @@
 import com.flightfeather.uav.socket.bean.AirData
 import com.flightfeather.uav.socket.eunm.FactorType
 import com.flightfeather.uav.socket.eunm.FactorType.*
-import java.time.LocalDateTime
-import java.time.ZoneId
-import java.util.*
 import kotlin.math.atan
 import kotlin.math.cos
 import kotlin.math.round
@@ -26,7 +23,7 @@
     list.add("绾害")
     val values = GsonUtils.parserJsonToArrayBeans(factors, AirData::class.java)
     values.forEach {
-        if (FactorType.outputFactor(it.factorName)) {
+        if (FactorType.isOutputFactor(it.factorName)) {
             val name = it.factorName ?: ""
             list.add(name)
 //            list.add("$name(鐗╃悊閲�)")
@@ -51,7 +48,7 @@
     }
     val values = GsonUtils.parserJsonToArrayBeans(factors, AirData::class.java)
     values.forEach {
-        if (FactorType.outputFactor(it.factorName)) {
+        if (FactorType.isOutputFactor(it.factorName)) {
             row.add(it.factorData ?: -1.0)
 //            row.add(it.physicalQuantity ?: -1.0)
         }
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/entity/Mission.java b/src/main/kotlin/com/flightfeather/uav/domain/entity/Mission.java
index 68280ab..e4367b9 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/entity/Mission.java
+++ b/src/main/kotlin/com/flightfeather/uav/domain/entity/Mission.java
@@ -63,6 +63,8 @@
     @Column(name = "pollution_degree")
     private String pollutionDegree;
 
+    private Integer aqi;
+
     /**
      * @return mission_code
      */
@@ -312,4 +314,18 @@
     public void setPollutionDegree(String pollutionDegree) {
         this.pollutionDegree = pollutionDegree == null ? null : pollutionDegree.trim();
     }
+
+    /**
+     * @return aqi
+     */
+    public Integer getAqi() {
+        return aqi;
+    }
+
+    /**
+     * @param aqi
+     */
+    public void setAqi(Integer aqi) {
+        this.aqi = aqi;
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/repository/MissionRep.kt b/src/main/kotlin/com/flightfeather/uav/domain/repository/MissionRep.kt
index ce7cdec..164f303 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/repository/MissionRep.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/repository/MissionRep.kt
@@ -2,6 +2,7 @@
 
 import com.flightfeather.uav.domain.entity.Mission
 import com.flightfeather.uav.domain.mapper.MissionMapper
+import com.flightfeather.uav.lightshare.bean.AreaVo
 import org.springframework.stereotype.Repository
 import tk.mybatis.mapper.entity.Example
 import java.util.*
@@ -44,4 +45,25 @@
                 .andIsNotNull("endTime")
         })
     }
+    
+    /**
+     * 鏍规嵁鍖哄煙鍜屾椂闂磋寖鍥存煡璇㈡湁鏁堣蛋鑸换鍔″垪琛�
+     * 绛涢�夋寚瀹氳鏀垮尯鍒掑唴銆佺壒瀹氭椂闂存涓斿凡瀹屾垚鏈夋晥閲岀▼璁板綍鐨勮蛋鑸换鍔�
+     * @param areaVo 鍖哄煙鍙傛暟瀵硅薄锛屽寘鍚渷銆佸競銆佸尯涓夌骇琛屾斂鍖哄垝缂栫爜
+     * @param startTime 鏌ヨ璧峰鏃堕棿锛堝寘鍚級
+     * @param endTime 鏌ヨ缁撴潫鏃堕棿锛堝寘鍚級
+     * @return 绗﹀悎鏉′欢鐨勮蛋鑸换鍔″垪琛紝鑻ユ棤鍙繑鍥炵┖鍒楄〃
+     * @see AreaVo 鍖哄煙鍙傛暟鏁版嵁缁撴瀯
+     * @see Mission 璧拌埅浠诲姟瀹炰綋绫�
+     */
+    fun findByAreaAndTime(areaVo: AreaVo, startTime: Date, endTime: Date): List<Mission?> {
+        return missionMapper.selectByExample(Example(Mission::class.java).apply {
+            createCriteria().andBetween("startTime", startTime, endTime)
+                .andEqualTo("provinceCode", areaVo.provinceCode)
+                .andEqualTo("cityCode", areaVo.cityCode)
+                .andEqualTo("districtCode", areaVo.districtCode)
+                .andIsNotNull("kilometres")
+                .andNotEqualTo("kilometres", 0)
+        })
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/repository/SceneInfoRep.kt b/src/main/kotlin/com/flightfeather/uav/domain/repository/SceneInfoRep.kt
index e38cd28..e0e9729 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/repository/SceneInfoRep.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/repository/SceneInfoRep.kt
@@ -41,4 +41,13 @@
                 .andLessThanOrEqualTo("latitude", range[3])
         })
     }
+
+    fun findBySceneTypes(sceneTypes: List<Int>): List<SceneInfo?> {
+        if (sceneTypes.isEmpty()){
+            return emptyList()
+        }
+        return sceneInfoMapper.selectByExample(Example(SceneInfo::class.java).apply {
+            createCriteria().andIn("typeId", sceneTypes)
+        })
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt b/src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt
index b8c4c4c..23f46e0 100644
--- a/src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt
+++ b/src/main/kotlin/com/flightfeather/uav/domain/repository/SourceTraceRep.kt
@@ -99,9 +99,18 @@
         return if (res.isEmpty()) null else res[0]
     }
 
-    fun fetchList(deviceCode: String, startTime: Date, endTime: Date): List<BaseExceptionResult?> {
-        return sourceTraceMsgBlobMapper.selectWithBlob(deviceCode, startTime, endTime)
-            .map { stm ->
+    fun fetchList(
+        deviceCode: String,
+        startTime: Date,
+        endTime: Date,
+        msgType: MsgType? = null,
+    ): List<BaseExceptionResult?> {
+        var res = sourceTraceMsgBlobMapper.selectWithBlob(deviceCode, startTime, endTime)
+        if (msgType !== null) {
+            res = res.filter { it?.msgType == msgType.value }
+        }
+
+        return res.map { stm ->
             when (stm?.msgType) {
                 MsgType.PolClue.value,
                 MsgType.DataChange.value,
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt
index b548bc7..65c0fab 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/DataVo.kt
@@ -42,7 +42,7 @@
         row.add(lng ?: -1.0)
         row.add(lat ?: -1.0)
         values?.forEach {
-            if (FactorType.outputFactor(it.factorName)) {
+            if (FactorType.isOutputFactor(it.factorName)) {
                 row.add(it.factorData ?: -1.0)
 //                                row.add(it.physicalQuantity ?: -1.0)
             }
@@ -57,7 +57,7 @@
         list.add("缁忓害")
         list.add("绾害")
         values?.forEach {
-            if (FactorType.outputFactor(it.factorName)) {
+            if (FactorType.isOutputFactor(it.factorName)) {
                 val name = it.factorName ?: ""
                 list.add(name)
 //                        list.add("$name(鐗╃悊閲�)")
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/FactorStatistics.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/FactorStatistics.kt
index f81e97f..87f730e 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/bean/FactorStatistics.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/bean/FactorStatistics.kt
@@ -9,11 +9,24 @@
  */
 data class FactorStatistics(
     // 鐩戞祴鍥犲瓙绫诲瀷
-    val factor: FactorType,
+    var factor: FactorType,
     // 鏈�灏忓��
-    val minValue: Double,
+    var minValue: Float = Float.NaN,
     // 鏈�澶у��
-    val maxValue: Double,
+    var maxValue: Float = Float.NaN,
     // 鍧囧��
-    val avgValue: Double
-)
\ No newline at end of file
+    var avgValue: Float = Float.NaN,
+){
+    fun updateMinAndMaxValue(value: Float?){
+        minValue = if (minValue.isNaN()) {
+            value ?: Float.NaN
+        } else {
+            minValue.coerceAtMost(value ?: minValue)
+        }
+        maxValue = if (maxValue.isNaN()) {
+            value ?: Float.NaN
+        } else {
+            maxValue.coerceAtLeast(value ?: maxValue)
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt
index 594f4a7..15d6979 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/eunm/SceneType.kt
@@ -26,4 +26,5 @@
     TYPE18(18, "鍟嗕笟浣�"),
     TYPE19(19, "鍥芥帶鐐�"),
     TYPE20(20, "甯傛帶鐐�"),
+    TYPE21(21, "灏忓井绔�"),
 }
\ 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 61683df..bb440bf 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
@@ -1,14 +1,21 @@
 package com.flightfeather.uav.lightshare.service
 
-import com.flightfeather.uav.biz.dataanalysis.BaseExceptionResult
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
+import com.flightfeather.uav.biz.report.MissionInventory
+import com.flightfeather.uav.biz.report.MissionInventory.MissionDetail
+import com.flightfeather.uav.biz.report.MissionRiskArea
 import com.flightfeather.uav.biz.report.MissionSummary
-import com.flightfeather.uav.biz.sourcetrace.model.BasePollutedMsg
+import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import com.flightfeather.uav.domain.entity.Mission
+import com.flightfeather.uav.domain.entity.SceneInfo
 import com.flightfeather.uav.lightshare.bean.AreaVo
 import java.util.*
 
 /**
- *
+ * 鏁版嵁鍒嗘瀽鏈嶅姟鎺ュ彛
+ * 鎻愪緵璧拌埅浠诲姟鏁版嵁鐨勭粺璁″垎鏋愩�佹薄鏌撴函婧愩�佷换鍔℃竻鍗曠敓鎴愮瓑鏍稿績鍔熻兘
+ * 鐢ㄤ簬鏁村悎鍜屽鐞嗘棤浜烘満鐩戞祴鏁版嵁锛岀敓鎴愬悇绫诲垎鏋愭姤鍛婂拰缁熻缁撴灉
  * @date 2025/5/8
  * @author feiyu02
  */
@@ -16,11 +23,68 @@
 
     /**
      * 姹℃煋婧簮鍒嗘瀽
-     * @param missionCode 璧拌埅浠诲姟缂栧彿
+     * 鏍规嵁璧拌埅浠诲姟缂栧彿鍒嗘瀽姹℃煋绾跨储锛岃瘑鍒紓甯告暟鎹偣鍜屾綔鍦ㄦ薄鏌撴簮
+     * @param missionCode 璧拌埅浠诲姟缂栧彿锛岀敤浜庡敮涓�鏍囪瘑鐗瑰畾鐨勮蛋鑸换鍔�
+     * @return 寮傚父缁撴灉鍒楄〃锛屾瘡涓厓绱犲寘鍚紓甯哥被鍨嬨�佷綅缃拰璇︾粏淇℃伅
      */
     fun pollutionTrace(missionCode: String): List<ExceptionResult>
 
+    /**
+     * 鑾峰彇鍘嗗彶姹℃煋婧簮缁撴灉
+     * 鏍规嵁浠诲姟缂栧彿鏌ヨ骞惰繑鍥炲巻鍙叉薄鏌撴函婧愮粨鏋�
+     * @param missionCode 璧拌埅浠诲姟缂栧彿
+     * @return 鍘嗗彶姹℃煋婧簮缁撴灉鐨勫瓧绗︿覆琛ㄧず锛堝叿浣撴牸寮忛渶鍙傝�冨疄鐜扮被锛�
+     */
     fun fetchHistory(missionCode: String): String
 
-    fun missionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary
+    /**
+     * 鐢熸垚璧拌埅浠诲姟姹囨�荤粺璁�
+     * 鎸夋椂闂磋寖鍥村拰鍖哄煙缁熻璧拌埅浠诲姟鐨勫叧閿寚鏍囧拰鎬讳綋鎯呭喌
+     * @param startTime 寮�濮嬫椂闂达紝鐢ㄤ簬绛涢�夋寚瀹氭椂闂存鍐呯殑浠诲姟
+     * @param endTime 缁撴潫鏃堕棿锛岀敤浜庣瓫閫夋寚瀹氭椂闂存鍐呯殑浠诲姟
+     * @param areaVo 鍖哄煙鍙傛暟锛屽寘鍚粡绾害鑼冨洿绛夊湴鐞嗕俊鎭�
+     * @return 姹囨�荤粺璁″璞★紝鍖呭惈浠诲姟鎬绘暟銆佸紓甯哥巼銆佸钩鍧囨暟鎹瓑鎸囨爣
+     */
+    fun generateMissionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary
+
+    /**
+     * 鐢熸垚璧拌埅浠诲姟娓呭崟锛堟寜鏃堕棿鍜屽尯鍩熺瓫閫夛級
+     * 鏍规嵁鏃堕棿鑼冨洿鍜屽湴鐞嗗尯鍩熸煡璇㈣蛋鑸换鍔★紝骞剁敓鎴愬寘鍚粺璁′俊鎭殑浠诲姟鍒楄〃
+     * @param startTime 寮�濮嬫椂闂�
+     * @param endTime 缁撴潫鏃堕棿
+     * @param areaVo 鍖哄煙鍙傛暟
+     * @return 璧拌埅浠诲姟淇℃伅鍒楄〃锛屾瘡涓厓绱犲寘鍚换鍔″熀鏈俊鎭拰缁熻鏁版嵁
+     */
+    fun generateMissionList(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionInventory.MissionInfo>
+
+    /**
+     * 鐢熸垚璧拌埅浠诲姟娓呭崟锛堢洿鎺ュ鐞嗕换鍔$嚎绱級
+     * 澶勭悊宸叉湁鐨勮蛋鑸换鍔″拰姹℃煋绾跨储鏁版嵁锛岀敓鎴愪换鍔℃竻鍗�
+     * @param missionClues 鍖呭惈璧拌埅浠诲姟鍜屽搴旀薄鏌撶嚎绱㈢殑Pair鍒楄〃
+     * @return 璧拌埅浠诲姟淇℃伅鍒楄〃
+     */
+    fun generateMissionList(missionClues: List<Pair<Mission, List<PollutedClue?>>>): List<MissionInventory.MissionInfo>
+
+    /**
+     * 鑾峰彇璧拌埅浠诲姟璇︽儏锛堟寜鏃堕棿鍜屽尯鍩熺瓫閫夛級
+     * 鏍规嵁鏃堕棿鑼冨洿鍜屽尯鍩熸煡璇㈠苟鐢熸垚璇︾粏鐨勪换鍔℃姤鍛婏紝鍖呭惈鍏抽敭鍦烘櫙鍜屾暟鎹粺璁�
+     * @param startTime 寮�濮嬫椂闂�
+     * @param endTime 缁撴潫鏃堕棿
+     * @param areaVo 鍖哄煙鍙傛暟
+     * @return 浠诲姟璇︽儏鍒楄〃锛屾瘡涓厓绱犲寘鍚换鍔″畬鏁翠俊鎭�佸満鏅暟鎹拰缁熻缁撴灉
+     */
+    fun generateMissionDetail(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionDetail>
+
+    /**
+     * 鑾峰彇璧拌埅浠诲姟璇︽儏锛堢洿鎺ュ鐞嗕换鍔℃暟鎹級
+     * 澶勭悊宸叉湁鐨勪换鍔°�佹薄鏌撶嚎绱㈠拰瀹炴椂鏁版嵁锛岀敓鎴愯缁嗕换鍔℃姤鍛�
+     * @param keyScenes 鍏抽敭鍦烘櫙鍒楄〃锛岀敤浜庡垎鏋愯蛋鑸槸鍚︾粡杩囪鍖哄煙
+     * @param missionCluesData 鍖呭惈浠诲姟銆佹薄鏌撶嚎绱㈠拰瀹炴椂鏁版嵁鐨凾riple鍒楄〃
+     * @return 浠诲姟璇︽儏鍒楄〃
+     */
+    fun generateMissionDetail(keyScenes: List<SceneInfo?>, missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>): List<MissionDetail>
+
+    fun generateClueByRiskArea(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionRiskArea.ClueByArea>
+
+    fun generateClueByRiskArea(keyScenes: List<SceneInfo?>, pollutedClues: List<PollutedClue?>): List<MissionRiskArea.ClueByArea>
 }
\ 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 3982b5c..9826661 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
@@ -1,32 +1,33 @@
 package com.flightfeather.uav.lightshare.service.impl
 
 import com.flightfeather.uav.biz.FactorFilter
-import com.flightfeather.uav.biz.dataanalysis.BaseExceptionResult
 import com.flightfeather.uav.biz.dataanalysis.ExceptionAnalysisController
 import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
+import com.flightfeather.uav.biz.report.MissionInventory
+import com.flightfeather.uav.biz.report.MissionRiskArea
 import com.flightfeather.uav.biz.report.MissionSummary
-import com.flightfeather.uav.biz.sourcetrace.model.BasePollutedMsg
 import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
 import com.flightfeather.uav.common.exception.BizException
 import com.flightfeather.uav.common.location.LocationRoadNearby
 import com.flightfeather.uav.common.utils.GsonUtils
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
 import com.flightfeather.uav.domain.entity.Mission
+import com.flightfeather.uav.domain.entity.SceneInfo
 import com.flightfeather.uav.domain.mapper.MissionMapper
-import com.flightfeather.uav.domain.repository.MissionRep
-import com.flightfeather.uav.domain.repository.RealTimeDataRep
-import com.flightfeather.uav.domain.repository.SegmentInfoRep
-import com.flightfeather.uav.domain.repository.SourceTraceRep
+import com.flightfeather.uav.domain.repository.*
 import com.flightfeather.uav.lightshare.bean.AreaVo
+import com.flightfeather.uav.lightshare.eunm.SceneType
 import com.flightfeather.uav.lightshare.service.DataAnalysisService
 import com.flightfeather.uav.socket.eunm.FactorType
+import com.flightfeather.uav.socket.sender.MsgType
 import org.springframework.stereotype.Service
 import tk.mybatis.mapper.entity.Example
-import java.time.LocalDateTime
-import java.time.ZoneId
 import java.util.*
 
 /**
- *
+ * 鏁版嵁鍒嗘瀽鏈嶅姟鎺ュ彛瀹炵幇绫�
+ * 鎻愪緵璧拌埅浠诲姟鏁版嵁鐨勭粺璁″垎鏋愩�佹薄鏌撴函婧愩�佷换鍔℃竻鍗曞強璇︽儏鐢熸垚绛夋牳蹇冧笟鍔″姛鑳�
+ * 鏁村悎澶氭暟鎹簮瀹屾垚鏁版嵁鑱氬悎涓庡垎鏋愶紝涓哄墠绔彁渚涙爣鍑嗗寲鐨勭粺璁$粨鏋�
  * @date 2025/5/8
  * @author feiyu02
  */
@@ -37,9 +38,18 @@
     private val realTimeDataRep: RealTimeDataRep,
     private val locationRoadNearby: LocationRoadNearby,
     private val segmentInfoRep: SegmentInfoRep,
-    private val sourceTraceRep: SourceTraceRep
+    private val sourceTraceRep: SourceTraceRep,
+    private val sceneInfoRep: SceneInfoRep
 ) : DataAnalysisService {
 
+    /**
+     * 姹℃煋婧簮鍒嗘瀽
+     * 瀵规寚瀹氳蛋鑸换鍔¤繘琛屽鍥犲瓙姹℃煋鏁版嵁鍒嗘瀽锛岃瘑鍒紓甯告暟鎹偣鍜屾綔鍦ㄦ薄鏌撴簮
+     * @param missionCode 璧拌埅浠诲姟缂栫爜锛堜富閿級
+     * @return 寮傚父缁撴灉鍒楄〃锛屽寘鍚紓甯哥被鍨嬨�佷綅缃�佹祿搴﹀�肩瓑璇︾粏淇℃伅
+     * @throws BizException 褰撹蛋鑸换鍔′笉瀛樺湪鏃舵姏鍑�
+     * @see ExceptionAnalysisController 寮傚父鍒嗘瀽鎺у埗鍣紝澶勭悊鍏蜂綋鐨勬暟鎹垎鏋愰�昏緫
+     */
     override fun pollutionTrace(missionCode: String): List<ExceptionResult> {
         val mission = missionRep.findOne(missionCode) ?: throw BizException("璧拌埅浠诲姟涓嶅瓨鍦�")
 
@@ -60,6 +70,13 @@
         )
     }
 
+    /**
+     * 鑾峰彇鍘嗗彶姹℃煋婧簮缁撴灉
+     * 鏌ヨ鎸囧畾浠诲姟鐨勫巻鍙叉薄鏌撴函婧愮粨鏋滃苟搴忓垪鍖栦负JSON瀛楃涓�
+     * @param missionCode 璧拌埅浠诲姟缂栫爜
+     * @return 鍘嗗彶姹℃煋婧簮缁撴灉鐨凧SON瀛楃涓诧紝鍏蜂綋鏍煎紡鐢眘ourceTraceRep瀹炵幇鍐冲畾
+     * @throws BizException 褰撹蛋鑸换鍔′笉瀛樺湪鏃舵姏鍑�
+     */
     override fun fetchHistory(missionCode: String): String {
         val mission = missionRep.findOne(missionCode) ?: throw BizException("璧拌埅浠诲姟涓嶅瓨鍦�")
 
@@ -67,21 +84,124 @@
         return GsonUtils.gson.toJson(res)
     }
 
-    override fun missionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary {
+    /**
+     * 鐢熸垚璧拌埅浠诲姟姹囨�荤粺璁�
+     * 鎸夋椂闂磋寖鍥村拰琛屾斂鍖哄垝缁熻璧拌埅浠诲姟鐨勫叧閿寚鏍囷紙浠诲姟鏁伴噺銆佸紓甯哥巼銆佸钩鍧囬噷绋嬬瓑锛�
+     * @param startTime 缁熻璧峰鏃堕棿锛堝寘鍚級
+     * @param endTime 缁熻缁撴潫鏃堕棿锛堝寘鍚級
+     * @param areaVo 鍖哄煙鍙傛暟锛屽寘鍚渷銆佸競銆佸尯涓夌骇琛屾斂鍖哄垝缂栫爜
+     * @return 姹囨�荤粺璁″璞★紝鍖呭惈浠诲姟鎬绘暟銆佸紓甯哥偣鏁伴噺銆佸钩鍧囬噷绋嬬瓑鏍稿績鎸囨爣
+     * @see MissionSummary 姹囨�荤粺璁″鐞嗗櫒锛屽皝瑁呭叿浣撶殑缁熻閫昏緫瀹炵幇
+     */
+    override fun generateMissionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary {
         val clues = mutableListOf<PollutedClue?>()
-        val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
-            createCriteria().andBetween("startTime", startTime, endTime)
-                .andEqualTo("provinceCode", areaVo.provinceCode)
-                .andEqualTo("cityCode", areaVo.cityCode)
-                .andEqualTo("districtCode", areaVo.districtCode)
-                .andIsNotNull("kilometres")
-                .andNotEqualTo("kilometres", 0)
-        }).onEach {
+        val missions = missionRep.findByAreaAndTime(areaVo, startTime, endTime).onEach {
             it ?: return@onEach
-            val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime).filterIsInstance<PollutedClue?>()
+            val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?>
             clues.addAll(clue)
         }
         val summary = MissionSummary().execute(startTime, endTime, missions, clues)
         return summary
     }
+
+    /**
+     * 鐢熸垚璧拌埅浠诲姟娓呭崟锛堟寜鏃堕棿鍜屽尯鍩熺瓫閫夛級
+     * 鏍规嵁鏃堕棿鑼冨洿鍜岃鏀垮尯鍒掓煡璇㈣蛋鑸换鍔★紝骞跺叧鑱旀薄鏌撶嚎绱㈡暟鎹敓鎴愪换鍔″垪琛�
+     * @param startTime 鏌ヨ璧峰鏃堕棿锛堝寘鍚級
+     * @param endTime 鏌ヨ缁撴潫鏃堕棿锛堝寘鍚級
+     * @param areaVo 鍖哄煙鍙傛暟锛屽寘鍚渷銆佸競銆佸尯缂栫爜
+     * @return 璧拌埅浠诲姟淇℃伅鍒楄〃锛屾瘡涓厓绱犲寘鍚换鍔″熀鏈俊鎭拰鍏宠仈鐨勬薄鏌撶嚎绱�
+     * @see MissionRep.findByAreaAndTime 鍖哄煙鏃堕棿绛涢�夋暟鎹簮
+     * @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?>
+        }
+        return generateMissionList(missionClues)
+    }
+
+    /**
+     * 鐢熸垚璧拌埅浠诲姟娓呭崟锛堢洿鎺ュ鐞嗕换鍔℃暟鎹級
+     * 鎺ユ敹宸插叧鑱旂殑浠诲姟-姹℃煋绾跨储鏁版嵁瀵癸紝鐢熸垚鏍煎紡鍖栫殑浠诲姟鍒楄〃
+     * @param missionClues 浠诲姟-姹℃煋绾跨储鏁版嵁瀵瑰垪琛紝Pair.first涓轰换鍔″璞★紝Pair.second涓哄搴旀薄鏌撶嚎绱�
+     * @return 鏍囧噯鍖栫殑璧拌埅浠诲姟淇℃伅鍒楄〃锛屽寘鍚换鍔″熀鏈睘鎬у拰姹℃煋缁熻淇℃伅
+     * @see MissionInventory 浠诲姟娓呭崟鐢熸垚鍣紝灏佽鍏蜂綋鏍煎紡鍖栭�昏緫
+     */
+    override fun generateMissionList(missionClues: List<Pair<Mission, List<PollutedClue?>>>): List<MissionInventory.MissionInfo> {
+        return MissionInventory().generateMissionList(missionClues)
+    }
+
+    /**
+     * 鐢熸垚璧拌埅浠诲姟璇︽儏锛堟寜鏃堕棿鍜屽尯鍩熺瓫閫夛級
+     * 鏍规嵁鏃堕棿鑼冨洿鍜岃鏀垮尯鍒掓煡璇换鍔★紝鏁村悎瀹炴椂鐩戞祴鏁版嵁鐢熸垚璇︾粏浠诲姟鎶ュ憡
+     * @param startTime 鏌ヨ璧峰鏃堕棿锛堝寘鍚級
+     * @param endTime 鏌ヨ缁撴潫鏃堕棿锛堝寘鍚級
+     * @param areaVo 鍖哄煙鍙傛暟锛屽寘鍚渷銆佸競銆佸尯缂栫爜
+     * @return 浠诲姟璇︽儏鍒楄〃锛屾瘡涓厓绱犲寘鍚换鍔″畬鏁翠俊鎭�佹薄鏌撶嚎绱㈠強瀹炴椂鐩戞祴鏁版嵁
+     * @see MissionRep.findByAreaAndTime 鍖哄煙鏃堕棿绛涢�夋暟鎹簮
+     * @see realTimeDataRep.fetchData 瀹炴椂鏁版嵁鑾峰彇鎺ュ彛
+     */
+    override fun generateMissionDetail(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionInventory.MissionDetail> {
+        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)
+            )
+        }
+        val keyScenes = sceneInfoRep.findBySceneTypes(
+            listOf(
+                SceneType.TYPE19.value,
+                SceneType.TYPE20.value,
+                SceneType.TYPE21.value
+            )
+        )
+        return generateMissionDetail(keyScenes, missionCluesData)
+    }
+
+    /**
+     * 鐢熸垚璧拌埅浠诲姟璇︽儏锛堢洿鎺ュ鐞嗕换鍔℃暟鎹級
+     * 鎺ユ敹宸插叧鑱旂殑浠诲姟-姹℃煋绾跨储-瀹炴椂鏁版嵁涓夊厓缁勶紝鐢熸垚璇︾粏浠诲姟鎶ュ憡
+     * @param keyScenes 鍏抽敭鍦烘櫙鍒楄〃锛岀敤浜庡垎鏋愯蛋鑸槸鍚︾粡杩囪鍖哄煙
+     * @param missionCluesData 浠诲姟鏁版嵁涓夊厓缁勫垪琛紝Triple鍒嗗埆涓轰换鍔″璞°�佹薄鏌撶嚎绱㈠垪琛ㄣ�佸疄鏃舵暟鎹垪琛�
+     * @return 鏍囧噯鍖栫殑浠诲姟璇︽儏鍒楄〃锛屽寘鍚畬鏁寸殑浠诲姟灞炴�с�佹薄鏌撳垎鏋愬拰鐩戞祴鏁版嵁缁熻
+     * @see MissionInventory.generateMissionDetail 璇︽儏鐢熸垚鏍稿績閫昏緫
+     */
+    override fun generateMissionDetail(
+        keyScenes: List<SceneInfo?>,
+        missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>,
+    ): List<MissionInventory.MissionDetail> {
+        return missionCluesData.map {
+            MissionInventory().generateMissionDetail(keyScenes, it.first, it.second, it.third)
+        }
+    }
+
+    override fun generateClueByRiskArea(
+        startTime: Date,
+        endTime: Date,
+        areaVo: AreaVo,
+    ): List<MissionRiskArea.ClueByArea> {
+        val clues = mutableListOf<PollutedClue?>()
+        missionRep.findByAreaAndTime(areaVo, startTime, endTime).onEach {
+            it ?: return@onEach
+            val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?>
+            clues.addAll(clue)
+        }
+        val keyScenes = sceneInfoRep.findBySceneTypes(
+            listOf(
+                SceneType.TYPE19.value,
+                SceneType.TYPE20.value,
+                SceneType.TYPE21.value
+            )
+        )
+        return generateClueByRiskArea(keyScenes, clues)
+    }
+
+    override fun generateClueByRiskArea(
+        keyScenes: List<SceneInfo?>,
+        pollutedClues: List<PollutedClue?>,
+    ): List<MissionRiskArea.ClueByArea> {
+        return MissionRiskArea().generateClueByRiskArea(keyScenes, pollutedClues)
+    }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt
index 67634b9..f1f7957 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt
@@ -1,11 +1,14 @@
 package com.flightfeather.uav.lightshare.web
 
+import com.fasterxml.jackson.annotation.JsonFormat
 import com.flightfeather.uav.lightshare.bean.AreaVo
 import com.flightfeather.uav.lightshare.service.DataAnalysisService
 import io.swagger.annotations.Api
 import io.swagger.annotations.ApiOperation
 import io.swagger.annotations.ApiParam
+import org.springframework.format.annotation.DateTimeFormat
 import org.springframework.web.bind.annotation.*
+import java.util.*
 
 /**
  * 璧拌埅鏁版嵁鍒嗘瀽
@@ -31,4 +34,59 @@
         @ApiParam("璧拌埅浠诲姟缂栧彿") @RequestParam missionCode: String,
     ) = resPack { dataAnalysisService.fetchHistory(missionCode) }
 
+    @ApiOperation(value = "鐢熸垚璧拌埅浠诲姟姹囨�荤粺璁�")
+    @PostMapping("/report/missionSummary")
+    fun generateMissionSummary(
+        @ApiParam("寮�濮嬫椂闂�") @RequestParam
+        @JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        @DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        startTime: Date,
+        @ApiParam("缁撴潫鏃堕棿") @RequestParam
+        @JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        @DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        endTime: Date,
+        @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo,
+    ) = resPack { dataAnalysisService.generateMissionSummary(startTime, endTime, areaVo) }
+
+    @ApiOperation(value = "鐢熸垚璧拌埅浠诲姟娓呭崟")
+    @PostMapping("/report/missionList")
+    fun generateMissionList(
+        @ApiParam("寮�濮嬫椂闂�") @RequestParam
+        @JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        @DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        startTime: Date,
+        @ApiParam("缁撴潫鏃堕棿") @RequestParam
+        @JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        @DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        endTime: Date,
+        @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo,
+    ) = resPack { dataAnalysisService.generateMissionList(startTime, endTime, areaVo) }
+
+    @ApiOperation(value = "鐢熸垚璧拌埅浠诲姟璇︽儏")
+    @PostMapping("/report/missionDetail")
+    fun generateMissionDetail(
+        @ApiParam("寮�濮嬫椂闂�") @RequestParam
+        @JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        @DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        startTime: Date,
+        @ApiParam("缁撴潫鏃堕棿") @RequestParam
+        @JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        @DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        endTime: Date,
+        @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo,
+    ) = resPack { dataAnalysisService.generateMissionDetail(startTime, endTime, areaVo) }
+
+    @ApiOperation(value = "璧拌埅鍏稿瀷闅愭偅鍖哄煙缁熻")
+    @PostMapping("/report/clueByRiskArea")
+    fun generateClueByRiskArea(
+        @ApiParam("寮�濮嬫椂闂�") @RequestParam
+        @JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        @DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        startTime: Date,
+        @ApiParam("缁撴潫鏃堕棿") @RequestParam
+        @JsonFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        @DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss")
+        endTime: Date,
+        @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo,
+    ) = resPack { dataAnalysisService.generateClueByRiskArea(startTime, endTime, areaVo) }
 }
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/socket/eunm/AggregatedFactorType.kt b/src/main/kotlin/com/flightfeather/uav/socket/eunm/AggregatedFactorType.kt
new file mode 100644
index 0000000..bce6c3e
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/socket/eunm/AggregatedFactorType.kt
@@ -0,0 +1,24 @@
+package com.flightfeather.uav.socket.eunm
+
+/**
+ * 鑱氬悎鐩戞祴鍥犲瓙
+ * @date 2025/8/29
+ * @author feiyu02
+ */
+enum class AggregatedFactorType(val des: String) {
+    PM_AF("棰楃矑鐗�(PM)"),
+    NO_AF("姘哀鍖栫墿");
+
+    companion object {
+        /**
+         * 鏍规嵁鍥犲瓙绫诲瀷鑾峰彇鑱氬悎鍥犲瓙绫诲瀷
+         */
+        fun getAFType(factorType: FactorType?): AggregatedFactorType? {
+            return when (factorType) {
+                FactorType.PM25, FactorType.PM10 -> PM_AF
+                FactorType.NO, FactorType.NO2 -> NO_AF
+                else -> null
+            }
+        }
+    }
+}
\ 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 bd019b6..cd76007 100644
--- a/src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt
+++ b/src/main/kotlin/com/flightfeather/uav/socket/eunm/FactorType.kt
@@ -174,7 +174,7 @@
             else -> 10.0
         }
 
-        fun outputFactor(factorName: String?): Boolean {
+        fun isOutputFactor(factorName: String?): Boolean {
             return when (factorName) {
                 NO2.des,
                 CO.des,
diff --git a/src/main/resources/generator/generatorConfig.xml b/src/main/resources/generator/generatorConfig.xml
index 50c00e3..df72831 100644
--- a/src/main/resources/generator/generatorConfig.xml
+++ b/src/main/resources/generator/generatorConfig.xml
@@ -4,8 +4,8 @@
         "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
 <generatorConfiguration>
     <!-- 鏁版嵁搴撻┍鍔�:閫夋嫨浣犵殑鏈湴纭洏涓婇潰鐨勬暟鎹簱椹卞姩鍖�-->
-    <classPathEntry  location="C:\Users\feiyu\.m2\repository\mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar"/>
-<!--    <classPathEntry  location="C:\Users\feiyu02\.m2\repository\mysql\mysql-connector-java\8.0.21\mysql-connector-java-8.0.21.jar"/>-->
+<!--    <classPathEntry  location="C:\Users\feiyu\.m2\repository\mysql\mysql-connector-java\5.1.46\mysql-connector-java-5.1.46.jar"/>-->
+    <classPathEntry  location="C:\Users\feiyu02\.m2\repository\mysql\mysql-connector-java\8.0.21\mysql-connector-java-8.0.21.jar"/>
     <!--defaultModelType{
     conditional:杩欎釜妯″瀷涓巋ierarchical妯″瀷鐩镐技,闄や簡濡傛灉涓�涓疄浣撶被鍙寘鍚竴涓瓧娈�,鍒欎笉浼氬崟鐙敓鎴愭瀹炰綋绫汇�傚洜姝�,濡傛灉涓�涓〃鐨勪富閿彧鏈変竴涓瓧娈�,閭d箞涓嶄細涓鸿瀛楁鐢熸垚鍗曠嫭鐨勫疄浣撶被,浼氬皢璇ュ瓧娈靛悎骞跺埌鍩烘湰瀹炰綋绫讳腑銆�
     flat:璇ユā鍨嬩负姣忎竴寮犺〃鍙敓鎴愪竴涓疄浣撶被銆傝繖涓疄浣撶被鍖呭惈琛ㄤ腑鐨勬墍鏈夊瓧娈点��
diff --git a/src/main/resources/mapper/MissionMapper.xml b/src/main/resources/mapper/MissionMapper.xml
index 3a44488..0a10c92 100644
--- a/src/main/resources/mapper/MissionMapper.xml
+++ b/src/main/resources/mapper/MissionMapper.xml
@@ -22,6 +22,7 @@
     <result column="kilometres" jdbcType="REAL" property="kilometres" />
     <result column="region" jdbcType="VARCHAR" property="region" />
     <result column="pollution_degree" jdbcType="VARCHAR" property="pollutionDegree" />
+    <result column="aqi" jdbcType="INTEGER" property="aqi" />
   </resultMap>
   <sql id="Base_Column_List">
     <!--
@@ -29,6 +30,6 @@
     -->
     mission_code, device_type, device_code, start_time, end_time, data_pulled, province_code, 
     province_name, city_code, city_name, district_code, district_name, town_code, town_name, 
-    kilometres, region, pollution_degree
+    kilometres, region, pollution_degree, aqi
   </sql>
 </mapper>
\ No newline at end of file
diff --git a/src/test/kotlin/com/flightfeather/uav/biz/report/MissionSummaryTest.kt b/src/test/kotlin/com/flightfeather/uav/biz/report/MissionSummaryTest.kt
index 398bd1e..3a11591 100644
--- a/src/test/kotlin/com/flightfeather/uav/biz/report/MissionSummaryTest.kt
+++ b/src/test/kotlin/com/flightfeather/uav/biz/report/MissionSummaryTest.kt
@@ -1,7 +1,5 @@
 package com.flightfeather.uav.biz.report
 
-import com.flightfeather.uav.biz.sourcetrace.model.PollutedClue
-import com.flightfeather.uav.domain.entity.Mission
 import com.flightfeather.uav.domain.mapper.MissionMapper
 import com.flightfeather.uav.domain.repository.SourceTraceRep
 import com.flightfeather.uav.lightshare.bean.AreaVo
@@ -11,10 +9,8 @@
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.test.context.junit4.SpringRunner
-import tk.mybatis.mapper.entity.Example
 import java.time.LocalDateTime
 import java.time.ZoneId
-import java.time.ZoneOffset
 import java.util.Date
 
 @RunWith(SpringRunner::class)
@@ -36,7 +32,7 @@
     fun testMissionSummary() {
         val startTime = Date.from(LocalDateTime.of(2025,7,1,0,0,0).atZone(ZoneId.systemDefault()).toInstant())
         val endTime = Date.from(LocalDateTime.of(2025, 9, 30, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant())
-        val summary = dataAnalysisService.missionSummary(startTime, endTime, AreaVo().apply {
+        val summary = dataAnalysisService.generateMissionSummary(startTime, endTime, AreaVo().apply {
             provinceCode = "31"
             cityCode = "3100"
             districtCode = "310106"
diff --git a/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImplTest.kt b/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImplTest.kt
index c9c3b1a..ddb0aeb 100644
--- a/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImplTest.kt
+++ b/src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImplTest.kt
@@ -1,5 +1,6 @@
 package com.flightfeather.uav.lightshare.service.impl
 
+import com.flightfeather.uav.lightshare.bean.AreaVo
 import com.flightfeather.uav.lightshare.service.DataAnalysisService
 import junit.framework.TestCase
 import org.junit.Test
@@ -7,6 +8,9 @@
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.test.context.junit4.SpringRunner
+import java.time.LocalDateTime
+import java.time.ZoneId
+import java.util.*
 
 @RunWith(SpringRunner::class)
 @SpringBootTest
@@ -14,6 +18,14 @@
 
     @Autowired
     lateinit var dataAnalysisService: DataAnalysisService
+
+    val startTime = Date.from(LocalDateTime.of(2025,7,1,0,0,0).atZone(ZoneId.systemDefault()).toInstant())
+    val endTime = Date.from(LocalDateTime.of(2025, 9, 30, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant())
+    val areaVo = AreaVo().apply {
+        provinceCode = "31"
+        cityCode = "3100"
+        districtCode = "310106"
+    }
 
     @Test
     fun testPollutionTrace() {
@@ -24,4 +36,22 @@
     fun fetchHistory() {
         dataAnalysisService.fetchHistory("SH-CN-20250723(01)")
     }
+
+    @Test
+    fun generateMissionList() {
+        val res = dataAnalysisService.generateMissionList(startTime, endTime, areaVo)
+        println(res)
+    }
+
+    @Test
+    fun generateMissionDetail() {
+        val res = dataAnalysisService.generateMissionDetail(startTime, endTime, areaVo)
+        println(res)
+    }
+
+    @Test
+    fun generateClueByRiskArea() {
+        val res = dataAnalysisService.generateClueByRiskArea(startTime, endTime, areaVo)
+        println(res)
+    }
 }
\ No newline at end of file

--
Gitblit v1.9.3