feiyu02
2025-08-28 ddaa44400aa478058ffe9349d59904a130b7ce9c
2025.8.28
1. 新增走航任务统计功能(待完成)
已修改10个文件
已添加1个文件
522 ■■■■■ 文件已修改
pom.xml 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/mission/MissionUtil.kt 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/common/file/Docx4jGenerator.kt 228 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/biz/report/MissionSummaryTest.kt 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -267,30 +267,31 @@
            <artifactId>commons-lang3</artifactId>
            <version>3.17.0</version>
        </dependency>
        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j-core</artifactId>
            <version>11.4.9</version> <!-- ä½¿ç”¨æœ€æ–°ç¨³å®šç‰ˆ -->
        </dependency>
        <!-- å¦‚需处理图片/表格等高级功能,可添加 -->
        <dependency>
            <groupId>org.docx4j</groupId>
            <artifactId>docx4j-ImportXHTML</artifactId>
            <version>11.4.8</version>
        </dependency>
        <!-- MockK å•元测试库(用于 Kotlin) -->
        <dependency>
            <groupId>io.mockk</groupId>
            <artifactId>mockk</artifactId>
            <version>1.14.5</version> <!-- ä½¿ç”¨æœ€æ–°ç¨³å®šç‰ˆ -->
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>test</scope>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.docx4j</groupId>-->
<!--            <artifactId>docx4j-core</artifactId>-->
<!--            <version>11.4.9</version> &lt;!&ndash; ä½¿ç”¨æœ€æ–°ç¨³å®šç‰ˆ &ndash;&gt;-->
<!--        </dependency>-->
<!--        &lt;!&ndash; å¦‚需处理图片/表格等高级功能,可添加 &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.docx4j</groupId>-->
<!--            <artifactId>docx4j-ImportXHTML</artifactId>-->
<!--            <version>11.4.8</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; MockK å•元测试库(用于 Kotlin) &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>io.mockk</groupId>-->
<!--            <artifactId>mockk</artifactId>-->
<!--            <version>1.14.5</version> &lt;!&ndash; ä½¿ç”¨æœ€æ–°ç¨³å®šç‰ˆ &ndash;&gt;-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->
<!--        <dependency>-->
<!--            <groupId>org.junit.jupiter</groupId>-->
<!--            <artifactId>junit-jupiter</artifactId>-->
<!--            <version>RELEASE</version>-->
<!--            <scope>test</scope>-->
<!--        </dependency>-->
    </dependencies>
src/main/kotlin/com/flightfeather/uav/biz/mission/MissionUtil.kt
@@ -54,6 +54,6 @@
        // å°†WGS84坐标转换为GCJ02坐标后进行逆地理编码获取地址信息
        val address = AMapService.reGeo(MapUtil.wgs84ToGcj02(pair as Pair<Double, Double>))
        // è¿”回乡镇和街道名称组合
        return address.township + address.street
        return address.township
    }
}
src/main/kotlin/com/flightfeather/uav/biz/report/MissionInventory.kt
@@ -1,9 +1,11 @@
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.entity.SceneInfo
import com.flightfeather.uav.lightshare.bean.FactorStatistics
import com.flightfeather.uav.socket.eunm.FactorType
import com.flightfeather.uav.socket.sender.MsgType
/**
 * èµ°èˆªæº¯æºæ¸…单
@@ -29,6 +31,37 @@
    class MissionDetail : Mission() {
        var keyScene: List<SceneInfo>? = null
        var dataStatistics: List<FactorStatistics>? = null
    }
    /**
     * è¾“出走航清单
     */
    fun missionList(missionClues: List<Pair<Mission?, List<PollutedClue?>>>): List<MissionInfo> {
        val missionMap = mutableMapOf<String, MissionInfo>()
        missionClues.forEach { (mission, clue) ->
            mission ?: return@forEach
            clue ?: return@forEach
            val missionInfo = missionMap[mission.missionCode] ?: MissionInfo().apply {
                missionMap[mission.missionCode] = this
            }
            val abnormalFactors = mutableListOf<FactorType>()
            var sceneCount = 0
            clue.forEach {
                if (it?.msgType == MsgType.PolClue.value) {
                    it.pollutedData?.statisticMap?.keys?.forEach { k->
                        if (!abnormalFactors.contains(k)) {
                            abnormalFactors.add(k)
                        }
                    }
                }
            }
            // è®¡ç®—每个走航任务的所有异常因子
            // è®¡ç®—每个走航任务的首要污染物
            // è®¡ç®—每个走航任务的溯源场景数量
        }
        return mutableListOf()
    }
}
src/main/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceController.kt
@@ -45,14 +45,16 @@
                    .withMain(FactorType.CO)
