| | |
| | | package com.flightfeather.uav.lightshare.service.impl |
| | | |
| | | import com.flightfeather.uav.domain.entity.GridCell |
| | | import com.flightfeather.uav.domain.entity.GridData |
| | | import com.flightfeather.uav.domain.entity.GridDataDetail |
| | | import com.flightfeather.uav.domain.entity.GridGroup |
| | | import com.flightfeather.uav.biz.satellite.SatelliteDataMix |
| | | import com.flightfeather.uav.biz.satellite.SatelliteGridManage |
| | | import com.flightfeather.uav.common.exception.BizException |
| | | import com.flightfeather.uav.common.utils.FileExchange |
| | | import com.flightfeather.uav.domain.entity.* |
| | | import com.flightfeather.uav.domain.repository.SatelliteGridRep |
| | | import com.flightfeather.uav.lightshare.bean.AreaVo |
| | | import com.flightfeather.uav.lightshare.bean.DataHead |
| | | import com.flightfeather.uav.lightshare.bean.GridDataImportResult |
| | | import com.flightfeather.uav.lightshare.eunm.SatelliteDataType |
| | | import com.flightfeather.uav.lightshare.service.SatelliteTelemetryService |
| | | import com.github.pagehelper.PageHelper |
| | | import org.springframework.stereotype.Service |
| | | import org.springframework.transaction.annotation.Transactional |
| | | import org.springframework.web.multipart.MultipartFile |
| | | import java.io.ByteArrayInputStream |
| | | import java.io.File |
| | | import java.time.LocalDateTime |
| | | import java.time.ZoneId |
| | | import java.util.* |
| | | import javax.servlet.http.HttpServletResponse |
| | | import kotlin.math.round |
| | | |
| | | /** |
| | | * |
| | |
| | | * @author feiyu02 |
| | | */ |
| | | @Service |
| | | class SatelliteTelemetryServiceImpl(private val satelliteGridRep: SatelliteGridRep) : SatelliteTelemetryService { |
| | | class SatelliteTelemetryServiceImpl( |
| | | private val satelliteGridRep: SatelliteGridRep, |
| | | private val satelliteDataMix: SatelliteDataMix, |
| | | ) : SatelliteTelemetryService { |
| | | |
| | | override fun fetchGridGroup(areaVo: AreaVo, page: Int?, perPage: Int?): Pair<DataHead, List<GridGroup?>> { |
| | | private val fileExchange = FileExchange() |
| | | override fun fetchGridGroup(areaVo: AreaVo, type: String?, page: Int?, perPage: Int?): Pair<DataHead, |
| | | List<GridGroup?>> { |
| | | val pageInfo = PageHelper.startPage<GridGroup>(page ?: 1, perPage ?: 100) |
| | | val res = satelliteGridRep.fetchGridGroup(areaVo) |
| | | val res = satelliteGridRep.fetchGridGroup(areaVo, type) |
| | | return DataHead(pageInfo.pageNum, pageInfo.pages) to res |
| | | } |
| | | |
| | | override fun deleteGridGroup(groupId: Int) { |
| | | satelliteGridRep.deleteGridGroup(groupId) |
| | | } |
| | | |
| | | override fun fetchGridCell(groupId: Int): List<GridCell?> { |
| | |
| | | } |
| | | |
| | | override fun fetchGridDataDetail(dataId: Int, groupId: Int?, cellId: Int?): List<GridDataDetail?> { |
| | | return satelliteGridRep.fetchGridDataDetail(dataId, groupId, cellId) |
| | | val res = satelliteGridRep.fetchGridDataDetail(dataId, groupId, cellId) |
| | | res.forEach { |
| | | if (it?.pm25 != null) { |
| | | it.pm25 = round(it.pm25 * 100) / 100 |
| | | } |
| | | } |
| | | return res |
| | | } |
| | | |
| | | @Transactional |
| | | override fun createGridDataAndDataDetail( |
| | | groupId: Int, |
| | | dataTime: LocalDateTime?, |
| | | gridDataDetail: List<GridDataDetail>, |
| | | ): Boolean { |
| | | // 保存拟合的卫星遥测数据 type始终为0 |
| | | val type = SatelliteDataType.Original.value |
| | | |
| | | // 查找是否有历史记录 |
| | | val gridData = satelliteGridRep.fetchGridData(groupId, dataTime, type) |
| | | // 无历史记录则创建数据索引GridData,之后再存入拟合的数据 |
| | | if (gridData.isEmpty()) { |
| | | val gridDataEntity = GridData() |
| | | gridDataEntity.groupId = groupId |
| | | gridDataEntity.dataTime = dataTime?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli() |
| | | ?.let { Date(it) } |
| | | gridDataEntity.type = type.toByte() |
| | | satelliteGridRep.insertGridDataAndDetail(gridDataEntity, gridDataDetail) |
| | | } |
| | | // 更新历史数据 |
| | | else { |
| | | gridDataDetail.forEach { |
| | | it.dataId = gridData[0]?.id |
| | | it.groupId = gridData[0]?.groupId |
| | | } |
| | | satelliteGridRep.updatePM25Batch(gridDataDetail) |
| | | } |
| | | |
| | | return true |
| | | } |
| | | |
| | | @Transactional |
| | | override fun mixGridData(dataIdList: List<Int>): List<GridData?> { |
| | | if (dataIdList.isEmpty()) throw BizException("融合所需数据id不能为空") |
| | | // 1. 根据数据主键id数组,查询该组合下是否已有数据融合记录 |
| | | val exist = satelliteGridRep.fetchGridData(GridData().apply { |
| | | type = 1 |
| | | mixDataId = dataIdList.sorted().joinToString(",") |
| | | }) |
| | | // 2. 若融合数据已存在,直接返回 |
| | | return exist.ifEmpty { |
| | | listOf(satelliteDataMix.mixData(dataIdList).first) |
| | | } |
| | | } |
| | | |
| | | override fun importGridData(groupId: Int, dataTime: LocalDateTime?, update: Boolean, file: MultipartFile): GridDataImportResult? { |
| | | // 因为是导入卫星遥测数据 type始终为0 |
| | | val type = 0 |
| | | // 首先判断文件类型,文件类型不是xlsx直接报错 |
| | | if (!file.contentType!!.startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) { |
| | | throw BizException("文件类型错误,请上传xlsx类型文件") |
| | | } |
| | | // 校验 用户更新指令 和 数据库中网格数据存在与否 是否匹配,不匹配抛出错误 |
| | | // 这个过程需要查询数据库中网格组数据 |
| | | val gridData = satelliteGridRep.fetchGridData(groupId, dataTime, type) |
| | | // 将用户导入的文件转换为数据 |
| | | // 转换的过程需要网格单元格数据 以判断网格单元格是否在正确的范围内 |
| | | val gridCellsInDBSet = satelliteGridRep.fetchGridCell(groupId).asSequence().map { it?.id ?: -1 }.toSet() |
| | | val importData |
| | | = fileExchange.exchangeGridData(ByteArrayInputStream(file.bytes), gridCellsInDBSet) |
| | | if (update) { |
| | | // 用户需要执行更新 |
| | | if (gridData.isEmpty()) { |
| | | throw BizException("指令错误,数据库对应网格组和日期下不存在遥测数据,请执行插入操作") |
| | | } |
| | | importData.forEach { |
| | | it.dataId = gridData[0]?.id |
| | | it.groupId = gridData[0]?.groupId |
| | | } |
| | | satelliteGridRep.updatePM25Batch(importData) |
| | | return GridDataImportResult(true, "覆盖成功") |
| | | }else { |
| | | // 用户需要执行插入 |
| | | if (gridData.isNotEmpty()) { |
| | | throw BizException("指令错误,数据库对应网格组和日期下已存在遥测数据,请执行更新操作") |
| | | } |
| | | val gridDataEntity = GridData() |
| | | gridDataEntity.groupId = groupId |
| | | gridDataEntity.dataTime = dataTime?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli() |
| | | ?.let { Date(it) } |
| | | gridDataEntity.type = type.toByte() |
| | | satelliteGridRep.insertGridDataAndDetail(gridDataEntity, importData) |
| | | return GridDataImportResult(true, "导入成功") |
| | | } |
| | | } |
| | | |
| | | override fun downloadTemplate(response: HttpServletResponse): Boolean { |
| | | val fileName = "GridData-PM2.5-Template.xlsx" |
| | | val path = (Thread.currentThread().contextClassLoader?.getResource("/")?.path |
| | | ?: "src/main/resources") + "/templates/" + fileName |
| | | val file = File(path) |
| | | if (file.exists()) { |
| | | val fName = Base64.getEncoder().encodeToString(fileName.toByteArray()) |
| | | response.apply { |
| | | setHeader("Content-Disposition", "attachment;filename=$fName") |
| | | setHeader("fileName", fName) |
| | | addHeader("Access-Control-Expose-Headers", "fileName") |
| | | contentType = "application/vnd.ms-excel;charset=UTF-8" |
| | | setHeader("Pragma", "no-cache") |
| | | setHeader("Cache-Control", "no-cache") |
| | | setDateHeader("Expires", 0) |
| | | } |
| | | response.outputStream.write(file.readBytes()) |
| | | } |
| | | return true |
| | | } |
| | | |
| | | override fun importGridAOD( |
| | | groupId: Int, |
| | | dataTime: LocalDateTime?, |
| | | update: Boolean, |
| | | file: MultipartFile |
| | | ): GridDataImportResult? { |
| | | // 首先判断文件类型,文件类型不是xlsx直接报错 |
| | | if (!file.contentType!!.startsWith("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")) { |
| | | throw BizException("文件类型错误,请上传xlsx类型文件") |
| | | } |
| | | // 校验 用户更新指令 和 数据库中网格数据存在与否 是否匹配,不匹配抛出错误 |
| | | // 这个过程需要查询数据库中网格组数据 |
| | | val gridAod = satelliteGridRep.fetchGridAod(groupId, dataTime) |
| | | // 将用户导入的文件转换为数据 |
| | | // 转换的过程需要网格单元格数据 以判断网格单元格是否在正确的范围内 |
| | | val gridCellsInDBSet = satelliteGridRep.fetchGridCell(groupId).asSequence().map { it?.id ?: -1 }.toSet() |
| | | val importAod |
| | | = fileExchange.exchangeGridAod(ByteArrayInputStream(file.bytes), gridCellsInDBSet) |
| | | if (update) { |
| | | // 用户需要执行更新 |
| | | if (gridAod.isEmpty()) { |
| | | throw BizException("指令错误,数据库对应网格组和日期下不存在AOD数据,请执行插入操作") |
| | | } |
| | | importAod.forEach { |
| | | it.aodId = gridAod[0]?.id |
| | | it.groupId = gridAod[0]?.groupId |
| | | } |
| | | satelliteGridRep.updateGridAodBatch(importAod) |
| | | return GridDataImportResult(true, "覆盖成功") |
| | | }else { |
| | | // 用户需要执行插入 |
| | | if (gridAod.isNotEmpty()) { |
| | | throw BizException("指令错误,数据库对应网格组和日期下已存在AOD数据,请执行更新操作") |
| | | } |
| | | val gridAodEntity = GridAod() |
| | | gridAodEntity.groupId = groupId |
| | | gridAodEntity.dataTime = dataTime?.atZone(ZoneId.systemDefault())?.toInstant()?.toEpochMilli() |
| | | ?.let { Date(it) } |
| | | satelliteGridRep.insertGridAodAndDetail(gridAodEntity, importAod) |
| | | return GridDataImportResult(true, "导入成功") |
| | | } |
| | | } |
| | | |
| | | override fun downloadAODTemplate(response: HttpServletResponse): Boolean { |
| | | val fileName = "GridData-AOD-Template.xlsx" |
| | | val path = (Thread.currentThread().contextClassLoader?.getResource("/")?.path |
| | | ?: "src/main/resources") + "/templates/" + fileName |
| | | val file = File(path) |
| | | if (file.exists()) { |
| | | val fName = Base64.getEncoder().encodeToString(fileName.toByteArray()) |
| | | response.apply { |
| | | setHeader("Content-Disposition", "attachment;filename=$fName") |
| | | setHeader("fileName", fName) |
| | | addHeader("Access-Control-Expose-Headers", "fileName") |
| | | contentType = "application/vnd.ms-excel;charset=UTF-8" |
| | | setHeader("Pragma", "no-cache") |
| | | setHeader("Cache-Control", "no-cache") |
| | | setDateHeader("Expires", 0) |
| | | } |
| | | response.outputStream.write(file.readBytes()) |
| | | } |
| | | return true |
| | | } |
| | | |
| | | override fun calGridVertex(groupId: Int): List<GridCell?> { |
| | | val cellList = satelliteGridRep.fetchGridCell(groupId) |
| | | val vertexList = SatelliteGridManage.calGridVertex(cellList.map { |
| | | if (it?.longitude == null || it.latitude == null) { |
| | | throw BizException("卫星遥测网格计算顶点坐标点失败,存在中心点坐标为空的情况") |
| | | } |
| | | it.longitude?.toDouble()!! to it.latitude?.toDouble()!! |
| | | }) |
| | | |
| | | cellList.forEachIndexed { i, c -> |
| | | val v = vertexList[i] |
| | | c?.point1Lon = v.point1Lon.toBigDecimal() |
| | | c?.point1Lat = v.point1Lat.toBigDecimal() |
| | | |
| | | c?.point2Lon = v.point2Lon.toBigDecimal() |
| | | c?.point2Lat = v.point2Lat.toBigDecimal() |
| | | |
| | | c?.point3Lon = v.point3Lon.toBigDecimal() |
| | | c?.point3Lat = v.point3Lat.toBigDecimal() |
| | | |
| | | c?.point4Lon = v.point4Lon.toBigDecimal() |
| | | c?.point4Lat = v.point4Lat.toBigDecimal() |
| | | } |
| | | |
| | | satelliteGridRep.updateGridCellBatch(cellList) |
| | | |
| | | return cellList |
| | | } |
| | | |
| | | override fun fetchGridAod(groupId: Int, dataTime: LocalDateTime?): List<GridAod?> { |
| | | return satelliteGridRep.fetchGridAod(groupId, dataTime) |
| | | } |
| | | |
| | | override fun fetchGridAODDetail(aodId: Int, groupId: Int?, cellId: Int?): List<GridAodDetail?> { |
| | | return satelliteGridRep.fetchGridAodDetail(aodId, groupId, cellId) |
| | | } |
| | | } |