feiyu02
2024-11-19 752e00503f672ddfe2066afb6c235721a3a912b5
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
package cn.flightfeather.supervision.common.net
 
import cn.flightfeather.supervision.infrastructure.utils.DateUtil
import cn.flightfeather.supervision.infrastructure.utils.GsonUtils
import cn.flightfeather.supervision.lightshare.vo.JinAnConstructionInfo
import cn.flightfeather.supervision.lightshare.vo.JinAnHourDustData
import cn.flightfeather.supervision.lightshare.vo.JinAnLampDeviceData
import cn.flightfeather.supervision.lightshare.vo.JinAnLampEnterBaseInfo
import com.google.gson.*
import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.codec.digest.Md5Crypt
import org.apache.tomcat.util.security.MD5Encoder
import java.nio.charset.Charset
import java.time.LocalDateTime
import java.util.*
import kotlin.time.seconds
 
/**
 * @author riku
 * Date: 2022/11/15
 * 静安联通公司提供,工地和餐饮监测数据抓取服务接口
 */
object JinAnLianTongHttpService {
 
    const val TAG = "JinAnLianTongHttpService"
    private const val TOKEN = "e6dc8bb9e1ff0ce973fb92b4af2e4c3f"
    private val httpMethod: HttpMethod = HttpMethod("101.230.224.77", 8088, true)
 
    open class RequestData(
        val current: Int = 1,
        val size: Int = 30,
    )
 
    class LampRequestData(c: Int, s: Int, val smDate: String? = null, val emDate: String? = null) : RequestData(c, s)
    class DustRequestData(c: Int, s: Int, val sdate: String? = null, val edate: String? = null) : RequestData(c, s)
 
    init {
        // FIXME: 2022/11/18 此处目前通过忽略SSL证书验证的方式进行访问
//        SkipCertificateValidation.ignoreSsl()
    }
 
    /**
     * 统一请求头
     */
    fun headTimeStamp(): List<Pair<String, String>> {
        val nowS = Date().time / 1000
        val t = nowS - 200
        val now = t.toString()
        val key = "$now$TOKEN"
        val sign = DigestUtils.md5Hex(key)
        return listOf(
            Pair("JA-TIMESTAMP", now),
            Pair("JA-TOKEN", TOKEN),
            Pair("JA-SIGN", sign)
        )
    }
 
    /**
     * 返回校验结果
     */
    private fun resCheck(json: JsonElement): JsonObject {
        if (!json.isJsonObject) throw IllegalStateException("${TAG}: 获取工地扬尘监测点信息失败,返回值不是一个object")
 
        val jo = json.asJsonObject
        if (jo["code"].asInt != 20000) throw IllegalStateException("${TAG}: 获取工地扬尘监测点信息失败,错误${jo["message"]}")
 
        if (!jo["data"].isJsonObject) throw IllegalStateException("${TAG}: 获取工地扬尘监测点信息失败,data字段不存在或不是object结构")
 
        val data = jo["data"].asJsonObject
 
        if (!data["current"].isJsonPrimitive) throw IllegalStateException("${TAG}: 获取工地扬尘监测点信息失败,current字段不存在或不是基础数据结构")
        if (!data["pages"].isJsonPrimitive) throw IllegalStateException("${TAG}: 获取工地扬尘监测点信息失败,pages字段不存在或不是基础数据结构")
        if (!data["records"].isJsonArray) throw IllegalStateException("${TAG}: 获取工地扬尘监测点信息失败,records字段不存在或不是Array结构")
 
        return data
    }
 
    /**
     * 根据返回结果判断是否有下一分页
     */
    private fun nextPage(data: JsonObject):Boolean {
        val current = data["current"].asInt
        val pages = data["pages"].asInt
        return current < pages
    }
 
