feiyu02
2024-08-27 9ed0b1847912221197697791d69e01ccae17f5b9
1. 新增NO监测因子
2. 新增第三方数据接口数据获取相关模块
已修改20个文件
已添加10个文件
1027 ■■■■■ 文件已修改
pom.xml 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/datafetch/ShenXinDataFetch.kt 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/biz/report/MissionReport.kt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/common/net/AMapService.kt 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/common/net/ShenXinService.kt 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/common/net/SoapClient.kt 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/repository/RealTimeDataRep.kt 129 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/domain/repository/RealTimeDataRepDelegate.kt 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/eunm/ThirdPartyLabel.kt 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/ThirdPartyService.kt 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImpl.kt 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/ThirdPartyServiceImpl.kt 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/lightshare/web/ThirdPartyController.kt 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/scheduler/ScheduleService.kt 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/kotlin/com/flightfeather/uav/socket/eunm/UWDeviceType.kt 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-dev.yml 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-pro.yml 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application-test.yml 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/generator/generatorConfig.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/RealTimeDataGridMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/RealTimeDataGridMinMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/RealTimeDataGridOptMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/RealTimeDataUavMapper.xml 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/RealTimeDataVehicleMapper.xml 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/common/net/ShenXinServiceTest.kt 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -217,6 +217,38 @@
            <artifactId>httpmime</artifactId>
            <version>4.5.14</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/javax.xml.ws/jaxws-api -->
<!--        <dependency>-->
<!--            <groupId>javax.xml.ws</groupId>-->
<!--            <artifactId>jaxws-api</artifactId>-->
<!--            <version>2.3.1</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; https://mvnrepository.com/artifact/org.apache.axis2/axis2-jaxws &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.axis2</groupId>-->
<!--            <artifactId>axis2-jaxws</artifactId>-->
<!--            <version>1.8.2</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; https://mvnrepository.com/artifact/org.apache.axis2/axis2-adb &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.axis2</groupId>-->
<!--            <artifactId>axis2-adb</artifactId>-->
<!--            <version>1.8.2</version>-->
<!--        </dependency>-->
<!--        &lt;!&ndash; https://mvnrepository.com/artifact/org.apache.axis2/axis2-transport-base &ndash;&gt;-->
<!--        <dependency>-->
<!--            <groupId>org.apache.axis2</groupId>-->
<!--            <artifactId>axis2-transport-base</artifactId>-->
<!--            <version>1.8.2</version>-->
<!--        </dependency>-->
        <!-- https://mvnrepository.com/artifact/org.apache.axis2/axis2-kernel -->
        <dependency>
            <groupId>org.apache.axis2</groupId>
            <artifactId>axis2-kernel</artifactId>
            <version>1.8.2</version>
        </dependency>
    </dependencies>
    <build>
src/main/kotlin/com/flightfeather/uav/biz/FactorFilter.kt
@@ -38,27 +38,37 @@
        fun default() = builder()
            .withMain(FactorType.VOC)
            .withSubs(listOf(
                FactorType.H2S,
//                FactorType.H2S,
                FactorType.O3,
                FactorType.PM25,
                FactorType.PM10,
            ))
            .withMain(FactorType.H2S)
            .withSubs(listOf(
                FactorType.VOC,
                FactorType.O3,
                FactorType.PM25,
            ))
//            .withMain(FactorType.H2S)
//            .withSubs(listOf(
//                FactorType.VOC,
//                FactorType.O3,
//                FactorType.PM25,
//            ))
            .withMain(FactorType.O3)
            .withSubs(listOf(
                FactorType.VOC,
                FactorType.H2S,
//                FactorType.H2S,
                FactorType.PM25,
                FactorType.PM10,
            ))
            .withMain(FactorType.PM25)
            .withSubs(listOf(
                FactorType.VOC,
                FactorType.H2S,
//                FactorType.H2S,
                FactorType.O3,
                FactorType.PM10,
            ))
            .withMain(FactorType.PM10)
            .withSubs(listOf(
                FactorType.VOC,
//                FactorType.H2S,
                FactorType.O3,
                FactorType.PM25,
            ))
//            .withSubs(listOf(
//                FactorType.NO2,
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/BaseExceptionContinuousSingle.kt
@@ -21,8 +21,7 @@
                // åˆ¤æ–­ç›¸é‚»æ•°æ®æ˜¯å¦è¿žç»­å¹¶ä¸”是否满足异常判断
                if (!isContinue) {
                    checkResult(s)
                    it.sIndex = it.eIndex
                    it.startData = data
                    it.refreshAfterCheckResult(data)
                } else {
                    if (hasException[f] == true) {
                        // ä¿®æ”¹äº†èµ·å§‹æ•°æ®çš„位置,变更为出现异常的该值,而不是原来的出现异常的数据的前一个值
@@ -31,8 +30,10 @@
                            it.startData = data
                        }
                        it.existException = true
                        it.exceptionData.add(data)
                    } else {
                        checkResult(s)
                        it.refreshAfterCheckResult(data)
                    }
                }
            }
