Auto-fill booking city via pin geocoding
All checks were successful
build-and-deploy / build-deploy (push) Successful in 33s

This commit is contained in:
androidlover5842
2026-01-31 10:48:25 +05:30
parent 366673c690
commit e148549b6c
4 changed files with 88 additions and 2 deletions

View File

@@ -2,6 +2,7 @@ package com.android.trisolarisserver.component
import com.android.trisolarisserver.controller.DocumentPrompts import com.android.trisolarisserver.controller.DocumentPrompts
import com.android.trisolarisserver.db.repo.GuestRepo import com.android.trisolarisserver.db.repo.GuestRepo
import com.android.trisolarisserver.db.repo.BookingRepo
import com.android.trisolarisserver.models.booking.GuestDocument import com.android.trisolarisserver.models.booking.GuestDocument
import com.android.trisolarisserver.models.booking.GuestVehicle import com.android.trisolarisserver.models.booking.GuestVehicle
import com.android.trisolarisserver.repo.GuestVehicleRepo import com.android.trisolarisserver.repo.GuestVehicleRepo
@@ -16,7 +17,9 @@ class DocumentExtractionService(
private val guestRepo: GuestRepo, private val guestRepo: GuestRepo,
private val guestVehicleRepo: GuestVehicleRepo, private val guestVehicleRepo: GuestVehicleRepo,
private val propertyRepo: PropertyRepo, private val propertyRepo: PropertyRepo,
private val paddleOcrClient: PaddleOcrClient private val paddleOcrClient: PaddleOcrClient,
private val bookingRepo: BookingRepo,
private val googleGeocodingClient: GoogleGeocodingClient
) { ) {
private val logger = LoggerFactory.getLogger(DocumentExtractionService::class.java) private val logger = LoggerFactory.getLogger(DocumentExtractionService::class.java)
@@ -241,6 +244,7 @@ class DocumentExtractionService(
normalizePinCode(results) normalizePinCode(results)
normalizeIdNumber(results) normalizeIdNumber(results)
markAadhaarIfValid(results) markAadhaarIfValid(results)
applyBookingCityUpdates(document, results)
results["docType"] = computeDocType(results, handled) results["docType"] = computeDocType(results, handled)
applyGuestUpdates(document, propertyId, results) applyGuestUpdates(document, propertyId, results)
return ExtractionResult(results, handled) return ExtractionResult(results, handled)
@@ -421,6 +425,27 @@ class DocumentExtractionService(
} }
} }
} }
private fun applyBookingCityUpdates(document: GuestDocument, results: Map<String, String>) {
val booking = document.booking
if (booking.fromCity?.isNotBlank() == true && booking.toCity?.isNotBlank() == true) return
val pin = cleanedValue(results[DocumentPrompts.PIN_CODE.first]) ?: return
if (!isValidPin(pin)) return
val resolved = googleGeocodingClient.resolveCityState(pin) ?: return
var updated = false
if (booking.fromCity.isNullOrBlank()) {
booking.fromCity = resolved
updated = true
}
if (booking.toCity.isNullOrBlank()) {
booking.toCity = resolved
updated = true
}
if (updated) {
booking.updatedAt = OffsetDateTime.now()
bookingRepo.save(booking)
}
}
} }
data class ExtractionResult( data class ExtractionResult(

View File

@@ -0,0 +1,59 @@
package com.android.trisolarisserver.component
import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory
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 GoogleGeocodingClient(
private val restTemplate: RestTemplate,
private val objectMapper: ObjectMapper,
@Value("\${google.maps.apiKey:}")
private val apiKey: String,
@Value("\${google.maps.geocode.baseUrl:https://maps.googleapis.com/maps/api/geocode/json}")
private val baseUrl: String
) {
private val logger = LoggerFactory.getLogger(GoogleGeocodingClient::class.java)
fun resolveCityState(pinCode: String): String? {
if (apiKey.isBlank()) return null
return try {
val url = UriComponentsBuilder.fromUriString(baseUrl)
.queryParam("address", "${pinCode} India")
.queryParam("key", apiKey)
.toUriString()
val response = restTemplate.getForEntity(url, String::class.java)
val body = response.body ?: return null
parseCityState(body)
} catch (ex: Exception) {
logger.warn("Geocoding failed: {}", ex.message)
null
}
}
private fun parseCityState(body: String): String? {
val root = objectMapper.readTree(body)
val results = root.path("results")
if (!results.isArray || results.isEmpty) return null
val components = results.first().path("address_components")
if (!components.isArray) return null
var city: String? = null
var state: String? = null
for (comp in components) {
val types = comp.path("types").mapNotNull { it.asText(null) }.toSet()
when {
"locality" in types -> city = comp.path("long_name").asText(null) ?: city
"administrative_area_level_2" in types -> if (city == null) {
city = comp.path("long_name").asText(null)
}
"administrative_area_level_1" in types -> state = comp.path("long_name").asText(null) ?: state
}
}
if (city.isNullOrBlank() && state.isNullOrBlank()) return null
return listOfNotNull(city?.trim()?.ifBlank { null }, state?.trim()?.ifBlank { null }).joinToString(", ")
}
}

View File

@@ -33,3 +33,5 @@ ocr.paddle.baseUrl=https://ocr.hoteltrisolaris.in/
ocr.paddle.minScore=0.9 ocr.paddle.minScore=0.9
ocr.paddle.minAverageScore=0.75 ocr.paddle.minAverageScore=0.75
ocr.paddle.minTextLength=4 ocr.paddle.minTextLength=4
google.maps.apiKey=AIzaSyAMuRNFWjccKSmPeR0loQI8etHMDtUIZ_k
google.maps.geocode.baseUrl=https://maps.googleapis.com/maps/api/geocode/json