Use JPA repo for local pincode resolution
All checks were successful
build-and-deploy / build-deploy (push) Successful in 40s

This commit is contained in:
androidlover5842
2026-02-05 19:56:59 +05:30
parent 1153193723
commit a0e354b464
4 changed files with 124 additions and 78 deletions

View File

@@ -1,76 +0,0 @@
package com.android.trisolarisserver.component.geo
import org.slf4j.LoggerFactory
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Component
@Component
class LocalPincodeDirectoryClient(
private val jdbcTemplate: JdbcTemplate
) {
private val logger = LoggerFactory.getLogger(LocalPincodeDirectoryClient::class.java)
fun resolve(pinCode: String): PincodeLookupResult {
val normalizedPin = pinCode.trim()
if (!PIN_REGEX.matches(normalizedPin)) {
return PincodeLookupResult(
resolvedCityState = null,
rawResponse = null,
status = "INVALID_PIN",
source = "local-db",
errorMessage = "PIN must be 6 digits"
)
}
return try {
val rows = jdbcTemplate.query(
"""
select city, state
from india_pincode_city_state
where pincode = ?
and nullif(trim(city), '') is not null
and nullif(trim(state), '') is not null
group by city, state
order by count(*) desc, city asc, state asc
limit 1
""".trimIndent(),
{ rs, _ ->
val city = rs.getString("city")?.trim()?.ifBlank { null }
val state = rs.getString("state")?.trim()?.ifBlank { null }
if (city == null || state == null) null else "$city, $state"
},
normalizedPin.toInt()
).filterNotNull()
val resolved = rows.firstOrNull()
if (resolved != null) {
PincodeLookupResult(
resolvedCityState = resolved,
rawResponse = null,
status = "OK",
source = "local-db"
)
} else {
PincodeLookupResult(
resolvedCityState = null,
rawResponse = null,
status = "ZERO_RESULTS",
source = "local-db"
)
}
} catch (ex: Exception) {
logger.warn("Local pincode lookup failed: {}", ex.message)
PincodeLookupResult(
resolvedCityState = null,
rawResponse = null,
status = "ERROR",
source = "local-db",
errorMessage = ex.message
)
}
}
companion object {
private val PIN_REGEX = Regex("\\d{6}")
}
}

View File

@@ -1,16 +1,21 @@
package com.android.trisolarisserver.component.geo
import com.android.trisolarisserver.repo.property.IndiaPincodeCityStateRepo
import org.slf4j.LoggerFactory
import org.springframework.data.domain.PageRequest
import org.springframework.stereotype.Component
@Component
class PincodeResolver(
private val localPincodeDirectoryClient: LocalPincodeDirectoryClient,
private val indiaPincodeCityStateRepo: IndiaPincodeCityStateRepo,
private val dataGovPincodeClient: DataGovPincodeClient,
private val postalPincodeClient: PostalPincodeClient,
private val googleGeocodingClient: GoogleGeocodingClient
) {
private val logger = LoggerFactory.getLogger(PincodeResolver::class.java)
fun resolve(pinCode: String): PincodeResolveResult {
val primary = localPincodeDirectoryClient.resolve(pinCode)
val primary = resolveFromLocalDb(pinCode)
if (primary.status == "OK" && primary.resolvedCityState != null) {
return PincodeResolveResult(primary, null, null, null)
}
@@ -34,6 +39,53 @@ class PincodeResolver(
)
return PincodeResolveResult(primary, secondary, tertiary, quaternary)
}
private fun resolveFromLocalDb(pinCode: String): PincodeLookupResult {
val normalizedPin = pinCode.trim()
if (!PIN_REGEX.matches(normalizedPin)) {
return PincodeLookupResult(
resolvedCityState = null,
rawResponse = null,
status = "INVALID_PIN",
source = "local-db",
errorMessage = "PIN must be 6 digits"
)
}
return try {
val candidate = indiaPincodeCityStateRepo
.findCityStateCandidates(normalizedPin.toInt(), PageRequest.of(0, 1))
.firstOrNull()
if (candidate != null) {
PincodeLookupResult(
resolvedCityState = "${candidate.city}, ${candidate.state}",
rawResponse = null,
status = "OK",
source = "local-db"
)
} else {
PincodeLookupResult(
resolvedCityState = null,
rawResponse = null,
status = "ZERO_RESULTS",
source = "local-db"
)
}
} catch (ex: Exception) {
logger.warn("Local pincode lookup failed: {}", ex.message)
PincodeLookupResult(
resolvedCityState = null,
rawResponse = null,
status = "ERROR",
source = "local-db",
errorMessage = ex.message
)
}
}
companion object {
private val PIN_REGEX = Regex("\\d{6}")
}
}
data class PincodeResolveResult(

View File

@@ -0,0 +1,36 @@
package com.android.trisolarisserver.models.property
import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.Id
import jakarta.persistence.Table
import java.time.OffsetDateTime
import java.util.UUID
@Entity
@Table(name = "india_pincode_city_state")
class IndiaPincodeCityState(
@Id
@GeneratedValue
@Column(columnDefinition = "uuid")
val id: UUID? = null,
@Column(name = "pincode", nullable = false)
val pincode: Int,
@Column(name = "locality", nullable = false)
val locality: String,
@Column(name = "city", nullable = false)
val city: String,
@Column(name = "state", nullable = false)
val state: String,
@Column(name = "source_file")
val sourceFile: String? = null,
@Column(name = "imported_at", columnDefinition = "timestamptz")
val importedAt: OffsetDateTime? = null
)

View File

@@ -0,0 +1,34 @@
package com.android.trisolarisserver.repo.property
import com.android.trisolarisserver.models.property.IndiaPincodeCityState
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.query.Param
import java.util.UUID
interface IndiaPincodeCityStateRepo : JpaRepository<IndiaPincodeCityState, UUID> {
@Query(
"""
select p.city as city,
p.state as state,
count(p.id) as hitCount
from IndiaPincodeCityState p
where p.pincode = :pincode
and trim(p.city) <> ''
and trim(p.state) <> ''
group by p.city, p.state
order by count(p.id) desc, p.city asc, p.state asc
"""
)
fun findCityStateCandidates(
@Param("pincode") pincode: Int,
pageable: Pageable
): List<PincodeCityStateCandidate>
}
interface PincodeCityStateCandidate {
val city: String
val state: String
val hitCount: Long
}