feiyu02
2025-09-30 94fee0b511279679b43e210878d3d36e5a14384b
2025.9.30
1. 新增走航任务统计功能
已修改12个文件
273 ■■■■ 文件已修改
src/main/kotlin/com/flightfeather/uav/biz/mission/MissionUtil.kt 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/entity/Mission.java 79 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/repository/RealTimeDataRep.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/repository/RealTimeDataRepDelegate.kt 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/generator/generatorConfig.xml 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/MissionMapper.xml 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/biz/mission/MissionUtilTest.kt 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/mission/MissionUtil.kt
@@ -56,4 +56,37 @@
        // 返回乡镇和街道名称组合
        return address.township
    }
    /**
     * 数据清洗
     * 1. 修复由于硬件设备卡顿导致的数据采样时间不变问题,采用自动累加数据周期的方式修改采样时间
     * @param data 原始数据列表
     * @param period 数据周期,单位:秒
     * @return 清洗后需要修改的数据列表
     */
    fun dataClean(dataList: List<BaseRealTimeData>, period: Long): List<BaseRealTimeData> {
        val cleanedData = mutableListOf<BaseRealTimeData>()
        var errorData: BaseRealTimeData? = null
        dataList.forEachIndexed { index, data ->
            if (index == 0) {
                return@forEachIndexed
            }
            val lastOne = dataList[index - 1]
            if (errorData == null) {
                if (data.dataTime!!.time == lastOne.dataTime!!.time) {
                    data.dataTime?.time = lastOne.dataTime?.time!!.plus(period * 1000)
                    cleanedData.add(data)
                    errorData = lastOne
                }
            } else {
                if (data.dataTime!!.time == errorData!!.dataTime!!.time) {
                    data.dataTime?.time = lastOne.dataTime?.time!!.plus(period * 1000)
                    cleanedData.add(data)
                } else {
                    errorData = null
                }
            }
        }
        return cleanedData
    }
}
src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt
@@ -19,7 +19,7 @@
    // 走航清单信息
    class MissionInfo : Mission() {
        // 首要污染物
        var mainFactor: String? = null
//        var mainFactor: String? = null
        // 监测异常因子
        var abnormalFactors: List<FactorType>? = null
@@ -74,7 +74,7 @@
            val missionInfo = MissionInfo()
            BeanUtils.copyProperties(mission, missionInfo)
            missionInfo.apply {
                mainFactor = factorMap.maxByOrNull { it.value }?.key?.name
//                mainFactor = factorMap.maxByOrNull { it.value }?.key?.name
                this.abnormalFactors = abnormalFactors
                this.sceneCount = sceneCount
            }
@@ -152,7 +152,7 @@
        val missionInfo = MissionInfo()
        BeanUtils.copyProperties(mission, missionInfo)
        missionInfo.apply {
            mainFactor = factorMap.maxByOrNull { it.value }?.key?.name
//            mainFactor = factorMap.maxByOrNull { it.value }?.key?.name
            this.abnormalFactors = abnormalFactors
            this.sceneCount = sceneCount
            this.scenes = scenes
src/main/kotlin/com/flightfeather/uav/domain/entity/Mission.java
@@ -20,6 +20,9 @@
    @Column(name = "end_time")
    private Date endTime;
    /**
     * 数据是否已经拉取入库
     */
    @Column(name = "data_pulled")
    private Boolean dataPulled;
@@ -53,9 +56,14 @@
    private Float kilometres;
    /**
     * 所属区域
     * 走航围绕主要区域
     */
    private String region;
    /**
     * 走航围绕中心区域半径,公里
     */
    private Float radius;
    /**
     * 空气质量等级
@@ -63,7 +71,16 @@
    @Column(name = "pollution_degree")
    private String pollutionDegree;
    /**
     * 空气质量等级指数AQI
     */
    private Integer aqi;
    /**
     * 首要污染因子
     */
    @Column(name = "main_factor")
    private String mainFactor;
    /**
     * @return mission_code
@@ -136,14 +153,18 @@
    }
    /**
     * @return data_pulled
     * 获取数据是否已经拉取入库
     *
     * @return data_pulled - 数据是否已经拉取入库
     */
    public Boolean getDataPulled() {
        return dataPulled;
    }
    /**
     * @param dataPulled
     * 设置数据是否已经拉取入库
     *
     * @param dataPulled 数据是否已经拉取入库
     */
    public void setDataPulled(Boolean dataPulled) {
        this.dataPulled = dataPulled;
@@ -280,21 +301,39 @@
    }
    /**
     * 获取所属区域
     * 获取走航围绕主要区域
     *
     * @return region - 所属区域
     * @return region - 走航围绕主要区域
     */
    public String getRegion() {
        return region;
    }
    /**
     * 设置所属区域
     * 设置走航围绕主要区域
     *
     * @param region 所属区域
     * @param region 走航围绕主要区域
     */
    public void setRegion(String region) {
        this.region = region == null ? null : region.trim();
    }
    /**
     * 获取走航围绕中心区域半径,公里
     *
     * @return radius - 走航围绕中心区域半径,公里
     */
    public Float getRadius() {
        return radius;
    }
    /**
     * 设置走航围绕中心区域半径,公里
     *
     * @param radius 走航围绕中心区域半径,公里
     */
    public void setRadius(Float radius) {
        this.radius = radius;
    }
    /**
@@ -316,16 +355,38 @@
    }
    /**
     * @return aqi
     * 获取空气质量等级指数AQI
     *
     * @return aqi - 空气质量等级指数AQI
     */
    public Integer getAqi() {
        return aqi;
    }
    /**
     * @param aqi
     * 设置空气质量等级指数AQI
     *
     * @param aqi 空气质量等级指数AQI
     */
    public void setAqi(Integer aqi) {
        this.aqi = aqi;
    }
    /**
     * 获取首要污染因子
     *
     * @return main_factor - 首要污染因子
     */
    public String getMainFactor() {
        return mainFactor;
    }
    /**
     * 设置首要污染因子
     *
     * @param mainFactor 首要污染因子
     */
    public void setMainFactor(String mainFactor) {
        this.mainFactor = mainFactor == null ? null : mainFactor.trim();
    }
}
src/main/kotlin/com/flightfeather/uav/domain/repository/RealTimeDataRep.kt
@@ -58,6 +58,10 @@
        return delegate.insertByDeviceType(deviceType, type, data)
    }
    fun updateData(deviceType: UWDeviceType?, data: List<BaseRealTimeData>, type: Int? = 0): Int {
        return delegate.updateByDeviceType(deviceType, type, data)
    }
    fun deleteData(mission: Mission, type: Int? = 0): Int {
        if (mission.deviceCode == null || mission.startTime == null || mission.endTime == null) {
            throw BizException("要删除的走航任务缺失设备编号或采样时间范围,无法删除对应监测数据")
src/main/kotlin/com/flightfeather/uav/domain/repository/RealTimeDataRepDelegate.kt
@@ -89,6 +89,32 @@
    }
    /**
     * 统一的update方法
     * @param deviceType 设备类型
     * @param type 数据统计周期,0:秒级值;1:分钟值
     * @param data 数据
     */
    fun updateByDeviceType(deviceType: UWDeviceType?, type: Int? = 0, data: List<BaseRealTimeData>): Int {
        return byDeviceType(deviceType, type, {
            var count = 0
            data.forEach { count += realTimeDataVehicleMapper.updateByPrimaryKeySelective(it as RealTimeDataVehicle) }
            count
        }, {
            var count = 0
            data.forEach { count += realTimeDataUavMapper.updateByPrimaryKeySelective(it as RealTimeDataUav) }
            count
        }, {
            var count = 0
            data.forEach { count += realTimeDataGridMapper.updateByPrimaryKeySelective(it as RealTimeDataGrid) }
            count
        }, {
            var count = 0
            data.forEach { count += realTimeDataGridMinMapper.updateByPrimaryKeySelective(it as RealTimeDataGridMin) }
            count
        }) ?: 0
    }
    /**
     * 统一的delete方法
     * @param deviceType 设备类型
     * @param type 数据统计周期,0:秒级值;1:分钟值
src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
@@ -52,6 +52,8 @@
     */
    fun generateMissionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary
    fun generateMissionSummary(missionCode: String): MissionSummary.Summary
    /**
     * 生成走航任务清单(按时间和区域筛选)
     * 根据时间范围和地理区域查询走航任务,并生成包含统计信息的任务列表
@@ -86,6 +88,14 @@
    fun generateMissionDetail(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionDetail>
    /**
     * 生成走航任务详情(按任务编号筛选)
     * 根据任务编号查询并生成详细的任务报告,包含任务完整信息、场景数据和统计结果
     * @param missionCode 任务编号,用于唯一标识特定的走航任务
     * @return 任务详情对象,包含任务完整信息、场景数据和统计结果
     */
    fun generateMissionDetail(missionCode: String): MissionDetail
    /**
     * 获取走航任务详情(直接处理任务数据)
     * 处理已有的任务、污染线索和实时数据,生成详细任务报告
     * @param keyScenes 关键场景列表,用于分析走航是否经过该区域
@@ -96,6 +106,8 @@
    fun generateClueByRiskArea(startTime: Date, endTime: Date, areaVo: AreaVo): List<MissionRiskArea.ClueByArea>
    fun generateClueByRiskArea(missionCode: String): List<MissionRiskArea.ClueByArea>
    fun generateClueByRiskArea(keyScenes: List<SceneInfo?>, pollutedClues: List<PollutedClue?>): List<MissionRiskArea.ClueByArea>
    fun generateGridFusion(factorTypes: List<FactorType>, startTime: Date, endTime: Date, areaVo: AreaVo):
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt
@@ -108,6 +108,13 @@
        return summary
    }
    override fun generateMissionSummary(missionCode: String): MissionSummary.Summary {
        val mission = missionRep.findOne(missionCode) ?: throw BizException("走航任务不存在")
        val clues = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime, MsgType.PolClue) as List<PollutedClue?>
        val summary = MissionSummary().execute(mission.startTime, mission.endTime, listOf(mission), clues)
        return summary
    }
    /**
     * 生成走航任务清单(按时间和区域筛选)
     * 根据时间范围和行政区划查询走航任务,并关联污染线索数据生成任务列表
@@ -188,6 +195,20 @@
        return generateMissionDetail(keyScenes, missionCluesData)
    }
    override fun generateMissionDetail(missionCode: 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)
        val keyScenes = sceneInfoRep.findBySceneTypes(
            listOf(
                SceneType.TYPE19.value,
                SceneType.TYPE20.value,
                SceneType.TYPE21.value
            )
        )
        return MissionInventory().generateMissionDetail(keyScenes, mission, missionClues, realTimeData)
    }
    /**
     * 生成走航任务详情(直接处理任务数据)
     * 接收已关联的任务-污染线索-实时数据三元组,生成详细任务报告
@@ -226,6 +247,19 @@
        return generateClueByRiskArea(keyScenes, clues)
    }
    override fun generateClueByRiskArea(missionCode: String): List<MissionRiskArea.ClueByArea> {
        val mission = missionRep.findOne(missionCode) ?: throw BizException("任务不存在")
        val pollutedClues = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime, MsgType.PolClue) as List<PollutedClue?>
        val keyScenes = sceneInfoRep.findBySceneTypes(
            listOf(
                SceneType.TYPE19.value,
                SceneType.TYPE20.value,
                SceneType.TYPE21.value
            )
        )
        return generateClueByRiskArea(keyScenes, pollutedClues)
    }
    override fun generateClueByRiskArea(
        keyScenes: List<SceneInfo?>,
        pollutedClues: List<PollutedClue?>,
src/main/kotlin/com/flightfeather/uav/lightshare/web/DataAnalysisController.kt
@@ -57,6 +57,12 @@
        )
    }
    @ApiOperation(value = "生成走航任务汇总统计")
    @GetMapping("/report/missionSummary/one")
    fun generateOneMissionSummary(
        @ApiParam("任务编号") @RequestParam missionCode: String,
    ) = resPack { dataAnalysisService.generateMissionSummary(missionCode) }
    @ApiOperation(value = "生成走航任务清单")
    @PostMapping("/report/missionList")
    fun generateMissionList(
@@ -97,6 +103,12 @@
        )
    }
    @ApiOperation(value = "生成走航任务详情")
    @GetMapping("/report/missionDetail/one")
    fun generateOneMissionDetail(
        @ApiParam("任务编号") @RequestParam missionCode: String,
    ) = resPack { dataAnalysisService.generateMissionDetail(missionCode) }
    @ApiOperation(value = "走航典型隐患区域统计")
    @PostMapping("/report/clueByRiskArea")
    fun generateClueByRiskArea(
@@ -117,6 +129,12 @@
        )
    }
    @ApiOperation(value = "走航典型隐患区域统计")
    @GetMapping("/report/clueByRiskArea/one")
    fun generateOneClueByRiskArea(
        @ApiParam("任务编号") @RequestParam missionCode: String,
    ) = resPack { dataAnalysisService.generateClueByRiskArea(missionCode) }
    @ApiOperation(value = "叠加融合分析")
    @PostMapping("/report/gridFusion")
    fun generateGridFusion(
src/main/resources/generator/generatorConfig.xml
@@ -25,15 +25,15 @@
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--数据库链接URL,用户名、密码 -->
<!--        <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai"-->
<!--                        userId="remoteU1"-->
<!--                        password="eSoF8DnzfGTlhAjE">-->
<!--        </jdbcConnection>-->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai"
                        userId="root"
                        password="123456">
        <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai"
                        userId="remoteU1"
                        password="eSoF8DnzfGTlhAjE">
        </jdbcConnection>
<!--        <jdbcConnection driverClass="com.mysql.jdbc.Driver"-->
<!--                        connectionURL="jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai"-->
<!--                        userId="root"-->
<!--                        password="123456">-->
<!--        </jdbcConnection>-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
src/main/resources/mapper/MissionMapper.xml
@@ -21,8 +21,10 @@
    <result column="town_name" jdbcType="VARCHAR" property="townName" />
    <result column="kilometres" jdbcType="REAL" property="kilometres" />
    <result column="region" jdbcType="VARCHAR" property="region" />
    <result column="radius" jdbcType="REAL" property="radius" />
    <result column="pollution_degree" jdbcType="VARCHAR" property="pollutionDegree" />
    <result column="aqi" jdbcType="INTEGER" property="aqi" />
    <result column="main_factor" jdbcType="VARCHAR" property="mainFactor" />
  </resultMap>
  <sql id="Base_Column_List">
    <!--
@@ -30,6 +32,6 @@
    -->
    mission_code, device_type, device_code, start_time, end_time, data_pulled, province_code, 
    province_name, city_code, city_name, district_code, district_name, town_code, town_name, 
    kilometres, region, pollution_degree, aqi
    kilometres, region, radius, pollution_degree, aqi, main_factor
  </sql>
</mapper>
src/test/kotlin/com/flightfeather/uav/biz/mission/MissionUtilTest.kt
@@ -3,30 +3,40 @@
import com.flightfeather.uav.common.utils.MapUtil
import com.flightfeather.uav.domain.repository.MissionRep
import com.flightfeather.uav.domain.repository.RealTimeDataRep
import com.flightfeather.uav.socket.eunm.UWDeviceType
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner
//@RunWith(SpringRunner::class)
//@SpringBootTest
@RunWith(SpringRunner::class)
@SpringBootTest
class MissionUtilTest {
//    @Autowired
    @Autowired
    lateinit var missionRep: MissionRep
//    @Autowired
    @Autowired
    lateinit var realTimeDataRep: RealTimeDataRep
    @Test
    fun calKilometres() {
//        val m = missionRep.findOne("20250819") ?: return
//        val data = realTimeDataRep.fetchData(m)
//        MissionUtil.calKilometres(data)
        val d = MapUtil.getDistance(121.425187, 31.225907, 121.425196, 31.225892)
        println(d)
        val d1 = MapUtil.getDistance(121.425196, 31.225892, 121.425187, 31.225907)
        println(d1)
        val m = missionRep.findOne("20250819") ?: return
        val data = realTimeDataRep.fetchData(m)
        MissionUtil.calKilometres(data)
//        val d = MapUtil.getDistance(121.425187, 31.225907, 121.425196, 31.225892)
//        println(d)
//        val d1 = MapUtil.getDistance(121.425196, 31.225892, 121.425187, 31.225907)
//        println(d1)
    }
    @Test
    fun dataClean() {
        val m = missionRep.findOne("SH-CN-20250411") ?: return
        val data = realTimeDataRep.fetchData(m)
        val cleanedData = MissionUtil.dataClean(data, 4)
        realTimeDataRep.updateData(UWDeviceType.fromValue(m.deviceType), cleanedData)
        println(cleanedData.size)
    }
}
src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt
@@ -45,8 +45,9 @@
//            "SH-CN-20240723(02)",
////            "SH-CN-20250723(01)"
//        )
        val startTime = LocalDateTime.of(2025, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
        val endTime = LocalDateTime.of(2025, 9, 30, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
//        val startTime = LocalDateTime.of(2024, 12, 31, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
        val startTime = LocalDateTime.of(2024, 12, 4, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
        val endTime = LocalDateTime.of(2025, 4, 11, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
        val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
            createCriteria().andBetween("startTime", startTime, endTime)
        })
@@ -114,8 +115,8 @@
//            "SH-CN-20240723(02)",
////            "SH-CN-20250723(01)"
//        )
        val startTime = LocalDateTime.of(2025, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
        val endTime = LocalDateTime.of(2025, 9, 30, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
        val startTime = LocalDateTime.of(2024, 12, 4, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()
        val endTime = LocalDateTime.of(2025, 4, 11, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant()
        val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
            createCriteria().andBetween("startTime", startTime, endTime)
        })