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> <!– ä½¿ç¨ææ°ç¨³å®ç –>--> <!-- </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>--> </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,30 +39,33 @@ @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 rtData = realTimeDataService.getSecondData( m?.deviceType, m?.deviceCode, DateUtil.instance.dateToString(m?.startTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS), DateUtil.instance.dateToString(m?.endTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS), null, 1, 10000 ) rtData.data?.forEach { d -> val rtdVehicle = d.toBaseRealTimeData(RealTimeDataVehicle::class.java) // 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, DateUtil.instance.dateToString(m?.startTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS), DateUtil.instance.dateToString(m?.endTime, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS), null, 1, 10000 ) rtData.data?.forEach { d -> val rtdVehicle = d.toBaseRealTimeData(RealTimeDataVehicle::class.java) // Thread.sleep(500) sourceTraceController.addOneData(rtdVehicle) } sourceTraceController.initTask() sourceTraceController.addOneData(rtdVehicle) } sourceTraceController.initTask() } // val missions = missionMapper.selectByExample(Example(Mission::class.java).apply { // createCriteria().andEqualTo("deviceType", "0a") @@ -109,7 +114,7 @@ "SH-CN-20240723(02)", // "SH-CN-20250723(01)" ) mCode.forEach {c -> mCode.forEach { c -> missionRep.findOne(c)?.let { sourceTraceRep.delete(it) } 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) } }