Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 25 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,20 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("org.springframework.boot") version "3.1.0"
id("io.spring.dependency-management") version "1.1.0"
id("org.jlleitschuh.gradle.ktlint") version "11.5.0"
id("jacoco")
kotlin("jvm") version "1.8.21"
kotlin("plugin.spring") version "1.8.21"
kotlin("plugin.jpa") version "1.8.21"
kotlin("plugin.allopen") version "1.6.21"
kotlin("plugin.noarg") version "1.6.21"
id("org.jlleitschuh.gradle.ktlint") version "11.5.0"
id("jacoco")
kotlin("kapt") version "1.8.21"
idea
}

group = "com.example"
version = "0.0.1-SNAPSHOT"
val queryDslVersion = "5.0.0"

java {
sourceCompatibility = JavaVersion.VERSION_17
Expand Down Expand Up @@ -74,6 +77,11 @@ dependencies {
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2")
implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("io.micrometer:micrometer-registry-prometheus")

implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
kapt("com.querydsl:querydsl-apt:${dependencyManagement.importedProperties["querydsl.version"]}:jakarta")
kapt("jakarta.annotation:jakarta.annotation-api")
kapt("jakarta.persistence:jakarta.persistence-api")
}

tasks.withType<KotlinCompile> {
Expand All @@ -88,6 +96,21 @@ tasks.withType<Test> {
finalizedBy(tasks.jacocoTestReport)
}

val querydslDir = "build/generated/source/kapt/main"
idea {
module {
val kaptMain = file(querydslDir)
sourceDirs.add(kaptMain)
generatedSourceDirs.add(kaptMain)
}
}

tasks.named("clean") {
doLast {
file(querydslDir).deleteRecursively()
}
}

allOpen {
annotation("jakarta.persistence.Entity")
annotation("jakarta.persistence.MappedSuperclass")
Expand Down
39 changes: 23 additions & 16 deletions docs/open-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -188,16 +188,23 @@ paths:
- event-controller
operationId: getEvents
parameters:
- name: name
- name: sort
in: query
required: false
schema:
type: string
- name: pageable
- name: id
in: query
required: true
required: false
schema:
$ref: '#/components/schemas/Pageable'
type: integer
format: int32
- name: time
in: query
required: false
schema:
type: string
format: date-time
Comment on lines +196 to +207
Copy link
Member

@junha-ahn junha-ahn Oct 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

키 이름을 last_access_id, last_access_time 등 으로 변경할 필요가 있어보입니다.

  • 더 좋은 이름 있을 수 있으니 찾아보세요.
  • 당근마켓, 인스타, 트위터, 페이스북 등 참고
    • 무한스크롤 형태니까... Open API Docs 또는 개발자 도구 열고 직접 요청을 확인해보세요

responses:
"200":
description: OK
Expand Down Expand Up @@ -494,15 +501,15 @@ components:
format: int32
sort:
$ref: '#/components/schemas/SortObject'
first:
type: boolean
last:
type: boolean
numberOfElements:
type: integer
format: int32
pageable:
$ref: '#/components/schemas/PageableObject'
first:
type: boolean
last:
type: boolean
empty:
type: boolean
PageableObject:
Expand Down Expand Up @@ -640,15 +647,15 @@ components:
format: int32
sort:
$ref: '#/components/schemas/SortObject'
first:
type: boolean
last:
type: boolean
numberOfElements:
type: integer
format: int32
pageable:
$ref: '#/components/schemas/PageableObject'
first:
type: boolean
last:
type: boolean
empty:
type: boolean
EventResponse:
Expand Down Expand Up @@ -703,14 +710,14 @@ components:
format: int32
sort:
$ref: '#/components/schemas/SortObject'
first:
type: boolean
last:
type: boolean
numberOfElements:
type: integer
format: int32
pageable:
$ref: '#/components/schemas/PageableObject'
first:
type: boolean
last:
type: boolean
empty:
type: boolean
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.group4.ticketingservice.config

import com.querydsl.jpa.impl.JPAQueryFactory
import jakarta.persistence.EntityManager
import jakarta.persistence.PersistenceContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class QuerydslConfiguration(
@PersistenceContext
private val entityManager: EntityManager
) {
@Bean
fun jpaQueryFactory(): JPAQueryFactory = JPAQueryFactory(entityManager)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ import jakarta.servlet.http.HttpServletRequest
import jakarta.validation.Valid
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.web.PageableDefault
import org.springframework.http.HttpHeaders
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
Expand All @@ -22,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.time.OffsetDateTime

@RestController
@RequestMapping("/events")
Expand Down Expand Up @@ -84,10 +82,11 @@ class EventController @Autowired constructor(
@GetMapping
fun getEvents(
request: HttpServletRequest,
@RequestParam(required = false) name: String?,
@PageableDefault(size = 10, sort = ["id"], direction = Sort.Direction.DESC) pageable: Pageable
@RequestParam sort: String?,
@RequestParam id: Int?,
@RequestParam time: OffsetDateTime?
): ResponseEntity<Page<Event>> {
val page = eventService.getEvents(name, pageable)
val page = eventService.getEvents(sort, id, time)

val headers = HttpHeaders()
headers.set("Content-Location", request.requestURI)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import org.springframework.data.jpa.domain.Specification
class EventSpecifications {
companion object {
fun withName(name: String?): Specification<Event> {
return Specification { root: Root<Event>, query: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder ->
return Specification { root: Root<Event>, _: CriteriaQuery<*>, criteriaBuilder: CriteriaBuilder ->
val predicates = mutableListOf<Predicate>()

if (!name.isNullOrBlank()) {
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%${name.toLowerCase()}%"))
predicates.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("name")), "%${name.lowercase()}%"))
}

criteriaBuilder.and(*predicates.toTypedArray())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.group4.ticketingservice.repository

import com.group4.ticketingservice.entity.Event
import com.group4.ticketingservice.entity.QEvent.event
import com.group4.ticketingservice.utils.exception.CustomException
import com.group4.ticketingservice.utils.exception.ErrorCodes
import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.jpa.impl.JPAQueryFactory
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport
import org.springframework.stereotype.Repository
import java.time.Duration
import java.time.OffsetDateTime

@Repository
class EventRepositorySupport(
private val queryFactory: JPAQueryFactory
) : QuerydslRepositorySupport(
Event::class.java
) {

private fun ltEventId(id: Int?): BooleanExpression? {
return if (id == null) null else event.id.lt(id)
}

fun getEvent(sort: String?, id: Int?, dateTime: OffsetDateTime?): List<Event> {
val whereSpecifier = if (id == null) {
when (sort) {
"deadline" -> event.reservationEndTime.after(OffsetDateTime.now())
"startDate" -> event.startDate.before(OffsetDateTime.now() + Duration.ofDays(60))
"createdAt" -> null
null -> null
else -> throw CustomException(ErrorCodes.MESSAGE_NOT_READABLE)
}
} else {
when (sort) {
"deadline" -> event.reservationEndTime.gt(dateTime).or(event.reservationEndTime.eq(dateTime).and(ltEventId(id)))
"startDate" -> event.startDate.gt(dateTime).or(event.startDate.eq(dateTime).and(ltEventId(id)))
"createdAt" -> event.createdAt.lt(dateTime).or(event.createdAt.eq(dateTime).and(ltEventId(id)))
null -> ltEventId(id)
else -> throw CustomException(ErrorCodes.MESSAGE_NOT_READABLE)
}
}

val orderSpecifier = when (sort) {
"deadline" -> event.reservationEndTime.asc()
"startDate" -> event.startDate.asc()
"createdAt" -> event.createdAt.desc()
null -> event.id.desc()
else -> throw CustomException(ErrorCodes.MESSAGE_NOT_READABLE)
}

return queryFactory.selectFrom(event)
.where(whereSpecifier)
.orderBy(orderSpecifier, event.id.desc())
.limit(10)
.fetch()
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
package com.group4.ticketingservice.service

import com.group4.ticketingservice.dto.EventSpecifications
import com.group4.ticketingservice.entity.Event
import com.group4.ticketingservice.repository.EventRepository
import com.group4.ticketingservice.repository.EventRepositorySupport
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import java.time.OffsetDateTime

@Service
class EventService(
private val eventRepository: EventRepository
private val eventRepository: EventRepository,
private val eventRepositorySupport: EventRepositorySupport

) {
fun createEvent(
name: String,
Expand All @@ -36,8 +37,10 @@ class EventService(
return eventRepository.findById(id).orElse(null)
}

fun getEvents(name: String?, pageable: Pageable): Page<Event> {
val specification = EventSpecifications.withName(name)
return PageImpl(eventRepository.findAllBy(specification, pageable))
fun getEvents(sort: String?, id: Int?, time: OffsetDateTime?): Page<Event> {
// val specification = EventSpecifications.withName(name)
// val lastAccessId=13
// val lastAccessTime= OffsetDateTime.of(2024,9,6,0,0,0,0, ZoneOffset.UTC)
return PageImpl(eventRepositorySupport.getEvent(sort, id, time))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class ResponseAdvice<T>(
response: ServerHttpResponse
): T? {
if (body == null) {
@Suppress("UNCHECKED_CAST")
return SuccessResponseDTO(
data = null,
path = response.headers.getFirst("Content-Location")
Expand All @@ -62,6 +63,7 @@ class ResponseAdvice<T>(
}

if (body !is Page<*>) {
@Suppress("UNCHECKED_CAST")
return SuccessResponseDTO(
data = body as Any,
path = response.headers.getFirst("Content-Location")
Expand Down Expand Up @@ -97,6 +99,7 @@ class ResponseAdvice<T>(
if (data.isEmpty()) {
data = listOf()
} else if (data[0] is Event) {
@Suppress("UNCHECKED_CAST")
data = (page.content as List<Event>).map {
EventResponse(
id = it.id!!,
Expand All @@ -109,6 +112,7 @@ class ResponseAdvice<T>(
)
}
} else if (data[0] is Reservation) {
@Suppress("UNCHECKED_CAST")
data = (page.content as List<Reservation>).map {
ReservationResponse(
id = it.id!!,
Expand All @@ -122,7 +126,7 @@ class ResponseAdvice<T>(
)
}
}

@Suppress("UNCHECKED_CAST")
return SuccessResponseDTO(
data = data,
path = response.headers.getFirst("Content-Location")
Expand Down