riku
2025-08-28 3bb4fb15c664d29d179083698fdad35a661b1d7f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package com.flightfeather.uav.biz.report
 
import com.flightfeather.uav.biz.FactorFilter
import com.flightfeather.uav.biz.dataanalysis.ExceptionAnalysisController
import com.flightfeather.uav.biz.dataanalysis.model.ExceptionResult
import com.flightfeather.uav.biz.dataprocess.PreData
import com.flightfeather.uav.common.chart.DataToChartUtil
import com.flightfeather.uav.common.exception.BizException
import com.flightfeather.uav.common.location.LocationRoadNearby
import com.flightfeather.uav.common.pdf.GeneratePdfUtil
import com.flightfeather.uav.common.utils.DateUtil
import com.flightfeather.uav.common.utils.ImageUtil
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.domain.repository.SegmentInfoRep
import org.springframework.beans.BeanUtils
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
 
/**
 * 走航任务报告
 */
@Component
class MissionReport(
    private val missionRep: MissionRep,
    private val realTimeDataRep: RealTimeDataRep,
    private val locationRoadNearby: LocationRoadNearby,
    private val segmentInfoRep: SegmentInfoRep,
    @Value("\${filePath}")
    private val filePath: String,
) {
 
    private val exceptionAnalysisController =
        ExceptionAnalysisController(realTimeDataRep, locationRoadNearby, segmentInfoRep)
 
    data class Param(
        val district: String,
        val town: String,
        val year: Int,
        val month: Int,
        val day: Int,
        val first_party: String,
        val start_time: String,
        val end_time: String,
    ) {
        var factor_names: String = ""
        var summary: List<Summary> = emptyList()
            set(value) {
                factor_names = value.map { it.factor }.joinToString("、")
                field = value
            }
        var exceptions: List<ExceptionChart> = emptyList()
 
        /**
         * 传入数据异常集合,自动为每条异常生成折线图
         */
        fun addExceptions(exceptions: List<ExceptionResult>) {
            this.exceptions = exceptions.map {
                val c = ExceptionChart()
                BeanUtils.copyProperties(it, c)
                // 创建主污染因子的数据折线图
                val byteArray = DataToChartUtil.lineToByteArray(it.selectedFactor?.main, it.dataList)
                val base64Str = ImageUtil.compressImage2(byteArray, 800, needPrefix = false)
                c.mainPict = base64Str
                // 创建关联因子的数据折线图
                if (it.selectedFactor?.subs?.isNotEmpty() == true) {
                    val subList = mutableListOf<String>()
                    it.selectedFactor!!.subs.forEach { type ->
                        val byteArray1 = DataToChartUtil.lineToByteArray(type, it.dataList)
                        val base64Str1 = ImageUtil.compressImage2(byteArray1, 800, needPrefix = false)
                        subList.add(base64Str1)
                    }
                    c.subPictList = subList
                }
                return@map c
            }
        }
    }
 
    /**
     * 数据统计
     */
    class Summary {
        var id: String? = null
        var factor: String? = null
        var avg: Double? = null
        var min: Double? = null
        var max: Double? = null
 
        // 数据折线图Base64编码
        var pict: String? = null
    }
 
    /**
     * 数据异常所在时段的折线图
     */
    class ExceptionChart : ExceptionResult() {
        // 污染因子的异常数据折线图Base64编码
        var mainPict: String? = null
 
        // 关联因子的异常数据折线图Base64编码
        var subPictList: List<String>? = null
    }
 
    private val templateName = "report-underway.ftl"
 
    private val dateFormatter = DateTimeFormatter.ofPattern("HH:mm")
 
    // 获取异常分析结果
    fun exceptionAnalysis(mission: Mission, factorFilter: FactorFilter): List<ExceptionResult> {
        return exceptionAnalysisController.execute(mission, factorFilter)
    }
 
    // 计算均值和数据范围
    fun dataSummary(mission: Mission, factorFilter: FactorFilter): List<Summary> {
        val preData = PreData(
            DateUtil.instance.dateToString(mission.startTime, DateUtil.DateStyle.YYYY_MM_DD),
            factorFilter.mainList()
        )
        val realTimeData = realTimeDataRep.fetchData(mission)
        realTimeData.forEach {
            preData.add(it.toDataVo())
        }
        val resMap = preData.calculate()
 
        val summaries = mutableListOf<Summary>()
        resMap.forEach { (t, u) ->
            summaries.add(Summary().apply {
                id = t.value.toString()
                factor = t.des
                avg = u.avg
                min = u.min
                max = u.max
 
                val byteArray = DataToChartUtil.lineToByteArray(t, realTimeData)
                val base64Str = ImageUtil.compressImage2(byteArray, 800, needPrefix = false)
                pict = base64Str
            })
        }
        return summaries
    }
 
    // 生成参数
    // 根据报告模板生成对应报告
    fun execute(missionCode: String, factorFilter: FactorFilter): String {
        // 1. 任务合法性检查
        val mission = missionRep.findOne(missionCode) ?: throw  BizException("该任务编号不存在")
        // 2. 获取数据异常统计结果,根据
        val exceptions = exceptionAnalysis(mission, factorFilter)
        // 3. 获取均值、范围等统计数据
        val summaries = dataSummary(mission, factorFilter)
        // 4. 生成报告
        val fileName = "report/" + "${mission.districtName}走航监测报告-${
            DateUtil.instance.dateToString(mission.startTime, DateUtil.DateStyle.YYYY_MM_DD)
        }(${mission.missionCode}).doc"
        val reportTemplate = ReportTemplate(templateName, filePath, fileName)
        val param = getParam(mission, exceptions, summaries)
        val params = reportTemplate.getParam(param)
        GeneratePdfUtil.generateWord(params)
        return filePath + fileName
    }
 
    private fun getParam(mission: Mission, exceptions: List<ExceptionResult>, summaries: List<Summary>): Param {
        val startTime = LocalDateTime.ofInstant(mission.startTime.toInstant(), ZoneId.systemDefault())
        val endTime = LocalDateTime.ofInstant(mission.endTime.toInstant(), ZoneId.systemDefault())
        return Param(
            mission.districtName, "", startTime.year, startTime.monthValue, startTime.dayOfMonth, "",
            startTime.format(dateFormatter), endTime.format(dateFormatter)
        ).apply {
            summary = summaries
            addExceptions(exceptions)
        }
    }
}