| | |
| | | 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.sourcetrace.model.BasePollutedMsg |
| | | import com.flightfeather.uav.biz.report.MissionGridFusion |
| | | 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.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.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.entity.* |
| | | 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.GridDataDetailMixVo |
| | | 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 org.springframework.stereotype.Service |
| | | import tk.mybatis.mapper.entity.Example |
| | | import java.util.* |
| | | |
| | | /** |
| | | * |
| | | * 数据分析服务接口实现类 |
| | | * 提供走航任务数据的统计分析、污染溯源、任务清单及详情生成等核心业务功能 |
| | | * 整合多数据源完成数据聚合与分析,为前端提供标准化的统计结果 |
| | | * @date 2025/5/8 |
| | | * @author feiyu02 |
| | | */ |
| | | @Service |
| | | class DataAnalysisServiceImpl( |
| | | private val missionRep: MissionRep, |
| | | private val missionMapper: MissionMapper, |
| | | private val realTimeDataRep: RealTimeDataRep, |
| | | private val locationRoadNearby: LocationRoadNearby, |
| | | private val segmentInfoRep: SegmentInfoRep, |
| | | private val sourceTraceRep: SourceTraceRep |
| | | private val sourceTraceRep: SourceTraceRep, |
| | | private val sceneInfoRep: SceneInfoRep, |
| | | private val satelliteGridRep: SatelliteGridRep, |
| | | private val satelliteDataCalculateService: SatelliteDataCalculateService |
| | | ) : DataAnalysisService { |
| | | |
| | | /** |
| | | * 污染溯源分析 |
| | | * 对指定走航任务进行多因子污染数据分析,识别异常数据点和潜在污染源 |
| | | * @param missionCode 走航任务编码(主键) |
| | | * @return 异常结果列表,包含异常类型、位置、浓度值等详细信息 |
| | | * @throws BizException 当走航任务不存在时抛出 |
| | | * @see ExceptionAnalysisController 异常分析控制器,处理具体的数据分析逻辑 |
| | | */ |
| | | override fun pollutionTrace(missionCode: String): List<ExceptionResult> { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("走航任务不存在") |
| | | |
| | |
| | | ) |
| | | } |
| | | |
| | | /** |
| | | * 获取历史污染溯源结果 |
| | | * 查询指定任务的历史污染溯源结果并序列化为JSON字符串 |
| | | * @param missionCode 走航任务编码 |
| | | * @return 历史污染溯源结果的JSON字符串,具体格式由sourceTraceRep实现决定 |
| | | * @throws BizException 当走航任务不存在时抛出 |
| | | */ |
| | | override fun fetchHistory(missionCode: String): String { |
| | | val mission = missionRep.findOne(missionCode) ?: throw BizException("走航任务不存在") |
| | | |
| | | val res = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime) |
| | | return GsonUtils.gson.toJson(res) |
| | | } |
| | | |
| | | /** |
| | | * 生成走航任务汇总统计 |
| | | * 按时间范围和行政区划统计走航任务的关键指标(任务数量、异常率、平均里程等) |
| | | * @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 = 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 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) |
| | | 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 generateMissionInfo(keyScenes, missionCluesData) |
| | | } |
| | | |
| | | /** |
| | | * 生成走航任务清单(直接处理任务数据) |
| | | * 接收已关联的任务-污染线索数据对,生成格式化的任务列表 |
| | | * @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) |
| | | } |
| | | |
| | | override fun generateMissionInfo( |
| | | keyScenes: List<SceneInfo?>, |
| | | missionCluesData: List<Triple<Mission, List<PollutedClue?>, List<BaseRealTimeData>>>, |
| | | ): List<MissionInventory.MissionInfo> { |
| | | return missionCluesData.map { |
| | | MissionInventory().generateMissionInfo(keyScenes, it.first, it.second, it.third) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成走航任务详情(按时间和区域筛选) |
| | | * 根据时间范围和行政区划查询任务,整合实时监测数据生成详细任务报告 |
| | | * @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) |
| | | } |
| | | |
| | | override fun generateGridFusion( |
| | | factorTypes: List<FactorType>, |
| | | startTime: Date, |
| | | endTime: Date, |
| | | areaVo: AreaVo, |
| | | ): List<MissionGridFusion.GridFusionByAQI> { |
| | | val gridLen = 100 |
| | | // 查询100米网格的具体网格数据 |
| | | val gridGroup = satelliteGridRep.fetchGridGroup(GridGroup().apply { |
| | | type = "sub" |
| | | length = gridLen.toDouble() |
| | | provinceCode = areaVo.provinceCode |
| | | cityCode = areaVo.cityCode |
| | | districtCode = areaVo.districtCode |
| | | }).firstOrNull() ?: throw BizException("未查询到100米网格") |
| | | val gridCells = satelliteGridRep.fetchGridCell(gridGroup.id).filterNotNull() |
| | | // 查询范围内的所有走航任务 |
| | | val missions = missionRep.findByAreaAndTime(areaVo, startTime, endTime) |
| | | // 根据空气质量等级分类 |
| | | val missionGroups = missions.groupBy { PollutionDegree.getByDes(it?.pollutionDegree ?: "") } |
| | | // 查询每个等级下的走航任务对应的网格数据(如果没有数据则剔除该任务) |
| | | 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 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 keyScenes = sceneInfoRep.findBySceneTypes( |
| | | listOf( |
| | | SceneType.TYPE19.value, |
| | | SceneType.TYPE20.value, |
| | | SceneType.TYPE21.value |
| | | ) |
| | | ) |
| | | val missionInfos = generateMissionInfo(keyScenes, missionCluesData) |
| | | |
| | | return@mapNotNull Triple(degree, missionInfos, gridDataDetailMixVos) |
| | | }.filter { it.second.isNotEmpty() } |
| | | |
| | | return generateGridFusion(factorTypes, gridLen, gridCells, gridDataDetailList) |
| | | } |
| | | |
| | | override fun generateGridFusion( |
| | | factorTypes: List<FactorType>, |
| | | gridLen: Int, |
| | | gridCells: List<GridCell>, |
| | | dataList: List<Triple<PollutionDegree, List<MissionInventory.MissionInfo>, List<GridDataDetailMixVo>>>, |
| | | ): List<MissionGridFusion.GridFusionByAQI> { |
| | | return MissionGridFusion(sceneInfoRep).generateGridFusion(factorTypes, gridLen, gridCells, dataList) |
| | | } |
| | | } |