src/main/kotlin/com/flightfeather/uav/biz/dataanalysis/ExceptionAnalysisController.kt
@@ -11,6 +11,7 @@
import com.flightfeather.uav.domain.entity.Mission
import com.flightfeather.uav.domain.repository.RealTimeDataRep
import com.flightfeather.uav.domain.repository.SegmentInfoRep
import com.flightfeather.uav.socket.eunm.UWDeviceType
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.ZoneId
@@ -47,7 +48,11 @@
        val result = mutableListOf<ExceptionResult>()
        taskList.forEach { it.init() }
        // è½®è¯¢æ•°æ®ï¼Œè®¡ç®—各个异常
        realTimeDataRep.fetchData(mission.deviceCode, mission.startTime, mission.endTime).forEach { d ->
        realTimeDataRep.fetchData(UWDeviceType.fromValue(mission.deviceType),
            mission.deviceCode,
            mission.startTime,
            mission.endTime
        ).forEach { d ->
            taskList.forEach { it.onNextData(d) }
        }
        // å„个异常分析分别结束
src/main/kotlin/com/flightfeather/uav/biz/datafetch/ShenXinDataFetch.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
package com.flightfeather.uav.biz.datafetch
import com.flightfeather.uav.common.exception.BizException
import com.flightfeather.uav.common.net.ShenXinService
import com.flightfeather.uav.domain.entity.Mission
import com.flightfeather.uav.domain.repository.MissionRep
import com.flightfeather.uav.domain.repository.RealTimeDataRep
import com.flightfeather.uav.socket.eunm.UWDeviceType
import org.springframework.stereotype.Component
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.concurrent.ConcurrentHashMap
/**
 * ç”³æ¬£çŽ¯ä¿èµ°èˆªç›‘æµ‹æ•°æ®èŽ·å–
 * æ¯å°è®¾å¤‡åªæœ‰ä¸€ä¸ªçº¿ç¨‹ï¼Œç¡®ä¿ä¸ä¼šé‡å¤èŽ·å–
 * @date 2024/8/23
 * @author feiyu02
 */
@Component
class ShenXinDataFetch(private val missionRep: MissionRep, private val realTimeDataRep: RealTimeDataRep) {
    // è®¾å¤‡æ˜¯å¦æ­£åœ¨èŽ·å–æœ€æ–°æ•°æ®
    private val deviceStatusMap = ConcurrentHashMap<String, Boolean>()
    // èµ°èˆªä»»åŠ¡æ˜¯å¦æ­£åœ¨èŽ·å–æ•°æ®
    private val missionStatusMap = ConcurrentHashMap<String, Boolean>()
    /**
     * èŽ·å–æœ€æ–°çš„æ•°æ®
     * @param deviceType è®¾å¤‡ç±»åž‹
     * @param code è®¾å¤‡ç¼–号
     */
    private fun fetchLatestData(deviceType: UWDeviceType, code: String) {
        if (deviceStatusMap.containsKey(code)) {
            // è®¾å¤‡æ­£åœ¨èŽ·å–å®žæ—¶æ•°æ®ï¼Œåˆ™ç›´æŽ¥è¿”å›žï¼Œæ”¾å¼ƒæœ¬æ¬¡è¯·æ±‚
            if (deviceStatusMap[code] == true) {
                return
            }
            // å¦åˆ™å¼€å§‹èŽ·å–æ•°æ®ï¼Œä¿®æ”¹çŠ¶æ€ä¸ºtrue
            else {
                deviceStatusMap[code] = true
            }
        }
        // å½“设备首次获取数据时,直接开始并修改状态为true
        else {
            deviceStatusMap[code] = true
        }
        try {
            val latestData = realTimeDataRep.fetchData(deviceType, code, null, null, null, 1, 1)
            val st = if (latestData.isEmpty()) {
                LocalDate.now().atStartOfDay()
            } else {
                LocalDateTime.ofInstant(latestData[0].dataTime?.toInstant(), ZoneId.systemDefault()).plusSeconds(1)
            }
            val data = ShenXinService.fetchData(code, st, LocalDateTime.now())
            realTimeDataRep.saveData(deviceType, data)
        } finally {
            // è®¾å¤‡å®Œæˆæ•°æ®èŽ·å–åŽï¼Œä¿®æ”¹çŠ¶æ€ä¸ºfalse
            deviceStatusMap[code] = false
        }
    }
    /**
     * èŽ·å–ç»™å®šæ—¶é—´èŒƒå›´å†…çš„æ•°æ®
     */
    fun fetchLatestData(deviceType: UWDeviceType, code: String, sTime: LocalDateTime?, eTime: LocalDateTime?) {
        if (sTime != null && eTime != null) {
            val data = ShenXinService.fetchData(code, sTime, eTime)
            realTimeDataRep.deleteData(deviceType, code, sTime, eTime)
            realTimeDataRep.saveData(deviceType, data)
        } else if (sTime == null && eTime == null) {
            fetchLatestData(deviceType, code)
        } else {
            throw BizException("开始和结束时间需要都省略或者都填写")
        }
    }
    /**
     * èŽ·å–ä»»åŠ¡çš„æ‰€æœ‰æ•°æ®
     * @param mission ä»»åŠ¡ä¿¡æ¯
     * @param forceUpdate å¼ºåˆ¶å°†èŽ·å–çš„æ•°æ®æ›¿æ¢åŽŸæœ‰çš„æ•°æ®
     */
    fun fetchMissionData(mission: Mission, forceUpdate: Boolean = false) {
        if (missionStatusMap.containsKey(mission.missionCode)) {
            // èµ°èˆªä»»åŠ¡æ­£åœ¨èŽ·å–æ•°æ®ï¼Œåˆ™ç›´æŽ¥è¿”å›žï¼Œæ”¾å¼ƒæœ¬æ¬¡è¯·æ±‚
            if (missionStatusMap[mission.missionCode] == true) {
                return
            }
            // å¦åˆ™å¼€å§‹èŽ·å–æ•°æ®ï¼Œä¿®æ”¹çŠ¶æ€ä¸ºtrue
            else {
                missionStatusMap[mission.missionCode] = true
            }
        }
        // å½“走航任务首次获取数据时,直接开始并修改状态为true
        else {
            missionStatusMap[mission.missionCode] = true
        }
        try {
            val st = LocalDateTime.ofInstant(mission.startTime?.toInstant(), ZoneId.systemDefault())
            val et = LocalDateTime.ofInstant(mission.endTime?.toInstant(), ZoneId.systemDefault())
            val data = ShenXinService.fetchData(mission.deviceCode, st, et)
            val type = UWDeviceType.fromValue(mission.deviceType)
            val oldData = realTimeDataRep.fetchData(mission)
            // åˆ¤æ–­æ–°æ•°æ®æ•°æ®é‡æ˜¯å¦ç­‰äºŽæ—§æ•°æ®ï¼Œè‹¥ä¸ç›¸ç­‰ï¼Œåˆ™ç®€å•认为数据有变动;或者设置了强制更新
            if (data.size != oldData.size || forceUpdate) {
                if (oldData.isNotEmpty()) {
                    realTimeDataRep.deleteData(mission)
                }
                realTimeDataRep.saveData(type, data)
            }
        } finally {
            // èµ°èˆªä»»åŠ¡å®Œæˆæ•°æ®èŽ·å–åŽï¼Œä¿®æ”¹çŠ¶æ€ä¸ºfalse
            missionStatusMap[mission.missionCode] = false
        }
    }
    /**
     * èŽ·å–ä»»åŠ¡çš„æ‰€æœ‰æ•°æ®
     * @param missionCode ä»»åŠ¡ç¼–å·
     * @param forceUpdate å¼ºåˆ¶å°†èŽ·å–çš„æ•°æ®æ›¿æ¢åŽŸæœ‰çš„æ•°æ®
     */
    fun fetchMissionData(missionCode: String, forceUpdate: Boolean = false) {
        val mission = missionRep.findOne(missionCode) ?: throw BizException("任务不存在,无法获取第三方数据")
        fetchMissionData(mission, forceUpdate)
    }
}
src/main/kotlin/com/flightfeather/uav/biz/report/MissionReport.kt
@@ -149,12 +149,12 @@
        // 4. ç”ŸæˆæŠ¥å‘Š
        val fileName = "report/" + "${mission.districtName}走航监测报告-${
            DateUtil.instance.dateToString(mission.startTime, DateUtil.DateStyle.YYYY_MM_DD)
        }.doc"
        }(${mission.missionCode}).doc"
        val reportTemplate = ReportTemplate(templateName, filePath, fileName)
        val param = getParam(mission, exceptions, summaries)
        val params = reportTemplate.getParam(param)
        GeneratePdfUtil.generateWord(params)
        return fileName
        return filePath + fileName
    }
    private fun getParam(mission: Mission, exceptions: List<ExceptionResult>, summaries: List<Summary>): Param {
src/main/kotlin/com/flightfeather/uav/common/net/AMapService.kt
@@ -11,6 +11,7 @@
/**
 * é«˜å¾·åœ°å›¾Web服务API
 * Date: 2024/07/14
 */
object AMapService {
src/main/kotlin/com/flightfeather/uav/common/net/ShenXinService.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,128 @@
package com.flightfeather.uav.common.net
import com.flightfeather.uav.common.exception.BizException
import com.flightfeather.uav.common.utils.DateUtil
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.domain.entity.Mission
import com.flightfeather.uav.domain.entity.RealTimeDataVehicle
import org.w3c.dom.Element
import java.math.BigDecimal
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.*
import javax.xml.soap.Node
/**
 * ç”³æ¬£çŽ¯ä¿èµ°èˆªç›‘æµ‹æ•°æ®æŽ¥å£API
 * Date: 2024/08/22
 */
object ShenXinService {
    private const val USER = "user1"
    private const val PW = "User1@jingan"
    //    "
    private val soapClient = SoapClient(
        "http://tempuri.org/",
        "http://180.169.129.106:9001/WebService.asmx?wsdl",
        "WebService", "WebServiceSoap", "GetData",
        "GetDataResponse"
    )
    data class DataParams(
        val mn: String?,
        val dtFrom: String?,
        val dtTo: String?,
        val compUser: String = USER,
        val compPassword: String = PW,
    ) {
        fun toMap(): MutableMap<String?, String?> {
            return mutableMapOf(
                "compUser" to compUser,
                "compPassword" to compPassword,
                "mn" to mn,
                "dtFrom" to dtFrom,
                "dtTo" to dtTo,
            )
        }
    }
    fun fetchData(mission: Mission, updateTime: LocalDateTime): List<BaseRealTimeData> {
        val et = LocalDateTime.ofInstant(mission.endTime.toInstant(), ZoneId.systemDefault())
        if (updateTime.isAfter(et)) return emptyList()
        return fetchData(mission.deviceCode, updateTime, et)
    }
    fun fetchData(deviceCode: String?, startTime: LocalDateTime, endTime:LocalDateTime): List<BaseRealTimeData> {
        val st = startTime.format(DateTimeFormatter.ofPattern(DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS.value))
        val et = endTime.format(DateTimeFormatter.ofPattern(DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS.value))
        val param = DataParams(deviceCode, st, et)
        val realTimeDataVehicleList = mutableListOf<BaseRealTimeData>()
        soapClient.sendMessage(param.toMap())?.let { res ->
            try {
                // èŠ‚ç‚¹GetDataResult
                val result = res.item(0).firstChild
                // èŠ‚ç‚¹ReturnCode,0表示成功
                if (result.firstChild.textContent == "0") {
//                    println(result.childNodes.item(2).nodeName)
                    // èŠ‚ç‚¹ListInfo内的数据列表
                    val dataNodes = result.childNodes.item(2).childNodes
                    // éåŽ†æ¯ä¸ªDataInfo节点,转换为 [RealTimeDataVehicle] å¯¹è±¡
                    for (i in 0 until dataNodes.length) {
                        val dataInfo = dataNodes.item(i)
                        if (dataInfo.nodeType == Element.ELEMENT_NODE) {
                            val element = dataInfo as Element
                            realTimeDataVehicleList.add(elementToRealTimeData(deviceCode, element))
                        }
                    }
                }
            } catch (e: Exception) {
                throw BizException("申欣环保走航监测数据接口解析失败", e)
            }
        }
        return realTimeDataVehicleList
    }
    /**
     * DataInfo转换为RealTimeDataVehicle
     */
    private fun elementToRealTimeData(deviceCode: String?, element: Element): BaseRealTimeData {
        return BaseRealTimeData().apply {
            this.deviceCode = deviceCode
            no = toFloatBlank(element.getElementsByTagName("NO").item(0).textContent)
            no2 = toFloatBlank(element.getElementsByTagName("NO2").item(0).textContent)
            co = toFloatBlank(element.getElementsByTagName("CO").item(0).textContent) * 1000
            pm25 = toFloatBlank(element.getElementsByTagName("PM25").item(0).textContent)
            pm10 = toFloatBlank(element.getElementsByTagName("PM10").item(0).textContent)
            longitude = toBigDecimal(element.getElementsByTagName("Lon").item(0).textContent)
            latitude = toBigDecimal(element.getElementsByTagName("Lat").item(0).textContent)
            temperature = toFloatBlank(element.getElementsByTagName("Temperature").item(0).textContent)
            velocity = toFloatBlank(element.getElementsByTagName("Speed").item(0).textContent)
            windSpeed = toFloatBlank(element.getElementsByTagName("WindSpeed").item(0).textContent)
            windDirection = toFloatBlank(element.getElementsByTagName("WindDirection").item(0).textContent)
            dataTime = DateUtil.instance.StringToDate(
                element.getElementsByTagName("PostTime").item(0).textContent,
                DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS_EN)
            createTime = Date()
        }
    }
    private fun toFloatBlank(str: String): Float {
        val _str = str.trim()
        return if (_str.isBlank()) {
            0f
        } else {
            _str.toFloat()
        }
    }
    private fun toBigDecimal(str: String): BigDecimal {
        val _str = str.trim()
        return if (_str.isBlank()) {
            BigDecimal.ZERO
        } else {
            _str.toBigDecimal()
        }
    }
}
src/main/kotlin/com/flightfeather/uav/common/net/SoapClient.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,140 @@
package com.flightfeather.uav.common.net
import com.flightfeather.uav.common.exception.BizException
import com.sun.xml.internal.ws.client.BindingProviderProperties
import com.sun.xml.internal.ws.developer.JAXWSProperties
import javax.xml.soap.*
import javax.xml.ws.Dispatch
import javax.xml.ws.Service
import org.w3c.dom.Document
import org.w3c.dom.NodeList
import java.net.URL
import javax.xml.namespace.QName
import javax.xml.ws.BindingProvider
import javax.xml.ws.BindingProvider.SOAPACTION_URI_PROPERTY
import javax.xml.ws.BindingProvider.SOAPACTION_USE_PROPERTY
import javax.xml.ws.soap.SOAPBinding
/**
 * soap客户端
 * @date 2024/8/22
 * @author feiyu02
 */
class SoapClient {
    var nameSpace = "" //wsdl的命名空间
    var wsdlUrl = "" //wsdl文档地址
    var serviceName = "" //服务的名字
    var portName = ""
    var responseName = "" //@WebResult:注解上的name值
    var elementName = "" //默认是要访问的方法名 å¦‚æžœ@WebMethod属性name有值 åˆ™æ˜¯è¯¥å€¼ï¼Œå®žé™…还是以wsdl文档为主
    var timeout = 20000
    /**
     * @param nameSpace
     * @param wsdlUrl
     * @param serviceName
     * @param portName
     * @param element
     * @param responseName
     */
    constructor(
        nameSpace: String, wsdlUrl: String,
        serviceName: String, portName: String, element: String,
        responseName: String,
    ) {
        this.nameSpace = nameSpace
        this.wsdlUrl = wsdlUrl
        this.serviceName = serviceName
        this.portName = portName
        elementName = element
        this.responseName = responseName
    }
    /**
     * @param nameSpace
     * @param wsdlUrl
     * @param serviceName
     * @param portName
     * @param element
     * @param responseName
     * @param timeOut      æ¯«ç§’
     */
    constructor(
        nameSpace: String, wsdlUrl: String,
        serviceName: String, portName: String, element: String,
        responseName: String, timeOut: Int,
    ) {
        this.nameSpace = nameSpace
        this.wsdlUrl = wsdlUrl
        this.serviceName = serviceName
        this.portName = portName
        elementName = element
        this.responseName = responseName
        timeout = timeOut
    }
    @Throws(Exception::class)
    fun sendMessage(inMsg: Map<String?, String?>): NodeList? {
        // åˆ›å»ºURL对象
        var url: URL? = null
        url = try {
            URL(wsdlUrl)
        } catch (e: Exception) {
            throw BizException("soap:创建URL对象异常", e)
        }
        // åˆ›å»ºæœåŠ¡(Service)
        val sname = QName(nameSpace, serviceName)
        val service: Service = Service.create(url, sname)
        // åˆ›å»ºDispatch对象
        var dispatch: Dispatch<SOAPMessage?>? = null
        dispatch = try {
            service.createDispatch(QName(nameSpace, portName), SOAPMessage::class.java, Service.Mode.MESSAGE)
        } catch (e: Exception) {
            throw BizException("soap:创建Dispatch对象异常", e)
        }
        // åˆ›å»ºSOAPMessage
        return try {
            //这句话很重要,否则报错服务器未能识别 HTTP å¤´ SOAPAction çš„值
            dispatch?.requestContext?.put(SOAPACTION_URI_PROPERTY, nameSpace + elementName)
            dispatch?.requestContext?.put(SOAPACTION_USE_PROPERTY, true)
            dispatch?.requestContext?.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, wsdlUrl.replace("?wsdl", ""))
            val msg: SOAPMessage = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL).createMessage()
            msg.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8")
            val envelope: SOAPEnvelope = msg.getSOAPPart().getEnvelope()
            // åˆ›å»ºSOAPHeader(不是必需)
            // SOAPHeader header = envelope.getHeader();
            // if (header == null)
            // header = envelope.addHeader();
            // QName hname = new QName(nameSpace, "username", "nn");
            // header.addHeaderElement(hname).setValue("huoyangege");
            // åˆ›å»ºSOAPBody
            val body: SOAPBody = envelope.getBody()
            val ename = QName(nameSpace, elementName, "")
            val ele: SOAPBodyElement = body.addBodyElement(ename)
            // å¢žåŠ Body元素和值
            inMsg.forEach { (key, value) ->
                ele.addChildElement(QName(nameSpace, key)).value = value
            }
            // è¶…时设置
            dispatch?.requestContext?.put(BindingProviderProperties.CONNECT_TIMEOUT, timeout)
            dispatch?.requestContext?.put(JAXWSProperties.REQUEST_TIMEOUT, timeout)
            // é€šè¿‡Dispatch传递消息,会返回响应消息
            val response = dispatch?.invoke(msg)
            // å“åº”消息处理,将响应的消息转换为doc对象
            val doc = response?.soapPart?.envelope?.body?.extractContentAsDocument()
//            doc?.getElementsByTagName(responseName)?.item(0)?.textContent ?: ""
            doc?.getElementsByTagName(responseName)
        } catch (e: Exception) {
            throw BizException("soap:接口访问异常", e)
        }
    }
}
src/main/kotlin/com/flightfeather/uav/domain/entity/BaseRealTimeData.kt
@@ -69,6 +69,9 @@
    @Column(name = "NOI")
    var noi: Float? = null
    @Column(name = "NO")
    var no: Float? = null
    var velocity: Float? = null
    @Column(name = "wind_speed")
