Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[refactor] 큐레이션 도메인 수정 및 API 추가 #89

Merged
merged 6 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
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
Loading