From 538ba7a3bbc682f4537f1dd34f93feb2cf56b08e Mon Sep 17 00:00:00 2001 From: feiyu02 <risaku@163.com> Date: 星期二, 14 十月 2025 17:32:04 +0800 Subject: [PATCH] 2025.10.14 1. 新增数据统计颗粒度选项,可选秒级数据、分钟数据进行数据统计 2. 典型隐患区域统计新增按照污染溯源区域进行分类统计的功能 --- src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImplTest.kt | 2 src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt | 12 +++- src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt | 36 +++++++----- src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt | 52 +++++++++++++++++ src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt | 29 ++++++++- src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt | 8 ++ src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt | 3 src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt | 25 ++++++-- 8 files changed, 134 insertions(+), 33 deletions(-) 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 7f0fb2e..302871e 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt @@ -182,6 +182,7 @@ mission: Mission, pollutedClues: List<PollutedClue?>, data: List<BaseRealTimeData>, + granularity: String, minDis: Double = 100.0, ): MissionDetail { // 鍒涘缓浠诲姟璇︽儏瀵硅薄骞跺鍒跺熀鏈俊鎭� @@ -222,7 +223,7 @@ // missionDetail.keyScene = relatedScenes // 璁$畻鐜鍥犲瓙缁熻鏁版嵁锛堝钩鍧囧�笺�佹渶灏忓�笺�佹渶澶у�硷級 - missionDetail.dataStatistics = data.calDataStatistics() + missionDetail.dataStatistics = data.calDataStatistics(granularity) // 寮傚父鏁版嵁鐐规暟閲忕粺璁� // val clues = pollutedClues.filter { it?.msgType == MsgType.PolClue.value } diff --git a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt index 38eef5b..570e3b8 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionRiskArea.kt @@ -15,6 +15,7 @@ class ClueByArea { var sceneInfo: SceneInfo? = null + var address: String? = null var clueByFactorList: MutableList<ClueByFactor>? = null } @@ -23,7 +24,13 @@ var clues: MutableList<PollutedClue>? = null } - fun generateClueByRiskArea(keyScenes: List<SceneInfo?>, pollutedClues: List<PollutedClue?>): List<ClueByArea> { + /** + * 鐢熸垚璧拌埅鍏稿瀷闅愭偅鍖哄煙锛屾牴鎹叧閿満鏅垪琛ㄨ繘琛屽垎缁� + * @param keyScenes 鍏抽敭鍦烘櫙鍒楄〃 + * @param pollutedClues 姹℃煋绾跨储鍒楄〃 + * @return 鎸夊尯鍩熷拰鍥犲瓙鍒嗙粍鐨勬薄鏌撶嚎绱� + */ + fun generateClueByKeyRiskScene(keyScenes: List<SceneInfo?>, pollutedClues: List<PollutedClue?>): List<ClueByArea> { val result = mutableListOf<ClueByArea>() pollutedClues.forEach { pollutedClue -> @@ -83,4 +90,47 @@ return result } + + /** + * 鐢熸垚璧拌埅鍏稿瀷闅愭偅鍖哄煙锛屾牴鎹薄鏌撶嚎绱㈡函婧愬湴鍧�杩涜鍒嗙粍 + * @param pollutedClues 姹℃煋绾跨储鍒楄〃 + * @return 鎸夊尯鍩熷拰鍥犲瓙鍒嗙粍鐨勬薄鏌撶嚎绱� + */ + fun generateClueByRiskArea(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.pollutedArea?.address?.let { address -> + var clueByArea = result.find { it.address == address } + if (clueByArea == null) { + clueByArea = ClueByArea().apply { + this.address = address + 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/biz/report/MissionSummary.kt b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt index ff1ab18..28101c9 100644 --- a/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt +++ b/src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt @@ -106,7 +106,7 @@ val probByFactor = clueRes.third // 7. 浠庡紓甯告墍鍦ㄥ湴鍖哄拰婧簮鐨勫満鏅腑缁熻鑱氱劍鍖哄煙 - val focusRegion = calFocusRegion(clues) + val focusRegion = calFocusRegion(clues, area) // 8. 鏋勫缓骞惰繑鍥炵粺璁$粨鏋� return Summary( @@ -141,17 +141,17 @@ } } val probByFactor = probByFactorMap.entries.map { - val per = if(probCount == 0) .0 else round(it.value.toDouble() / probCount * 100) / 100 + 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> { + private fun calFocusRegion(clues: List<PollutedClue?>, area: AreaVo): List<String> { // 缁熻姣忎釜鍖哄煙鎴栧満鏅嚭鐜扮殑娆℃暟 val focusArea = mutableMapOf<String, Int>() val focusScene = mutableMapOf<String, Int>() - clues.forEach { c-> + clues.forEach { c -> if (c?.msgType == MsgType.PolClue.value) { if (!c.pollutedArea?.address.isNullOrBlank()) { if (focusArea.containsKey(c.pollutedArea?.address)) { @@ -160,7 +160,7 @@ focusArea[c.pollutedArea?.address!!] = 1 } } - c.pollutedSource?.sceneList?.forEach { s-> + c.pollutedSource?.sceneList?.forEach { s -> if (s.name != null) { if (focusScene.containsKey(s.name!!)) { focusScene[s.name!!] = focusScene[s.name!!]!! + 1 @@ -171,7 +171,18 @@ } } } - 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) + return focusArea.entries.sortedByDescending { it.value } + .filter { + // 浠呯粺璁″寘鍚尯鍘垮悕绉扮殑鍖哄煙 + area.districtName ?: return@filter true + return@filter it.key.contains(area.districtName!!) + } + .map { it.key }.take(FOCUS_AREA_COUNT) + + focusScene.entries.sortedByDescending { it.value } + .filter { + // 浠呯粺璁″寘鍚尯鍘垮悕绉扮殑鍖哄煙 + area.districtName ?: return@filter true + return@filter it.key.contains(area.districtName!!) + }.map { it.key }.take(FOCUS_AREA_COUNT) } } \ 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 53989b5..1066c78 100644 --- a/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt +++ b/src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt @@ -10,6 +10,7 @@ import java.math.BigDecimal import java.time.LocalDateTime import java.time.ZoneId +import java.time.temporal.ChronoUnit import java.util.* import javax.persistence.Column import javax.persistence.GeneratedValue @@ -332,11 +333,33 @@ /** * 璁$畻瀹炴椂鐩戞祴鏁版嵁鍒楄〃鐨勭粺璁′俊鎭� * 涓烘瘡绉嶇幆澧冨洜瀛愯绠楁渶灏忓�笺�佹渶澶у�煎拰骞冲潎鍊� - * + * @param granularity 鏁版嵁棰楃矑搴︼紝鍙�夊�间负SECOND, MINUTE, HOUR, 榛樿MINUTE * @return 鍖呭惈鍚勭幆澧冨洜瀛愮粺璁′俊鎭殑FactorStatistics鍒楄〃 * 姣忎釜FactorStatistics瀵硅薄鍖呭惈鍥犲瓙绫诲瀷銆佹渶灏忓�笺�佹渶澶у�煎拰骞冲潎鍊� */ -fun List<BaseRealTimeData>.calDataStatistics(): List<FactorStatistics> { +fun List<BaseRealTimeData>.calDataStatistics(granularity: String): List<FactorStatistics> { + + // 妫�鏌ラ绮掑害鏄惁鏈夋晥 + if (granularity !in listOf("SECOND", "MINUTE", "HOUR")) { + throw IllegalArgumentException("鏃犳晥鐨勯绮掑害鍙傛暟锛屽彲閫夊�间负SECOND, MINUTE, HOUR") + } + + val groupedData = when (granularity) { + "SECOND" -> this + "MINUTE" -> groupBy { it.dataTime?.toInstant()?.truncatedTo(ChronoUnit.MINUTES) }.mapValues { + it.value.avg().apply { + dataTime = Date.from(it.key) + createTime = dataTime + } + }.values.toList() + "HOUR" -> groupBy { it.dataTime?.toInstant()?.truncatedTo(ChronoUnit.HOURS) }.mapValues { + it.value.avg().apply { + dataTime = Date.from(it.key) + createTime = dataTime + } + }.values.toList() + else -> throw IllegalArgumentException("鏃犳晥鐨勯绮掑害鍙傛暟锛屽彲閫夊�间负SECOND, MINUTE, HOUR") + } // 鍒濆鍖栧悇鐜鍥犲瓙鐨勭粺璁″璞″垪琛� val statistics = mutableListOf<FactorStatistics>() @@ -357,7 +380,7 @@ ).forEach { statistics.add(FactorStatistics(it)) } // 璁$畻骞冲潎鍊煎苟鍚屾椂鏇存柊鍚勫洜瀛愮殑鏈�灏忓�煎拰鏈�澶у�� - val avgData = avg { item -> + val avgData = groupedData.avg { item -> // 鏇存柊姣忎釜鍥犲瓙鐨勬渶灏忓拰鏈�澶у�� statistics[0].updateMinAndMaxValue(item.no2) statistics[1].updateMinAndMaxValue(item.co) 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 2c5081e..a2d5daa 100644 --- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt +++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt @@ -85,24 +85,30 @@ * @param areaVo 鍖哄煙鍙傛暟 * @return 浠诲姟璇︽儏鍒楄〃锛屾瘡涓厓绱犲寘鍚换鍔″畬鏁翠俊鎭�佸満鏅暟鎹拰缁熻缁撴灉 */ - fun generateMissionDetail(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionDetail> + fun generateMissionDetail(startTime: Date, endTime: Date, areaVo: AreaVo,granularity: String?): List<MissionDetail> /** * 鐢熸垚璧拌埅浠诲姟璇︽儏锛堟寜浠诲姟缂栧彿绛涢�夛級 * 鏍规嵁浠诲姟缂栧彿鏌ヨ骞剁敓鎴愯缁嗙殑浠诲姟鎶ュ憡锛屽寘鍚换鍔″畬鏁翠俊鎭�佸満鏅暟鎹拰缁熻缁撴灉 * @param missionCode 浠诲姟缂栧彿锛岀敤浜庡敮涓�鏍囪瘑鐗瑰畾鐨勮蛋鑸换鍔� + * @param granularity 鏁版嵁棰楃矑搴︼紝鍙�夊�间负SECOND, MINUTE, HOUR, 榛樿MINUTE * @return 浠诲姟璇︽儏瀵硅薄锛屽寘鍚换鍔″畬鏁翠俊鎭�佸満鏅暟鎹拰缁熻缁撴灉 */ - fun generateMissionDetail(missionCode: String): MissionDetail + fun generateMissionDetail(missionCode: String, granularity: String?): MissionDetail /** * 鑾峰彇璧拌埅浠诲姟璇︽儏锛堢洿鎺ュ鐞嗕换鍔℃暟鎹級 * 澶勭悊宸叉湁鐨勪换鍔°�佹薄鏌撶嚎绱㈠拰瀹炴椂鏁版嵁锛岀敓鎴愯缁嗕换鍔℃姤鍛� * @param keyScenes 鍏抽敭鍦烘櫙鍒楄〃锛岀敤浜庡垎鏋愯蛋鑸槸鍚︾粡杩囪鍖哄煙 * @param missionCluesData 鍖呭惈浠诲姟銆佹薄鏌撶嚎绱㈠拰瀹炴椂鏁版嵁鐨凾riple鍒楄〃 + * @param granularity 鏁版嵁棰楃矑搴︼紝鍙�夊�间负SECOND, MINUTE, HOUR, 榛樿MINUTE * @return 浠诲姟璇︽儏鍒楄〃 */ - fun generateMissionDetail(keyScenes: List<SceneInfo?>, missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>): List<MissionDetail> + fun generateMissionDetail( + keyScenes: List<SceneInfo?>, + missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>, + granularity: String, + ): List<MissionDetail> fun generateClueByRiskArea(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionRiskArea.ClueByArea> 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 abdba09..52efc03 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 @@ -23,7 +23,6 @@ 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.util.* /** @@ -173,11 +172,17 @@ * @param startTime 鏌ヨ璧峰鏃堕棿锛堝寘鍚級 * @param endTime 鏌ヨ缁撴潫鏃堕棿锛堝寘鍚級 * @param areaVo 鍖哄煙鍙傛暟锛屽寘鍚渷銆佸競銆佸尯缂栫爜 + * @param granularity 鏁版嵁棰楃矑搴︼紝鍙�夊�间负SECOND, MINUTE, HOUR, 榛樿MINUTE * @return 浠诲姟璇︽儏鍒楄〃锛屾瘡涓厓绱犲寘鍚换鍔″畬鏁翠俊鎭�佹薄鏌撶嚎绱㈠強瀹炴椂鐩戞祴鏁版嵁 * @see MissionRep.findByAreaAndTime 鍖哄煙鏃堕棿绛涢�夋暟鎹簮 * @see realTimeDataRep.fetchData 瀹炴椂鏁版嵁鑾峰彇鎺ュ彛 */ - override fun generateMissionDetail(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionInventory.MissionDetail> { + override fun generateMissionDetail( + startTime: Date, + endTime: Date, + areaVo: AreaVo, + granularity: String?, + ): List<MissionInventory.MissionDetail> { val missionCluesData = missionRep.findByAreaAndTime(areaVo, startTime, endTime).filterNotNull().map { Triple( it, @@ -192,10 +197,10 @@ SceneType.TYPE21.value ) ) - return generateMissionDetail(keyScenes, missionCluesData) + return generateMissionDetail(keyScenes, missionCluesData, granularity ?: "MINUTE") } - override fun generateMissionDetail(missionCode: String): MissionInventory.MissionDetail { + override fun generateMissionDetail(missionCode: String, granularity: String?): MissionInventory.MissionDetail { val mission = missionRep.findOne(missionCode) ?: throw BizException("浠诲姟涓嶅瓨鍦�") val missionClues = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime, MsgType.PolClue) as List<PollutedClue?> val realTimeData = realTimeDataRep.fetchData(mission) @@ -206,7 +211,7 @@ SceneType.TYPE21.value ) ) - return MissionInventory().generateMissionDetail(keyScenes, mission, missionClues, realTimeData) + return MissionInventory().generateMissionDetail(keyScenes, mission, missionClues, realTimeData, granularity ?: "MINUTE") } /** @@ -220,9 +225,10 @@ override fun generateMissionDetail( keyScenes: List<SceneInfo?>, missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>, + granularity: String, ): List<MissionInventory.MissionDetail> { return missionCluesData.map { - MissionInventory().generateMissionDetail(keyScenes, it.first, it.second, it.third) + MissionInventory().generateMissionDetail(keyScenes, it.first, it.second, it.third, granularity) } } @@ -237,14 +243,14 @@ 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) +// val keyScenes = sceneInfoRep.findBySceneTypes( +// listOf( +// SceneType.TYPE19.value, +// SceneType.TYPE20.value, +// SceneType.TYPE21.value +// ) +// ) + return MissionRiskArea().generateClueByRiskArea(clues) } override fun generateClueByRiskArea(missionCode: String): List<MissionRiskArea.ClueByArea> { @@ -264,7 +270,7 @@ keyScenes: List<SceneInfo?>, pollutedClues: List<PollutedClue?>, ): List<MissionRiskArea.ClueByArea> { - return MissionRiskArea().generateClueByRiskArea(keyScenes, pollutedClues) + return MissionRiskArea().generateClueByKeyRiskScene(keyScenes, pollutedClues) } override fun generateGridFusion( 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 5bd3414..c6c2a83 100644 --- a/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt +++ b/src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt @@ -94,12 +94,14 @@ @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") endTime: LocalDateTime, + @ApiParam("鏁版嵁棰楃矑搴�", allowableValues = "SECOND, MINUTE, HOUR") @RequestParam(required = false) + granularity: String?, @ApiParam("鍖哄煙") @RequestBody areaVo: AreaVo, ) = resPack { dataAnalysisService.generateMissionDetail( Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant()), Date.from(endTime.atZone(ZoneId.systemDefault()).toInstant()), - areaVo + areaVo,granularity ) } @@ -107,7 +109,9 @@ @GetMapping("/report/missionDetail/one") fun generateOneMissionDetail( @ApiParam("浠诲姟缂栧彿") @RequestParam missionCode: String, - ) = resPack { dataAnalysisService.generateMissionDetail(missionCode) } + @ApiParam("鏁版嵁棰楃矑搴�", allowableValues = "SECOND, MINUTE, HOUR") @RequestParam(required = false) + granularity: String?, + ) = resPack { dataAnalysisService.generateMissionDetail(missionCode, granularity) } @ApiOperation(value = "璧拌埅鍏稿瀷闅愭偅鍖哄煙缁熻") @PostMapping("/report/clueByRiskArea") 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 ddb0aeb..e8ccfd2 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 @@ -45,7 +45,7 @@ @Test fun generateMissionDetail() { - val res = dataAnalysisService.generateMissionDetail(startTime, endTime, areaVo) + val res = dataAnalysisService.generateMissionDetail(startTime, endTime, areaVo, "MINUTE") println(res) } -- Gitblit v1.9.3