diff --git a/src/main/kotlin/io/github/devcavin/gatelog/common/service/BaseEntityService.kt b/src/main/kotlin/io/github/devcavin/gatelog/common/service/BaseEntityService.kt deleted file mode 100644 index b889217..0000000 --- a/src/main/kotlin/io/github/devcavin/gatelog/common/service/BaseEntityService.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.github.devcavin.gatelog.common.service - -import io.github.devcavin.gatelog.common.exception.ResourceNotFoundException -import org.springframework.data.jpa.repository.JpaRepository -import java.util.UUID - -abstract class BaseEntityService( - private val repository: JpaRepository, - private val resourceName: String -) { - - protected fun findEntityById(id: UUID): T = - repository.findById(id) - .orElseThrow { - ResourceNotFoundException(resourceName, id) - } -} \ No newline at end of file diff --git a/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeConfig.kt b/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeConfig.kt index 924411c..5a0529d 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeConfig.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeConfig.kt @@ -1,13 +1,18 @@ package io.github.devcavin.gatelog.common.time +import org.springframework.beans.factory.annotation.Value import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import java.time.Clock -import java.time.ZoneOffset +import java.time.ZoneId @Configuration -class TimeConfig { +class TimeConfig( + @Value($$"${gatelog.timezone}") + private val timeZone: String +) { + @Bean fun clock(): Clock = - Clock.system(ZoneOffset.UTC) + Clock.system(ZoneId.of(timeZone)) } \ No newline at end of file diff --git a/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeProvider.kt b/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeProvider.kt deleted file mode 100644 index 3b65d4a..0000000 --- a/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeProvider.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.devcavin.gatelog.common.time - -import org.springframework.stereotype.Component -import java.time.Clock -import java.time.OffsetDateTime - -@Component -class TimeProvider(private val clock: Clock) { - - fun timeNow() : OffsetDateTime = OffsetDateTime.now(clock) -} \ No newline at end of file diff --git a/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeUtil.kt b/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeUtil.kt index 7e8af3e..585a8e1 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeUtil.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/common/time/TimeUtil.kt @@ -1,24 +1,20 @@ package io.github.devcavin.gatelog.common.time +import org.springframework.stereotype.Component +import java.time.Clock +import java.time.LocalDate import java.time.OffsetDateTime -import java.time.ZoneOffset -object TimeUtil { +@Component +class TimeUtil(private val clock: Clock) { fun timeNow(): OffsetDateTime = - OffsetDateTime.now(ZoneOffset.UTC) + OffsetDateTime.now(clock) fun startOfToday(): OffsetDateTime = - timeNow() - .toLocalDate() - .atStartOfDay() - .atOffset(ZoneOffset.UTC) + LocalDate.now(clock).atStartOfDay(clock.zone).toOffsetDateTime() - fun startOfTomorrow(): OffsetDateTime = - startOfToday().plusDays(1) - - fun endOfToday(): OffsetDateTime = - startOfTomorrow() + fun endOfToday(): OffsetDateTime = startOfToday().plusDays(1) fun isOvernight( checkInTime: OffsetDateTime, diff --git a/src/main/kotlin/io/github/devcavin/gatelog/dashboard/DashboardService.kt b/src/main/kotlin/io/github/devcavin/gatelog/dashboard/DashboardService.kt index 339467d..a0e91ab 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/dashboard/DashboardService.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/dashboard/DashboardService.kt @@ -7,8 +7,8 @@ import io.github.devcavin.gatelog.dashboard.dto.DashboardFeed import io.github.devcavin.gatelog.dashboard.dto.DashboardSummary import io.github.devcavin.gatelog.users.User import io.github.devcavin.gatelog.visitors.VisitRepository +import io.github.devcavin.gatelog.visitors.VisitResponseMapper import io.github.devcavin.gatelog.visitors.VisitStatusRepository -import io.github.devcavin.gatelog.visitors.dto.toResponse import org.springframework.data.domain.PageRequest import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -17,7 +17,9 @@ import org.springframework.transaction.annotation.Transactional class DashboardService( private val visitRepository: VisitRepository, private val visitStatusRepository: VisitStatusRepository, - private val authorizationService: AuthorizationService + private val authorizationService: AuthorizationService, + private val timeUtil: TimeUtil, + private val visitResponseMapper: VisitResponseMapper ) { @Transactional(readOnly = true) @@ -26,8 +28,8 @@ class DashboardService( val scope = authorizationService.scopeFor(requestedBy) val siteId = scope.siteIdOrNull - val startOfToday = TimeUtil.startOfToday() - val endOfTheDay = TimeUtil.endOfToday() + val startOfToday = timeUtil.startOfToday() + val endOfTheDay = timeUtil.endOfToday() val checkedInStatus = visitStatusRepository.findByName("CHECKED_IN") @@ -118,12 +120,13 @@ class DashboardService( checkedInToday = checkedInToday, checkedOutToday = checkedOutToday, overdueCount = overdueCount, - overnightCount = overnightCount + overnightCount = overnightCount, + asOf = timeUtil.timeNow() ), - activeVisitors = activeVisitors.map { it.toResponse() }, - overdueVisitors = overdueVisitors.map { it.toResponse() }, - overnightVisitors = overnightVisitors.map { it.toResponse() }, - recentlyCheckedOut = recentlyCheckedOut.map { it.toResponse() } + activeVisitors = activeVisitors.map(visitResponseMapper::toResponse), + overdueVisitors = overdueVisitors.map(visitResponseMapper::toResponse), + overnightVisitors = overnightVisitors.map(visitResponseMapper::toResponse), + recentlyCheckedOut = recentlyCheckedOut.map(visitResponseMapper::toResponse), ) } } \ No newline at end of file diff --git a/src/main/kotlin/io/github/devcavin/gatelog/dashboard/dto/DashboardDtos.kt b/src/main/kotlin/io/github/devcavin/gatelog/dashboard/dto/DashboardDtos.kt index b9fb688..a57eabf 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/dashboard/dto/DashboardDtos.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/dashboard/dto/DashboardDtos.kt @@ -1,6 +1,5 @@ package io.github.devcavin.gatelog.dashboard.dto -import io.github.devcavin.gatelog.common.time.TimeUtil import io.github.devcavin.gatelog.visitors.dto.VisitResponse import java.time.OffsetDateTime @@ -10,7 +9,7 @@ data class DashboardSummary( val checkedOutToday: Long, val overdueCount: Long, val overnightCount: Long, - val asOf: OffsetDateTime = TimeUtil.timeNow() + val asOf: OffsetDateTime ) data class DashboardFeed( diff --git a/src/main/kotlin/io/github/devcavin/gatelog/reports/ReportService.kt b/src/main/kotlin/io/github/devcavin/gatelog/reports/ReportService.kt index 6bcb6f1..a6a00e6 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/reports/ReportService.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/reports/ReportService.kt @@ -17,7 +17,8 @@ import java.time.format.DateTimeFormatter @Service class ReportService( private val visitRepository: VisitRepository, - private val authorizationService: AuthorizationService + private val authorizationService: AuthorizationService, + private val timeUtil: TimeUtil ) { private val formatter = DateTimeFormatter.ofPattern( @@ -82,7 +83,7 @@ class ReportService( ?.let(formatter::format) ?: "", durationMinutes(visit), - TimeUtil.isOvernight(visit.checkInTime).toString() + timeUtil.isOvernight(visit.checkInTime).toString() ) ) } diff --git a/src/main/kotlin/io/github/devcavin/gatelog/visitors/OverdueVisitJob.kt b/src/main/kotlin/io/github/devcavin/gatelog/visitors/OverdueVisitJob.kt index 4990885..bb1a917 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/visitors/OverdueVisitJob.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/visitors/OverdueVisitJob.kt @@ -14,6 +14,7 @@ class OverdueVisitJob( private val visitRepository: VisitRepository, private val visitorStatusRepository: VisitStatusRepository, private val siteRepository: SiteRepository, + private val timeUtil: TimeUtil, @Value($$"${gatelog.scheduler.overdue-threshold-hours:2}") private val overdueThresholdHours: Long @@ -35,7 +36,7 @@ class OverdueVisitJob( ) val threshold = - TimeUtil.timeNow().minusHours(overdueThresholdHours) + timeUtil.timeNow().minusHours(overdueThresholdHours) val sites = siteRepository.findAll() diff --git a/src/main/kotlin/io/github/devcavin/gatelog/visitors/Visit.kt b/src/main/kotlin/io/github/devcavin/gatelog/visitors/Visit.kt index f3f00b0..1858883 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/visitors/Visit.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/visitors/Visit.kt @@ -52,7 +52,7 @@ class Visit( var purpose: String = "General Visit", @Column(name = "check_in_time", nullable = false, updatable = false) - var checkInTime: OffsetDateTime = TimeUtil.timeNow(), + var checkInTime: OffsetDateTime, @Column(name = "check_out_time") var checkOutTime: OffsetDateTime? = null diff --git a/src/main/kotlin/io/github/devcavin/gatelog/visitors/VisitResponseMapper.kt b/src/main/kotlin/io/github/devcavin/gatelog/visitors/VisitResponseMapper.kt new file mode 100644 index 0000000..a8d2028 --- /dev/null +++ b/src/main/kotlin/io/github/devcavin/gatelog/visitors/VisitResponseMapper.kt @@ -0,0 +1,36 @@ +package io.github.devcavin.gatelog.visitors + +import io.github.devcavin.gatelog.common.time.TimeUtil +import io.github.devcavin.gatelog.visitors.dto.VisitResponse +import io.github.devcavin.gatelog.visitors.dto.VisitorProfileSummary +import org.springframework.stereotype.Component + +@Component +class VisitResponseMapper( + private val timeUtil: TimeUtil +) { + + fun toResponse(visit: Visit): VisitResponse { + val profile = visit.visitorProfile + + return VisitResponse( + id = requireNotNull(visit.id), + profile = VisitorProfileSummary( + id = requireNotNull(profile.id), + name = profile.name, + phoneNumber = profile.phoneNumber + ), + visitorType = visit.visitorType, + purpose = visit.purpose, + status = visit.visitStatus.name, + siteId = requireNotNull(visit.site.id), + zoneId = visit.zone?.id, + zoneName = visit.zone?.name, + createdById = requireNotNull(visit.createdBy.id), + createdByName = visit.createdBy.name, + checkInTime = visit.checkInTime, + checkOutTime = visit.checkOutTime, + overnight = visit.checkOutTime == null && timeUtil.isOvernight(visit.checkInTime) + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/github/devcavin/gatelog/visitors/VisitService.kt b/src/main/kotlin/io/github/devcavin/gatelog/visitors/VisitService.kt index 769008f..61ea216 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/visitors/VisitService.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/visitors/VisitService.kt @@ -4,7 +4,7 @@ import io.github.devcavin.gatelog.auth.AuthorizationService import io.github.devcavin.gatelog.common.exception.ConflictException import io.github.devcavin.gatelog.common.exception.InvalidStateException import io.github.devcavin.gatelog.common.exception.ResourceNotFoundException -import io.github.devcavin.gatelog.common.time.TimeProvider +import io.github.devcavin.gatelog.common.time.TimeUtil import io.github.devcavin.gatelog.users.User import io.github.devcavin.gatelog.visitors.dto.* import io.github.devcavin.gatelog.zones.ZoneRepository @@ -24,7 +24,8 @@ class VisitService( private val zoneRepository: ZoneRepository, private val visitorProfileRepository: VisitorProfileRepository, private val authorizationService: AuthorizationService, - private val timeProvider: TimeProvider + private val timeUtil: TimeUtil, + private val visitResponseMapper: VisitResponseMapper ) { @Transactional @@ -88,23 +89,23 @@ class VisitService( visitStatus = checkedInStatus, visitorType = request.visitorType, purpose = request.purpose, - checkInTime = timeProvider.timeNow() + checkInTime = timeUtil.timeNow() ) - return visitRepository - .save(visit) - .toResponse() + val saved = visitRepository.save(visit) + + return visitResponseMapper.toResponse(saved) } @Transactional(readOnly = true) fun getById( requestedBy: User, visitId: UUID - ): VisitResponse = - findAccessibleVisit( - requestedBy, - visitId - ).toResponse() + ): VisitResponse { + val visit = findAccessibleVisit(requestedBy, visitId) + + return visitResponseMapper.toResponse(visit) + } @Transactional(readOnly = true) fun search( @@ -122,7 +123,7 @@ class VisitService( ), pageable ) - .map { it.toResponse() } + .map(visitResponseMapper::toResponse) } @Transactional @@ -153,11 +154,11 @@ class VisitService( ) visit.visitStatus = checkedOutStatus - visit.checkOutTime = timeProvider.timeNow() + visit.checkOutTime = timeUtil.timeNow() - return visitRepository - .save(visit) - .toResponse() + val checkedOut = visitRepository.save(visit) + + return visitResponseMapper.toResponse(checkedOut) } @Transactional(readOnly = true) diff --git a/src/main/kotlin/io/github/devcavin/gatelog/visitors/dto/VisitorResponses.kt b/src/main/kotlin/io/github/devcavin/gatelog/visitors/dto/VisitorResponses.kt index ce2c2b1..67f6916 100644 --- a/src/main/kotlin/io/github/devcavin/gatelog/visitors/dto/VisitorResponses.kt +++ b/src/main/kotlin/io/github/devcavin/gatelog/visitors/dto/VisitorResponses.kt @@ -1,10 +1,9 @@ package io.github.devcavin.gatelog.visitors.dto -import io.github.devcavin.gatelog.common.time.TimeUtil import io.github.devcavin.gatelog.visitors.Visit import io.github.devcavin.gatelog.visitors.VisitorProfile import java.time.OffsetDateTime -import java.util.UUID +import java.util.* data class VisitorProfileSummary( val id: UUID, @@ -52,31 +51,6 @@ data class VisitSummary( val checkOutTime: OffsetDateTime? ) -fun Visit.toResponse(): VisitResponse { - val profile = visitorProfile - - return VisitResponse( - id = requireNotNull(id), - profile = VisitorProfileSummary( - id = requireNotNull(profile.id), - name = profile.name, - phoneNumber = profile.phoneNumber - ), - visitorType = visitorType, - purpose = purpose, - status = visitStatus.name, - siteId = requireNotNull(site.id), - zoneId = zone?.id, - zoneName = zone?.name, - createdById = requireNotNull(createdBy.id), - createdByName = createdBy.name, - checkInTime = checkInTime, - checkOutTime = checkOutTime, - overnight = checkOutTime == null && - TimeUtil.isOvernight(checkInTime) - ) -} - fun VisitorProfile.toResponse( visitCount: Long ): VisitorProfileResponse = diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index f888654..6468ce4 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -47,4 +47,6 @@ gatelog: scheduler: overdue-threshold-hours: 2 - overdue-job-rate-ms: 900000 \ No newline at end of file + overdue-job-rate-ms: 900000 + + timezone: ${GATELOG_TIMEZONE} \ No newline at end of file