//                    .withMain(FactorType.H2S)
//                    .withMain(FactorType.SO2)
                    .withMain(FactorType.O3)
//                    .withMain(FactorType.O3)
                    .withMain(FactorType.PM25)
                    .withMain(FactorType.PM10)
                    .withMain(FactorType.VOC)
//                    .withMain(FactorType.VOC)
                    .withMain(FactorType.NO)
                    .withCombination(
                        listOf(
                            listOf(FactorType.PM25, FactorType.PM10),
                            listOf(FactorType.VOC, FactorType.CO),
//                            listOf(FactorType.VOC, FactorType.CO),
                            listOf(FactorType.NO, FactorType.NO2),
                        )
                    )
                    .create()
src/main/kotlin/com/flightfeather/uav/common/file/Docx4jGenerator.kt
@@ -1,114 +1,114 @@
package com.flightfeather.uav.common.file
import freemarker.template.Configuration
import freemarker.template.Template
import org.docx4j.openpackaging.packages.WordprocessingMLPackage
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.OutputStreamWriter
import java.nio.charset.StandardCharsets
/**
 * Word文件生成器(基于Docx4j + FreeMarker)
 * @date 2025/8/28 09:29
 * @author feiyu
 */
class Docx4jGenerator(
    private val templatePath: String,
    private val freemarkerConfig: Configuration = defaultFreemarkerConfig()
) {
    private var wordMLPackage: WordprocessingMLPackage? = null
    private var mainDocumentPart: MainDocumentPart? = null
    /**
     * åˆ›å»ºWord文档包
     */
    fun loadTemplate(): Docx4jGenerator {
        wordMLPackage = WordprocessingMLPackage.createPackage()
        mainDocumentPart = wordMLPackage?.mainDocumentPart
        return this
    }
    /**
     * ä½¿ç”¨FreeMarker填充模板数据
     */
    fun fillData(dataModel: Map<String, Any>): Docx4jGenerator {
        val template = freemarkerConfig.getTemplate(templatePath.substringAfterLast("/"))
        val xmlContent = renderTemplate(template, dataModel)
        mainDocumentPart?.unmarshal(ByteArrayInputStream(xmlContent.toByteArray(StandardCharsets.UTF_8)))
        return this
    }
    /**
     * æ·»åŠ å›¾ç‰‡åˆ°Word文档
     * @param imagePath å›¾ç‰‡è·¯å¾„
     * @param width å®½åº¦(像素)
     * @param height é«˜åº¦(像素)
     * @param paragraphId æ®µè½ID,指定图片插入位置
     */
    fun addImage(imagePath: String, width: Int, height: Int, paragraphId: String): Docx4jGenerator {
        // å®žçŽ°å›¾ç‰‡æ·»åŠ é€»è¾‘
        return this
    }
    /**
     * æ·»åŠ è¡¨æ ¼åˆ°Word文档
     * @param data è¡¨æ ¼æ•°æ®
     * @param paragraphId æ®µè½ID,指定表格插入位置
     */
    fun addTable(data: List<List<String>>, paragraphId: String): Docx4jGenerator {
        // å®žçŽ°è¡¨æ ¼æ·»åŠ é€»è¾‘
        return this
    }
    /**
     * ä¿å­˜ç”Ÿæˆçš„Word文件
     * @param outputPath è¾“出文件路径
     */
    fun save(outputPath: String) {
        wordMLPackage?.save(File(outputPath))
    }
    /**
     * ä½¿ç”¨FreeMarker渲染模板
     */
    private fun renderTemplate(template: Template, dataModel: Map<String, Any>): String {
        val outputStream = ByteArrayOutputStream()
        val writer = OutputStreamWriter(outputStream, StandardCharsets.UTF_8)
        template.process(dataModel, writer)
        writer.flush()
        return outputStream.toString(StandardCharsets.UTF_8.name())
    }
    companion object {
        /**
         * é»˜è®¤FreeMarker配置
         */
        fun defaultFreemarkerConfig(): Configuration {
            val config = Configuration(Configuration.VERSION_2_3_31)
            config.defaultEncoding = "UTF-8"
            config.setClassForTemplateLoading(Docx4jGenerator::class.java, "/templates")
            return config
        }
        /**
         * ç®€åŒ–调用的静态方法
         */
        fun generate(
            templatePath: String,
            outputPath: String,
            dataModel: Map<String, Any>,
            config: Configuration = defaultFreemarkerConfig()
        ) {
            Docx4jGenerator(templatePath, config)
                .loadTemplate()
                .fillData(dataModel)
                .save(outputPath)
        }
    }
}
//package com.flightfeather.uav.common.file
//
//import freemarker.template.Configuration
//import freemarker.template.Template
//
//import org.docx4j.openpackaging.packages.WordprocessingMLPackage
//import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart
//import java.io.ByteArrayInputStream
//import java.io.ByteArrayOutputStream
//import java.io.File
//import java.io.OutputStreamWriter
//import java.nio.charset.StandardCharsets
//
///**
// * Word文件生成器(基于Docx4j + FreeMarker)
// * @date 2025/8/28 09:29
// * @author feiyu
// */
//class Docx4jGenerator(
//    private val templatePath: String,
//    private val freemarkerConfig: Configuration = defaultFreemarkerConfig()
//) {
//    private var wordMLPackage: WordprocessingMLPackage? = null
//    private var mainDocumentPart: MainDocumentPart? = null
//
//    /**
//     * åˆ›å»ºWord文档包
//     */
//    fun loadTemplate(): Docx4jGenerator {
//        wordMLPackage = WordprocessingMLPackage.createPackage()
//        mainDocumentPart = wordMLPackage?.mainDocumentPart
//
//        return this
//    }
//
//    /**
//     * ä½¿ç”¨FreeMarker填充模板数据
//     */
//    fun fillData(dataModel: Map<String, Any>): Docx4jGenerator {
//        val template = freemarkerConfig.getTemplate(templatePath.substringAfterLast("/"))
//        val xmlContent = renderTemplate(template, dataModel)
//
//        mainDocumentPart?.unmarshal(ByteArrayInputStream(xmlContent.toByteArray(StandardCharsets.UTF_8)))
//        return this
//    }
//
//    /**
//     * æ·»åŠ å›¾ç‰‡åˆ°Word文档
//     * @param imagePath å›¾ç‰‡è·¯å¾„
//     * @param width å®½åº¦(像素)
//     * @param height é«˜åº¦(像素)
//     * @param paragraphId æ®µè½ID,指定图片插入位置
//     */
//    fun addImage(imagePath: String, width: Int, height: Int, paragraphId: String): Docx4jGenerator {
//        // å®žçŽ°å›¾ç‰‡æ·»åŠ é€»è¾‘
//        return this
//    }
//
//    /**
//     * æ·»åŠ è¡¨æ ¼åˆ°Word文档
//     * @param data è¡¨æ ¼æ•°æ®
//     * @param paragraphId æ®µè½ID,指定表格插入位置
//     */
//    fun addTable(data: List<List<String>>, paragraphId: String): Docx4jGenerator {
//        // å®žçŽ°è¡¨æ ¼æ·»åŠ é€»è¾‘
//        return this
//    }
//
//    /**
//     * ä¿å­˜ç”Ÿæˆçš„Word文件
//     * @param outputPath è¾“出文件路径
//     */
//    fun save(outputPath: String) {
//        wordMLPackage?.save(File(outputPath))
//    }
//
//    /**
//     * ä½¿ç”¨FreeMarker渲染模板
//     */
//    private fun renderTemplate(template: Template, dataModel: Map<String, Any>): String {
//        val outputStream = ByteArrayOutputStream()
//        val writer = OutputStreamWriter(outputStream, StandardCharsets.UTF_8)
//        template.process(dataModel, writer)
//        writer.flush()
//        return outputStream.toString(StandardCharsets.UTF_8.name())
//    }
//
//    companion object {
//        /**
//         * é»˜è®¤FreeMarker配置
//         */
//        fun defaultFreemarkerConfig(): Configuration {
//            val config = Configuration(Configuration.VERSION_2_3_31)
//            config.defaultEncoding = "UTF-8"
//            config.setClassForTemplateLoading(Docx4jGenerator::class.java, "/templates")
//            return config
//        }
//
//        /**
//         * ç®€åŒ–调用的静态方法
//         */
//        fun generate(
//            templatePath: String,
//            outputPath: String,
//            dataModel: Map<String, Any>,
//            config: Configuration = defaultFreemarkerConfig()
//        ) {
//            Docx4jGenerator(templatePath, config)
//                .loadTemplate()
//                .fillData(dataModel)
//                .save(outputPath)
//        }
//    }
//}
src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
@@ -147,6 +147,9 @@
}
fun List<BaseRealTimeData>.avg(): BaseRealTimeData {
    if (isEmpty()) {
        return BaseRealTimeData()
    }
    //风向采用单位矢量法求取均值
    var u = .0//东西方位分量总和
    var v = .0//南北方位分量总和
src/main/kotlin/com/flightfeather/uav/lightshare/service/DataAnalysisService.kt
@@ -2,7 +2,10 @@
import com.flightfeather.uav.biz.dataanalysis.BaseExceptionResult
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
import com.flightfeather.uav.biz.report.MissionSummary
import com.flightfeather.uav.biz.sourcetrace.model.BasePollutedMsg
import com.flightfeather.uav.lightshare.bean.AreaVo
import java.util.*
/**
 *
@@ -18,4 +21,6 @@
    fun pollutionTrace(missionCode: String): List<ExceptionResult>
    fun fetchHistory(missionCode: String): String
    fun missionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/DataAnalysisServiceImpl.kt
@@ -4,17 +4,26 @@
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.report.MissionSummary
import com.flightfeather.uav.biz.sourcetrace.model.BasePollutedMsg
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.entity.Mission
import com.flightfeather.uav.domain.mapper.MissionMapper
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.lightshare.bean.AreaVo
import com.flightfeather.uav.lightshare.service.DataAnalysisService
import com.flightfeather.uav.socket.eunm.FactorType
import org.springframework.stereotype.Service
import tk.mybatis.mapper.entity.Example
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
/**
 *
@@ -24,6 +33,7 @@
@Service
class DataAnalysisServiceImpl(
    private val missionRep: MissionRep,
    private val missionMapper: MissionMapper,
    private val realTimeDataRep: RealTimeDataRep,
    private val locationRoadNearby: LocationRoadNearby,
    private val segmentInfoRep: SegmentInfoRep,
@@ -56,4 +66,22 @@
        val res = sourceTraceRep.fetchList(mission.deviceCode, mission.startTime, mission.endTime)
        return GsonUtils.gson.toJson(res)
    }
    override fun missionSummary(startTime: Date, endTime: Date, areaVo: AreaVo): MissionSummary.Summary {
        val clues = mutableListOf<PollutedClue?>()
        val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
            createCriteria().andBetween("startTime", startTime, endTime)
                .andEqualTo("provinceCode", areaVo.provinceCode)
                .andEqualTo("cityCode", areaVo.cityCode)
                .andEqualTo("districtCode", areaVo.districtCode)
                .andIsNotNull("kilometres")
                .andNotEqualTo("kilometres", 0)
        }).onEach {
            it ?: return@onEach
            val clue = sourceTraceRep.fetchList(it.deviceCode, it.startTime, it.endTime).filterIsInstance<PollutedClue?>()
            clues.addAll(clue)
        }
        val summary = MissionSummary().execute(startTime, endTime, missions, clues)
        return summary
    }
}
src/test/kotlin/com/flightfeather/uav/biz/report/MissionSummaryTest.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
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.mapper.MissionMapper
import com.flightfeather.uav.domain.repository.SourceTraceRep
import com.flightfeather.uav.lightshare.bean.AreaVo
import com.flightfeather.uav.lightshare.service.DataAnalysisService
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
import tk.mybatis.mapper.entity.Example
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.util.Date
@RunWith(SpringRunner::class)
@SpringBootTest
class MissionSummaryTest {
    private val missionSummary: MissionSummary = MissionSummary()
    @Autowired
    lateinit var missionMapper: MissionMapper
    @Autowired
    lateinit var sourceTraceRep: SourceTraceRep
    @Autowired
    lateinit var dataAnalysisService: DataAnalysisService
    @Test
    fun testMissionSummary() {
        val startTime = Date.from(LocalDateTime.of(2025,7,1,0,0,0).atZone(ZoneId.systemDefault()).toInstant())
        val endTime = Date.from(LocalDateTime.of(2025, 9, 30, 23, 59, 59).atZone(ZoneId.systemDefault()).toInstant())
        val summary = dataAnalysisService.missionSummary(startTime, endTime, AreaVo().apply {
            provinceCode = "31"
            cityCode = "3100"
            districtCode = "310106"
        })
//        val deviceCode = "TX105"
//        val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
//            createCriteria().andEqualTo("deviceCode", deviceCode)
//                .andBetween("startTime", startTime, endTime)
//        })
//        val clues = sourceTraceRep.fetchList(deviceCode, startTime, endTime)
//            .filterIsInstance<PollutedClue?>()
//        val summary = missionSummary.execute(startTime, endTime, missions, clues)
        println(summary)
    }
}
src/test/kotlin/com/flightfeather/uav/biz/sourcetrace/SourceTraceControllerTest.kt
@@ -14,6 +14,8 @@
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.junit4.SpringRunner
import tk.mybatis.mapper.entity.Example
import java.time.LocalDateTime
import java.time.ZoneId
@RunWith(SpringRunner::class)
@SpringBootTest
@@ -37,14 +39,18 @@
    @Test
    fun autoSourceTrace() {
        val sourceTraceController = SourceTraceController(sceneInfoRep, sourceTraceRep, false)
        val mCode = listOf(
            "SH-CN-20241227", "SH-CN-20241127", "SH-CN-20240906", "SH-CN-20240830(05)",
            "SH-CN-20240830(04)", "SH-CN-20240823",
            "SH-CN-20240723(02)",
//            "SH-CN-20250723(01)"
        )
        mCode.forEach { c->
            missionRep.findOne(c)?.let {m ->
//        val mCode = listOf(
//            "SH-CN-20241227", "SH-CN-20241127", "SH-CN-20240906", "SH-CN-20240830(05)",
//            "SH-CN-20240830(04)", "SH-CN-20240823",
//            "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 missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
            createCriteria().andBetween("startTime", startTime, endTime)
        })
        missions.forEach { m ->
                val rtData = realTimeDataService.getSecondData(
                    m?.deviceType,
                    m?.deviceCode,
@@ -60,7 +66,6 @@
                    sourceTraceController.addOneData(rtdVehicle)
                }
                sourceTraceController.initTask()
            }
        }
//        val missions = missionMapper.selectByExample(Example(Mission::class.java).apply {
//            createCriteria().andEqualTo("deviceType", "0a")
src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt
@@ -2,30 +2,25 @@
import com.flightfeather.uav.biz.FactorFilter
import com.flightfeather.uav.biz.report.MissionReport
import com.flightfeather.uav.common.exception.BizException
import com.flightfeather.uav.domain.repository.MissionRep
import com.flightfeather.uav.domain.entity.Mission
import com.flightfeather.uav.domain.mapper.MissionMapper
import com.flightfeather.uav.lightshare.service.MissionService
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
import javax.servlet.http.HttpServletResponse
import tk.mybatis.mapper.entity.Example
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.runner.RunWith
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@RunWith(SpringRunner::class)
@SpringBootTest
class MissionServiceImplTest {
    @Autowired
    lateinit var missionService: MissionService
    private var missionRep: MissionRep = mockk()
    @Autowired
    lateinit var missionMapper: MissionMapper
    @Autowired
    lateinit var missionReport: MissionReport
@@ -37,65 +32,12 @@
    }
    @Test
    fun `calMissionInfo should throw BizException when mission not found`() {
        // Arrange
        val missionCode = "M001"
        every { missionRep.findOne(missionCode) } returns null
        // Act & Assert
        val exception = assertThrows<BizException> {
            missionService.calMissionInfo(missionCode)
    fun calMissionInfo() {
        missionMapper.selectByExample(Example(Mission::class.java).apply {
            createCriteria().andGreaterThanOrEqualTo("startTime", "2025-08-08 08:30:00")
        }).forEach {mission ->
            mission?.let { missionService.calMissionInfo(it.missionCode) }
            Thread.sleep(1000)
        }
        assertEquals("走航任务不存在", exception.message)
    }
    @Test
    fun `calMissionInfo should calculate and update mission info successfully`() {
        // Arrange
        val missionCode = "M001"
        val mission = Mission(missionCode)
        val data = listOf<RealTimeData>()
        every { missionRep.findOne(missionCode) } returns mission
        every { realTimeDataRep.fetchData(mission) } returns data
        every { missionUtil.calKilometres(data) } returns 100.0
        every { missionUtil.calRegion(data) } returns "Center"
        every { missionRep.updateMission(mission) } returns true
        // Act
        val result = missionService.calMissionInfo(missionCode)
        // Assert
        assertTrue(result)
        assertEquals(100.0f, mission.kilometres)
        assertEquals("Center", mission.region)
        verify {
            missionRep.findOne(missionCode)
            realTimeDataRep.fetchData(mission)
            missionUtil.calKilometres(data)
            missionUtil.calRegion(data)
            missionRep.updateMission(mission)
        }
    }
    @Test
    fun `calMissionInfo should return false when update fails`() {
        // Arrange
        val missionCode = "M001"
        val mission = Mission(missionCode)
        val data = listOf<RealTimeData>()
        every { missionRep.findOne(missionCode) } returns mission
        every { realTimeDataRep.fetchData(mission) } returns data
        every { missionUtil.calKilometres(data) } returns 100.0
        every { missionUtil.calRegion(data) } returns "Center"
        every { missionRep.updateMission(mission) } returns false
        // Act
        val result = missionService.calMissionInfo(missionCode)
        // Assert
        assertFalse(result)
    }
}