90 lines
3.8 KiB
Kotlin
90 lines
3.8 KiB
Kotlin
package com.android.trisolarisserver.component.geo
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode
|
|
import com.fasterxml.jackson.databind.ObjectMapper
|
|
import org.slf4j.LoggerFactory
|
|
import org.springframework.http.HttpEntity
|
|
import org.springframework.http.HttpHeaders
|
|
import org.springframework.http.HttpMethod
|
|
import org.springframework.beans.factory.annotation.Value
|
|
import org.springframework.stereotype.Component
|
|
import org.springframework.web.client.RestTemplate
|
|
import org.springframework.web.util.UriComponentsBuilder
|
|
|
|
@Component
|
|
class PostalPincodeClient(
|
|
private val restTemplate: RestTemplate,
|
|
private val objectMapper: ObjectMapper,
|
|
@Value("\${pincode.postal.baseUrl:https://api.postalpincode.in}")
|
|
private val baseUrl: String
|
|
) {
|
|
private val logger = LoggerFactory.getLogger(PostalPincodeClient::class.java)
|
|
|
|
fun resolve(pinCode: String): PincodeLookupResult {
|
|
val first = fetch(baseUrl, pinCode)
|
|
if (first.resolvedCityState != null) return first
|
|
if (first.status == "ERROR" && baseUrl.startsWith("https://")) {
|
|
val httpUrl = baseUrl.replaceFirst("https://", "http://")
|
|
val second = fetch(httpUrl, pinCode)
|
|
if (second.resolvedCityState != null) return second
|
|
return second
|
|
}
|
|
return first
|
|
}
|
|
|
|
private fun fetch(base: String, pinCode: String): PincodeLookupResult {
|
|
val url = UriComponentsBuilder.fromUriString(base)
|
|
.path("/pincode/{pin}")
|
|
.buildAndExpand(pinCode)
|
|
.toUriString()
|
|
val headers = HttpHeaders().apply {
|
|
set("User-Agent", "Mozilla/5.0 (TrisolarisServer)")
|
|
set("Accept", "application/json")
|
|
set("Connection", "close")
|
|
}
|
|
val entity = HttpEntity<Unit>(headers)
|
|
var lastError: Exception? = null
|
|
repeat(2) {
|
|
try {
|
|
val response = restTemplate.exchange(url, HttpMethod.GET, entity, String::class.java)
|
|
val body = response.body ?: return PincodeLookupResult(null, null, "EMPTY_BODY", "postalpincode.in", requestUrl = url)
|
|
val resolved = parseCityState(body)
|
|
val status = if (resolved == null) "ZERO_RESULTS" else "OK"
|
|
return PincodeLookupResult(resolved, body, status, "postalpincode.in", requestUrl = url)
|
|
} catch (ex: Exception) {
|
|
lastError = ex
|
|
try {
|
|
Thread.sleep(200)
|
|
} catch (_: InterruptedException) {
|
|
Thread.currentThread().interrupt()
|
|
}
|
|
}
|
|
}
|
|
val errorMessage = lastError?.message
|
|
if (errorMessage != null) {
|
|
logger.warn("Postalpincode lookup failed: {}", errorMessage)
|
|
}
|
|
return PincodeLookupResult(null, null, "ERROR", "postalpincode.in", errorMessage, url)
|
|
}
|
|
|
|
private fun parseCityState(body: String): String? {
|
|
val root = objectMapper.readTree(body)
|
|
if (!root.isArray || root.isEmpty) return null
|
|
val first = root.first()
|
|
val status = first.path("Status").asText(null)
|
|
if (!status.equals("Success", true)) return null
|
|
val offices = first.path("PostOffice")
|
|
if (!offices.isArray || offices.isEmpty) return null
|
|
val office = chooseOffice(offices) ?: return null
|
|
val district = office.path("District").asText(null)
|
|
val state = office.path("State").asText(null)
|
|
if (district.isNullOrBlank() && state.isNullOrBlank()) return null
|
|
return listOfNotNull(district?.ifBlank { null }, state?.ifBlank { null }).joinToString(", ")
|
|
}
|
|
|
|
private fun chooseOffice(offices: JsonNode): JsonNode? {
|
|
val delivery = offices.firstOrNull { it.path("DeliveryStatus").asText("").equals("Delivery", true) }
|
|
return delivery ?: offices.firstOrNull()
|
|
}
|
|
}
|