src/main/kotlin/com/flightfeather/uav/domain/repository/RealTimeDataRep.kt
@@ -1,5 +1,6 @@
package com.flightfeather.uav.domain.repository
import com.flightfeather.uav.common.exception.BizException
import com.flightfeather.uav.domain.entity.*
import com.flightfeather.uav.domain.mapper.RealTimeDataGridMapper
import com.flightfeather.uav.domain.mapper.RealTimeDataGridMinMapper
@@ -9,6 +10,8 @@
import com.github.pagehelper.PageHelper
import org.springframework.stereotype.Repository
import tk.mybatis.mapper.entity.Example
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
/**
@@ -22,9 +25,19 @@
    private val realTimeDataGridMinMapper: RealTimeDataGridMinMapper,
) {
    private fun getSecondDataExample(example: Example, deviceCode: String?, sTime: Date?, eTime: Date?) {
    private val delegate = RealTimeDataRepDelegate(realTimeDataVehicleMapper, realTimeDataUavMapper,
        realTimeDataGridMapper, realTimeDataGridMinMapper)
    fun fetchData(
        deviceType: UWDeviceType?, deviceCode: String, sTime: Date? = null, eTime: Date? = null, type: Int? = 0,
        page: Int? = null, perPage: Int? = null,
    ): List<BaseRealTimeData> {
        if (page != null && perPage != null) {
            var pageInfo = PageHelper.startPage<BaseRealTimeData>(page, perPage)
        }
        return delegate.selectByDeviceType(deviceType, type) { example ->
        example.createCriteria().apply {
            deviceCode?.let { andEqualTo("deviceCode", it) }
                andEqualTo("deviceCode", deviceCode)
            sTime?.let { andGreaterThanOrEqualTo("dataTime", it) }
            eTime?.let { andLessThanOrEqualTo("dataTime", it) }
        }
@@ -36,42 +49,82 @@
        }
    }
    fun fetchData(
        deviceCode: String,
        sTime: Date?,
        eTime: Date?,
        type: Int? = 0,
    ): List<BaseRealTimeData> {
        var result = listOf<BaseRealTimeData>()
        when (UWDeviceType.getType(deviceCode)) {
            UWDeviceType.VEHICLE -> {
                result = realTimeDataVehicleMapper.selectByExample(Example(RealTimeDataVehicle::class.java).apply {
                    getSecondDataExample(this, deviceCode, sTime, eTime)
                })
            }
            UWDeviceType.UAV -> {
                result = realTimeDataUavMapper.selectByExample(Example(RealTimeDataUav::class.java).apply {
                    getSecondDataExample(this, deviceCode, sTime, eTime)
                })
            }
            UWDeviceType.GRID -> {
                // ç½‘格化监测秒级值
                result = if (type == null || type == 0) {
                    realTimeDataGridMapper.selectByExample(Example(RealTimeDataGrid::class.java).apply {
                        getSecondDataExample(this, deviceCode, sTime, eTime)
                    })
                }
                // ç½‘格化监测分钟值
                else {
                    realTimeDataGridMinMapper.selectByExample(Example(RealTimeDataGridMin::class.java).apply {
                        getSecondDataExample(this, deviceCode, sTime, eTime)
                    })
                }
            }
            else -> Unit
        }
        return result
//        var result = listOf<BaseRealTimeData>()
//        when (deviceType) {
//            UWDeviceType.VEHICLE -> {
//                result = realTimeDataVehicleMapper.selectByExample(Example(RealTimeDataVehicle::class.java).apply {
//                    getSecondDataExample(this, deviceCode, sTime, eTime)
//                })
//            }
//            UWDeviceType.UAV -> {
//                result = realTimeDataUavMapper.selectByExample(Example(RealTimeDataUav::class.java).apply {
//                    getSecondDataExample(this, deviceCode, sTime, eTime)
//                })
//            }
//            UWDeviceType.GRID -> {
//                // ç½‘格化监测秒级值
//                result = if (type == null || type == 0) {
//                    realTimeDataGridMapper.selectByExample(Example(RealTimeDataGrid::class.java).apply {
//                        getSecondDataExample(this, deviceCode, sTime, eTime)
//                    })
//                }
//                // ç½‘格化监测分钟值
//                else {
//                    realTimeDataGridMinMapper.selectByExample(Example(RealTimeDataGridMin::class.java).apply {
//                        getSecondDataExample(this, deviceCode, sTime, eTime)
//                    })
//                }
//            }
//            else -> Unit
//        }
//        return result
    }
    fun fetchData(mission: Mission) = fetchData(mission.deviceCode, mission.startTime, mission.endTime)
    fun fetchData(mission: Mission) =
        fetchData(UWDeviceType.fromValue(mission.deviceType), mission.deviceCode, mission.startTime, mission.endTime)
    fun saveData(deviceType: UWDeviceType?, data: List<BaseRealTimeData>, type: Int? = 0): Int {
        return delegate.insertByDeviceType(deviceType, type, data)
//        return when (deviceType) {
//            UWDeviceType.UAV -> realTimeDataUavMapper.insertList(data as List<RealTimeDataUav>)
//            UWDeviceType.VEHICLE -> realTimeDataVehicleMapper.insertList(data as List<RealTimeDataVehicle>)
//            UWDeviceType.GRID -> realTimeDataGridMapper.insertList(data as List<RealTimeDataGrid>)
//            UWDeviceType.BOAT -> 0
//            else -> 0
//        }
    }
    fun deleteData(mission: Mission, type: Int? = 0): Int {
        if (mission.deviceCode == null || mission.startTime == null || mission.endTime == null) {
            throw BizException("要删除的走航任务缺失设备编号或采样时间范围,无法删除对应监测数据")
        }
        return deleteData(UWDeviceType.fromValue(mission.deviceType),
            mission.deviceCode,
            mission.startTime,
            mission.endTime, type)
    }
    fun deleteData(deviceType: UWDeviceType?, deviceCode: String, sTime: Date?, eTime: Date?, type: Int? = 0): Int {
        return delegate.deleteByDeviceType(deviceType, type) {
            it.createCriteria().apply {
                andEqualTo("deviceCode", deviceCode)
                andGreaterThanOrEqualTo("dataTime", sTime)
                andLessThanOrEqualTo("dataTime", eTime)
            }
        }
    }
    fun deleteData(
        deviceType: UWDeviceType?, deviceCode: String, sTime: LocalDateTime?, eTime: LocalDateTime?, type: Int? = 0,
    ): Int = deleteData(deviceType, deviceCode,
        Date.from(sTime?.atZone(ZoneId.systemDefault())?.toInstant()),
        Date.from(eTime?.atZone(ZoneId.systemDefault())?.toInstant()),
        type
    )
    fun deleteData(deviceType: UWDeviceType?, data: List<BaseRealTimeData>, type: Int? = 0) {
        delegate.deleteByDeviceType(deviceType, type, data)
    }
}
src/main/kotlin/com/flightfeather/uav/domain/repository/RealTimeDataRepDelegate.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,134 @@
package com.flightfeather.uav.domain.repository
import com.flightfeather.uav.domain.entity.*
import com.flightfeather.uav.domain.mapper.RealTimeDataGridMapper
import com.flightfeather.uav.domain.mapper.RealTimeDataGridMinMapper
import com.flightfeather.uav.domain.mapper.RealTimeDataUavMapper
import com.flightfeather.uav.domain.mapper.RealTimeDataVehicleMapper
import com.flightfeather.uav.socket.eunm.UWDeviceType
import tk.mybatis.mapper.entity.Example
import java.util.*
/**
 * å®žæ—¶èµ°èˆªæ•°æ®æ•°æ®åº“相关操作代理类
 * @date 2024/8/27
 * @author feiyu02
 */
