Skip to content

Commit

Permalink
[refactor] 큐레이션 도메인 수정 및 API 추가 (#89)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gopistol authored Mar 2, 2024
1 parent 8676f3e commit eb0cc24
Show file tree
Hide file tree
Showing 17 changed files with 364 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ class Curation() : BaseEntity() {
constructor(
title: String,
description: String,
imageAddress: String,
) : this() {
this.title = title
this.description = description
this.imageAddress = imageAddress
}

@Column(nullable = false)
Expand All @@ -36,11 +38,14 @@ class Curation() : BaseEntity() {
field = value
}

@Column(nullable = false)
var imageAddress: String = ""

@ManyToMany(fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST, CascadeType.MERGE])
@JoinTable(
name = "curation_store",
joinColumns = [JoinColumn(name = "store_id")],
inverseJoinColumns = [JoinColumn(name = "curation_id")]
joinColumns = [JoinColumn(name = "curation_id")],
inverseJoinColumns = [JoinColumn(name = "store_id")]
)
val mutableStores: MutableSet<Store> = mutableSetOf()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ class CurationCommandService(

) {
fun create(command: CreateCurationCommand): Long {
return repository.save(Curation(command.title, command.description)).id
return repository.save(Curation(command.title, command.description, command.imageAddress)).id
}

fun update(id: Long, command: UpdateCurationCommand) {
val (newTitle, newDescription) = command
val (newTitle, newDescription, newImageAddress) = command
val updatedCuration = repository.getReferenceById(id).apply {
title = newTitle
description = newDescription
imageAddress = newImageAddress
}

repository.save(updatedCuration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ data class CreateCurationCommand(
val title: String,
@Schema(name = "description", example = "큐레이션 설명")
val description: String,
@Schema(name = "imageAddress", example = "store/232D8241-C6A9-4AD9-B0EA-56F6DD24BADF.png")
val imageAddress: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ data class UpdateCurationCommand(
val title: String,
@Schema(name = "description", example = "수정할 큐레이션 설명")
val description: String,
@Schema(name = "imageAddress", example = "store/232D8241-C6A9-4AD9-B0EA-56F6DD24BADF.png")
val imageAddress: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ data class CurationDto(
val title: String,
@Schema(name = "description", example = "점심 특선 메뉴를 판매하는 음식점들이에요.")
val description: String,
@Schema(name = "imageAddress", example = "store/232D8241-C6A9-4AD9-B0EA-56F6DD24BADF.png")
val imageAddress: String,
@JsonUnwrapped val stores: Stores? = null,
) {

Expand All @@ -22,6 +24,7 @@ data class CurationDto(
id = domain.id,
title = domain.title,
description = domain.description,
imageAddress = domain.imageAddress,
stores = Stores(domain.getStores().map(StoreDto::from))
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ class StoreQueryService(
) {
fun findAllByCategoryAndCursor(cursor: Long? = null, categoryId: Long? = null, size: Int): List<StoreDto> {
return if (categoryId == null) {
repository.findAllByIdLessThanOrderByIdDesc(
size = size,
id = cursor
).map(StoreDto::from)
findAllByCursor(cursor, size)
} else {
// FIXME(cache): store 캐시 처리 이후 store 조회 개선하기
val storeIds = repository.findIdsByCategoryIdOrderByIdDesc(
Expand All @@ -29,11 +26,28 @@ class StoreQueryService(
}
}

fun findAllByCurationAndCursor(cursor: Long? = null, curationId: Long, size: Int): List<StoreDto> {
val storeIds = repository.findIdsByCurationIdOrderByIdDesc(
curationId = curationId,
size = size,
id = cursor
)

return repository.findAllByIdInOrderByIdDesc(storeIds).map(StoreDto::from)
}

fun findById(storeId: Long): StoreDetailDto {
val entity = repository.getReferenceById(storeId)
// FIXME(async): findById가 popular store command 과정에서 실패하지 않도록 비동기 호출 처리
// TODO: Redis 이슈 해결 후 주석 제거
// popularStoreCommandService.setStore(storeId)
return StoreDetailDto.from(entity)
}

private fun findAllByCursor(cursor: Long?, size: Int): List<StoreDto> {
return repository.findAllByIdLessThanOrderByIdDesc(
size = size,
id = cursor
).map(StoreDto::from)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ import com.mjucow.eatda.domain.store.entity.Store

interface StoreCustomRepository {
fun findIdsByCategoryIdOrderByIdDesc(categoryId: Long, size: Int, id: Long? = null): List<Long>
fun findIdsByCurationIdOrderByIdDesc(curationId: Long, size: Int, id: Long? = null): List<Long>
fun findAllByIdLessThanOrderByIdDesc(size: Int, id: Long? = null): List<Store>
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.mjucow.eatda.persistence.store

import com.mjucow.eatda.domain.store.entity.Store
import com.mjucow.eatda.jooq.Tables.CURATION_STORE
import com.mjucow.eatda.jooq.Tables.STORE
import com.mjucow.eatda.jooq.Tables.STORE_CATEGORY
import org.jooq.DSLContext
Expand All @@ -26,6 +27,22 @@ class StoreCustomRepositoryImpl(
.toList()
}

override fun findIdsByCurationIdOrderByIdDesc(curationId: Long, size: Int, id: Long?): List<Long> {
val query = db.select(CURATION_STORE.STORE_ID)
.from(CURATION_STORE)
.where(CURATION_STORE.CURATION_ID.eq(curationId))

if (id != null) {
query.and(CURATION_STORE.STORE_ID.lessThan(id))
}

return query.orderBy(CURATION_STORE.STORE_ID.desc())
.limit(size + 1)
.fetch()
.into(Long::class.java)
.toList()
}

override fun findAllByIdLessThanOrderByIdDesc(size: Int, id: Long?): List<Store> {
val query = db.select()
.from(STORE)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.mjucow.eatda.presentation.curation

import com.mjucow.eatda.common.dto.CursorPage
import com.mjucow.eatda.domain.curation.service.command.dto.AddStoreCommand
import com.mjucow.eatda.domain.curation.service.command.dto.CreateCurationCommand
import com.mjucow.eatda.domain.curation.service.command.dto.UpdateCurationCommand
import com.mjucow.eatda.domain.curation.service.query.dto.Curations
import com.mjucow.eatda.domain.store.service.query.dto.StoreDto
import com.mjucow.eatda.presentation.common.ApiResponse
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestParam

@Tag(name = "큐레이션 API", description = "큐레이션을 관리해주는 API")
interface CurationApiPresentation {
Expand All @@ -17,6 +21,13 @@ interface CurationApiPresentation {
@Operation(summary = "큐레이션 전체 조회", description = "모든 큐레이션를 조회합니다.")
fun findAll(): ApiResponse<Curations>

@Operation(summary = "큐레이션별 가게 전체 조회", description = "특정 큐레이션에 있는 모든 가게를 조회합니다.")
fun findAllStoresByCurationId(
@PathVariable("curationId") id: Long,
@RequestParam("cursor", required = false) cursor: Long?,
@RequestParam("pageSize", required = false, defaultValue = "20") pageSize: Int,
): ApiResponse<CursorPage<StoreDto>>

@Operation(summary = "큐레이션 수정", description = "큐레이션의 내용을 수정합니다.")
@Parameter(name = "curationId", description = "수정할 큐레이션의 ID")
fun update(id: Long, command: UpdateCurationCommand)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.mjucow.eatda.presentation.curation

import com.mjucow.eatda.common.dto.CursorPage
import com.mjucow.eatda.domain.curation.service.command.CurationCommandService
import com.mjucow.eatda.domain.curation.service.command.dto.AddStoreCommand
import com.mjucow.eatda.domain.curation.service.command.dto.CreateCurationCommand
import com.mjucow.eatda.domain.curation.service.command.dto.UpdateCurationCommand
import com.mjucow.eatda.domain.curation.service.query.CurationQueryService
import com.mjucow.eatda.domain.curation.service.query.dto.Curations
import com.mjucow.eatda.domain.store.service.query.StoreQueryService
import com.mjucow.eatda.domain.store.service.query.dto.StoreDto
import com.mjucow.eatda.presentation.common.ApiResponse
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.DeleteMapping
Expand All @@ -15,12 +18,15 @@ import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
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.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import kotlin.math.min

@RequestMapping("/api/v1/curations")
@RestController
@RequestMapping("/api/v1/curations")
class CurationController(
private val storeQueryService: StoreQueryService,
private val curationQueryService: CurationQueryService,
private val curationCommandService: CurationCommandService,
) : CurationApiPresentation {
Expand All @@ -37,6 +43,22 @@ class CurationController(
return ApiResponse.success(curationQueryService.findAll())
}

@GetMapping("/{curationId}/stores")
@ResponseStatus(HttpStatus.OK)
override fun findAllStoresByCurationId(
@PathVariable("curationId", required = false) id: Long,
@RequestParam("cursor", required = false) cursor: Long?,
@RequestParam("pageSize", required = false, defaultValue = "20") pageSize: Int,
): ApiResponse<CursorPage<StoreDto>> {
val results = storeQueryService.findAllByCurationAndCursor(cursor, id, pageSize)

val contents = results.subList(0, min(pageSize, results.size))
val hasNext = results.size > pageSize
val nextCursor = if (hasNext) contents.last().id.toString() else null

return ApiResponse.success(CursorPage(contents, hasNext, nextCursor))
}

@PatchMapping("/{curationId}")
@ResponseStatus(HttpStatus.OK)
override fun update(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- liquibase formatted sql

-- changeset liquibase:9
ALTER TABLE curation
ADD COLUMN image_address TEXT NOT NULL DEFAULT 'image';

ALTER TABLE curation_store
ALTER COLUMN updated_at SET DEFAULT NOW();
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class CurationTest {
val throwable = Assertions.catchThrowable {
Curation(
title = title,
description = CurationMother.DESCRIPTION
description = CurationMother.DESCRIPTION,
imageAddress = CurationMother.IMAGE_ADDRESS
)
}

Expand All @@ -38,7 +39,8 @@ class CurationTest {
val throwable = Assertions.catchThrowable {
Curation(
title = title,
description = CurationMother.DESCRIPTION
description = CurationMother.DESCRIPTION,
imageAddress = CurationMother.IMAGE_ADDRESS
)
}

Expand All @@ -56,7 +58,8 @@ class CurationTest {
val throwable = Assertions.catchThrowable {
Curation(
title = CurationMother.TITLE,
description = description
description = description,
imageAddress = CurationMother.IMAGE_ADDRESS
)
}

Expand All @@ -74,7 +77,8 @@ class CurationTest {
val throwable = Assertions.catchThrowable {
Curation(
title = CurationMother.TITLE,
description = description
description = description,
imageAddress = CurationMother.IMAGE_ADDRESS
)
}

Expand Down Expand Up @@ -120,7 +124,8 @@ class CurationTest {
// when
val curation = Curation(
title = CurationMother.TITLE,
description = CurationMother.DESCRIPTION
description = CurationMother.DESCRIPTION,
imageAddress = CurationMother.IMAGE_ADDRESS
)

// then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import com.mjucow.eatda.domain.curation.entity.Curation
object CurationMother : EntityMother<Curation>() {
const val TITLE = "명지대 점심 특선"
const val DESCRIPTION = "점심 특선 메뉴를 판매하는 음식점들이에요."
const val IMAGE_ADDRESS = "store/232D8241-C6A9-4AD9-B0EA-56F6DD24BADF.png"

override fun createFillInstance() = Curation(TITLE, DESCRIPTION)
override fun createFillInstance() = Curation(TITLE, DESCRIPTION, IMAGE_ADDRESS)

override fun createDefaultInstance() = Curation(TITLE, DESCRIPTION)
override fun createDefaultInstance() = Curation(TITLE, DESCRIPTION, IMAGE_ADDRESS)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class CurationCommandServiceTest : AbstractDataTest() {
// given
val command = CreateCurationCommand(
title = CurationMother.TITLE,
description = CurationMother.DESCRIPTION
description = CurationMother.DESCRIPTION,
imageAddress = CurationMother.IMAGE_ADDRESS
)

// when
Expand All @@ -51,9 +52,11 @@ class CurationCommandServiceTest : AbstractDataTest() {
val curation = repository.save(CurationMother.create())
val updatedTitle = CurationMother.TITLE + "x"
val updatedDescription = CurationMother.DESCRIPTION + "x"
val updatedImageAddress = CurationMother.IMAGE_ADDRESS + "x"
val command = UpdateCurationCommand(
title = updatedTitle,
description = updatedDescription
description = updatedDescription,
imageAddress = updatedImageAddress
)

// when
Expand Down Expand Up @@ -117,7 +120,8 @@ class CurationCommandServiceTest : AbstractDataTest() {
val id = curationCommandService.create(
CreateCurationCommand(
title = CurationMother.TITLE,
description = CurationMother.DESCRIPTION
description = CurationMother.DESCRIPTION,
imageAddress = CurationMother.IMAGE_ADDRESS
)
)
val command = AddStoreCommand(storeId)
Expand All @@ -136,7 +140,8 @@ class CurationCommandServiceTest : AbstractDataTest() {
val id = curationCommandService.create(
CreateCurationCommand(
title = CurationMother.TITLE,
description = CurationMother.DESCRIPTION
description = CurationMother.DESCRIPTION,
imageAddress = CurationMother.IMAGE_ADDRESS
)
)
val storeId = storeRepository.save(StoreMother.create()).id
Expand Down
Loading

0 comments on commit eb0cc24

Please sign in to comment.