feiyu02
2025-10-21 eb3dd00b0b7fcda477229d518d250f9c842b790b
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/model/PollutedSource.kt
@@ -1,7 +1,9 @@
package com.flightfeather.uav.biz.sourcetrace.model
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionType
import com.flightfeather.uav.common.utils.DateUtil
import com.flightfeather.uav.common.utils.MapUtil
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.domain.entity.SceneInfo
import com.flightfeather.uav.domain.repository.SceneInfoRep
import com.flightfeather.uav.lightshare.bean.AreaVo
@@ -99,13 +101,16 @@
        pollutedData.statisticMap.entries.forEach { s ->
            val res = when (s.key) {
                // 氮氧化合物,一般由于机动车尾气,同步计算CO
                FactorType.NO2 -> {
                    val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
                FactorType.NO,
                FactorType.NO2,
                    -> {
//                    val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
                    val coAvg = round(pollutedData.statisticMap[FactorType.CO]?.avg ?: .0) / 1000
                    "氮氧化合物偏高,CO的量级为${coAvg}mg/m³,一般由于机动车尾气造成,污染源以汽修、加油站为主" to
                            listOf(SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
                }
                FactorType.CO -> null
                FactorType.CO -> "" to listOf(SceneType.TYPE6, SceneType.TYPE10, SceneType.TYPE17)
                FactorType.H2S -> null
@@ -117,8 +122,10 @@
                FactorType.PM25,
                FactorType.PM10,
                    -> {
                    val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
                    val pm10Avg = round(pollutedData.dataList.map { it.pm10!! }.average() * 10) / 10
//                    val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
//                    val pm10Avg = round(pollutedData.dataList.map { it.pm10!! }.average() * 10) / 10
                    val pm25Avg = round((pollutedData.statisticMap[FactorType.PM25]?.avg ?: .0) * 10) / 10
                    val pm10Avg = round((pollutedData.statisticMap[FactorType.PM10]?.avg ?: .0) * 10) / 10
                    // 计算异常数据的pm2.5占pm10比重的均值
                    val percentageAvg = pollutedData.dataList.map {
                        it.pm25!! / it.pm10!!
@@ -132,7 +139,8 @@
                                    SceneType.TYPE2,
                                    SceneType.TYPE3,
                                    SceneType.TYPE14,
                                    SceneType.TYPE5
                                    SceneType.TYPE5,
                                    SceneType.TYPE18
                                )
                    } else if (percentageAvg < 0.333) {
                        "${str},比重较小,属于大颗粒扬尘污染,污染源以工地为主" to
@@ -141,7 +149,8 @@
                                    SceneType.TYPE2,
                                    SceneType.TYPE3,
                                    SceneType.TYPE14,
                                    SceneType.TYPE5
                                    SceneType.TYPE5,
                                    SceneType.TYPE18
                                )
                    } else {
                        "${str},污染源以餐饮、工地为主" to
@@ -150,18 +159,22 @@
                                    SceneType.TYPE2,
                                    SceneType.TYPE3,
                                    SceneType.TYPE14,
                                    SceneType.TYPE5
                                    SceneType.TYPE5,
                                    SceneType.TYPE18
                                )
                    }
                }
                // c) VOC较高,同比计算pm2.5的量级,可能存在同步偏高(汽修、加油站), 同步计算O3是否有高值
                // d) VOC较高,处于加油站(车辆拥堵情况),CO一般较高, 同步计算O3是否有高值
                FactorType.VOC -> {
                    val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
                    val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
                    val o3Avg = round(pollutedData.dataList.map { it.o3!! }.average() * 10) / 10
//                    val pm25Avg = round(pollutedData.dataList.map { it.pm25!! }.average() * 10) / 10
//                    val coAvg = round(pollutedData.dataList.map { it.co!! }.average()) / 1000
//                    val o3Avg = round(pollutedData.dataList.map { it.o3!! }.average() * 10) / 10
                    val pm25Avg = round((pollutedData.statisticMap[FactorType.PM25]?.avg ?: .0)) / 10
                    val coAvg = round((pollutedData.statisticMap[FactorType.CO]?.avg ?: .0)) / 1000
                    val o3Avg = round((pollutedData.statisticMap[FactorType.O3]?.avg ?: .0)) / 10
                    "VOC偏高,同时PM2.5量级为${pm25Avg}μg/m³,CO量级为${coAvg}mg/m³,O3量级为${o3Avg}μg/m³,污染源以汽修、加油站为主" to
                            listOf(SceneType.TYPE6, SceneType.TYPE17, SceneType.TYPE12)
                            listOf(SceneType.TYPE5, SceneType.TYPE6, SceneType.TYPE17, SceneType.TYPE12, SceneType.TYPE18)
                }
                else -> null
@@ -209,16 +222,72 @@
        }
    }
    /**
     * 溯源解析
     * @param pollutedData 污染数据
     * @param sceneList 风险源列表
     * @return 溯源描述
     */
    private fun summaryTxt(pollutedData: PollutedData, sceneList: List<SceneInfoVo>): String {
//        pollutedData.exception
//        pollutedData.selectedFactor?.main
        val st = DateUtil.instance.getTime(pollutedData.startTime)
        val et = DateUtil.instance.getTime(pollutedData.endTime)
        var txt =
            "在${st}至${et}之间,出现${pollutedData.exception}"
        pollutedData.statisticMap.entries.forEach { s ->
            txt += ",${s.key.des}最低值为${s.value.min}μg/m³,最高值为${s.value.max}μg/m³,均值为${s.value.avg}μg/m³"
        // 1. 描述异常发生的时间和异常类型
        var txt = "在${st}至${et}之间,出现${pollutedData.exception}"
        // 2. 描述异常数据的变化情况
        // 异常数据长度应该大于1,首个值是异常开始数据的前一个正常值,后续为异常数据值(但不一定时间连续)
        if (pollutedData.dataList.size > 1) {
            val historyDataList = pollutedData.historyDataList.map { it.toBaseRealTimeData(BaseRealTimeData::class.java) }
            when (pollutedData.exceptionType) {
                // 量级突变
                ExceptionType.TYPE4.value -> {
                    val exceptionPair = mutableListOf<Pair<BaseRealTimeData, BaseRealTimeData>>()
                    pollutedData.dataList.forEachIndexed { index, baseRealTimeData ->
                        if (index == 0) return@forEachIndexed
                        val preIndex = historyDataList.indexOfFirst {
                            it.dataTime == baseRealTimeData.dataTime
                        }
                        exceptionPair.add(
                            (if (preIndex - 1 < 0) historyDataList[0] else historyDataList[preIndex - 1])
                                    to baseRealTimeData
                        )
                    }
                    val statArr = mutableListOf<String>()
                    pollutedData.statisticMap.entries.forEach { s ->
                        val txtArr = mutableListOf<String>()
                        exceptionPair.forEach exception@{ p ->
                            val preValue = p.first.getByFactorType(s.key)
                            val curValue = p.second.getByFactorType(s.key)
                            if (preValue == null || curValue == null) return@exception
                            val r = round((curValue - preValue) / preValue * 100)
                            txtArr.add("从${preValue}μg/m³突变至${curValue}μg/m³,变化率为${r}%")
                        }
                        statArr.add("${s.key.getTxt()}量级${txtArr.joinToString(",")}")
                    }
                    txt += ",${statArr.joinToString(";")}"
                }
                // 快速上升
                ExceptionType.TYPE9.value -> {
                    pollutedData.statisticMap.entries.forEach { s ->
                        val preValue = pollutedData.dataList.first().getByFactorType(s.key)
                        val curValue = pollutedData.dataList.last().getByFactorType(s.key)
                        if (preValue == null || curValue == null) return@forEach
                        val r = round((curValue - preValue) / preValue * 100)
                        txt += ",从${preValue}μg/m³快速上升至${curValue}μg/m³,变化率为${r}%"
                    }
                }
            }
        } else {
            pollutedData.statisticMap.entries.forEach { s ->
                txt += ",${s.key.des}最低值为${s.value.min}μg/m³,最高值为${s.value.max}μg/m³,均值为${s.value.avg}μg/m³"
            }
        }
        // 3. 描述发现的风险源情况
        if (sceneList.isEmpty()) {
            txt += (",可能存在隐藏风险源。")
        } else {