class RealTimeDataRepDelegate(
    private val realTimeDataVehicleMapper: RealTimeDataVehicleMapper,
    private val realTimeDataUavMapper: RealTimeDataUavMapper,
    private val realTimeDataGridMapper: RealTimeDataGridMapper,
    private val realTimeDataGridMinMapper: RealTimeDataGridMinMapper,
) {
    /**
     * æ ¹æ®è®¾å¤‡ç±»åž‹æ‰§è¡Œä¸åŒæ•°æ®åº“操作步骤
     * å„类设备的监测数据虽分不同数据表存储,但数据表结构相同
     * @param deviceType è®¾å¤‡ç±»åž‹
     * @param type æ•°æ®ç»Ÿè®¡å‘¨æœŸï¼Œ0:秒级值;1:分钟值
     * @param callbacks é’ˆå¯¹ä¸åŒç±»åž‹çš„设备对应的不同回调函数
     */
    private fun <T> byDeviceType(deviceType: UWDeviceType?, type: Int? = 0, vararg callbacks: () -> T): T? {
        return when (deviceType) {
            UWDeviceType.VEHICLE -> {
                if (callbacks.isNotEmpty()) callbacks[0].invoke() else null
            }
            UWDeviceType.UAV -> {
                if (callbacks.size > 1) callbacks[1].invoke() else null
            }
            UWDeviceType.GRID -> {
                // ç½‘格化监测秒级值
                if (type == null || type == 0) {
                    if (callbacks.size > 2) callbacks[2].invoke() else null
                }
                // ç½‘格化监测分钟值
                else {
                    if (callbacks.size > 3) callbacks[3].invoke() else null
                }
            }
            else -> null
        }
    }
    /**
     * ç»Ÿä¸€çš„select方法
     * @param deviceType è®¾å¤‡ç±»åž‹
     * @param type æ•°æ®ç»Ÿè®¡å‘¨æœŸï¼Œ0:秒级值;1:分钟值
     * @param condition æŸ¥è¯¢æ¡ä»¶
     */
    fun selectByDeviceType(
        deviceType: UWDeviceType?, type: Int? = 0,
        condition: (example: Example) -> Unit,
    ): List<BaseRealTimeData> {
        return byDeviceType(deviceType, type, {
            realTimeDataVehicleMapper.selectByExample(Example(RealTimeDataVehicle::class.java).apply { condition(this) })
        }, {
            realTimeDataUavMapper.selectByExample(Example(RealTimeDataUav::class.java).apply { condition(this) })
        }, {
            realTimeDataGridMapper.selectByExample(Example(RealTimeDataGrid::class.java).apply { condition(this) })
        }, {
            realTimeDataGridMinMapper.selectByExample(Example(RealTimeDataGridMin::class.java).apply { condition(this) })
        }) ?: emptyList()
    }
    /**
     * ç»Ÿä¸€çš„insert方法
     * @param deviceType è®¾å¤‡ç±»åž‹
     * @param type æ•°æ®ç»Ÿè®¡å‘¨æœŸï¼Œ0:秒级值;1:分钟值
     * @param data æ•°æ®
     */
    fun insertByDeviceType(deviceType: UWDeviceType?, type: Int? = 0, data: List<BaseRealTimeData>): Int {
        return byDeviceType(deviceType, type, {
            realTimeDataVehicleMapper.insertList(data as List<RealTimeDataVehicle>)
        }, {
            realTimeDataUavMapper.insertList(data as List<RealTimeDataUav>)
        }, {
            realTimeDataGridMapper.insertList(data as List<RealTimeDataGrid>)
        }, {
            realTimeDataGridMinMapper.insertList(data as List<RealTimeDataGridMin>)
        }) ?: 0
    }
    /**
     * ç»Ÿä¸€çš„delete方法
     * @param deviceType è®¾å¤‡ç±»åž‹
     * @param type æ•°æ®ç»Ÿè®¡å‘¨æœŸï¼Œ0:秒级值;1:分钟值
     * @param data æ•°æ®
     */
    fun deleteByDeviceType(deviceType: UWDeviceType?, type: Int? = 0, data: List<BaseRealTimeData>): Int {
        return byDeviceType(deviceType, type, {
            var count = 0
            data.forEach { count += realTimeDataVehicleMapper.delete(it as RealTimeDataVehicle) }
            count
        }, {
            var count = 0
            data.forEach { count += realTimeDataUavMapper.delete(it as RealTimeDataUav) }
            count
        }, {
            var count = 0
            data.forEach { count += realTimeDataGridMapper.delete(it as RealTimeDataGrid) }
            count
        }, {
            var count = 0
            data.forEach { count += realTimeDataGridMinMapper.delete(it as RealTimeDataGridMin) }
            count
        }) ?: 0
    }
    /**
     * ç»Ÿä¸€çš„delete方法
     * @param deviceType è®¾å¤‡ç±»åž‹
     * @param type æ•°æ®ç»Ÿè®¡å‘¨æœŸï¼Œ0:秒级值;1:分钟值
     * @param condition æŸ¥è¯¢æ¡ä»¶
     */
    fun deleteByDeviceType(deviceType: UWDeviceType?, type: Int? = 0, condition: (example: Example) -> Unit,): Int {
        return byDeviceType(deviceType, type, {
            realTimeDataVehicleMapper.deleteByExample(Example(RealTimeDataVehicle::class.java).apply { condition(this) })
        }, {
            realTimeDataUavMapper.deleteByExample(Example(RealTimeDataUav::class.java).apply { condition(this) })
        }, {
            realTimeDataGridMapper.deleteByExample(Example(RealTimeDataGrid::class.java).apply { condition(this) })
        }, {
            realTimeDataGridMinMapper.deleteByExample(Example(RealTimeDataGridMin::class.java).apply { condition(this) })
        }) ?: 0
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/eunm/ThirdPartyLabel.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
package com.flightfeather.uav.lightshare.eunm
/**
 * ç¬¬ä¸‰æ–¹æ•°æ®æŽ¥å£æ ‡è¯†
 * @date 2024/8/26
 * @author feiyu02
 */
enum class ThirdPartyLabel(val value: String) {
    ShenXin("shenxin")
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/ThirdPartyService.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.flightfeather.uav.lightshare.service
import com.flightfeather.uav.socket.eunm.UWDeviceType
import java.time.LocalDateTime
/**
 *
 * @date 2024/8/26
 * @author feiyu02
 */
interface ThirdPartyService {
    /**
     * èŽ·å–èµ°èˆªä»»åŠ¡å¯¹åº”çš„èŒƒå›´å†…çš„æ•°æ®
     * @param label æŽ¥å£æ ‡ç­¾ï¼Œè¡¨æ˜Žæ˜¯å“ªä¸ªç¬¬ä¸‰æ–¹æ•°æ®æŽ¥å£
     * @param missionCode ä»»åŠ¡ç¼–å·
     */
    fun fetchMissionData(label: String, missionCode: String): Boolean
    /**
     * èŽ·å–è®¾å¤‡æœ€æ–°æ•°æ®
     * @param label æŽ¥å£æ ‡ç­¾ï¼Œè¡¨æ˜Žæ˜¯å“ªä¸ªç¬¬ä¸‰æ–¹æ•°æ®æŽ¥å£
     * @param type è®¾å¤‡ç±»åž‹
     * @param deviceCode è®¾å¤‡ç¼–号
     * @param startTime æ•°æ®èµ·å§‹æ—¶é—´
     */
    fun fetchLatestData(
        label: String, type: UWDeviceType, deviceCode: String, startTime: LocalDateTime?, endTime: LocalDateTime?,
    ): Boolean
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImpl.kt
@@ -13,6 +13,8 @@
import com.github.pagehelper.PageHelper
import org.springframework.stereotype.Service
import tk.mybatis.mapper.entity.Example
import java.io.File
import java.util.*
import javax.servlet.http.HttpServletResponse
@Service
@@ -35,6 +37,7 @@
        return BaseResponse(true, head = DataHead(pageInfo.pageNum, pageInfo.pages),data = result)
    }
    @Synchronized
    override fun createMission(mission: Mission): BaseResponse<Boolean> {
        missionMapper.selectByPrimaryKey(mission.missionCode)?.run {
            return BaseResponse(false, "任务编号已存在")
@@ -61,6 +64,15 @@
    }
    override fun getReport(missionCode: String, response: HttpServletResponse) {
        missionReport.execute(missionCode, FactorFilter.default())
        val path = missionReport.execute(missionCode, FactorFilter.default())
        val pathArr = path.split("/")
        val file = File(path)
        if (file.exists()) {
            response.contentType = "application/vnd.ms-excel;charset=UTF-8"
            val name = Base64.getEncoder().encodeToString(pathArr.last().toByteArray())
            response.setHeader("fileName", name)
            response.outputStream.write(file.readBytes())
        }
        return
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/ThirdPartyServiceImpl.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package com.flightfeather.uav.lightshare.service.impl
import com.flightfeather.uav.biz.datafetch.ShenXinDataFetch
import com.flightfeather.uav.common.exception.BizException
import com.flightfeather.uav.lightshare.eunm.ThirdPartyLabel
import com.flightfeather.uav.lightshare.service.ThirdPartyService
import com.flightfeather.uav.socket.eunm.UWDeviceType
import org.springframework.stereotype.Service
import java.time.LocalDateTime
/**
 *
 * @date 2024/8/26
 * @author feiyu02
 */
@Service
class ThirdPartyServiceImpl(private val shenXinDataFetch: ShenXinDataFetch) : ThirdPartyService {
    override fun fetchMissionData(label: String, missionCode: String): Boolean {
        when (label) {
            ThirdPartyLabel.ShenXin.value -> {
                shenXinDataFetch.fetchMissionData(missionCode)
                return true
            }
            else -> throw BizException("第三方接口标识不存在")
        }
    }
    override fun fetchLatestData(
        label: String, type: UWDeviceType, deviceCode: String, startTime: LocalDateTime?, endTime: LocalDateTime?,
    ): Boolean {
        when (label) {
            ThirdPartyLabel.ShenXin.value -> {
                shenXinDataFetch.fetchLatestData(type, deviceCode, startTime, endTime)
                return true
            }
            else -> throw BizException("第三方接口标识不存在")
        }
    }
}
src/main/kotlin/com/flightfeather/uav/lightshare/web/ThirdPartyController.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package com.flightfeather.uav.lightshare.web
import com.fasterxml.jackson.annotation.JsonFormat
import com.flightfeather.uav.common.exception.BizException
import com.flightfeather.uav.lightshare.service.ThirdPartyService
import com.flightfeather.uav.socket.eunm.UWDeviceType
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.time.LocalDateTime
/**
 * ç¬¬ä¸‰æ–¹å¯¹æŽ¥ç›¸å…³æŽ¥å£ï¼ŒåŒ…含
 * 1. ç”³æ¬£çŽ¯ä¿èµ°èˆªæ•°æ®ç›¸å…³æŽ¥å£
 * @date 2024/8/26
 * @author feiyu02
 */
@Api(tags = ["第三方对接相关API接口"])
@RestController
@RequestMapping("air/thirdParty")
class ThirdPartyController(private val thirdPartyService: ThirdPartyService) {
    @ApiOperation(value = "根据任务信息获取对应范围内的第三方走航数据")
    @GetMapping("/data/fetch/mission")
    fun fetchMissionData(
        @RequestParam label: String,
        @RequestParam missionCode: String,
    ) = resPack { thirdPartyService.fetchMissionData(label, missionCode) }
    @ApiOperation(value = "获取实时最新的第三方走航数据")
    @GetMapping("/data/fetch/latest")
    fun fetchLatestData(
        @RequestParam label: String,
        @RequestParam deviceType: String,
        @RequestParam deviceCode: String,
        @RequestParam(required = false) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") startTime: LocalDateTime?,
        @RequestParam(required = false) @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") endTime: LocalDateTime?,
    ) = resPack {
        val type = UWDeviceType.fromValue(deviceType) ?: throw BizException("设备类型不存在")
        thirdPartyService.fetchLatestData(label, type, deviceCode, startTime, endTime)
    }
}
src/main/kotlin/com/flightfeather/uav/scheduler/ScheduleService.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.flightfeather.uav.scheduler
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.scheduling.annotation.Async
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import java.time.LocalDate
import java.time.LocalDateTime
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦
 * ä¸€ä¸ªcron表达式有至少6个(也可能7个)有空格分隔的时间元素。按顺序依次为:
 * ç§’(0~59)
 * åˆ†é’Ÿï¼ˆ0~59)
 * å°æ—¶ï¼ˆ0~23)
 * å¤©ï¼ˆ1~31)
 * æœˆï¼ˆ1~12)
 * æ˜ŸæœŸï¼ˆ1~7 1=SUN æˆ– SUN,MON,TUE,WED,THU,FRI,SAT)
 */
