feiyu02
2025-09-04 707b00a0ca6604c249a110b376ac1e44e408e624
src/main/kotlin/com/flightfeather/uav/biz/report/MissionSummary.kt
@@ -1,16 +1,29 @@
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.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
/**
 * 走航任务汇总
 * @date 2025/8/22
 * @author feiyu02
 */
@Component
class MissionSummary {
class MissionSummary() {
    inner class Summary(
    data class Summary(
        // 汇总周期开始时间
        val startTime: Date,
        // 汇总周期结束时间
        val endTime: Date,
        // 走航区域信息
        val area: AreaVo,
        // 走航次数
        val count: Int,
        // 总里程数(公里)
@@ -27,7 +40,101 @@
        val probByFactor:List<Triple<String, Int, Double>>
    )
    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()
            )
        }
        // 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. 构建并返回统计结果
        return Summary(
            startTime = startTime,
            endTime = endTime,
            area = area,
            count = totalCount,
            kilometres = totalKilometres,
            regionList = regionList,
            countByDegree = countByDegree,
            probCount = probCount,
            highRiskSceneCount = highRiskSceneCount,
            probByFactor = probByFactor
        )
    }
    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)
    }
}