Формат времени в UTC

This commit is contained in:
emelianov
2026-06-25 16:21:17 +03:00
parent d0ec0ef754
commit fe54c095ab
164 changed files with 1847 additions and 634 deletions
@@ -1,10 +1,19 @@
package org.nstart.dep265.requestservice.config
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.databind.DeserializationContext
import com.fasterxml.jackson.databind.JsonDeserializer
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.LocalDateTime
@Configuration
class JacksonConfig {
@@ -18,5 +27,23 @@ class JacksonConfig {
return jacksonObjectMapper()
.registerModule(JavaTimeModule())
.findAndRegisterModules()
.registerModule(utcLocalDateTimeModule())
}
private fun utcLocalDateTimeModule(): SimpleModule = SimpleModule("pcp-request-service-jackson2-utc-local-date-time")
.addSerializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeSerializer())
.addDeserializer(LocalDateTime::class.java, Jackson2UtcLocalDateTimeDeserializer())
}
private class Jackson2UtcLocalDateTimeSerializer : JsonSerializer<LocalDateTime>() {
override fun serialize(value: LocalDateTime, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeString(UtcDateTimes.format(value))
}
}
private class Jackson2UtcLocalDateTimeDeserializer : JsonDeserializer<LocalDateTime>() {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
return UtcDateTimes.parse(value)
}
}
@@ -0,0 +1,55 @@
package org.nstart.dep265.requestservice.config
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.convert.converter.Converter
import org.springframework.format.FormatterRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import tools.jackson.core.JsonGenerator
import tools.jackson.core.JsonParser
import tools.jackson.databind.DeserializationContext
import tools.jackson.databind.SerializationContext
import tools.jackson.databind.deser.std.StdDeserializer
import tools.jackson.databind.module.SimpleModule
import tools.jackson.databind.ser.std.StdSerializer
import java.time.Clock
import java.time.LocalDateTime
@Configuration
class UtcTimeConfiguration : WebMvcConfigurer {
@Bean
fun clock(): Clock = Clock.systemUTC()
@Bean
fun requestServiceUtcLocalDateTimeCustomizer(): JsonMapperBuilderCustomizer = JsonMapperBuilderCustomizer { builder ->
builder.addModule(
SimpleModule("pcp-request-service-utc-local-date-time")
.addSerializer(LocalDateTime::class.java, UtcLocalDateTimeSerializer())
.addDeserializer(LocalDateTime::class.java, UtcLocalDateTimeDeserializer())
)
}
override fun addFormatters(registry: FormatterRegistry) {
registry.addConverter(UtcLocalDateTimeQueryParamConverter())
}
}
private class UtcLocalDateTimeQueryParamConverter : Converter<String, LocalDateTime> {
override fun convert(source: String): LocalDateTime = UtcDateTimes.parse(source)
}
private class UtcLocalDateTimeSerializer : StdSerializer<LocalDateTime>(LocalDateTime::class.java) {
override fun serialize(value: LocalDateTime, gen: JsonGenerator, ctxt: SerializationContext) {
gen.writeString(UtcDateTimes.format(value))
}
}
private class UtcLocalDateTimeDeserializer : StdDeserializer<LocalDateTime>(LocalDateTime::class.java) {
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): LocalDateTime {
val value = p.valueAsString ?: throw IllegalArgumentException("Expected ISO-8601 UTC date-time string")
return UtcDateTimes.parse(value)
}
}
@@ -12,7 +12,6 @@ import org.nstart.dep265.requestservice.dto.RequestStatusDto
import org.nstart.dep265.requestservice.dto.RequestWithCellsResponseDto
import org.nstart.dep265.requestservice.dto.SurveyTypeDto
import org.nstart.dep265.requestservice.service.RequestService
import org.springframework.format.annotation.DateTimeFormat
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
@@ -41,10 +40,10 @@ class RequestController(
@RequestParam(required = false) surveyType: SurveyTypeDto?,
@RequestParam(required = false) kpp: Int?,
@RequestParam(required = false) highPriorityTransmit: Boolean?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginFrom: LocalDateTime?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginTo: LocalDateTime?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endFrom: LocalDateTime?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endTo: LocalDateTime?,
@RequestParam(required = false) beginFrom: LocalDateTime?,
@RequestParam(required = false) beginTo: LocalDateTime?,
@RequestParam(required = false) endFrom: LocalDateTime?,
@RequestParam(required = false) endTo: LocalDateTime?,
@RequestParam(defaultValue = "false") includeDeleted: Boolean,
@RequestParam(defaultValue = "0") page: Int,
@RequestParam(defaultValue = "50") size: Int,
@@ -73,10 +72,10 @@ class RequestController(
@RequestParam(required = false) surveyType: SurveyTypeDto?,
@RequestParam(required = false) kpp: Int?,
@RequestParam(required = false) highPriorityTransmit: Boolean?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginFrom: LocalDateTime?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) beginTo: LocalDateTime?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endFrom: LocalDateTime?,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) endTo: LocalDateTime?,
@RequestParam(required = false) beginFrom: LocalDateTime?,
@RequestParam(required = false) beginTo: LocalDateTime?,
@RequestParam(required = false) endFrom: LocalDateTime?,
@RequestParam(required = false) endTo: LocalDateTime?,
@RequestParam(defaultValue = "false") includeDeleted: Boolean,
@RequestParam(defaultValue = "0") page: Int,
@RequestParam(defaultValue = "50") size: Int,
@@ -7,6 +7,8 @@ import org.nstart.dep265.requestservice.repository.RequestCellRepository
import org.nstart.dep265.requestservice.repository.RequestRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.Clock
import java.time.LocalDateTime
class GridRebuildAlreadyRunningException : RuntimeException("Grid rebuild is already running")
@@ -20,6 +22,7 @@ class GridRebuildService(
private val requestGridProjectionService: RequestGridProjectionService,
private val earthCellRepository: EarthCellRepository,
private val gridRebuildLockService: GridRebuildLockService,
private val clock: Clock = Clock.systemUTC(),
) {
@Transactional
fun rebuildGrid(): GridRebuildResponseDto {
@@ -40,7 +43,7 @@ class GridRebuildService(
return GridRebuildResponseDto(
cellsCount = earthCellRepository.count(),
rebuiltRequestProjectionsCount = activeRequests.size.toLong(),
rebuiltAt = LocalDateTime.now(),
rebuiltAt = UtcDateTimes.now(clock),
)
}
@@ -11,6 +11,8 @@ import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import org.springframework.transaction.support.TransactionCallback
import org.springframework.transaction.support.TransactionOperations
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.Clock
import java.time.Duration
import java.time.LocalDateTime
import java.util.concurrent.ExecutionException
@@ -25,6 +27,7 @@ class OutboxPublisherService(
@param:Qualifier("outboxTransactionOperations")
private val transactionOperations: TransactionOperations,
private val clock: Clock = Clock.systemUTC(),
) {
private val log = LoggerFactory.getLogger(this::class.java)
@@ -42,7 +45,7 @@ class OutboxPublisherService(
return try {
transactionOperations.execute(
TransactionCallback {
val now = LocalDateTime.now()
val now = UtcDateTimes.now(clock)
val events = outboxEventRepository.findPublishableForUpdateSkipLocked(
eventType = RequestStatusOutboxService.REQUEST_STATUS_EVENT_TYPE,
now = now,
@@ -9,6 +9,8 @@ import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import org.springframework.transaction.support.TransactionCallback
import org.springframework.transaction.support.TransactionOperations
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.Clock
import java.time.LocalDateTime
/**
@@ -33,6 +35,7 @@ class RequestExpiryService(
@param:Qualifier("requestExpiryTransactionOperations")
private val transactionOperations: TransactionOperations,
private val clock: Clock = Clock.systemUTC(),
) {
private val log = LoggerFactory.getLogger(this::class.java)
@@ -55,7 +58,7 @@ class RequestExpiryService(
return 0
}
val maxBatches = requestExpiryProperties.maxBatchesPerRun.coerceAtLeast(1)
val now = LocalDateTime.now()
val now = UtcDateTimes.now(clock)
var expiredTotal = 0
var batchIndex = 0
@@ -38,6 +38,8 @@ import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.Clock
import java.time.LocalDateTime
import java.util.UUID
@@ -53,6 +55,7 @@ class RequestService(
private val requestGridProjectionService: RequestGridProjectionService,
private val requestLifecycleService: RequestLifecycleService,
private val entityManager: EntityManager,
private val clock: Clock = Clock.systemUTC(),
) {
@Transactional
fun createRequest(request: CreateRequestDto): CreateRequestResponseDto {
@@ -65,7 +68,7 @@ class RequestService(
"beginDateTime должен быть раньше endDateTime"
}
val now = LocalDateTime.now()
val now = UtcDateTimes.now(clock)
val normalizedGeometry = geometryService.parsePolygonalGeometry(request.geometry)
val normalizedGeometryWkt = geometryService.toWkt(normalizedGeometry)
val geometryArea = geometryService.area(normalizedGeometry)
@@ -159,7 +162,7 @@ class RequestService(
@Transactional
fun deleteRequest(id: UUID): DeleteRequestResponseDto? {
val request = requestRepository.findById(id).orElse(null) ?: return null
val deletedAt = request.deletedAt ?: LocalDateTime.now()
val deletedAt = request.deletedAt ?: UtcDateTimes.now(clock)
request.status = RequestStatus.DELETED
request.deletedAt = deletedAt
request.updatedAt = deletedAt
@@ -14,6 +14,8 @@ import org.springframework.dao.DuplicateKeyException
import org.springframework.stereotype.Service
import org.springframework.transaction.support.TransactionCallback
import org.springframework.transaction.support.TransactionOperations
import space.nstart.pcp.pcp_types_lib.utils.UtcDateTimes
import java.time.Clock
import java.time.LocalDateTime
import java.util.UUID
import kotlin.math.max
@@ -35,6 +37,7 @@ class RouteMatchingService(
@param:Qualifier("routeMatchingTransactionOperations")
private val routeMatchingTransactionOperations: TransactionOperations,
private val clock: Clock = Clock.systemUTC(),
) {
private val log = LoggerFactory.getLogger(this::class.java)
@@ -43,7 +46,7 @@ class RouteMatchingService(
val routeId = route.routeId
val routeRollRange = route.rollAngle
val matchedAt: LocalDateTime = LocalDateTime.now()
val matchedAt: LocalDateTime = UtcDateTimes.now(clock)
val routeGeometry = geometryService.parsePolygonalGeometry(route.geometry)
val candidateRequestIds = requestRepository.findRequestIdsForRoute(