| | |
| | | import com.flightfeather.uav.domain.mapper.MissionMapper |
| | | import com.flightfeather.uav.domain.repository.* |
| | | import com.flightfeather.uav.lightshare.bean.AreaVo |
| | | import com.flightfeather.uav.lightshare.bean.DataHead |
| | | import com.flightfeather.uav.lightshare.bean.GridDataDetailMixVo |
| | | import com.flightfeather.uav.lightshare.bean.SourceTraceMsgVo |
| | | import com.flightfeather.uav.lightshare.eunm.PollutionDegree |
| | | import com.flightfeather.uav.lightshare.eunm.SceneType |
| | | import com.flightfeather.uav.lightshare.service.DataAnalysisService |
| | | import com.flightfeather.uav.lightshare.service.SatelliteDataCalculateService |
| | | import com.flightfeather.uav.socket.eunm.FactorType |
| | | import com.flightfeather.uav.socket.sender.MsgType |
| | | import com.github.pagehelper.PageHelper |
| | | import org.springframework.stereotype.Service |
| | | import tk.mybatis.mapper.entity.Example |
| | | import java.util.* |
| | | |
| | | /** |
| | |
| | | private val sourceTraceRep: SourceTraceRep, |
| | | private val sceneInfoRep: SceneInfoRep, |
| | | private val satelliteGridRep: SatelliteGridRep, |
| | | private val satelliteDataCalculateService: SatelliteDataCalculateService |
| | | private val satelliteDataCalculateService: SatelliteDataCalculateService, |
| | | ) : DataAnalysisService { |
| | | |
| | | /** |
| | |
| | | * 获取历史污染溯源结果 |
| | | * 查询指定任务的历史污染溯源结果并序列化为JSON字符串 |
| | | * @param missionCode 走航任务编码 |
| | | * @param minPer 最小污染百分比,用于筛选异常数据点(可选) |
| | | * @return 历史污染溯源结果的JSON字符串,具体格式由sourceTraceRep实现决定 |
| | | * @throws BizException 当走航任务不存在时抛出 |
| | | */ |
| | | override fun fetchHistory(missionCode: String): String { |
| | | override fun fetchHistory(missionCode: String, minPer: Double?, page: Int?, perPage: Int?): Pair<DataHead, String> { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("走航任务不存在") |
| | | |
| | | val res = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime) |
| | | return GsonUtils.gson.toJson(res) |
| | | val p = if (page != null && perPage != null) { |
| | | PageHelper.startPage<SourceTraceMsgVo>(page, perPage) |
| | | } else { |
| | | null |
| | | } |
| | | val res = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime, minPer = minPer ?: 0.5) |
| | | return DataHead(p?.pageNum ?: 1, p?.pages ?: 1) to GsonUtils.gson.toJson(res) |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param endTime 统计结束时间(包含) |
| | | * @param areaVo 区域参数,包含省、市、区三级行政区划编码 |
| | | * @return 汇总统计对象,包含任务总数、异常点数量、平均里程等核心指标 |
| | | * @see MissionSummary 汇总统计处理器,封装具体的统计逻辑实现 |
| | | * @see MissionSummary 汇总统计处理器,封装具体统计逻辑的实现 |
| | | */ |
| | | override fun generateMissionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary { |
| | | override fun generateMissionSummary( |
| | | startTime: Date, endTime: Date, areaVo: AreaVo, removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, minPer: Double?, |
| | | ): MissionSummary.Summary { |
| | | val clues = mutableListOf<PollutedClue?>() |
| | | val missions = missionRep.findByAreaAndTime(areaVo, startTime, endTime).onEach { |
| | | it ?: return@onEach |
| | | val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?> |
| | | val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue, minPer ?: 0.5) as List<PollutedClue?> |
| | | clues.addAll(clue) |
| | | } |
| | | filterClue(areaVo, clues, removeOtherDistrict, removeNoPollutedSource) |
| | | val summary = MissionSummary().execute(startTime, endTime, missions, clues) |
| | | return summary |
| | | } |
| | | |
| | | override fun generateMissionSummary( |
| | | missionCode: String, minPer: Double?, |
| | | ): MissionSummary.Summary { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("走航任务不存在") |
| | | val clues = sourceTraceRep.fetchList( |
| | | mission.deviceCode, |
| | | mission.startTime, |
| | | mission.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5, |
| | | ) as List<PollutedClue?> |
| | | val summary = MissionSummary().execute(mission.startTime, mission.endTime, listOf(mission), clues) |
| | | return summary |
| | | } |
| | | |
| | |
| | | * @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) |
| | | override fun generateMissionList( |
| | | startTime: Date, endTime: Date, areaVo: AreaVo, removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, minPer: Double?, |
| | | ): List<MissionInventory.MissionInfo> { |
| | | 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 clues = sourceTraceRep.fetchList( |
| | | it.deviceCode, |
| | | it.startTime, |
| | | it.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | filterClue(areaVo, clues.toMutableList(), removeOtherDistrict, removeNoPollutedSource) |
| | | Triple(it, clues, realTimeDataRep.fetchData(it)) |
| | | } |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | |
| | | * @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?, |
| | | removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | minPer: Double?, |
| | | ): 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 clues = sourceTraceRep.fetchList( |
| | | it.deviceCode, |
| | | it.startTime, |
| | | it.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | filterClue(areaVo, clues.toMutableList(), removeOtherDistrict, removeNoPollutedSource) |
| | | Triple(it, clues, realTimeDataRep.fetchData(it)) |
| | | } |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | |
| | | SceneType.TYPE21.value |
| | | ) |
| | | ) |
| | | return generateMissionDetail(keyScenes, missionCluesData) |
| | | return generateMissionDetail(keyScenes, missionCluesData, granularity ?: "MINUTE") |
| | | } |
| | | |
| | | override fun generateMissionDetail( |
| | | missionCode: String, |
| | | granularity: String?, |
| | | minPer: Double?, |
| | | ): MissionInventory.MissionDetail { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("任务不存在") |
| | | val missionClues = sourceTraceRep.fetchList( |
| | | mission.deviceCode, |
| | | mission.startTime, |
| | | mission.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | val realTimeData = realTimeDataRep.fetchData(mission) |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | | SceneType.TYPE19.value, |
| | | SceneType.TYPE20.value, |
| | | SceneType.TYPE21.value |
| | | ) |
| | | ) |
| | | return MissionInventory().generateMissionDetail(keyScenes, mission, missionClues, realTimeData, granularity ?: "MINUTE") |
| | | } |
| | | |
| | | /** |
| | |
| | | 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) |
| | | } |
| | | } |
| | | |
| | |
| | | startTime: Date, |
| | | endTime: Date, |
| | | areaVo: AreaVo, |
| | | ): List<MissionRiskArea.ClueByArea> { |
| | | removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | minPer: Double?, |
| | | ): List<MissionRiskArea.ClassifyClue> { |
| | | 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?> |
| | | val clue = sourceTraceRep.fetchList( |
| | | it.deviceCode, |
| | | it.startTime, |
| | | it.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | clues.addAll(clue) |
| | | } |
| | | // if (removeOtherDistrict) { |
| | | // clues.removeIf { |
| | | // !areaVo.districtName.isNullOrBlank() && |
| | | // (it?.pollutedArea?.address.isNullOrBlank() |
| | | // || !it!!.pollutedArea!!.address!!.contains(areaVo.districtName!!)) |
| | | // } |
| | | // } |
| | | // if (removeNoPollutedSource) { |
| | | // clues.removeIf { it?.pollutedSource?.sceneList.isNullOrEmpty() } |
| | | // } |
| | | filterClue(areaVo, clues, removeOtherDistrict, removeNoPollutedSource) |
| | | return MissionRiskArea().generateClueByRiskArea(clues) |
| | | } |
| | | |
| | | override fun generateClueByRiskArea(missionCode: String, minPer: Double?): List<MissionRiskArea.ClueByArea> { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("任务不存在") |
| | | val pollutedClues = sourceTraceRep.fetchList( |
| | | mission.deviceCode, |
| | | mission.startTime, |
| | | mission.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | | SceneType.TYPE19.value, |
| | |
| | | SceneType.TYPE21.value |
| | | ) |
| | | ) |
| | | return generateClueByRiskArea(keyScenes, clues) |
| | | return generateClueByRiskArea(keyScenes, pollutedClues) |
| | | } |
| | | |
| | | override fun generateClueByRiskArea( |
| | | keyScenes: List<SceneInfo?>, |
| | | pollutedClues: List<PollutedClue?>, |
| | | ): List<MissionRiskArea.ClueByArea> { |
| | | return MissionRiskArea().generateClueByRiskArea(keyScenes, pollutedClues) |
| | | return MissionRiskArea().generateClueByKeyRiskScene(keyScenes, pollutedClues) |
| | | } |
| | | |
| | | override fun generateGridFusion( |
| | |
| | | startTime: Date, |
| | | endTime: Date, |
| | | areaVo: AreaVo, |
| | | removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | minPer: Double?, |
| | | ): List<MissionGridFusion.GridFusionByAQI> { |
| | | val gridLen = 100 |
| | | // 查询100米网格的具体网格数据 |
| | |
| | | val gridDataDetailList = missionGroups.mapNotNull { (degree, missionList) -> |
| | | // 筛选出有网格融合数据的走航任务(同时获取对应的融合数据id列表) |
| | | val gridDataIds = mutableListOf<Int>() |
| | | val validMissions = missionList.filter {mission -> |
| | | val gridData = satelliteGridRep.fetchGridData(GridData().apply { missionCode = mission?.missionCode }).firstOrNull() |
| | | val validMissions = missionList.filter { mission -> |
| | | val gridData = |
| | | satelliteGridRep.fetchGridData(GridData().apply { missionCode = mission?.missionCode }).firstOrNull() |
| | | val res = gridData != null |
| | | if (res) gridDataIds.add(gridData?.id ?: 0) |
| | | res |
| | |
| | | val gridDataDetailMixVos = satelliteDataCalculateService.mixUnderwayGridData(gridGroup.id, gridDataIds) |
| | | // 统计每个走航任务的走航详情信息 |
| | | val missionCluesData = validMissions.filterNotNull().map { |
| | | Triple( |
| | | it, |
| | | sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime, MsgType.PolClue) as List<PollutedClue?>, |
| | | realTimeDataRep.fetchData(it) |
| | | ) |
| | | val clues = sourceTraceRep.fetchList( |
| | | it.deviceCode, |
| | | it.startTime, |
| | | it.endTime, |
| | | MsgType.PolClue, |
| | | minPer ?: 0.5 |
| | | ) as List<PollutedClue?> |
| | | filterClue(areaVo, clues.toMutableList(), removeOtherDistrict, removeNoPollutedSource) |
| | | Triple(it, clues, realTimeDataRep.fetchData(it)) |
| | | } |
| | | val keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | |
| | | ): List<MissionGridFusion.GridFusionByAQI> { |
| | | return MissionGridFusion(sceneInfoRep).generateGridFusion(factorTypes, gridLen, gridCells, dataList) |
| | | } |
| | | |
| | | private fun filterClue( |
| | | areaVo: AreaVo, clues: MutableList<PollutedClue?>, removeOtherDistrict: Boolean, |
| | | removeNoPollutedSource: Boolean, |
| | | ) { |
| | | if (removeOtherDistrict) { |
| | | clues.removeIf { |
| | | !areaVo.districtName.isNullOrBlank() && |
| | | (it?.pollutedArea?.address.isNullOrBlank() |
| | | || !it!!.pollutedArea!!.address!!.contains(areaVo.districtName!!)) |
| | | } |
| | | clues.forEach { |
| | | it?.pollutedSource?.sceneList = it?.pollutedSource?.sceneList?.filter { s-> |
| | | s.districtCode == areaVo.districtCode |
| | | } |
| | | } |
| | | } |
| | | if (removeNoPollutedSource) { |
| | | clues.removeIf { it?.pollutedSource?.sceneList.isNullOrEmpty() } |
| | | } |
| | | |
| | | } |
| | | } |