    private inline fun <reified T> post(url: String, requestData: RequestData): Pair<Boolean, List<T>> {
        val dataJson = Gson().toJson(requestData)
        val res = httpMethod.post(url, dataJson, headTimeStamp())
        return if (res.success) {
            val str = if (res.m.responseCharSet == "utf-8") {
                res.m.responseBodyAsString
            } else {
                String(res.m.responseBody, Charset.forName("utf-8"))
            }
            val json = JsonParser.parseString(str)
            val data = resCheck(json)
            val records = data["records"]
            val next = nextPage(data)
            val result = GsonUtils.parserJsonToArrayBeans(records.asJsonArray.toString(), T::class.java)
            Pair(next, result)
        } else {
            throw IllegalStateException("网路链接错误,状态码:${res.m.statusCode}")
        }
    }
 
    /**
     * 接口时间适配
     * 时间字段目前由于接口提供方的原因,导致格式不固定
     */
    private fun dateAdapter(e: JsonElement): Date? {
        if (e.isJsonNull) return null
 
        // 尝试是否是数值格式
        try {
            var d = e.asLong
            // 可能是10位数的秒值,需要转换为毫秒值
            if (d.toString().length == 10) {
                d *= 1000
            }
            return Date(d)
        } catch (e: Exception) {
 
        }
        // 尝试是否是字符串格式
        try {
            val d = e.asString
            return DateUtil.StringToDate(d)
        } catch (e: Exception) {
 
        }
        throw IllegalStateException("时间字段的格式适配失败")
    }
 
    /**
     * 获取工地扬尘监测点信息
     */
    fun getConstructionDustMonitorSiteInfo(page: Int = 1): Pair<Boolean, List<JinAnConstructionInfo>> {
        val requestData = RequestData(page, 100)
        return post("/api/dust/v1/getConstructionDustMonitorSiteInfo", requestData)
    }
 
    /**
     * 查询工地扬尘监测点小时数据
     */
    fun getHourlyDustData(page: Int = 1, sDate: Date?, eDate: Date?, perPage:Int = 5000): Pair<Boolean,
            List<JinAnHourDustData>> {
        val sStr = DateUtil.DateToString(sDate, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
        val eStr = DateUtil.DateToString(eDate, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
        val requestData = DustRequestData(page, perPage, sStr, eStr)
//        return post("/api/dust/v1/getHourlyDustData", requestData)
        val url = "/api/dust/v1/getHourlyDustData"
        val dataJson = Gson().toJson(requestData)
        val res = httpMethod.post(url, dataJson, headTimeStamp())
        return if (res.success) {
            val str = if (res.m.responseCharSet == "utf-8") {
                res.m.responseBodyAsString
            } else {
                String(res.m.responseBody, Charset.forName("utf-8"))
            }
            val json = JsonParser.parseString(str)
            val data = resCheck(json)
            val records = data["records"]
            val next = nextPage(data)
 
            val jsonArray = records.asJsonArray
            val beans = mutableListOf<JinAnHourDustData>()
            jsonArray.forEach {
                val bean = Gson().fromJson(it, JinAnHourDustData::class.java)
                bean.createTime = dateAdapter(it.asJsonObject["inserttime"])
                bean.stTime = dateAdapter(it.asJsonObject["lst"])
                bean.etTime = dateAdapter(it.asJsonObject["lst_END"])
                beans.add(bean)
            }
            Pair(next, beans)
        } else {
            throw IllegalStateException("网路链接错误,状态码:${res.m.statusCode}")
        }
    }
 
    /**
     * 查询油烟监测企业
     */
    fun getLampEnterBaseInfo(page: Int = 1): Pair<Boolean, List<JinAnLampEnterBaseInfo>> {
        val requestData = RequestData(page, 1000)
        return post("/api/lamp/v1/getLampEnterBaseInfo", requestData)
    }
 
    /**
     * 查询油烟设备数据
     */
    fun getLampDeviceData(page: Int = 1, smDate: Date?, emDate: Date?): Pair<Boolean, List<JinAnLampDeviceData>> {
        val sStr = DateUtil.DateToString(smDate, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
        val eStr = DateUtil.DateToString(emDate, DateUtil.DateStyle.YYYY_MM_DD_HH_MM_SS)
        val requestData = LampRequestData(page, 5000, sStr, eStr)
        return post("/api/lamp/v1/getLampDeviceData", requestData)
    }
 
}