@Component
class ScheduleService(
    @Value("\${mode}")
    var mode: String,
) {
    val logger: Logger = LoggerFactory.getLogger(ScheduleService::class.java)
//    @Async
//    @Scheduled(cron = "0 0 0 * * *")
    fun eachDay() {
        if (mode != "pro") return
        logger.info("=====>>>>>每日任务执行 {}", System.currentTimeMillis())
        logger.info("=====>>>>>每日任务结束 {}", System.currentTimeMillis())
    }
//    @Async
//    @Scheduled(cron = "0 0 0 * * MON")
    fun eachWeek() {
        if (mode != "pro") return
        logger.info("=====>>>>>每周任务执行 {}", System.currentTimeMillis())
        // æ‰§è¡Œä¸Šå‘¨çš„自评任务
        logger.info("=====>>>>>每周任务结束 {}", System.currentTimeMillis())
    }
//    @Async
//    @Scheduled(cron = "0 0 0 1 * *")
    fun eachStartOfMonth() {
        if (mode != "pro") return
        logger.info("=====>>>>>每月1号任务执行 {}", System.currentTimeMillis())
        logger.info("=====>>>>>每月1号任务结束 {}", System.currentTimeMillis())
    }
//    @Async
//    @Scheduled(cron = "0 0 0 2 * *")
    fun eachMonth() {
        if (mode != "pro") return
        logger.info("=====>>>>>每月2号任务执行 {}", System.currentTimeMillis())
        logger.info("=====>>>>>每月2号任务结束 {}", System.currentTimeMillis())
    }
}
src/main/kotlin/com/flightfeather/uav/socket/eunm/UWDeviceType.kt
@@ -13,7 +13,9 @@
        /**
         * æ ¹æ®è®¾å¤‡ç¼–号获取设备类型
         */
        fun getType(deviceCode: String?): UWDeviceType? = when (deviceCode?.substring(0, 2)) {
        fun getType(deviceCode: String?): UWDeviceType? = fromValue(deviceCode?.substring(0, 2))
        fun fromValue(value: String?): UWDeviceType? = when (value) {
            UAV.value -> UAV
            VEHICLE.value -> VEHICLE
            BOAT.value -> BOAT
src/main/resources/application-dev.yml
@@ -13,3 +13,4 @@
imgPath: target/
filePath: target/
mode: dev
src/main/resources/application-pro.yml
@@ -1,14 +1,14 @@
spring:
  datasource:
    #    çº¿ä¸ŠæœåС噍 47
    url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: dronemonitor
    password: dronemonitor_hackxrnomxm
    #    çº¿ä¸ŠæœåС噍 114
#    url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
#    username: dronemonitor
#    password: dronemonitor_hackxrnomxm
    #    çº¿ä¸ŠæœåС噍 114
    url: jdbc:mysql://localhost:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: dronemonitor
    password: dronemonitor_hackxrnomxm
springfox:
  documentation:
@@ -18,3 +18,4 @@
imgPath: D:/02product/10underway/images/
filePath: D:/02product/10underway/files/
mode: pro
src/main/resources/application-test.yml
@@ -7,13 +7,13 @@
    #    password: cn.FLIGHTFEATHER
    #   è¿œç¨‹æœåС噍
    url: jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: remoteU1
    password: eSoF8DnzfGTlhAjE
#    url: jdbc:mysql://114.215.109.124:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
#    url: jdbc:mysql://47.100.191.150:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
#    username: remoteU1
#    password: feiyu2024
#    password: eSoF8DnzfGTlhAjE
    url: jdbc:mysql://114.215.109.124:3306/dronemonitor?serverTimezone=Asia/Shanghai&prepStmtCacheSize=517&cachePrepStmts=true&autoReconnect=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: remoteU1
    password: feiyu2024
springfox:
  documentation:
@@ -23,3 +23,4 @@
imgPath: C:/02product/10underway/images/
filePath: C:/02product/10underway/files/
mode: test
src/main/resources/generator/generatorConfig.xml
@@ -54,13 +54,13 @@
<!--        <table tableName="co_assessment" domainObjectName="Assessment" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
<!--        <table tableName="real_time_data_grid" domainObjectName="RealTimeDataGrid" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
<!--        <table tableName="real_time_data_uav" domainObjectName="RealTimeDataUav" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
<!--        <table tableName="real_time_data_vehicle" domainObjectName="RealTimeDataVehicle" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
        <table tableName="real_time_data_vehicle" domainObjectName="RealTimeDataVehicle" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>
<!--        <table tableName="device_info" domainObjectName="DeviceInfo" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
<!--        <table tableName="factor_calibration" domainObjectName="FactorCalibration" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
<!--        <table tableName="real_time_data_grid_min" domainObjectName="RealTimeDataGridMin" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
<!--        <table tableName="real_time_data_grid_opt" domainObjectName="RealTimeDataGridOpt" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
<!--        <table tableName="scene_info" domainObjectName="SceneInfo" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
        <table tableName="segment_info" domainObjectName="SegmentInfo" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>
<!--        <table tableName="segment_info" domainObjectName="SegmentInfo" enableCountByExample="false"-->
<!--               enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>-->
    </context>
</generatorConfiguration>
src/main/resources/mapper/RealTimeDataGridMapper.xml
@@ -23,6 +23,7 @@
    <result column="humidity" jdbcType="REAL" property="humidity" />
    <result column="VOC" jdbcType="REAL" property="voc" />
    <result column="NOI" jdbcType="REAL" property="noi" />
    <result column="NO" jdbcType="REAL" property="no" />
    <result column="velocity" jdbcType="REAL" property="velocity" />
    <result column="wind_speed" jdbcType="REAL" property="windSpeed" />
    <result column="wind_direction" jdbcType="REAL" property="windDirection" />
@@ -33,7 +34,7 @@
      WARNING - @mbg.generated
    -->
    id, device_code, latitude, longitude, altitude, data_time, create_time, NO2, CO, 
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, velocity, wind_speed,
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, NO, velocity, wind_speed,
    wind_directon, height
  </sql>
</mapper>
src/main/resources/mapper/RealTimeDataGridMinMapper.xml
@@ -23,6 +23,7 @@
    <result column="humidity" jdbcType="REAL" property="humidity" />
    <result column="VOC" jdbcType="REAL" property="voc" />
    <result column="NOI" jdbcType="REAL" property="noi" />
    <result column="NO" jdbcType="REAL" property="no" />
    <result column="velocity" jdbcType="REAL" property="velocity" />
    <result column="wind_speed" jdbcType="REAL" property="windSpeed" />
    <result column="wind_direction" jdbcType="REAL" property="windDirection" />
@@ -33,7 +34,7 @@
      WARNING - @mbg.generated
    -->
    id, device_code, latitude, longitude, altitude, data_time, create_time, NO2, CO, 
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, velocity, wind_speed,
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, NO, velocity, wind_speed,
    wind_direction, height
  </sql>
</mapper>
src/main/resources/mapper/RealTimeDataGridOptMapper.xml
@@ -23,6 +23,7 @@
    <result column="humidity" jdbcType="REAL" property="humidity" />
    <result column="VOC" jdbcType="REAL" property="voc" />
    <result column="NOI" jdbcType="REAL" property="noi" />
    <result column="NO" jdbcType="REAL" property="no" />
    <result column="velocity" jdbcType="REAL" property="velocity" />
    <result column="wind_speed" jdbcType="REAL" property="windSpeed" />
    <result column="wind_direction" jdbcType="REAL" property="windDirection" />
@@ -33,7 +34,7 @@
      WARNING - @mbg.generated
    -->
    id, device_code, latitude, longitude, altitude, data_time, create_time, NO2, CO, 
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, velocity, wind_speed,
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, NO, velocity, wind_speed,
    wind_direction, height
  </sql>
</mapper>
src/main/resources/mapper/RealTimeDataUavMapper.xml
@@ -23,6 +23,7 @@
    <result column="humidity" jdbcType="REAL" property="humidity" />
    <result column="VOC" jdbcType="REAL" property="voc" />
    <result column="NOI" jdbcType="REAL" property="noi" />
    <result column="NO" jdbcType="REAL" property="no" />
    <result column="velocity" jdbcType="REAL" property="velocity" />
    <result column="wind_speed" jdbcType="REAL" property="windSpeed" />
    <result column="wind_direction" jdbcType="REAL" property="windDirection" />
@@ -33,7 +34,7 @@
      WARNING - @mbg.generated
    -->
    id, device_code, latitude, longitude, altitude, data_time, create_time, NO2, CO, 
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, velocity, wind_speed,
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, NO, velocity, wind_speed,
    wind_directon, height
  </sql>
</mapper>
src/main/resources/mapper/RealTimeDataVehicleMapper.xml
@@ -23,6 +23,7 @@
    <result column="humidity" jdbcType="REAL" property="humidity" />
    <result column="VOC" jdbcType="REAL" property="voc" />
    <result column="NOI" jdbcType="REAL" property="noi" />
    <result column="NO" jdbcType="REAL" property="no" />
    <result column="velocity" jdbcType="REAL" property="velocity" />
    <result column="wind_speed" jdbcType="REAL" property="windSpeed" />
    <result column="wind_direction" jdbcType="REAL" property="windDirection" />
@@ -33,7 +34,7 @@
      WARNING - @mbg.generated
    -->
    id, device_code, latitude, longitude, altitude, data_time, create_time, NO2, CO, 
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, velocity, wind_speed,
    wind_directon, height
    H2S, SO2, O3, PM25, PM10, temperature, humidity, VOC, NOI, NO, velocity, wind_speed,
    wind_direction, height
  </sql>
</mapper>
src/test/kotlin/com/flightfeather/uav/common/net/ShenXinServiceTest.kt
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package com.flightfeather.uav.common.net
import com.flightfeather.uav.domain.entity.Mission
import org.junit.Assert.*
import org.junit.Test
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.*
class ShenXinServiceTest {
    @Test
    fun fetchData() {
        val mission = Mission().apply {
            missionCode = "SHJA-20240813"
            deviceType = "0a"
            deviceCode = "TX105"
            startTime = Date.from(LocalDateTime.of(2024, 8, 13, 11, 30, 0, 0).atZone(ZoneId.systemDefault())
                .toInstant())
            endTime = Date.from(LocalDateTime.of(2024, 8, 13, 17, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant())
            districtName = "静安区"
        }
        val date = LocalDateTime.of(2024, 8, 13, 15, 43, 0, 0)
        val res = ShenXinService.fetchData(mission, date)
        println(res)
    }
}
src/test/kotlin/com/flightfeather/uav/lightshare/service/impl/MissionServiceImplTest.kt
@@ -24,6 +24,7 @@
    @Test
    fun getReport() {
        missionReport.execute("SH-CN-20240514", FactorFilter.default())
        missionReport.execute("SH-CN-20240723-01", FactorFilter.default())
        missionReport.execute("SH-CN-20240723-02", FactorFilter.default())
    }
}