diff --git a/cakk-admin/build.gradle.kts b/cakk-admin/build.gradle.kts index 6c431990..207c9889 100644 --- a/cakk-admin/build.gradle.kts +++ b/cakk-admin/build.gradle.kts @@ -15,6 +15,8 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-validation") + implementation("org.springframework.boot:spring-boot-starter-security") + implementation("org.springframework.boot:spring-boot-starter-oauth2-client") implementation("org.springframework:spring-tx") diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/annotation/AdminUser.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/annotation/AdminUser.kt new file mode 100644 index 00000000..75ad8606 --- /dev/null +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/annotation/AdminUser.kt @@ -0,0 +1,5 @@ +package com.cakk.admin.annotation + +@Target(AnnotationTarget.VALUE_PARAMETER) +@Retention(AnnotationRetention.RUNTIME) +annotation class AdminUser diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/annotation/OperationDay.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/annotation/OperationDay.kt index 25104f26..65d9052d 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/annotation/OperationDay.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/annotation/OperationDay.kt @@ -10,7 +10,7 @@ import kotlin.reflect.KClass @Constraint(validatedBy = [OperationValidator::class]) annotation class OperationDay( - val message: String = "영업 일자 형식이 잘못됐습니다.", - val groups: Array> = [], - val payload: Array> = [] + val message: String = "영업 일자 형식이 잘못됐습니다.", + val groups: Array> = [], + val payload: Array> = [] ) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/controller/BusinessInformationController.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/controller/BusinessInformationController.kt index a58ff10a..afe3b65e 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/controller/BusinessInformationController.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/controller/BusinessInformationController.kt @@ -1,38 +1,44 @@ package com.cakk.admin.controller -import com.cakk.admin.dto.request.PromotionRequest -import com.cakk.admin.dto.response.CakeShopOwnerCandidateResponse -import com.cakk.admin.dto.response.CakeShopOwnerCandidatesResponse -import com.cakk.admin.service.BusinessInformationService -import com.cakk.common.response.ApiResponse import jakarta.validation.Valid + import org.springframework.web.bind.annotation.* +import com.cakk.admin.dto.request.PromotionRequest +import com.cakk.admin.mapper.supplyPromotionParamBy +import com.cakk.common.response.ApiResponse +import com.cakk.core.dto.response.shop.CakeShopOwnerCandidateResponse +import com.cakk.core.dto.response.shop.CakeShopOwnerCandidatesResponse +import com.cakk.core.service.shop.ShopService + @RestController @RequestMapping("/business-information") class BusinessInformationController( - private val businessInformationService: BusinessInformationService + private val shopService: ShopService ) { - @GetMapping("/candidates") - fun getBusinessOwnerCandidates(): ApiResponse { - val response = businessInformationService.getBusinessOwnerCandidates() - return ApiResponse.success(response) - } - - @GetMapping("/candidates/{userId}") - fun getCandidateSpecificationInformation( - @PathVariable userId: Long - ): ApiResponse { - val response = businessInformationService.getCandidateInformation(userId) - return ApiResponse.success(response) - } - - @PutMapping("/promote") - fun promoteUser( - @RequestBody @Valid promotionRequest: PromotionRequest - ): ApiResponse { - businessInformationService.promoteUserToBusinessOwner(promotionRequest) - return ApiResponse.success() - } + @GetMapping("/candidates") + fun getBusinessOwnerCandidates(): ApiResponse { + val response = shopService.getBusinessOwnerCandidates() + return ApiResponse.success(response) + } + + @GetMapping("/candidates/{userId}") + fun getCandidateSpecificationInformation( + @PathVariable userId: Long + ): ApiResponse { + val response = shopService.getCandidateInformation(userId) + + return ApiResponse.success(response) + } + + @PutMapping("/promote") + fun promoteUser( + @RequestBody @Valid promotionRequest: PromotionRequest + ): ApiResponse { + val param = supplyPromotionParamBy(promotionRequest) + shopService.promoteUserToBusinessOwner(param) + + return ApiResponse.success() + } } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/controller/CakeController.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/controller/CakeController.kt index b7409c11..01aeaa43 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/controller/CakeController.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/controller/CakeController.kt @@ -1,44 +1,57 @@ package com.cakk.admin.controller +import jakarta.validation.Valid + +import org.springframework.web.bind.annotation.* + +import com.cakk.admin.annotation.AdminUser import com.cakk.admin.dto.request.CakeCreateByAdminRequest import com.cakk.admin.dto.request.CakeUpdateByAdminRequest -import com.cakk.admin.service.CakeService +import com.cakk.admin.mapper.supplyCakeCreateParamBy +import com.cakk.admin.mapper.supplyCakeUpdateParamBy import com.cakk.common.response.ApiResponse -import jakarta.validation.Valid -import org.springframework.web.bind.annotation.* +import com.cakk.core.service.cake.CakeService +import com.cakk.domain.mysql.entity.user.User @RestController @RequestMapping("/shops/{cakeShopId}/cakes") class CakeController( - private val cakeService: CakeService + private val cakeService: CakeService ) { - @PostMapping - fun create( - @PathVariable cakeShopId: Long, - @RequestBody @Valid dto: CakeCreateByAdminRequest - ): ApiResponse { - cakeService.createCake(dto.toParam(cakeShopId)) - return ApiResponse.success() - } - - @PutMapping("/{cakeId}") - fun update( - @PathVariable cakeShopId: Long, - @PathVariable cakeId: Long, - @RequestBody @Valid dto: CakeUpdateByAdminRequest - ): ApiResponse { - cakeService.updateCake(dto.toParam(cakeShopId)) - return ApiResponse.success() - } - - @DeleteMapping("/{cakeId}") - fun delete( - @PathVariable cakeShopId: Long, - @PathVariable cakeId: Long - ): ApiResponse { - cakeService.deleteCake(cakeId) - - return ApiResponse.success() - } + @PostMapping + fun create( + @PathVariable cakeShopId: Long, + @RequestBody @Valid dto: CakeCreateByAdminRequest, + @AdminUser admin: User + ): ApiResponse { + val param = supplyCakeCreateParamBy(dto, admin, cakeShopId) + cakeService.createCake(param) + + return ApiResponse.success() + } + + @PutMapping("/{cakeId}") + fun update( + @PathVariable cakeShopId: Long, + @PathVariable cakeId: Long, + @RequestBody @Valid dto: CakeUpdateByAdminRequest, + @AdminUser admin: User + ): ApiResponse { + val param = supplyCakeUpdateParamBy(dto, admin, cakeShopId) + cakeService.updateCake(param) + + return ApiResponse.success() + } + + @DeleteMapping("/{cakeId}") + fun delete( + @PathVariable cakeShopId: Long, + @PathVariable cakeId: Long, + @AdminUser admin: User + ): ApiResponse { + cakeService.deleteCake(admin, cakeId) + + return ApiResponse.success() + } } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/controller/ShopController.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/controller/ShopController.kt index e2641e47..3284199a 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/controller/ShopController.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/controller/ShopController.kt @@ -1,64 +1,78 @@ package com.cakk.admin.controller -import com.cakk.admin.dto.request.* -import com.cakk.admin.dto.response.CakeShopCreateResponse -import com.cakk.admin.service.ShopService -import com.cakk.common.response.ApiResponse import jakarta.validation.Valid + import org.springframework.web.bind.annotation.* +import com.cakk.admin.annotation.AdminUser +import com.cakk.admin.dto.request.* +import com.cakk.admin.mapper.* +import com.cakk.common.response.ApiResponse +import com.cakk.core.dto.response.shop.CakeShopCreateResponse +import com.cakk.core.service.shop.ShopService +import com.cakk.domain.mysql.entity.user.User + @RestController @RequestMapping("/shops") class ShopController( - private val shopService: ShopService + private val shopService: ShopService ) { - @PostMapping - fun createByAdmin( - @RequestBody @Valid request: CakeShopCreateByAdminRequest - ): ApiResponse { - val response = shopService.createCakeShopByCertification(request.toParam()) + @PostMapping + fun createByAdmin( + @RequestBody @Valid request: CakeShopCreateByAdminRequest + ): ApiResponse { + val param = supplyCreateShopParamBy(request) + val response = shopService.createCakeShopByCertification(param) - return ApiResponse.success(response) - } + return ApiResponse.success(response) + } - @PutMapping("/{cakeShopId}") - fun updateBasicInformation( - @PathVariable cakeShopId: Long, - @RequestBody @Valid request: CakeShopUpdateByAdminRequest - ): ApiResponse { - shopService.updateBasicInformation(request.toParam(cakeShopId)) + @PutMapping("/{cakeShopId}") + fun updateBasicInformation( + @PathVariable cakeShopId: Long, + @RequestBody @Valid request: CakeShopUpdateByAdminRequest, + @AdminUser admin: User + ): ApiResponse { + val param = supplyCakeShopUpdateParamBy(request, admin, cakeShopId) + shopService.updateBasicInformation(param) - return ApiResponse.success() - } + return ApiResponse.success() + } - @PutMapping("/{cakeShopId}/links") - fun updateLinks( - @PathVariable cakeShopId: Long, - @RequestBody request: @Valid LinkUpdateByAdminRequest - ): ApiResponse { - shopService.updateShopLinks(request.toParam(cakeShopId)) + @PutMapping("/{cakeShopId}/links") + fun updateLinks( + @PathVariable cakeShopId: Long, + @RequestBody @Valid request: LinkUpdateByAdminRequest, + @AdminUser admin: User + ): ApiResponse { + val param = supplyUpdateLinkParamBy(request, admin, cakeShopId) + shopService.updateShopLinks(param) - return ApiResponse.success() - } + return ApiResponse.success() + } - @PutMapping("/{cakeShopId}/operation-days") - fun updateOperationDays( - @PathVariable cakeShopId: Long, - @RequestBody @Valid request: ShopOperationUpdateByAdminRequest - ): ApiResponse { - shopService.updateShopOperationDays(request.toParam(cakeShopId)) + @PutMapping("/{cakeShopId}/operation-days") + fun updateOperationDays( + @PathVariable cakeShopId: Long, + @RequestBody @Valid request: ShopOperationUpdateByAdminRequest, + @AdminUser admin: User + ): ApiResponse { + val param = supplyUpdateShopOperationParamBy(request, admin, cakeShopId) + shopService.updateShopOperationDays(param) - return ApiResponse.success() - } + return ApiResponse.success() + } - @PutMapping("/{cakeShopId}/address") - fun updateAddress( - @PathVariable cakeShopId: Long, - @RequestBody @Valid request: AddressUpdateByAdminRequest - ): ApiResponse { - shopService.updateShopAddress(request.toParam(cakeShopId)) + @PutMapping("/{cakeShopId}/address") + fun updateAddress( + @PathVariable cakeShopId: Long, + @RequestBody @Valid request: AddressUpdateByAdminRequest, + @AdminUser admin: User + ): ApiResponse { + val param = supplyUpdateShopAddressParamBy(request, admin, cakeShopId) + shopService.updateShopAddress(param) - return ApiResponse.success() - } + return ApiResponse.success() + } } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeCreateByAdminParam.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeCreateByAdminParam.kt deleted file mode 100644 index 9278a135..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeCreateByAdminParam.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.cakk.admin.dto.param - -import com.cakk.domain.mysql.entity.cake.Cake -import com.cakk.domain.mysql.entity.cake.CakeCategory - -data class CakeCreateByAdminParam( - val cake: Cake, - val cakeCategories: List, - val tagNames: List, - val cakeShopId: Long -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeShopCreateByAdminParam.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeShopCreateByAdminParam.kt deleted file mode 100644 index 717c1dff..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeShopCreateByAdminParam.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.cakk.admin.dto.param - -import com.cakk.domain.mysql.entity.shop.CakeShop -import com.cakk.domain.mysql.entity.shop.CakeShopLink -import com.cakk.domain.mysql.entity.shop.CakeShopOperation -import com.cakk.domain.mysql.entity.user.BusinessInformation - -data class CakeShopCreateByAdminParam( - val cakeShop: CakeShop, - val businessInformation: BusinessInformation, - val cakeShopOperations: List, - val cakeShopLinks: List -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeUpdateByAdminParam.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeUpdateByAdminParam.kt deleted file mode 100644 index 88029662..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/CakeUpdateByAdminParam.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.cakk.admin.dto.param - -import com.cakk.domain.mysql.entity.cake.CakeCategory - -data class CakeUpdateByAdminParam( - val cakeId: Long, - val cakeImageUrl: String, - val cakeCategories: List, - val tagNames: List -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/OwnerCandidateParam.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/OwnerCandidateParam.kt deleted file mode 100644 index e6aef324..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/OwnerCandidateParam.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.cakk.admin.dto.param - -import java.time.LocalDateTime - -data class OwnerCandidateParam( - val userId: Long, - val nickname: String, - val profileImageUrl: String, - val email: String, - val timestamp: LocalDateTime -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/ShopLinkParam.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/ShopLinkParam.kt deleted file mode 100644 index 03951175..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/param/ShopLinkParam.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.cakk.admin.dto.param - -import com.cakk.common.enums.LinkKind -import jakarta.validation.constraints.NotBlank -import jakarta.validation.constraints.Size - -data class ShopLinkParam( - @field:NotBlank - val linkKind: LinkKind, - @field:NotBlank @field:Size(min = 1, max = 200) - val linkPath: String -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/AddressUpdateByAdminRequest.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/AddressUpdateByAdminRequest.kt index 01fdbede..eff22e6e 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/AddressUpdateByAdminRequest.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/AddressUpdateByAdminRequest.kt @@ -1,7 +1,5 @@ package com.cakk.admin.dto.request -import com.cakk.admin.mapper.supplyPointBy -import com.cakk.domain.mysql.dto.param.shop.UpdateShopAddressParam import jakarta.validation.constraints.Max import jakarta.validation.constraints.Min import jakarta.validation.constraints.NotBlank @@ -9,19 +7,10 @@ import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size data class AddressUpdateByAdminRequest( - @field:NotBlank @field:Size(max = 50) - val shopAddress: String?, - @field:NotNull @field:Min(-90) @field:Max(90) - val latitude: Double?, - @field:NotNull @field:Min(-180) @field:Max(180) - val longitude: Double? -) { - - fun toParam(cakeShopId: Long): UpdateShopAddressParam { - return UpdateShopAddressParam.builder() - .cakeShopId(cakeShopId) - .shopAddress(shopAddress) - .location(supplyPointBy(latitude, longitude)) - .build() - } -} + @field:NotBlank @field:Size(max = 50) + val shopAddress: String?, + @field:NotNull @field:Min(-90) @field:Max(90) + val latitude: Double?, + @field:NotNull @field:Min(-180) @field:Max(180) + val longitude: Double? +) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeCreateByAdminRequest.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeCreateByAdminRequest.kt index 6c9fca87..09e2067f 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeCreateByAdminRequest.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeCreateByAdminRequest.kt @@ -1,28 +1,16 @@ package com.cakk.admin.dto.request -import com.cakk.admin.dto.param.CakeCreateByAdminParam -import com.cakk.admin.mapper.supplyCakeBy -import com.cakk.admin.mapper.supplyCakeCategoryListBy -import com.cakk.common.enums.CakeDesignCategory import jakarta.validation.constraints.NotBlank -import jakarta.validation.constraints.NotNull +import jakarta.validation.constraints.NotEmpty import jakarta.validation.constraints.Size -data class CakeCreateByAdminRequest( - @field:NotBlank @field:Size(max = 100) - val cakeImageUrl: String, - @field:NotNull - val cakeDesignCategories: List, - @field:NotNull - val tagNames: List -) { +import com.cakk.common.enums.CakeDesignCategory - fun toParam(cakeShopId: Long): CakeCreateByAdminParam { - return CakeCreateByAdminParam( - cake = supplyCakeBy(cakeImageUrl), - cakeCategories = supplyCakeCategoryListBy(cakeDesignCategories), - tagNames = tagNames, - cakeShopId = cakeShopId - ) - } -} +data class CakeCreateByAdminRequest( + @field:NotBlank @field:Size(max = 100) + val cakeImageUrl: String?, + @field:NotEmpty + val cakeDesignCategories: List?, + @field:NotEmpty + val tagNames: List? +) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeShopCreateByAdminRequest.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeShopCreateByAdminRequest.kt index 4b527014..546cae8e 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeShopCreateByAdminRequest.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeShopCreateByAdminRequest.kt @@ -1,49 +1,32 @@ package com.cakk.admin.dto.request -import com.cakk.admin.annotation.OperationDay -import com.cakk.admin.dto.param.CakeShopCreateByAdminParam -import com.cakk.admin.dto.param.ShopLinkParam -import com.cakk.admin.mapper.supplyBusinessInformationBy -import com.cakk.admin.mapper.supplyCakeShopBy -import com.cakk.admin.mapper.supplyCakeShopLinksBy -import com.cakk.admin.mapper.supplyCakeShopOperationsBy -import com.cakk.domain.mysql.dto.param.shop.ShopOperationParam -import com.cakk.domain.mysql.entity.shop.CakeShop import jakarta.validation.constraints.Max import jakarta.validation.constraints.Min import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size -data class CakeShopCreateByAdminRequest( - @field:Size(max = 20) - val businessNumber: String, - @field:OperationDay - val operationDays: List, - @field:NotBlank @field:Size(max = 30) - val shopName: String, - @field:Size(max = 40) - val shopBio: String, - @field:Size(max = 500) - val shopDescription: String, - @field:NotBlank @field:Size(max = 50) - val shopAddress: String, - @field:NotNull @field:Min(-90) @field:Max(90) - val latitude: Double, - @field:NotNull @field:Min(-180) @field:Max(180) - val longitude: Double, - @field:NotNull - val links: List -) { - - fun toParam(): CakeShopCreateByAdminParam { - val cakeShop: CakeShop = supplyCakeShopBy(this) +import com.cakk.admin.annotation.OperationDay +import com.cakk.core.dto.param.shop.ShopLinkParam +import com.cakk.core.dto.param.shop.ShopOperationParam - return CakeShopCreateByAdminParam( - cakeShop = cakeShop, - businessInformation = supplyBusinessInformationBy(businessNumber, cakeShop), - cakeShopOperations = supplyCakeShopOperationsBy(cakeShop, operationDays), - cakeShopLinks = supplyCakeShopLinksBy(cakeShop, links) - ) - } -} +data class CakeShopCreateByAdminRequest( + @field:Size(max = 20) + val businessNumber: String?, + @field:OperationDay + val operationDays: List?, + @field:NotBlank @field:Size(max = 30) + val shopName: String?, + @field:Size(max = 40) + val shopBio: String?, + @field:Size(max = 500) + val shopDescription: String?, + @field:NotBlank @field:Size(max = 50) + val shopAddress: String?, + @field:NotNull @field:Min(-90) @field:Max(90) + val latitude: Double?, + @field:NotNull @field:Min(-180) @field:Max(180) + val longitude: Double?, + @field:NotNull + val links: List? +) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeShopUpdateByAdminRequest.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeShopUpdateByAdminRequest.kt index 0c4ca55b..4290b6eb 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeShopUpdateByAdminRequest.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeShopUpdateByAdminRequest.kt @@ -1,27 +1,15 @@ package com.cakk.admin.dto.request -import com.cakk.domain.mysql.dto.param.shop.CakeShopUpdateParam import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.Size data class CakeShopUpdateByAdminRequest( - @field:Size(max = 200) - val thumbnailUrl: String?, - @field:NotBlank @Size(max = 30) - val shopName: String, - @field:Size(max = 40) - val shopBio: String?, - @field:Size(max = 500) - val shopDescription: String? -) { - - fun toParam(cakeShopId: Long): CakeShopUpdateParam { - return CakeShopUpdateParam.builder() - .cakeShopId(cakeShopId) - .thumbnailUrl(thumbnailUrl) - .shopName(shopName) - .shopBio(shopBio) - .shopDescription(shopDescription) - .build() - } -} + @field:Size(max = 200) + val thumbnailUrl: String?, + @field:NotBlank @Size(max = 30) + val shopName: String?, + @field:Size(max = 40) + val shopBio: String?, + @field:Size(max = 500) + val shopDescription: String? +) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeUpdateByAdminRequest.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeUpdateByAdminRequest.kt index cf96f9f4..84bf3dae 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeUpdateByAdminRequest.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/CakeUpdateByAdminRequest.kt @@ -1,27 +1,15 @@ package com.cakk.admin.dto.request -import com.cakk.admin.dto.param.CakeUpdateByAdminParam -import com.cakk.admin.mapper.supplyCakeCategoryListBy import com.cakk.common.enums.CakeDesignCategory import jakarta.validation.constraints.NotBlank import jakarta.validation.constraints.NotNull import jakarta.validation.constraints.Size data class CakeUpdateByAdminRequest( - @field:NotBlank @field:Size(max = 200) - val cakeImageUrl: String, - @field:NotNull - val cakeDesignCategories: List, - @field:NotNull - val tagNames: List -) { - - fun toParam(cakeId: Long): CakeUpdateByAdminParam { - return CakeUpdateByAdminParam( - cakeId = cakeId, - cakeImageUrl = cakeImageUrl, - cakeCategories = supplyCakeCategoryListBy(cakeDesignCategories), - tagNames = tagNames - ) - } -} + @field:NotBlank @field:Size(max = 200) + val cakeImageUrl: String?, + @field:NotNull + val cakeDesignCategories: List?, + @field:NotNull + val tagNames: List? +) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/LinkUpdateByAdminRequest.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/LinkUpdateByAdminRequest.kt index 39ed9c08..ff265c5f 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/LinkUpdateByAdminRequest.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/LinkUpdateByAdminRequest.kt @@ -1,31 +1,12 @@ package com.cakk.admin.dto.request -import com.cakk.admin.mapper.supplyCakeShopLinkByInstagram -import com.cakk.admin.mapper.supplyCakeShopLinkByKakao -import com.cakk.admin.mapper.supplyCakeShopLinkByWeb -import com.cakk.domain.mysql.dto.param.link.UpdateLinkParam -import com.cakk.domain.mysql.entity.shop.CakeShopLink import jakarta.validation.constraints.Size data class LinkUpdateByAdminRequest( - @field:Size(min = 1, max = 200) - val instagram: String?, - @field:Size(min = 1, max = 200) - val kakao: String?, - @field:Size(min = 1, max = 200) - val web: String? -) { - - fun toParam(cakeShopId: Long): UpdateLinkParam { - val cakeShopLinks = mutableListOf() - - instagram?.let { cakeShopLinks.add(supplyCakeShopLinkByInstagram(it)) } - kakao?.let { cakeShopLinks.add(supplyCakeShopLinkByKakao(it)) } - web?.let { cakeShopLinks.add(supplyCakeShopLinkByWeb(it)) } - - return UpdateLinkParam.builder() - .cakeShopId(cakeShopId) - .cakeShopLinks(cakeShopLinks) - .build() - } -} + @field:Size(min = 1, max = 200) + val instagram: String?, + @field:Size(min = 1, max = 200) + val kakao: String?, + @field:Size(min = 1, max = 200) + val web: String? +) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/PromotionRequest.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/PromotionRequest.kt index 990d4aa8..fcfe2801 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/PromotionRequest.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/PromotionRequest.kt @@ -3,8 +3,8 @@ package com.cakk.admin.dto.request import jakarta.validation.constraints.NotNull data class PromotionRequest( - @field:NotNull - val userId: Long, - @field:NotNull - val cakeShopId: Long + @field:NotNull + val userId: Long?, + @field:NotNull + val cakeShopId: Long? ) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/ShopOperationUpdateByAdminRequest.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/ShopOperationUpdateByAdminRequest.kt index d36c25bd..ed55d3ce 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/ShopOperationUpdateByAdminRequest.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/request/ShopOperationUpdateByAdminRequest.kt @@ -1,19 +1,10 @@ package com.cakk.admin.dto.request -import com.cakk.admin.mapper.supplyCakeShopOperationListBy -import com.cakk.domain.mysql.dto.param.operation.UpdateShopOperationParam -import com.cakk.domain.mysql.dto.param.shop.ShopOperationParam import jakarta.validation.constraints.NotNull -data class ShopOperationUpdateByAdminRequest( - @field:NotNull - val operationDays: List -) { +import com.cakk.domain.mysql.dto.param.shop.ShopOperationParam - fun toParam(cakeShopId: Long): UpdateShopOperationParam { - return UpdateShopOperationParam.builder() - .cakeShopId(cakeShopId) - .cakeShopOperations(supplyCakeShopOperationListBy(operationDays)) - .build() - } -} +data class ShopOperationUpdateByAdminRequest( + @field:NotNull + val operationDays: List? +) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopCreateResponse.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopCreateResponse.kt deleted file mode 100644 index 180cfcf4..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopCreateResponse.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.cakk.admin.dto.response - -data class CakeShopCreateResponse( - val cakeShopId: Long -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopOwnerCandidateResponse.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopOwnerCandidateResponse.kt deleted file mode 100644 index 94991674..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopOwnerCandidateResponse.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.cakk.admin.dto.response - -data class CakeShopOwnerCandidateResponse( - val userId: Long, - val cakeShopId: Long, - val email: String, - val businessRegistrationImageUrl: String, - val idCardImageUrl: String, - val emergencyContact: String -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopOwnerCandidatesResponse.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopOwnerCandidatesResponse.kt deleted file mode 100644 index ba9d0cd0..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/CakeShopOwnerCandidatesResponse.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.cakk.admin.dto.response - -import com.cakk.admin.dto.param.OwnerCandidateParam - -data class CakeShopOwnerCandidatesResponse( - val candidates: List -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/TagResponse.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/TagResponse.kt deleted file mode 100644 index 959f6328..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/dto/response/TagResponse.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.cakk.admin.dto.response - -data class TagResponse( - val tagId: Long, - val tagName: String -) diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/BusinessInformationMapper.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/BusinessInformationMapper.kt deleted file mode 100644 index 0549ea5e..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/BusinessInformationMapper.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.cakk.admin.mapper - -import com.cakk.admin.dto.param.OwnerCandidateParam -import com.cakk.admin.dto.response.CakeShopOwnerCandidateResponse -import com.cakk.admin.dto.response.CakeShopOwnerCandidatesResponse -import com.cakk.domain.mysql.entity.shop.CakeShop -import com.cakk.domain.mysql.entity.user.BusinessInformation - -fun supplyCakeShopOwnerCandidatesResponseBy(businessInformationList: List): CakeShopOwnerCandidatesResponse { - val candidates: List = businessInformationList - .map { businessInformation: BusinessInformation -> - OwnerCandidateParam( - userId = businessInformation.user.id, - nickname = businessInformation.user.nickname, - profileImageUrl = businessInformation.user.profileImageUrl, - email = businessInformation.user.email, - timestamp = businessInformation.updatedAt - ) - }.toList() - - return CakeShopOwnerCandidatesResponse(candidates) -} - -fun supplyCakeShopOwnerCandidateResponseBy(businessInformation: BusinessInformation): CakeShopOwnerCandidateResponse { - return CakeShopOwnerCandidateResponse( - userId = businessInformation.user.id, - cakeShopId = businessInformation.cakeShop.id, - email = businessInformation.user.email, - businessRegistrationImageUrl = businessInformation.businessRegistrationImageUrl, - idCardImageUrl = businessInformation.idCardImageUrl, - emergencyContact = businessInformation.emergencyContact - ) -} - -fun supplyBusinessInformationBy(businessNumber: String, cakeShop: CakeShop): BusinessInformation { - return BusinessInformation.builder() - .businessNumber(businessNumber) - .cakeShop(cakeShop) - .build() -} diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/CakeDesignCategoryMapper.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/CakeDesignCategoryMapper.kt index 330e9f2b..cbda8889 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/CakeDesignCategoryMapper.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/CakeDesignCategoryMapper.kt @@ -4,11 +4,11 @@ import com.cakk.common.enums.CakeDesignCategory import com.cakk.domain.mysql.entity.cake.CakeCategory fun supplyCakeCategoryListBy(cakeDesignCategories: List): List { - return cakeDesignCategories.map { supplyCakeCategoryBy(it) }.toList() + return cakeDesignCategories.map { supplyCakeCategoryBy(it) }.toList() } -fun supplyCakeCategoryBy(cakeDesignCategory: CakeDesignCategory?): CakeCategory { - return CakeCategory.builder() - .cakeDesignCategory(cakeDesignCategory) - .build() +private fun supplyCakeCategoryBy(cakeDesignCategory: CakeDesignCategory?): CakeCategory { + return CakeCategory.builder() + .cakeDesignCategory(cakeDesignCategory) + .build() } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/CakeMapper.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/CakeMapper.kt index d571979e..4f735a5a 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/CakeMapper.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/CakeMapper.kt @@ -1,9 +1,28 @@ package com.cakk.admin.mapper -import com.cakk.domain.mysql.entity.cake.Cake +import com.cakk.admin.dto.request.CakeCreateByAdminRequest +import com.cakk.admin.dto.request.CakeUpdateByAdminRequest +import com.cakk.core.dto.param.cake.CakeCreateParam +import com.cakk.core.dto.param.cake.CakeUpdateParam +import com.cakk.core.mapper.supplyCakeBy +import com.cakk.domain.mysql.entity.user.User -fun supplyCakeBy(cakeImageUrl: String): Cake { - return Cake.builder() - .cakeImageUrl(cakeImageUrl) - .build() +fun supplyCakeCreateParamBy(dto: CakeCreateByAdminRequest, user: User, cakeShopId: Long): CakeCreateParam { + return CakeCreateParam( + cake = supplyCakeBy(dto.cakeImageUrl!!), + cakeCategories = supplyCakeCategoryListBy(dto.cakeDesignCategories!!), + tagNames = dto.tagNames!!, + owner = user, + cakeShopId = cakeShopId + ) +} + +fun supplyCakeUpdateParamBy(dto: CakeUpdateByAdminRequest, owner: User, cakeId: Long): CakeUpdateParam { + return CakeUpdateParam( + owner = owner, + cakeId = cakeId, + cakeImageUrl = dto.cakeImageUrl!!, + cakeCategories = supplyCakeCategoryListBy(dto.cakeDesignCategories!!), + tagNames = dto.tagNames!! + ) } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/LinkMapper.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/LinkMapper.kt index 33238fba..42026cc8 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/LinkMapper.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/LinkMapper.kt @@ -1,40 +1,29 @@ package com.cakk.admin.mapper -import com.cakk.admin.dto.param.ShopLinkParam import com.cakk.common.enums.LinkKind import com.cakk.domain.mysql.entity.shop.CakeShop import com.cakk.domain.mysql.entity.shop.CakeShopLink -fun supplyCakeShopLinksBy(cakeShop: CakeShop, links: List): List { - return links.map { - when (it.linkKind) { - LinkKind.WEB -> supplyCakeShopLinkByWeb(it.linkPath, cakeShop) - LinkKind.INSTAGRAM -> supplyCakeShopLinkByInstagram(it.linkPath, cakeShop) - LinkKind.KAKAOTALK -> supplyCakeShopLinkByKakao(it.linkPath, cakeShop) - } - }.toList() -} - fun supplyCakeShopLinkByWeb(web: String, cakeShop: CakeShop? = null): CakeShopLink { - return CakeShopLink.builder() - .linkKind(LinkKind.WEB) - .linkPath(web) - .cakeShop(cakeShop) - .build() + return CakeShopLink.builder() + .linkKind(LinkKind.WEB) + .linkPath(web) + .cakeShop(cakeShop) + .build() } fun supplyCakeShopLinkByInstagram(instagram: String, cakeShop: CakeShop? = null): CakeShopLink { - return CakeShopLink.builder() - .linkKind(LinkKind.INSTAGRAM) - .linkPath(instagram) - .cakeShop(cakeShop) - .build() + return CakeShopLink.builder() + .linkKind(LinkKind.INSTAGRAM) + .linkPath(instagram) + .cakeShop(cakeShop) + .build() } fun supplyCakeShopLinkByKakao(kakao: String, cakeShop: CakeShop? = null): CakeShopLink { - return CakeShopLink.builder() - .linkKind(LinkKind.KAKAOTALK) - .linkPath(kakao) - .cakeShop(cakeShop) - .build() + return CakeShopLink.builder() + .linkKind(LinkKind.KAKAOTALK) + .linkPath(kakao) + .cakeShop(cakeShop) + .build() } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/PointMapper.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/PointMapper.kt deleted file mode 100644 index 55043853..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/PointMapper.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.cakk.admin.mapper - -import com.cakk.common.enums.ReturnCode -import com.cakk.common.exception.CakkException -import org.locationtech.jts.geom.Coordinate -import org.locationtech.jts.geom.GeometryFactory -import org.locationtech.jts.geom.Point -import org.locationtech.jts.geom.PrecisionModel -import java.util.* - -private const val SPATIAL_REFERENCE_IDENTIFIER_NUMBER = 4326 - -private val geometryFactory = GeometryFactory(PrecisionModel(), SPATIAL_REFERENCE_IDENTIFIER_NUMBER) - -fun supplyPointBy(latitude: Double?, longitude: Double?): Point { - if (Objects.isNull(latitude) || Objects.isNull(longitude)) { - throw CakkException(ReturnCode.WRONG_PARAMETER) - } - return geometryFactory.createPoint(Coordinate(longitude!!, latitude!!)) -} diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/ShopMapper.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/ShopMapper.kt index 54f6112c..78706a0b 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/ShopMapper.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/ShopMapper.kt @@ -1,41 +1,87 @@ package com.cakk.admin.mapper -import com.cakk.admin.dto.request.CakeShopCreateByAdminRequest -import com.cakk.admin.dto.response.CakeShopCreateResponse -import com.cakk.domain.mysql.dto.param.shop.ShopOperationParam -import com.cakk.domain.mysql.entity.shop.CakeShop -import com.cakk.domain.mysql.entity.shop.CakeShopOperation - -fun supplyCakeShopBy(dto: CakeShopCreateByAdminRequest): CakeShop { - return CakeShop.builder() - .shopName(dto.shopName) - .shopBio(dto.shopBio) - .shopDescription(dto.shopDescription) - .shopAddress(dto.shopAddress) - .location(supplyPointBy(dto.latitude, dto.longitude)) - .build() +import java.util.ArrayList + +import com.cakk.admin.dto.request.* +import com.cakk.core.dto.param.shop.CreateShopParam +import com.cakk.core.dto.param.shop.PromotionParam +import com.cakk.core.mapper.supplyPointBy +import com.cakk.domain.mysql.dto.param.link.UpdateLinkParam +import com.cakk.domain.mysql.dto.param.operation.UpdateShopOperationParam +import com.cakk.domain.mysql.dto.param.shop.CakeShopUpdateParam +import com.cakk.domain.mysql.dto.param.shop.UpdateShopAddressParam +import com.cakk.domain.mysql.entity.shop.CakeShopLink +import com.cakk.domain.mysql.entity.user.User + +fun supplyCreateShopParamBy(request: CakeShopCreateByAdminRequest): CreateShopParam { + return CreateShopParam( + businessNumber = request.businessNumber, + operationDays = request.operationDays!!, + shopName = request.shopName!!, + shopBio = request.shopBio, + shopDescription = request.shopDescription, + shopAddress = request.shopAddress!!, + latitude = request.latitude!!, + longitude = request.longitude!!, + links = request.links!! + ) +} + +fun supplyCakeShopUpdateParamBy( + request: CakeShopUpdateByAdminRequest, + user: User, + cakeShopId: Long +): CakeShopUpdateParam { + return CakeShopUpdateParam.builder() + .user(user) + .cakeShopId(cakeShopId) + .thumbnailUrl(request.thumbnailUrl) + .shopName(request.shopName) + .shopBio(request.shopBio) + .shopDescription(request.shopDescription) + .build() +} + +fun supplyUpdateLinkParamBy( + dto: LinkUpdateByAdminRequest, + user: User, + cakeShopId: Long +): UpdateLinkParam { + val cakeShopLinks: MutableList = ArrayList() + + dto.instagram?.let { cakeShopLinks.add(supplyCakeShopLinkByInstagram(dto.instagram)) } + dto.kakao?.let { cakeShopLinks.add(supplyCakeShopLinkByKakao(dto.kakao)) } + dto.web?.let { cakeShopLinks.add(supplyCakeShopLinkByWeb(dto.web)) } + + return UpdateLinkParam(user, cakeShopId, cakeShopLinks) +} + +fun supplyUpdateShopOperationParamBy( + request: ShopOperationUpdateByAdminRequest, + user: User, + cakeShopId: Long +): UpdateShopOperationParam { + val cakeShopOperations = supplyCakeShopOperationListBy(request.operationDays!!) + + return UpdateShopOperationParam(cakeShopOperations, user, cakeShopId) } -fun supplyCakeShopOperationsBy( - cakeShop: CakeShop?, - operationDays: List -): List { - val cakeShopOperations = mutableListOf() - - operationDays.forEach { - cakeShopOperations.add( - CakeShopOperation.builder() - .operationDay(it.operationDay) - .operationStartTime(it.operationStartTime) - .operationEndTime(it.operationEndTime) - .cakeShop(cakeShop) - .build() - ) - } - - return cakeShopOperations +fun supplyUpdateShopAddressParamBy( + dto: AddressUpdateByAdminRequest, + user: User, + cakeShopId: Long +): UpdateShopAddressParam { + return UpdateShopAddressParam( + dto.shopAddress!!, + supplyPointBy(dto.latitude!!, dto.longitude!!), + user, + cakeShopId + ) } -fun supplyCakeShopCreateResponseBy(cakeShop: CakeShop): CakeShopCreateResponse { - return CakeShopCreateResponse(cakeShop.id) +fun supplyPromotionParamBy(request: PromotionRequest): PromotionParam { + return PromotionParam( + userId = request.userId!!, + cakeShopId = request.cakeShopId!! + ) } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/ShopOperationMapper.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/ShopOperationMapper.kt index 089ef73f..0b88c1bb 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/ShopOperationMapper.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/mapper/ShopOperationMapper.kt @@ -4,15 +4,15 @@ import com.cakk.domain.mysql.dto.param.shop.ShopOperationParam import com.cakk.domain.mysql.entity.shop.CakeShopOperation fun supplyCakeShopOperationListBy(operationDays: List): List { - return operationDays - .map { supplyCakeShopOperationBy(it) } - .toList() + return operationDays + .map { supplyCakeShopOperationBy(it) } + .toList() } fun supplyCakeShopOperationBy(param: ShopOperationParam): CakeShopOperation { - return CakeShopOperation.builder() - .operationDay(param.operationDay) - .operationStartTime(param.operationStartTime) - .operationEndTime(param.operationEndTime) - .build() + return CakeShopOperation.builder() + .operationDay(param.operationDay) + .operationStartTime(param.operationStartTime) + .operationEndTime(param.operationEndTime) + .build() } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/resolver/AdminUserResolver.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/resolver/AdminUserResolver.kt new file mode 100644 index 00000000..24cfe7a0 --- /dev/null +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/resolver/AdminUserResolver.kt @@ -0,0 +1,31 @@ +package com.cakk.admin.resolver + +import org.springframework.core.MethodParameter +import org.springframework.security.core.context.SecurityContextHolder +import org.springframework.web.bind.support.WebDataBinderFactory +import org.springframework.web.context.request.NativeWebRequest +import org.springframework.web.method.support.HandlerMethodArgumentResolver +import org.springframework.web.method.support.ModelAndViewContainer + +import com.cakk.admin.annotation.AdminUser +import com.cakk.admin.vo.OAuthUserDetails + +import com.cakk.domain.mysql.entity.user.User + +class AdminUserResolver : HandlerMethodArgumentResolver { + override fun supportsParameter(parameter: MethodParameter): Boolean { + return parameter.hasParameterAnnotation(AdminUser::class.java) + && User::class.java.isAssignableFrom(parameter.parameterType) + } + + override fun resolveArgument( + parameter: MethodParameter, + mavContainer: ModelAndViewContainer?, + webRequest: NativeWebRequest, + binderFactory: WebDataBinderFactory? + ): Any { + val userDetails = SecurityContextHolder.getContext().authentication.principal as OAuthUserDetails + + return userDetails.getUser() + } +} diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/service/BusinessInformationService.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/service/BusinessInformationService.kt deleted file mode 100644 index 4a5aa868..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/service/BusinessInformationService.kt +++ /dev/null @@ -1,49 +0,0 @@ -package com.cakk.admin.service - -import com.cakk.admin.dto.request.PromotionRequest -import com.cakk.admin.dto.response.CakeShopOwnerCandidateResponse -import com.cakk.admin.dto.response.CakeShopOwnerCandidatesResponse -import com.cakk.admin.mapper.* -import com.cakk.core.facade.cake.BusinessInformationReadFacade -import com.cakk.core.facade.cake.CakeShopReadFacade -import com.cakk.core.facade.user.UserReadFacade -import com.cakk.domain.mysql.bo.user.VerificationPolicy -import com.cakk.domain.mysql.entity.user.BusinessInformation -import com.cakk.domain.mysql.entity.user.User -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional - -@Service -class BusinessInformationService( - private val businessInformationReadFacade: BusinessInformationReadFacade, - private val userReadFacade: UserReadFacade, - private val cakeShopReadFacade: CakeShopReadFacade, - private val verificationPolicy: VerificationPolicy, -) { - - @Transactional - fun promoteUserToBusinessOwner(dto: PromotionRequest) { - val user: User = userReadFacade.findByUserId(dto.userId) - val businessInformation: BusinessInformation = cakeShopReadFacade.findBusinessInformationWithShop(dto.cakeShopId) - - businessInformation.updateBusinessOwner(verificationPolicy, user) - } - - @Transactional(readOnly = true) - fun getBusinessOwnerCandidates(): CakeShopOwnerCandidatesResponse { - var businessInformationList = businessInformationReadFacade.findAllCakeShopBusinessOwnerCandidates() - - businessInformationList = businessInformationList - .filter { it.isBusinessOwnerCandidate(verificationPolicy) } - .toList() - - return supplyCakeShopOwnerCandidatesResponseBy(businessInformationList) - } - - @Transactional(readOnly = true) - fun getCandidateInformation(userId: Long): CakeShopOwnerCandidateResponse { - val businessInformation = businessInformationReadFacade.findByUserId(userId) - - return supplyCakeShopOwnerCandidateResponseBy(businessInformation) - } -} diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/service/CakeService.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/service/CakeService.kt deleted file mode 100644 index e45fe772..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/service/CakeService.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.cakk.admin.service - -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional - -import com.cakk.admin.dto.param.CakeCreateByAdminParam -import com.cakk.admin.dto.param.CakeUpdateByAdminParam -import com.cakk.core.facade.cake.CakeManageFacade -import com.cakk.core.facade.cake.CakeReadFacade -import com.cakk.core.facade.cake.CakeShopReadFacade -import com.cakk.core.facade.tag.TagManageFacade - -@Service -class CakeService( - private val cakeReadFacade: CakeReadFacade, - private val cakeShopReadFacade: CakeShopReadFacade, - private val tagManageFacade: TagManageFacade, - private val cakeManageFacade: CakeManageFacade -) { - - @Transactional - fun createCake(dto: CakeCreateByAdminParam) { - val cakeShop = cakeShopReadFacade.findById(dto.cakeShopId) - val cake = dto.cake - val tags = dto.tagNames.map { tagManageFacade.create(it) }.toMutableList() - - cakeManageFacade.create(cakeShop, cake, tags, dto.cakeCategories) - } - - @Transactional - fun updateCake(dto: CakeUpdateByAdminParam) { - val cake = cakeReadFacade.findById(dto.cakeId) - val tags = dto.tagNames.map { tagManageFacade.create(it) }.toMutableList() - - cakeManageFacade.update(cake, dto.cakeImageUrl, tags, dto.cakeCategories) - } - - @Transactional - fun deleteCake(cakeId: Long) { - val cake = cakeReadFacade.findById(cakeId) - - cakeManageFacade.delete(cake) - } -} diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/service/ShopService.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/service/ShopService.kt deleted file mode 100644 index 9e12ec4d..00000000 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/service/ShopService.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.cakk.admin.service - -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional - -import com.cakk.admin.dto.param.CakeShopCreateByAdminParam -import com.cakk.admin.dto.response.CakeShopCreateResponse -import com.cakk.admin.mapper.supplyCakeShopCreateResponseBy -import com.cakk.core.facade.cake.CakeShopReadFacade -import com.cakk.core.facade.shop.CakeShopManageFacade -import com.cakk.domain.mysql.dto.param.link.UpdateLinkParam -import com.cakk.domain.mysql.dto.param.operation.UpdateShopOperationParam -import com.cakk.domain.mysql.dto.param.shop.CakeShopUpdateParam -import com.cakk.domain.mysql.dto.param.shop.UpdateShopAddressParam -import com.cakk.domain.mysql.entity.shop.CakeShop - -@Service -class ShopService( - private val cakeShopReadFacade: CakeShopReadFacade, - private val cakeShopManageFacade: CakeShopManageFacade -) { - - @Transactional - fun createCakeShopByCertification(dto: CakeShopCreateByAdminParam): CakeShopCreateResponse { - val result: CakeShop = cakeShopManageFacade.create( - dto.cakeShop, - dto.cakeShopOperations, - dto.businessInformation, - dto.cakeShopLinks - ) - - return supplyCakeShopCreateResponseBy(result) - } - - @Transactional - fun updateBasicInformation(dto: CakeShopUpdateParam) { - val cakeShop = cakeShopReadFacade.findById(dto.cakeShopId) - cakeShop.updateBasicInformation(dto) - } - - @Transactional - fun updateShopLinks(param: UpdateLinkParam) { - val cakeShop = cakeShopReadFacade.findById(param.cakeShopId) - cakeShop.updateShopLinks(param.cakeShopLinks) - } - - @Transactional - fun updateShopOperationDays(param: UpdateShopOperationParam) { - val cakeShop = cakeShopReadFacade.findById(param.cakeShopId) - cakeShop.updateShopOperationDays(param.cakeShopOperations) - } - - @Transactional - fun updateShopAddress(param: UpdateShopAddressParam) { - val cakeShop = cakeShopReadFacade.findById(param.cakeShopId) - cakeShop.updateShopAddress(param) - } -} diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/validator/OperationValidator.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/validator/OperationValidator.kt index 18f43e44..a7f5c452 100644 --- a/cakk-admin/src/main/kotlin/com/cakk/admin/validator/OperationValidator.kt +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/validator/OperationValidator.kt @@ -1,25 +1,27 @@ package com.cakk.admin.validator +import java.util.* + +import jakarta.validation.ConstraintValidator +import jakarta.validation.ConstraintValidatorContext + import com.cakk.admin.annotation.OperationDay import com.cakk.common.enums.Days import com.cakk.domain.mysql.dto.param.shop.ShopOperationParam -import jakarta.validation.ConstraintValidator -import jakarta.validation.ConstraintValidatorContext -import java.util.* class OperationValidator : ConstraintValidator> { - override fun isValid(operationParams: List?, context: ConstraintValidatorContext): Boolean { - operationParams ?: return false + override fun isValid(operationParams: List?, context: ConstraintValidatorContext): Boolean { + operationParams ?: return false - val days: MutableMap = EnumMap(Days::class.java) - for (operationParam in operationParams) { - if (days.containsKey(operationParam.operationDay)) { - return false - } else { - days[operationParam.operationDay] = true - } - } - return true - } + val days: MutableMap = EnumMap(Days::class.java) + for (operationParam in operationParams) { + if (days.containsKey(operationParam.operationDay)) { + return false + } else { + days[operationParam.operationDay] = true + } + } + return true + } } diff --git a/cakk-admin/src/main/kotlin/com/cakk/admin/vo/OAuthUserDetails.kt b/cakk-admin/src/main/kotlin/com/cakk/admin/vo/OAuthUserDetails.kt new file mode 100644 index 00000000..dacb5dc3 --- /dev/null +++ b/cakk-admin/src/main/kotlin/com/cakk/admin/vo/OAuthUserDetails.kt @@ -0,0 +1,69 @@ +package com.cakk.admin.vo + +import org.springframework.security.core.GrantedAuthority +import org.springframework.security.core.authority.SimpleGrantedAuthority +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.security.oauth2.core.oidc.OidcIdToken +import org.springframework.security.oauth2.core.oidc.OidcUserInfo +import org.springframework.security.oauth2.core.oidc.user.OidcUser +import org.springframework.security.oauth2.core.user.OAuth2User + +import com.cakk.domain.mysql.entity.user.User + +class OAuthUserDetails( + private val user: User, + private val attribute: Map +) : UserDetails, OidcUser, OAuth2User { + + constructor(user: User) : this(user, mapOf("id" to user.id)) + + fun getUser(): User = user + + override fun getName(): String { + return user.id.toString() + } + + override fun getAttributes(): Map { + return attribute + } + + override fun getAuthorities(): Collection { + return listOf(SimpleGrantedAuthority(user.role.securityRole)) + } + + override fun getPassword(): String { + return "password" + } + + override fun getUsername(): String { + return user.id.toString() + } + + override fun isAccountNonExpired(): Boolean { + return true + } + + override fun isAccountNonLocked(): Boolean { + return true + } + + override fun isCredentialsNonExpired(): Boolean { + return true + } + + override fun isEnabled(): Boolean { + return true + } + + override fun getClaims(): Map? { + return null + } + + override fun getUserInfo(): OidcUserInfo? { + return null + } + + override fun getIdToken(): OidcIdToken? { + return null + } +} diff --git a/cakk-api/src/main/kotlin/com/cakk/api/controller/cake/CakeController.kt b/cakk-api/src/main/kotlin/com/cakk/api/controller/cake/CakeController.kt index 8eac589c..5027b748 100644 --- a/cakk-api/src/main/kotlin/com/cakk/api/controller/cake/CakeController.kt +++ b/cakk-api/src/main/kotlin/com/cakk/api/controller/cake/CakeController.kt @@ -8,11 +8,11 @@ import com.cakk.api.annotation.SignInUser import com.cakk.api.dto.request.cake.* import com.cakk.api.mapper.* import com.cakk.core.dto.response.cake.CakeDetailResponse -import com.cakk.core.dto.response.cake.CakeImageListResponse import com.cakk.core.dto.response.like.HeartResponse import com.cakk.core.service.cake.CakeService import com.cakk.core.service.like.HeartService import com.cakk.common.response.ApiResponse +import com.cakk.core.dto.response.cake.CakeImageWithShopInfoListResponse import com.cakk.domain.mysql.entity.user.User @RestController @@ -25,7 +25,7 @@ class CakeController( @GetMapping("/search/categories") fun listByCategory( @ModelAttribute @Valid request: CakeSearchByCategoryRequest - ): ApiResponse { + ): ApiResponse { val param = supplyCakeSearchByCategoryParamBy(request) val response = cakeService.findCakeImagesByCursorAndCategory(param) @@ -35,7 +35,7 @@ class CakeController( @GetMapping("/search/shops") fun listByShop( @ModelAttribute @Valid request: CakeSearchByShopRequest - ): ApiResponse { + ): ApiResponse { val param = supplyCakeSearchByShopParamBy(request) val response = cakeService.findCakeImagesByCursorAndCakeShopId(param) @@ -45,7 +45,7 @@ class CakeController( @GetMapping("/search/cakes") fun listByKeywordAndLocation( @ModelAttribute @Valid request: CakeSearchByLocationRequest - ): ApiResponse { + ): ApiResponse { val param = supplyCakeSearchParamBy(request) val response = cakeService.findCakeImagesByCursorAndSearch(param) @@ -55,7 +55,7 @@ class CakeController( @GetMapping("/search/views") fun listByViews( @ModelAttribute @Valid request: CakeSearchByViewsRequest - ): ApiResponse { + ): ApiResponse { val param = supplyCakeSearchByViewsParamBy(request) val response = cakeService.searchCakeImagesByCursorAndViews(param) diff --git a/cakk-api/src/main/kotlin/com/cakk/api/dto/request/shop/SearchShopByLocationRequest.kt b/cakk-api/src/main/kotlin/com/cakk/api/dto/request/shop/SearchShopByLocationRequest.kt index 848031eb..51c2d365 100644 --- a/cakk-api/src/main/kotlin/com/cakk/api/dto/request/shop/SearchShopByLocationRequest.kt +++ b/cakk-api/src/main/kotlin/com/cakk/api/dto/request/shop/SearchShopByLocationRequest.kt @@ -10,7 +10,7 @@ data class SearchShopByLocationRequest( val latitude: Double?, @field:NotNull @field:Min(-180) @field:Max(180) val longitude: Double?, - @field:Min(0) @field:Max(1000) + @field:Min(0) @field:Max(3000) val distance: Double? = 1000.0 ) diff --git a/cakk-api/src/main/kotlin/com/cakk/api/mapper/ShopMapper.kt b/cakk-api/src/main/kotlin/com/cakk/api/mapper/ShopMapper.kt index 74d8423d..947d1d29 100644 --- a/cakk-api/src/main/kotlin/com/cakk/api/mapper/ShopMapper.kt +++ b/cakk-api/src/main/kotlin/com/cakk/api/mapper/ShopMapper.kt @@ -11,9 +11,7 @@ import com.cakk.api.dto.request.shop.UpdateShopRequest import com.cakk.api.dto.request.user.CertificationRequest import com.cakk.core.dto.param.shop.CreateShopParam import com.cakk.core.dto.param.shop.PromotionParam -import com.cakk.core.mapper.supplyCakeShopLinkByInstagram -import com.cakk.core.mapper.supplyCakeShopOperationListBy -import com.cakk.core.mapper.supplyPointBy +import com.cakk.core.mapper.* import com.cakk.domain.mysql.dto.param.link.UpdateLinkParam import com.cakk.domain.mysql.dto.param.operation.UpdateShopOperationParam import com.cakk.domain.mysql.dto.param.shop.CakeShopUpdateParam @@ -77,8 +75,8 @@ fun supplyUpdateLinkParamBy( val cakeShopLinks: MutableList = ArrayList() dto.instagram?.let { cakeShopLinks.add(supplyCakeShopLinkByInstagram(dto.instagram)) } - dto.kakao?.let { cakeShopLinks.add(supplyCakeShopLinkByInstagram(dto.kakao)) } - dto.web?.let { cakeShopLinks.add(supplyCakeShopLinkByInstagram(dto.web)) } + dto.kakao?.let { cakeShopLinks.add(supplyCakeShopLinkByKakao(dto.kakao)) } + dto.web?.let { cakeShopLinks.add(supplyCakeShopLinkByWeb(dto.web)) } return UpdateLinkParam(user, cakeShopId, cakeShopLinks) } diff --git a/cakk-api/src/test/java/com/cakk/api/controller/SignControllerTest.kt b/cakk-api/src/test/java/com/cakk/api/controller/SignControllerTest.kt index 64a7af7c..f5ac411b 100644 --- a/cakk-api/src/test/java/com/cakk/api/controller/SignControllerTest.kt +++ b/cakk-api/src/test/java/com/cakk/api/controller/SignControllerTest.kt @@ -9,24 +9,30 @@ import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status import com.cakk.api.common.annotation.TestWithDisplayName import com.cakk.api.common.base.MockMvcTest import com.cakk.api.common.fixture.FixtureCommon.fixtureMonkey +import com.cakk.api.common.fixture.FixtureCommon.getDateFixture +import com.cakk.api.common.fixture.FixtureCommon.getEnumFixture +import com.cakk.api.common.fixture.FixtureCommon.getStringFixtureBw import com.cakk.api.dto.request.user.GenerateCodeRequest +import com.cakk.api.dto.request.user.UserSignUpRequest import com.cakk.api.dto.request.user.VerifyEmailRequest +import com.cakk.common.enums.Gender +import com.cakk.common.enums.Provider import com.cakk.core.dto.param.user.UserSignInParam -import com.cakk.core.dto.param.user.UserSignUpParam import com.cakk.core.dto.response.user.JwtResponse +import org.mockito.kotlin.any internal class SignControllerTest : MockMvcTest() { @TestWithDisplayName("") fun signUp() { // given - val param = fixtureMonkey.giveMeBuilder(UserSignUpParam::class.java) - .setNotNull("provider") - .setNotNull("idToken") - .setNotNull("nickname") - .setNotNull("email") - .setNotNull("birthday") - .setNotNull("gender") + val dto = fixtureMonkey.giveMeBuilder(UserSignUpRequest::class.java) + .set("provider", getEnumFixture(Provider::class.java)) + .set("idToken", getStringFixtureBw(100, 200)) + .set("nickname", getStringFixtureBw(2, 10)) + .set("email", getStringFixtureBw(5, 20)) + .set("birthday", getDateFixture()) + .set("gender", getEnumFixture(Gender::class.java)) .sample() val jwt = fixtureMonkey.giveMeBuilder(JwtResponse::class.java) .setNotNull("accessToken") @@ -34,12 +40,12 @@ internal class SignControllerTest : MockMvcTest() { .setNotNull("grantType") .sample() - doReturn(jwt).`when`(signService).signUp(param) + doReturn(jwt).`when`(signService).signUp(any()) // when & then mockMvc.perform( post("/sign-up") - .content(objectMapper.writeValueAsString(param)) + .content(objectMapper.writeValueAsString(dto)) .contentType(MediaType.APPLICATION_JSON) ) .andExpect(status().isOk()) diff --git a/cakk-api/src/test/java/com/cakk/api/integration/cake/CakeIntegrationTest.kt b/cakk-api/src/test/java/com/cakk/api/integration/cake/CakeIntegrationTest.kt index 4bbdd0d9..9669a4e0 100644 --- a/cakk-api/src/test/java/com/cakk/api/integration/cake/CakeIntegrationTest.kt +++ b/cakk-api/src/test/java/com/cakk/api/integration/cake/CakeIntegrationTest.kt @@ -21,7 +21,7 @@ import com.cakk.api.common.fixture.FixtureCommon.fixtureMonkey import com.cakk.api.dto.request.cake.CakeCreateRequest import com.cakk.api.dto.request.cake.CakeUpdateRequest import com.cakk.core.dto.response.cake.CakeDetailResponse -import com.cakk.core.dto.response.cake.CakeImageListResponse +import com.cakk.core.dto.response.cake.CakeImageWithShopInfoListResponse import com.cakk.core.dto.response.like.HeartResponse import com.cakk.common.enums.CakeDesignCategory import com.cakk.common.enums.ReturnCode @@ -94,7 +94,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -105,6 +105,8 @@ internal class CakeIntegrationTest( data.cakeImages.forEach { val cakeCategory = cakeReadFacade.findCakeCategoryByCakeId(it.cakeId) cakeCategory.cakeDesignCategory shouldBe CakeDesignCategory.FLOWER + it.shopName shouldBe "케이크 맛집" + it.cakeShopId + it.thumbnailUrl shouldBe "thumbnail_url" + it.cakeShopId } } @@ -127,7 +129,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -138,6 +140,8 @@ internal class CakeIntegrationTest( data.cakeImages.forEach { val cakeCategory = cakeReadFacade.findCakeCategoryByCakeId(it.cakeId) cakeCategory.cakeDesignCategory shouldBe CakeDesignCategory.FLOWER + it.shopName shouldBe "케이크 맛집" + it.cakeShopId + it.thumbnailUrl shouldBe "thumbnail_url" + it.cakeShopId } } @@ -160,7 +164,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -189,7 +193,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -197,7 +201,11 @@ internal class CakeIntegrationTest( data.lastCakeId shouldBe data.cakeImages.minOfOrNull { it.cakeId } data.size shouldBe 3 - data.cakeImages.forEach { it.cakeShopId shouldBe 1 } + data.cakeImages.forEach { + it.cakeShopId shouldBe 1 + it.shopName shouldBe "케이크 맛집" + it.cakeShopId + it.thumbnailUrl shouldBe "thumbnail_url" + it.cakeShopId + } } @TestWithDisplayName("케이크 샵으로 첫 페이지가 아닌 케이크 이미지 조회에 성공한다") @@ -219,7 +227,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -227,7 +235,11 @@ internal class CakeIntegrationTest( data.lastCakeId shouldBe data.cakeImages.minOfOrNull { it.cakeId } data.size shouldBe 3 - data.cakeImages.forEach { it.cakeShopId shouldBe 1 } + data.cakeImages.forEach { + it.cakeShopId shouldBe 1 + it.shopName shouldBe "케이크 맛집" + it.cakeShopId + it.thumbnailUrl shouldBe "thumbnail_url" + it.cakeShopId + } } @TestWithDisplayName("케이크 샵으로 케이크 이미지 조회 시 데이터가 없으면 빈 배열을 반환한다") @@ -249,7 +261,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -279,7 +291,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -307,7 +319,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -334,7 +346,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code @@ -361,13 +373,17 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code response.returnMessage shouldBe ReturnCode.SUCCESS.message data.cakeImages shouldHaveSize 4 + data.cakeImages.forEach { + it.shopName shouldBe "케이크 맛집" + it.cakeShopId + it.thumbnailUrl shouldBe "thumbnail_url" + it.cakeShopId + } } @TestWithDisplayName("조회수로 케이크 이미지 조회에 성공한다") @@ -390,13 +406,17 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code response.returnMessage shouldBe ReturnCode.SUCCESS.message data.cakeImages shouldHaveSize 4 + data.cakeImages.forEach { + it.shopName shouldBe "케이크 맛집" + it.cakeShopId + it.thumbnailUrl shouldBe "thumbnail_url" + it.cakeShopId + } } @TestWithDisplayName("조회한 케이크가 없을 시, 인기 케이크 이미지 조회에 빈 배열을 리턴한다") @@ -416,7 +436,7 @@ internal class CakeIntegrationTest( // then val response = objectMapper.convertValue(responseEntity.body, ApiResponse::class.java) - val data = objectMapper.convertValue(response.data, CakeImageListResponse::class.java) + val data = objectMapper.convertValue(response.data, CakeImageWithShopInfoListResponse::class.java) responseEntity.statusCode shouldBe HttpStatusCode.valueOf(200) response.returnCode shouldBe ReturnCode.SUCCESS.code diff --git a/cakk-core/src/main/kotlin/com/cakk/core/dto/response/cake/CakeImageListResponse.kt b/cakk-core/src/main/kotlin/com/cakk/core/dto/response/cake/CakeImageListResponse.kt deleted file mode 100644 index 3440bf27..00000000 --- a/cakk-core/src/main/kotlin/com/cakk/core/dto/response/cake/CakeImageListResponse.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.cakk.core.dto.response.cake - -import com.cakk.domain.mysql.dto.param.cake.CakeImageResponseParam - -data class CakeImageListResponse( - val cakeImages: List, - val lastCakeId: Long?, - val size: Int -) diff --git a/cakk-core/src/main/kotlin/com/cakk/core/dto/response/cake/CakeImageWithShopInfoListResponse.kt b/cakk-core/src/main/kotlin/com/cakk/core/dto/response/cake/CakeImageWithShopInfoListResponse.kt new file mode 100644 index 00000000..1236d9b8 --- /dev/null +++ b/cakk-core/src/main/kotlin/com/cakk/core/dto/response/cake/CakeImageWithShopInfoListResponse.kt @@ -0,0 +1,10 @@ +package com.cakk.core.dto.response.cake + +import com.cakk.domain.mysql.dto.param.cake.CakeImageWithShopInfoResponseParam + +data class CakeImageWithShopInfoListResponse( + val cakeImages: List, + val lastCakeId: Long?, + val size: Int +) + diff --git a/cakk-core/src/main/kotlin/com/cakk/core/facade/cake/CakeReadFacade.kt b/cakk-core/src/main/kotlin/com/cakk/core/facade/cake/CakeReadFacade.kt index 94e71cbd..4de5cc5a 100644 --- a/cakk-core/src/main/kotlin/com/cakk/core/facade/cake/CakeReadFacade.kt +++ b/cakk-core/src/main/kotlin/com/cakk/core/facade/cake/CakeReadFacade.kt @@ -1,11 +1,13 @@ package com.cakk.core.facade.cake +import java.util.* + import com.cakk.common.enums.CakeDesignCategory import com.cakk.common.enums.ReturnCode import com.cakk.common.exception.CakkException import com.cakk.core.annotation.DomainFacade import com.cakk.domain.mysql.dto.param.cake.CakeDetailParam -import com.cakk.domain.mysql.dto.param.cake.CakeImageResponseParam +import com.cakk.domain.mysql.dto.param.cake.CakeImageWithShopInfoResponseParam import com.cakk.core.dto.param.cake.CakeSearchParam import com.cakk.domain.mysql.entity.cake.Cake import com.cakk.domain.mysql.entity.cake.CakeCategory @@ -13,66 +15,75 @@ import com.cakk.domain.mysql.entity.user.User import com.cakk.domain.mysql.repository.jpa.CakeCategoryJpaRepository import com.cakk.domain.mysql.repository.jpa.CakeJpaRepository import com.cakk.domain.mysql.repository.query.CakeQueryRepository -import java.util.* +import com.cakk.domain.redis.repository.CakeViewsRedisRepository @DomainFacade class CakeReadFacade( private val cakeJpaRepository: CakeJpaRepository, private val cakeCategoryJpaRepository: CakeCategoryJpaRepository, - private val cakeQueryRepository: CakeQueryRepository + private val cakeQueryRepository: CakeQueryRepository, + private val cakeViewsRedisRepository: CakeViewsRedisRepository, ) { - fun findById(cakeId: Long): Cake { - return cakeJpaRepository.findById(cakeId).orElseThrow { CakkException(ReturnCode.NOT_EXIST_CAKE) } + fun findById(cakeId: Long): Cake { + return cakeJpaRepository.findById(cakeId).orElseThrow { CakkException(ReturnCode.NOT_EXIST_CAKE) } } - fun findByIdWithHeart(cakeId: Long): Cake { - val cake: Cake = cakeQueryRepository.searchByIdWithHeart(cakeId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE) + fun findByIdWithHeart(cakeId: Long): Cake { + val cake: Cake = cakeQueryRepository.searchByIdWithHeart(cakeId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE) - return cake - } + return cake + } - fun searchCakeImagesByCursorAndCategory( + fun searchCakeImagesByCursorAndCategory( cakeId: Long?, category: CakeDesignCategory, pageSize: Int - ): List { - return cakeQueryRepository.searchCakeImagesByCursorAndCategory(cakeId, category, pageSize) - } + ): List { + return cakeQueryRepository.searchCakeImagesByCursorAndCategory(cakeId, category, pageSize) + } - fun searchCakeImagesByCursorAndCakeShopId( - cakeId: Long?, - cakeShopId: Long?, - pageSize: Int - ): List { - return cakeQueryRepository.searchCakeImagesByCursorAndCakeShopId(cakeId, cakeShopId, pageSize) - } + fun searchCakeImagesByCursorAndCakeShopId( + cakeId: Long?, + cakeShopId: Long?, + pageSize: Int + ): List { + return cakeQueryRepository.searchCakeImagesByCursorAndCakeShopId(cakeId, cakeShopId, pageSize) + } + + fun searchCakeImagesByCursorAndSearchKeyword(param: CakeSearchParam): List { + return cakeQueryRepository.searchCakeImagesByCursorAndSearchKeyword( + param.cakeId, + param.keyword, + param.location, + param.pageSize + ) + } - fun searchCakeImagesByCursorAndSearchKeyword(param: CakeSearchParam): List { - return cakeQueryRepository.searchCakeImagesByCursorAndSearchKeyword( - param.cakeId, - param.keyword, - param.location, - param.pageSize - ) - } + fun searchBestCakeImages( + offset: Long, + pageSize: Int + ): Pair, List> { + val cakeIds: List = cakeViewsRedisRepository.findTopCakeIdsByOffsetAndCount(offset, pageSize.toLong()) - fun searchCakeImagesByCakeIds(cakeIds: List): List { - return cakeQueryRepository.searchCakeImagesByCakeIds(cakeIds) - } + return when { + cakeIds.isEmpty() -> Pair(listOf(), listOf()) + else -> Pair(cakeIds, cakeQueryRepository.searchCakeImagesByCakeIds(cakeIds)) + } + } - fun findWithCakeTagsAndCakeCategories(cakeId: Long, owner: User): Cake { - return cakeQueryRepository.searchWithCakeTagsAndCakeCategories(cakeId, owner) - .orElseThrow { CakkException(ReturnCode.NOT_CAKE_SHOP_OWNER) } + fun findWithCakeTagsAndCakeCategories(cakeId: Long, owner: User): Cake { + return cakeQueryRepository.searchWithCakeTagsAndCakeCategories(cakeId, owner) + .orElseThrow { CakkException(ReturnCode.NOT_CAKE_SHOP_OWNER) } } - fun searchCakeDetailById(cakeId: Long?): CakeDetailParam { - val param: CakeDetailParam = cakeQueryRepository.searchCakeDetailById(cakeId) - if (Objects.isNull(param)) { - throw CakkException(ReturnCode.NOT_EXIST_CAKE) - } - return param - } + fun searchCakeDetailById(cakeId: Long?): CakeDetailParam { + val param: CakeDetailParam = cakeQueryRepository.searchCakeDetailById(cakeId) + if (Objects.isNull(param)) { + throw CakkException(ReturnCode.NOT_EXIST_CAKE) + } + return param + } fun findCakeCategoryByCakeId(cakeId: Long?): CakeCategory { return cakeCategoryJpaRepository.findByCakeId(cakeId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_CATEGORY) diff --git a/cakk-core/src/main/kotlin/com/cakk/core/facade/cake/CakeShopReadFacade.kt b/cakk-core/src/main/kotlin/com/cakk/core/facade/cake/CakeShopReadFacade.kt index c3a024a5..417dfff7 100644 --- a/cakk-core/src/main/kotlin/com/cakk/core/facade/cake/CakeShopReadFacade.kt +++ b/cakk-core/src/main/kotlin/com/cakk/core/facade/cake/CakeShopReadFacade.kt @@ -18,6 +18,7 @@ import com.cakk.domain.mysql.repository.jpa.CakeShopJpaRepository import com.cakk.domain.mysql.repository.jpa.CakeShopLinkJpaRepository import com.cakk.domain.mysql.repository.jpa.CakeShopOperationJpaRepository import com.cakk.domain.mysql.repository.query.CakeShopQueryRepository +import com.cakk.domain.redis.repository.CakeShopViewsRedisRepository import org.locationtech.jts.geom.Point @DomainFacade @@ -26,24 +27,25 @@ class CakeShopReadFacade( private val cakeShopQueryRepository: CakeShopQueryRepository, private val cakeShopOperationJpaRepository: CakeShopOperationJpaRepository, private val cakeShopLinkJpaRepository: CakeShopLinkJpaRepository, - private val businessInformationJpaRepository: BusinessInformationJpaRepository + private val businessInformationJpaRepository: BusinessInformationJpaRepository, + private val cakeShopViewsRedisRepository: CakeShopViewsRedisRepository ) { - fun findById(cakeShopId: Long): CakeShop { - return cakeShopJpaRepository.findById(cakeShopId).orElseThrow { CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) } + fun findById(cakeShopId: Long): CakeShop { + return cakeShopJpaRepository.findById(cakeShopId).orElseThrow { CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) } } - fun findByIdWithHeart(cakeShopId: Long): CakeShop { - return cakeShopQueryRepository.searchByIdWithHeart(cakeShopId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) - } + fun findByIdWithHeart(cakeShopId: Long): CakeShop { + return cakeShopQueryRepository.searchByIdWithHeart(cakeShopId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) + } - fun findByIdWithLike(cakeShopId: Long): CakeShop { + fun findByIdWithLike(cakeShopId: Long): CakeShop { return cakeShopQueryRepository.searchByIdWithLike(cakeShopId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) - } + } - fun searchSimpleById(cakeShopId: Long): CakeShopSimpleParam { + fun searchSimpleById(cakeShopId: Long): CakeShopSimpleParam { return cakeShopQueryRepository.searchSimpleById(cakeShopId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) - } + } fun searchDetailById(cakeShopId: Long): CakeShopDetailParam { return cakeShopQueryRepository.searchDetailById(cakeShopId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) @@ -53,45 +55,55 @@ class CakeShopReadFacade( return cakeShopQueryRepository.searchInfoById(cakeShopId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) } - fun findBusinessInformationWithShop(cakeShopId: Long): BusinessInformation { - return businessInformationJpaRepository.findBusinessInformationWithCakeShop(cakeShopId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) - } + fun findBusinessInformationWithShop(cakeShopId: Long): BusinessInformation { + return businessInformationJpaRepository.findBusinessInformationWithCakeShop(cakeShopId) + ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) + } - fun findBusinessInformationByCakeShopId(cakeShopId: Long): BusinessInformation { - return businessInformationJpaRepository.findBusinessInformationWithCakeShop(cakeShopId) ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) - } + fun findBusinessInformationByCakeShopId(cakeShopId: Long): BusinessInformation { + return businessInformationJpaRepository.findBusinessInformationWithCakeShop(cakeShopId) + ?: throw CakkException(ReturnCode.NOT_EXIST_CAKE_SHOP) + } - fun searchShopByLocationBased(point: Point?, distance: Double?): List { - return cakeShopQueryRepository.findShopsByLocationBased(point, distance) - } + fun searchShopByLocationBased(point: Point?, distance: Double?): List { + return cakeShopQueryRepository.findShopsByLocationBased(point, distance) + } - fun searchShopBySearch(param: CakeShopSearchParam): List { - return cakeShopQueryRepository.searchByKeywordWithLocation( - param.cakeShopId, - param.keyword, - param.location, - param.pageSize - ) - } + fun searchShopBySearch(param: CakeShopSearchParam): List { + return cakeShopQueryRepository.searchByKeywordWithLocation( + param.cakeShopId, + param.keyword, + param.location, + param.pageSize + ) + } - fun searchWithShopLinks(owner: User?, cakeShopId: Long?): CakeShop { - return cakeShopQueryRepository.searchWithShopLinks(owner, cakeShopId) - .orElseThrow { CakkException(ReturnCode.NOT_CAKE_SHOP_OWNER) } + fun searchWithShopLinks(owner: User?, cakeShopId: Long?): CakeShop { + return cakeShopQueryRepository.searchWithShopLinks(owner, cakeShopId) + .orElseThrow { CakkException(ReturnCode.NOT_CAKE_SHOP_OWNER) } } - fun searchByIdAndOwner(cakeShopId: Long, owner: User): CakeShop { - return cakeShopQueryRepository.searchWithBusinessInformationAndOwnerById(owner, cakeShopId) - .orElseThrow { CakkException(ReturnCode.NOT_CAKE_SHOP_OWNER) } + fun searchByIdAndOwner(cakeShopId: Long, owner: User): CakeShop { + return cakeShopQueryRepository.searchWithBusinessInformationAndOwnerById(owner, cakeShopId) + .orElseThrow { CakkException(ReturnCode.NOT_CAKE_SHOP_OWNER) } } - fun searchWithOperations(owner: User?, cakeShopId: Long?): CakeShop { - return cakeShopQueryRepository.searchWithOperations(owner, cakeShopId) - .orElseThrow { CakkException(ReturnCode.NOT_CAKE_SHOP_OWNER) } + fun searchWithOperations(owner: User?, cakeShopId: Long?): CakeShop { + return cakeShopQueryRepository.searchWithOperations(owner, cakeShopId) + .orElseThrow { CakkException(ReturnCode.NOT_CAKE_SHOP_OWNER) } } - fun searchShopsByShopIds(shopIds: List?): List { - return cakeShopQueryRepository.searchByShopIds(shopIds) - } + fun searchBestShops( + offset: Long, + pageSize: Int + ): List { + val cakeShopIds = cakeShopViewsRedisRepository.findTopShopIdsByOffsetAndCount(offset, pageSize.toLong()) + + return when { + cakeShopIds.isEmpty() -> listOf() + else -> cakeShopQueryRepository.searchByShopIds(cakeShopIds) + } + } fun findCakeShopOperationsByCakeShopId(cakeShopId: Long): List { return cakeShopOperationJpaRepository.findAllByCakeShopId(cakeShopId) diff --git a/cakk-core/src/main/kotlin/com/cakk/core/mapper/CakeMapper.kt b/cakk-core/src/main/kotlin/com/cakk/core/mapper/CakeMapper.kt index 1e8ed258..eb49394c 100644 --- a/cakk-core/src/main/kotlin/com/cakk/core/mapper/CakeMapper.kt +++ b/cakk-core/src/main/kotlin/com/cakk/core/mapper/CakeMapper.kt @@ -3,41 +3,44 @@ package com.cakk.core.mapper import java.util.* import com.cakk.core.dto.response.cake.CakeDetailResponse -import com.cakk.core.dto.response.cake.CakeImageListResponse +import com.cakk.core.dto.response.cake.CakeImageWithShopInfoListResponse import com.cakk.core.dto.response.like.HeartCakeImageListResponse import com.cakk.domain.mysql.dto.param.cake.CakeDetailParam -import com.cakk.domain.mysql.dto.param.cake.CakeImageResponseParam +import com.cakk.domain.mysql.dto.param.cake.CakeImageWithShopInfoResponseParam import com.cakk.domain.mysql.dto.param.like.HeartCakeImageResponseParam import com.cakk.domain.mysql.dto.param.tag.TagParam import com.cakk.domain.mysql.entity.cake.Cake -fun supplyCakeImageListResponse(cakeImages: List): CakeImageListResponse { + +fun supplyCakeImageWithShopInfoListResponse( + cakeImages: List +): CakeImageWithShopInfoListResponse { val size = cakeImages.size - return CakeImageListResponse( + return CakeImageWithShopInfoListResponse( cakeImages, - if (cakeImages.isEmpty()) null else cakeImages[size - 1].cakeId, + if(cakeImages.isEmpty()) null else cakeImages[size - 1].cakeId, cakeImages.size ) } -fun supplyCakeImageListResponse( - cakeImages: List, +fun supplyCakeImageWithShopInfoListResponse( + cakeImages: List, cakeIds: List -): CakeImageListResponse { - val sortedCakeImages: MutableList = ArrayList() +): CakeImageWithShopInfoListResponse { + val sortedCakeImages: MutableList = ArrayList() for (cakeId in cakeIds) { cakeImages.stream() - .filter { cakeImage: CakeImageResponseParam -> cakeImage.cakeId == cakeId } + .filter { cakeImage: CakeImageWithShopInfoResponseParam -> cakeImage.cakeId == cakeId } .findFirst() - .ifPresent { e: CakeImageResponseParam -> sortedCakeImages.add(e) } + .ifPresent { e: CakeImageWithShopInfoResponseParam -> sortedCakeImages.add(e) } } - return CakeImageListResponse( + return CakeImageWithShopInfoListResponse( sortedCakeImages, null, - sortedCakeImages.size + cakeImages.size ) } diff --git a/cakk-core/src/main/kotlin/com/cakk/core/service/cake/CakeService.kt b/cakk-core/src/main/kotlin/com/cakk/core/service/cake/CakeService.kt index 9eb6ba1d..76688164 100644 --- a/cakk-core/src/main/kotlin/com/cakk/core/service/cake/CakeService.kt +++ b/cakk-core/src/main/kotlin/com/cakk/core/service/cake/CakeService.kt @@ -7,15 +7,14 @@ import org.springframework.transaction.annotation.Transactional import com.cakk.core.dto.event.IncreaseSearchCountEvent import com.cakk.core.dto.param.cake.* import com.cakk.core.dto.response.cake.CakeDetailResponse -import com.cakk.core.dto.response.cake.CakeImageListResponse +import com.cakk.core.dto.response.cake.CakeImageWithShopInfoListResponse import com.cakk.core.facade.cake.CakeManageFacade import com.cakk.core.facade.cake.CakeReadFacade import com.cakk.core.facade.cake.CakeShopReadFacade import com.cakk.core.facade.tag.TagReadFacade import com.cakk.core.mapper.cakeDetailResponseFromParam -import com.cakk.core.mapper.supplyCakeImageListResponse +import com.cakk.core.mapper.supplyCakeImageWithShopInfoListResponse import com.cakk.domain.mysql.entity.user.User -import com.cakk.domain.redis.repository.CakeViewsRedisRepository @Transactional(readOnly = true) @Service @@ -24,24 +23,22 @@ class CakeService( private val tagReadFacade: TagReadFacade, private val cakeShopReadFacade: CakeShopReadFacade, private val cakeManageFacade: CakeManageFacade, - private val cakeViewsRedisRepository: CakeViewsRedisRepository, private val eventPublisher: ApplicationEventPublisher ) { - - fun findCakeImagesByCursorAndCategory(dto: CakeSearchByCategoryParam): CakeImageListResponse { + fun findCakeImagesByCursorAndCategory(dto: CakeSearchByCategoryParam): CakeImageWithShopInfoListResponse { val cakeImages = cakeReadFacade.searchCakeImagesByCursorAndCategory(dto.cakeId, dto.category, dto.pageSize) - return supplyCakeImageListResponse(cakeImages) + return supplyCakeImageWithShopInfoListResponse(cakeImages) } - fun findCakeImagesByCursorAndCakeShopId(dto: CakeSearchByShopParam): CakeImageListResponse { + fun findCakeImagesByCursorAndCakeShopId(dto: CakeSearchByShopParam): CakeImageWithShopInfoListResponse { val cakeImages = cakeReadFacade.searchCakeImagesByCursorAndCakeShopId(dto.cakeId, dto.shopId, dto.pageSize) - return supplyCakeImageListResponse(cakeImages) + return supplyCakeImageWithShopInfoListResponse(cakeImages) } - fun findCakeImagesByCursorAndSearch(dto: CakeSearchParam): CakeImageListResponse { + fun findCakeImagesByCursorAndSearch(dto: CakeSearchParam): CakeImageWithShopInfoListResponse { val cakeImages = cakeReadFacade.searchCakeImagesByCursorAndSearchKeyword(dto) dto.keyword?.run { @@ -49,24 +46,15 @@ class CakeService( eventPublisher.publishEvent(event) } - return supplyCakeImageListResponse(cakeImages) + return supplyCakeImageWithShopInfoListResponse(cakeImages) } - fun searchCakeImagesByCursorAndViews(dto: CakeSearchByViewsParam): CakeImageListResponse { + fun searchCakeImagesByCursorAndViews(dto: CakeSearchByViewsParam): CakeImageWithShopInfoListResponse { val offset = dto.offset val pageSize = dto.pageSize - val cakeIds: List = cakeViewsRedisRepository.findTopCakeIdsByOffsetAndCount(offset, pageSize.toLong()) - - return when { - cakeIds.isEmpty() -> { - supplyCakeImageListResponse(listOf(), cakeIds) - } + val (cakeIds, cakeImages) = cakeReadFacade.searchBestCakeImages(offset, pageSize) - else -> { - val cakeImages = cakeReadFacade.searchCakeImagesByCakeIds(cakeIds) - supplyCakeImageListResponse(cakeImages, cakeIds) - } - } + return supplyCakeImageWithShopInfoListResponse(cakeImages, cakeIds) } fun findCakeDetailById(cakeId: Long): CakeDetailResponse { diff --git a/cakk-core/src/main/kotlin/com/cakk/core/service/shop/ShopService.kt b/cakk-core/src/main/kotlin/com/cakk/core/service/shop/ShopService.kt index 2240c4bf..40acf342 100644 --- a/cakk-core/src/main/kotlin/com/cakk/core/service/shop/ShopService.kt +++ b/cakk-core/src/main/kotlin/com/cakk/core/service/shop/ShopService.kt @@ -1,7 +1,5 @@ package com.cakk.core.service.shop -import java.util.* - import org.springframework.context.ApplicationEventPublisher import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -25,7 +23,6 @@ import com.cakk.domain.mysql.dto.param.shop.CakeShopUpdateParam import com.cakk.domain.mysql.dto.param.shop.UpdateShopAddressParam import com.cakk.domain.mysql.dto.param.user.CertificationParam import com.cakk.domain.mysql.entity.user.User -import com.cakk.domain.redis.repository.CakeShopViewsRedisRepository @Service class ShopService( @@ -33,7 +30,6 @@ class ShopService( private val cakeShopReadFacade: CakeShopReadFacade, private val businessInformationReadFacade: BusinessInformationReadFacade, private val cakeShopManageFacade: CakeShopManageFacade, - private val cakeShopViewsRedisRepository: CakeShopViewsRedisRepository, private val verificationPolicy: VerificationPolicy, private val eventPublisher: ApplicationEventPublisher ) { @@ -160,17 +156,19 @@ class ShopService( fun searchCakeShopsByCursorAndViews(param: CakeShopSearchByViewsParam): CakeShopSearchResponse { val offset = param.offset ?: 0 val pageSize = param.pageSize - val cakeShopIds = cakeShopViewsRedisRepository.findTopShopIdsByOffsetAndCount(offset, pageSize.toLong()) - - if (Objects.isNull(cakeShopIds) || cakeShopIds.isEmpty()) { - return supplyCakeShopSearchResponseBy(listOf()) + val result = cakeShopReadFacade.searchBestShops(offset, pageSize) + + return when { + result.isEmpty() -> { + supplyCakeShopSearchResponseBy(listOf()) + } + else -> { + val cakeShopBySearchParams = supplyCakeShopBySearchParamListBy(result) + val cakeShops = CakeShops(cakeShopBySearchParams, 6, pageSize) + + supplyCakeShopSearchResponseBy(cakeShops.cakeShops) + } } - - val result = cakeShopReadFacade.searchShopsByShopIds(cakeShopIds) - val cakeShopBySearchParams = supplyCakeShopBySearchParamListBy(result) - val cakeShops = CakeShops(cakeShopBySearchParams, 6, pageSize) - - return supplyCakeShopSearchResponseBy(cakeShops.cakeShops) } @Transactional(readOnly = true) diff --git a/cakk-core/src/test/kotlin/com/cakk/core/service/cake/CakeServiceTest.kt b/cakk-core/src/test/kotlin/com/cakk/core/service/cake/CakeServiceTest.kt index 1089d42a..cd898c9e 100644 --- a/cakk-core/src/test/kotlin/com/cakk/core/service/cake/CakeServiceTest.kt +++ b/cakk-core/src/test/kotlin/com/cakk/core/service/cake/CakeServiceTest.kt @@ -3,10 +3,7 @@ package com.cakk.core.service.cake import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe -import net.jqwik.api.Arbitraries - import org.junit.jupiter.api.DisplayName -import org.mockito.ArgumentMatchers import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.Mockito.* @@ -16,7 +13,6 @@ import com.cakk.common.enums.CakeDesignCategory import com.cakk.core.common.annotation.TestWithDisplayName import com.cakk.core.common.base.MockitoTest import com.cakk.core.common.fixture.FixtureCommon.fixtureMonkey -import com.cakk.core.common.fixture.FixtureCommon.getLongFixtureBw import com.cakk.core.common.fixture.FixtureCommon.getLongFixtureGoe import com.cakk.core.common.fixture.FixtureCommon.getStringFixtureBw import com.cakk.core.dto.param.cake.CakeSearchByCategoryParam @@ -26,8 +22,7 @@ import com.cakk.core.facade.cake.CakeManageFacade import com.cakk.core.facade.cake.CakeReadFacade import com.cakk.core.facade.cake.CakeShopReadFacade import com.cakk.core.facade.tag.TagReadFacade -import com.cakk.domain.mysql.dto.param.cake.CakeImageResponseParam -import com.cakk.domain.redis.repository.CakeViewsRedisRepository +import com.cakk.domain.mysql.dto.param.cake.CakeImageWithShopInfoResponseParam @DisplayName("케이크 조회 관련 비즈니스 로직 테스트") internal class CakeServiceTest : MockitoTest() { @@ -47,9 +42,6 @@ internal class CakeServiceTest : MockitoTest() { @Mock private lateinit var cakeManageFacade: CakeManageFacade - @Mock - private lateinit var cakeViewsRedisRepository: CakeViewsRedisRepository - @Mock private lateinit var eventPublisher: ApplicationEventPublisher @@ -57,10 +49,12 @@ internal class CakeServiceTest : MockitoTest() { fun findCakeImagesByCursorAndCategory1() { // given val dto = CakeSearchByCategoryParam(null, CakeDesignCategory.FLOWER, 3) - val cakeImages = fixtureMonkey.giveMeBuilder(CakeImageResponseParam::class.java) + val cakeImages = fixtureMonkey.giveMeBuilder(CakeImageWithShopInfoResponseParam::class.java) .set("cakeId", getLongFixtureGoe(1)) .set("cakeShopId", getLongFixtureGoe(1)) .set("cakeImageUrl", getStringFixtureBw(10, 20)) + .set("thumbnailUrl", getStringFixtureBw(5, 10)) + .set("shopName", getStringFixtureBw(5, 10)) .sampleList(3) doReturn(cakeImages).`when`(cakeReadFacade).searchCakeImagesByCursorAndCategory(dto.cakeId, dto.category, dto.pageSize) @@ -79,7 +73,7 @@ internal class CakeServiceTest : MockitoTest() { fun findCakeImagesByCursorAndCategory2() { // given val dto = CakeSearchByCategoryParam(null, CakeDesignCategory.FLOWER, 3) - val cakeImages = fixtureMonkey.giveMe(CakeImageResponseParam::class.java, 0) + val cakeImages = fixtureMonkey.giveMe(CakeImageWithShopInfoResponseParam::class.java, 0) doReturn(cakeImages).`when`(cakeReadFacade).searchCakeImagesByCursorAndCategory(dto.cakeId, dto.category, dto.pageSize) @@ -97,10 +91,12 @@ internal class CakeServiceTest : MockitoTest() { fun findCakeImagesByCursorAndShop1() { // given val dto = CakeSearchByShopParam(null, 1L, 3) - val cakeImages = fixtureMonkey.giveMeBuilder(CakeImageResponseParam::class.java) - .set("cakeId", 1L) + val cakeImages = fixtureMonkey.giveMeBuilder(CakeImageWithShopInfoResponseParam::class.java) + .set("cakeId", getLongFixtureGoe(1)) .set("cakeShopId", getLongFixtureGoe(1)) .set("cakeImageUrl", getStringFixtureBw(10, 20)) + .set("thumbnailUrl", getStringFixtureBw(5, 10)) + .set("shopName", getStringFixtureBw(5, 10)) .sampleList(3) doReturn(cakeImages).`when`(cakeReadFacade).searchCakeImagesByCursorAndCakeShopId(dto.cakeId, dto.shopId, dto.pageSize) @@ -119,7 +115,7 @@ internal class CakeServiceTest : MockitoTest() { fun findCakeImagesByCursorAndShop2() { // given val dto = CakeSearchByShopParam(null, 1L, 3) - val cakeImages = fixtureMonkey.giveMe(CakeImageResponseParam::class.java, 0) + val cakeImages = fixtureMonkey.giveMe(CakeImageWithShopInfoResponseParam::class.java, 0) doReturn(cakeImages).`when`(cakeReadFacade).searchCakeImagesByCursorAndCakeShopId(dto.cakeId, dto.shopId, dto.pageSize) @@ -140,14 +136,15 @@ internal class CakeServiceTest : MockitoTest() { val pageSize = 3 val dto = CakeSearchByViewsParam(cursor, pageSize) val cakeIds = listOf(1L, 2L, 3L) - val cakeImages = fixtureMonkey.giveMeBuilder(CakeImageResponseParam::class.java) - .set("cakeId", getLongFixtureBw(1, 3)) - .set("cakeShopId", Arbitraries.longs().greaterOrEqual(1)) - .set("cakeImageUrl", Arbitraries.strings().alpha().ofMinLength(10).ofMaxLength(20)) + val cakeImages = fixtureMonkey.giveMeBuilder(CakeImageWithShopInfoResponseParam::class.java) + .set("cakeId", getLongFixtureGoe(1)) + .set("cakeShopId", getLongFixtureGoe(1)) + .set("cakeImageUrl", getStringFixtureBw(10, 20)) + .set("thumbnailUrl", getStringFixtureBw(5, 10)) + .set("shopName", getStringFixtureBw(5, 10)) .sampleList(3) - doReturn(cakeIds).`when`(cakeViewsRedisRepository).findTopCakeIdsByOffsetAndCount(cursor, pageSize.toLong()) - doReturn(cakeImages).`when`(cakeReadFacade).searchCakeImagesByCakeIds(cakeIds) + doReturn(Pair(cakeIds, cakeImages)).`when`(cakeReadFacade).searchBestCakeImages(cursor, pageSize) // when val result = cakeService.searchCakeImagesByCursorAndViews(dto) @@ -156,8 +153,7 @@ internal class CakeServiceTest : MockitoTest() { result.cakeImages shouldNotBe null result.lastCakeId shouldBe null - verify(cakeViewsRedisRepository, times(1)).findTopCakeIdsByOffsetAndCount(cursor, pageSize.toLong()) - verify(cakeReadFacade, times(1)).searchCakeImagesByCakeIds(cakeIds) + verify(cakeReadFacade, times(1)).searchBestCakeImages(cursor, pageSize) } @TestWithDisplayName("인기 케이크 목록이 없을 시 빈 배열을 조회한다") @@ -167,7 +163,7 @@ internal class CakeServiceTest : MockitoTest() { val pageSize = 3 val dto = CakeSearchByViewsParam(cursor, pageSize) - doReturn(listOf()).`when`(cakeViewsRedisRepository).findTopCakeIdsByOffsetAndCount(cursor, pageSize.toLong()) + doReturn(Pair(listOf(), listOf())).`when`(cakeReadFacade).searchBestCakeImages(cursor, pageSize) // when val result = cakeService.searchCakeImagesByCursorAndViews(dto) @@ -176,7 +172,6 @@ internal class CakeServiceTest : MockitoTest() { result.cakeImages.size shouldBe 0 result.lastCakeId shouldBe null - verify(cakeViewsRedisRepository, times(1)).findTopCakeIdsByOffsetAndCount(cursor, pageSize.toLong()) - verify(cakeReadFacade, never()).searchCakeImagesByCakeIds(ArgumentMatchers.anyList()) + verify(cakeReadFacade, times(1)).searchBestCakeImages(cursor, pageSize) } } diff --git a/cakk-core/src/test/kotlin/com/cakk/core/service/shop/ShopServiceTest.kt b/cakk-core/src/test/kotlin/com/cakk/core/service/shop/ShopServiceTest.kt index 6c5a90ee..38befd30 100644 --- a/cakk-core/src/test/kotlin/com/cakk/core/service/shop/ShopServiceTest.kt +++ b/cakk-core/src/test/kotlin/com/cakk/core/service/shop/ShopServiceTest.kt @@ -66,9 +66,6 @@ internal class ShopServiceTest : MockitoTest() { @Mock private lateinit var cakeShopManageFacade: CakeShopManageFacade - @Mock - private lateinit var cakeShopViewsRedisRepository: CakeShopViewsRedisRepository - @Mock private lateinit var publisher: ApplicationEventPublisher @@ -308,7 +305,6 @@ internal class ShopServiceTest : MockitoTest() { val offset: Long = 0L val pageSize: Int = 3 val param = CakeShopSearchByViewsParam(offset, pageSize) - val cakeShopIds = listOf(1L, 2L, 3L) val cakeShops = getConstructorMonkey().giveMeBuilder(CakeShop::class.java) .set("cakeShopId", getLongFixtureGoe(1)) .set("thumbnailUrl", getStringFixtureBw(100, 200)) @@ -316,8 +312,7 @@ internal class ShopServiceTest : MockitoTest() { .set("cakeShopBio", getStringFixtureBw(1, 40)) .sampleList(3) - doReturn(cakeShopIds).`when`(cakeShopViewsRedisRepository).findTopShopIdsByOffsetAndCount(offset, pageSize.toLong()) - doReturn(cakeShops).`when`(cakeShopReadFacade).searchShopsByShopIds(cakeShopIds) + doReturn(cakeShops).`when`(cakeShopReadFacade).searchBestShops(offset, pageSize) // when val result = shopService.searchCakeShopsByCursorAndViews(param) @@ -326,8 +321,7 @@ internal class ShopServiceTest : MockitoTest() { result shouldNotBe null result.size shouldBe cakeShops.size - verify(cakeShopViewsRedisRepository, times(1)).findTopShopIdsByOffsetAndCount(offset, pageSize.toLong()) - verify(cakeShopReadFacade, times(1)).searchShopsByShopIds(cakeShopIds) + verify(cakeShopReadFacade, times(1)).searchBestShops(offset, pageSize) } @TestWithDisplayName("인기 케이크 샵 목록이 없는 경우, 빈 배열을 조회한다.") @@ -338,7 +332,7 @@ internal class ShopServiceTest : MockitoTest() { val param = CakeShopSearchByViewsParam(offset, pageSize) val cakeShopIds: List = listOf() - doReturn(cakeShopIds).`when`(cakeShopViewsRedisRepository).findTopShopIdsByOffsetAndCount(offset, pageSize.toLong()) + doReturn(cakeShopIds).`when`(cakeShopReadFacade).searchBestShops(offset, pageSize) // when val result = shopService.searchCakeShopsByCursorAndViews(param) @@ -347,8 +341,7 @@ internal class ShopServiceTest : MockitoTest() { result shouldNotBe null result.cakeShops shouldHaveSize 0 - verify(cakeShopViewsRedisRepository, times(1)).findTopShopIdsByOffsetAndCount(offset, pageSize.toLong()) - verify(cakeShopReadFacade, never()).searchShopsByShopIds(cakeShopIds) + verify(cakeShopReadFacade, times(1)).searchBestShops(offset, pageSize) } @TestWithDisplayName("케이크샵 기본 정보 수정을 한다") diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/dto/param/cake/CakeImageResponseParam.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/dto/param/cake/CakeImageResponseParam.java deleted file mode 100644 index 6dd5e69c..00000000 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/dto/param/cake/CakeImageResponseParam.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.cakk.domain.mysql.dto.param.cake; - -public record CakeImageResponseParam( - Long cakeShopId, - Long cakeId, - String cakeImageUrl -) { -} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/dto/param/cake/CakeImageWithShopInfoResponseParam.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/dto/param/cake/CakeImageWithShopInfoResponseParam.java new file mode 100644 index 00000000..866f44a5 --- /dev/null +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/dto/param/cake/CakeImageWithShopInfoResponseParam.java @@ -0,0 +1,10 @@ +package com.cakk.domain.mysql.dto.param.cake; + +public record CakeImageWithShopInfoResponseParam( + Long cakeShopId, + Long cakeId, + String cakeImageUrl, + String thumbnailUrl, + String shopName +) { +} diff --git a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/query/CakeQueryRepository.java b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/query/CakeQueryRepository.java index 611ffcce..7859969f 100644 --- a/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/query/CakeQueryRepository.java +++ b/cakk-domain/mysql/src/main/java/com/cakk/domain/mysql/repository/query/CakeQueryRepository.java @@ -31,7 +31,7 @@ import com.cakk.common.enums.Role; import com.cakk.common.enums.VerificationStatus; import com.cakk.domain.mysql.dto.param.cake.CakeDetailParam; -import com.cakk.domain.mysql.dto.param.cake.CakeImageResponseParam; +import com.cakk.domain.mysql.dto.param.cake.CakeImageWithShopInfoResponseParam; import com.cakk.domain.mysql.dto.param.tag.TagParam; import com.cakk.domain.mysql.entity.cake.Cake; import com.cakk.domain.mysql.entity.user.User; @@ -42,16 +42,18 @@ public class CakeQueryRepository { private final JPAQueryFactory queryFactory; - public List searchCakeImagesByCursorAndCategory( + public List searchCakeImagesByCursorAndCategory( final Long cakeId, final CakeDesignCategory category, final int pageSize ) { return queryFactory - .select(constructor(CakeImageResponseParam.class, + .select(constructor(CakeImageWithShopInfoResponseParam.class, cakeShop.id, cake.id, - cake.cakeImageUrl)) + cake.cakeImageUrl, + cake.cakeShop.thumbnailUrl, + cake.cakeShop.shopName)) .from(cake) .innerJoin(cakeShop) .on(cake.cakeShop.eq(cakeShop)) @@ -65,16 +67,18 @@ public List searchCakeImagesByCursorAndCategory( .fetch(); } - public List searchCakeImagesByCursorAndCakeShopId( + public List searchCakeImagesByCursorAndCakeShopId( final Long cakeId, final Long cakeShopId, final int pageSize ) { return queryFactory - .select(constructor(CakeImageResponseParam.class, + .select(constructor(CakeImageWithShopInfoResponseParam.class, cakeShop.id, cake.id, - cake.cakeImageUrl)) + cake.cakeImageUrl, + cake.cakeShop.thumbnailUrl, + cake.cakeShop.shopName)) .from(cake) .innerJoin(cakeShop) .on(cake.cakeShop.eq(cakeShop)) @@ -86,7 +90,7 @@ public List searchCakeImagesByCursorAndCakeShopId( .fetch(); } - public List searchCakeImagesByCursorAndSearchKeyword( + public List searchCakeImagesByCursorAndSearchKeyword( final Long cakeId, final String keyword, final Point location, @@ -94,10 +98,12 @@ public List searchCakeImagesByCursorAndSearchKeyword( ) { return queryFactory .select( - constructor(CakeImageResponseParam.class, + constructor(CakeImageWithShopInfoResponseParam.class, cake.cakeShop.id, cake.id, - cake.cakeImageUrl)).distinct() + cake.cakeImageUrl, + cake.cakeShop.thumbnailUrl, + cake.cakeShop.shopName)).distinct() .from(cake) .innerJoin(cake.cakeShop, cakeShop) .leftJoin(cake.cakeCategories, cakeCategory) @@ -111,12 +117,14 @@ public List searchCakeImagesByCursorAndSearchKeyword( .fetch(); } - public List searchCakeImagesByCakeIds(final List cakeIds) { + public List searchCakeImagesByCakeIds(final List cakeIds) { return queryFactory - .select(constructor(CakeImageResponseParam.class, + .select(constructor(CakeImageWithShopInfoResponseParam.class, cakeShop.id, cake.id, - cake.cakeImageUrl)) + cake.cakeImageUrl, + cake.cakeShop.thumbnailUrl, + cake.cakeShop.shopName)) .from(cake) .innerJoin(cakeShop) .on(cake.cakeShop.eq(cakeShop)) diff --git a/cakk-domain/mysql/src/test/java/com/cakk/domain/entity/user/BusinessInformationTest.kt b/cakk-domain/mysql/src/test/java/com/cakk/domain/entity/user/BusinessInformationTest.kt index c74cf428..d3a47e12 100644 --- a/cakk-domain/mysql/src/test/java/com/cakk/domain/entity/user/BusinessInformationTest.kt +++ b/cakk-domain/mysql/src/test/java/com/cakk/domain/entity/user/BusinessInformationTest.kt @@ -48,7 +48,7 @@ internal class BusinessInformationTest : DomainTest() { //given val businessInformation = getBusinessInformationFixtureWithCakeShop(VerificationStatus.PENDING) val verificationPolicy = verificationPolicy - val user = getUserFixture(Role.USER) + val user = getUserFixture() //when businessInformation.updateBusinessOwner(verificationPolicy, user)