Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: 카트와 유저의 연관관계 변경 #35

Merged
merged 1 commit into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/main/kotlin/store/baribari/demo/MyDataInit.kt
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,17 @@ class MyDataInit(

private fun userCreate(): List<User> {
// 37.4927015,127.0615472 400미터 예상
val customerCart = Cart()
val storeCart = Cart()
// 1차로 flush
cartRepository.saveAllAndFlush(listOf(customerCart, storeCart))

val customer =
User(
email = "[email protected]",
password = "customer",
role = Role.ROLE_CUSTOMER,
userCart = Cart(),
userCart = customerCart,
phoneNumber = "010-1234-5678",
nickname = "testuser",
position =
Expand All @@ -220,15 +225,20 @@ class MyDataInit(
email = "[email protected]",
password = "store",
role = Role.ROLE_STORE,
userCart = Cart(),
userCart = storeCart,
phoneNumber = "010-9876-5432",
nickname = "testuser2",
)
val encodedPassword2 = passwordEncoder.encode(storeOwner.password)
storeOwner.encodePassword(encodedPassword2)

userRepository.saveAll(listOf(customer, storeOwner))
userRepository.flush()
val newCustomer = userRepository.saveAndFlush(customer)
val newStoreOwner = userRepository.saveAndFlush(storeOwner)

// 2차로 save
customerCart.userId = newCustomer.id
storeCart.userId = newStoreOwner.id
cartRepository.saveAll(listOf(customerCart, storeCart))

return listOf(customer, storeOwner)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ import store.baribari.demo.domain.auth.OAuth2UserInfoFactory
import store.baribari.demo.domain.auth.ProviderType
import store.baribari.demo.domain.auth.UserPrincipal
import store.baribari.demo.domain.cart.domain.Cart
import store.baribari.demo.domain.cart.repository.CartRepository
import store.baribari.demo.domain.user.entity.User
import store.baribari.demo.domain.user.repository.UserRepository
import java.util.Locale

@Service
class CustomOAuth2UserService(
private val userRepository: UserRepository,
private val cartRepository: CartRepository,
) : DefaultOAuth2UserService() {
// 받아온 token을 분석해서 필요한 정보를 넘겨주는 역할
override fun loadUser(userRequest: OAuth2UserRequest?): OAuth2User {
Expand Down Expand Up @@ -65,17 +67,25 @@ class CustomOAuth2UserService(
userInfo: OAuth2UserInfo,
providerType: ProviderType,
): User {
// TODO: 추후 삭제하기
val cart = Cart()
cartRepository.saveAndFlush(cart)

val user =
User(
email = userInfo.email,
providerId = userInfo.id,
providerType = providerType,
profileImageUrl = userInfo.imageUrl,
userCart = Cart(),
userCart = cart,
phoneNumber = userInfo.phoneNumber ?: "",
nickname = userInfo.name,
)

return userRepository.saveAndFlush(user)
val uuidUser = userRepository.saveAndFlush(user)
cart.userId = uuidUser.id
cartRepository.saveAndFlush(cart)

return uuidUser
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ data class UserSignUpDto(
val password: String,
val phoneNumber: String?,
) {
fun toUser(): User =
fun toUser(cart: Cart): User =
User(
email = email,
nickname = nickname,
password = password,
userCart = Cart(),
userCart = cart,
phoneNumber = phoneNumber,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import store.baribari.demo.domain.auth.dto.TokenDto
import store.baribari.demo.domain.auth.dto.UserInfoDto
import store.baribari.demo.domain.auth.dto.UserLoginRequestDto
import store.baribari.demo.domain.auth.dto.UserSignUpDto
import store.baribari.demo.domain.cart.domain.Cart
import store.baribari.demo.domain.cart.repository.CartRepository
import store.baribari.demo.domain.user.entity.User
import store.baribari.demo.domain.user.repository.UserRepository
import java.util.Date
Expand All @@ -34,13 +36,21 @@ class AuthServiceImpl(
private val authTokenProvider: AuthTokenProvider,
private val passwordEncoder: BCryptPasswordEncoder,
private val redisRepository: RedisRepository,
private val cartRepository: CartRepository,
) : AuthService {
override fun saveUser(signUpDto: UserSignUpDto): UUID {
checkEmailAndUserName(signUpDto.email, signUpDto.nickname)
val user = signUpDto.toUser()
// TODO: cart 추후에 수정
val cart = Cart()
cartRepository.saveAndFlush(cart)
val user = signUpDto.toUser(cart)
val encodedPassword = passwordEncoder.encode(user.password)
user.encodePassword(encodedPassword)
return userRepository.save(user).id!!
val newUser = userRepository.save(user)
cart.userId = newUser.id
cartRepository.saveAndFlush(cart)

return newUser.id!!
}

override fun loginUser(userLoginRequestDto: UserLoginRequestDto): UserInfoDto {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package store.baribari.demo.domain.cart.domain

import store.baribari.demo.common.entity.BaseEntity
import java.util.UUID
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.GeneratedValue
Expand All @@ -14,6 +15,9 @@ class Cart(
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "cart_id")
var id: Long? = null,
// 임시로 user_id를 배치해 놓았지만 추후에는 제거하는 방식으로
@Column(name = "user_id", columnDefinition = "BINARY(16)")
var userId: UUID? = null,
@OneToMany(mappedBy = "cart")
val cartItemList: MutableList<CartItem> = mutableListOf(),
) : BaseEntity() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package store.baribari.demo.domain.cart.repository
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.Query
import store.baribari.demo.domain.cart.domain.Cart
import java.util.UUID

interface CartRepository : JpaRepository<Cart, Long> {
@Query(
Expand All @@ -15,4 +16,15 @@ interface CartRepository : JpaRepository<Cart, Long> {
""",
)
fun findByIdFetchItemListAndDosirak(id: Long): Cart?

@Query(
"""
SELECT c
FROM Cart c
LEFT JOIN FETCH c.cartItemList ci
left JOIN FETCH ci.dosirak d
WHERE c.userId = :userId
""",
)
fun findByUserId(userId: UUID): Cart?
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package store.baribari.demo.domain.cart.service

import org.springframework.context.annotation.Primary
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
Expand All @@ -23,6 +24,7 @@ import store.baribari.demo.domain.user.repository.UserRepository
const val MAX_CART_ITEM_AMOUNT = 3

@Service
@Primary
class CartServiceImpl(
private val cartRepository: CartRepository,
private val cartItemRepository: CartItemRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package store.baribari.demo.domain.cart.service

import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import store.baribari.demo.common.enums.ErrorCode
import store.baribari.demo.common.exception.ConditionConflictException
import store.baribari.demo.common.exception.EntityNotFoundException
import store.baribari.demo.domain.cart.domain.Cart
import store.baribari.demo.domain.cart.domain.CartItem
import store.baribari.demo.domain.cart.dto.request.AddItemRequestDto
import store.baribari.demo.domain.cart.dto.response.AddCartItemResponseDto
import store.baribari.demo.domain.cart.dto.response.CartInfoResponseDto
import store.baribari.demo.domain.cart.dto.response.CartItemResponseDto
import store.baribari.demo.domain.cart.dto.response.ClearCartResponseDto
import store.baribari.demo.domain.cart.dto.response.DeleteCartItemResponseDto
import store.baribari.demo.domain.cart.dto.response.UpdateItemQuantityResponseDto
import store.baribari.demo.domain.cart.repository.CartItemRepository
import store.baribari.demo.domain.cart.repository.CartRepository
import store.baribari.demo.domain.menu.repository.DosirakRepository
import store.baribari.demo.domain.user.repository.UserRepository

// userCart를 사용하지 않고 cart를 사용하는 방식의 service
@Service
class NewCartServiceImpl(
private val userRepository: UserRepository,
private val cartRepository: CartRepository,
private val cartItemRepository: CartItemRepository,
private val dosirakRepository: DosirakRepository,
) : CartService {
@Transactional(readOnly = true)
override fun getCart(username: String): CartInfoResponseDto {
// 유저만 가져오기
val user =
userRepository.findByEmail(username)
?: throw EntityNotFoundException("$username 이라는 유저는 존재하지 않습니다.")
// itemlist를 조회할 때 fetch join으로 내부에 있는 dosiraklist의 정보를 가져온다.
// querydsl의 시간인가?
// 아니면 entity graph를 사용해야하나? -> 이게 답인거 같다.
// 일단 itemlist를 가져오면서
val cart =
cartRepository.findByUserId(user.id!!)
?: throw EntityNotFoundException("해당하는 장바구니가 존재하지 않습니다.")

val itemList = user.userCart.cartItemList

return CartInfoResponseDto(
cartId = user.userCart.id!!,
items = itemList.map { CartItemResponseDto.fromCartItem(it) },
price = itemList.sumOf { it.dosirak.price * it.count },
)
}

@Transactional
override fun addItem(
username: String,
request: AddItemRequestDto,
): AddCartItemResponseDto {
val itemMap = request.items.associate { it.dosirakId to it.amount }

val user =
userRepository.findByEmailFetchCart(username)
?: throw EntityNotFoundException("$username 이라는 유저는 존재하지 않습니다.")

val userCart =
cartRepository.findByIdFetchItemListAndDosirak(user.userCart.id!!)
?: throw EntityNotFoundException("해당하는 장바구니가 존재하지 않습니다.")

// 0으로 초기화
initializeItemZero(itemMap, userCart)
// 기존에 있던 아이템에 더하기
plusItemAmount(itemMap, userCart.cartItemList)
// 3개 넘는지 점검
checkAmountLimit(userCart.cartItemList)

return AddCartItemResponseDto(
itemKindAmount = userCart.cartItemList.size,
)
}

private fun checkAmountLimit(cartItemList: MutableList<CartItem>) {
require(cartItemList.all { it.count <= MAX_CART_ITEM_AMOUNT }) {
throw ConditionConflictException(ErrorCode.CONDITION_NOT_FULFILLED, "카트에 담긴 도시락의 개수는 한 품목당 3개를 넘을 수 없습니다.")
}
}

private fun initializeItemZero(
itemMap: Map<Long, Int>,
cart: Cart,
) {
val cartItems = cart.cartItemList
val dosirakIdSet = cartItems.map { it.dosirak.id!! }.toSet() // 도시락 id의 목록을 나타냄
val itemListToAdd = mutableListOf<CartItem>()

itemMap
.filterNot { it.key in dosirakIdSet }
.mapNotNull { filteredMap ->
dosirakRepository.findByIdOrNull(filteredMap.key)?.let { dosirak ->
itemListToAdd.add(
CartItem(dosirak = dosirak, cart = cart, count = 0),
)
} ?: throw EntityNotFoundException("${filteredMap.key}라는 도시락이 존재하지 않습니다.")
}

cartItemRepository.saveAll(itemListToAdd)
cartItems.addAll(itemListToAdd)
}

private fun plusItemAmount(
itemMap: Map<Long, Int>,
cartItems: MutableList<CartItem>,
) {
cartItems
.filter { it.dosirak.id in itemMap.keys }
.forEach { it.count += itemMap[it.dosirak.id!!]!! }
}

@Transactional
override fun updateItemQuantity(
username: String,
itemId: Long,
quantity: Int,
): UpdateItemQuantityResponseDto {
val user =
userRepository.findByEmailFetchCart(username)
?: throw EntityNotFoundException("$username 이라는 유저는 존재하지 않습니다.")

val userCart =
cartRepository.findByIdFetchItemListAndDosirak(user.userCart.id!!)
?: throw EntityNotFoundException("해당하는 장바구니가 존재하지 않습니다.")

val targetCartItem =
userCart.cartItemList.find { it.dosirak.id == itemId }
?: throw EntityNotFoundException("해당 아이템이 존재하지 않습니다.")

targetCartItem.count = quantity

return UpdateItemQuantityResponseDto(
itemId = itemId,
quantity = quantity,
)
}

@Transactional
override fun deleteItem(
username: String,
itemId: Long,
): DeleteCartItemResponseDto {
val user =
userRepository.findByEmailFetchCart(username)
?: throw EntityNotFoundException("$username 이라는 유저는 존재하지 않습니다.")

val userCart =
cartRepository.findByIdFetchItemListAndDosirak(user.userCart.id!!)
?: throw EntityNotFoundException("해당하는 장바구니가 존재하지 않습니다.")

val cartItems = userCart.cartItemList

val targetCartItem =
cartItems.find { it.dosirak.id == itemId }
?: throw EntityNotFoundException("해당 아이템이 존재하지 않습니다.")

cartItems.remove(targetCartItem)
cartItemRepository.delete(targetCartItem)

return DeleteCartItemResponseDto(
itemId = itemId,
)
}

@Transactional
override fun clearCart(username: String): ClearCartResponseDto {
val user =
userRepository.findByEmailFetchCart(username)
?: throw EntityNotFoundException("$username 이라는 유저는 존재하지 않습니다.")

val userCart =
cartRepository.findByIdFetchItemListAndDosirak(user.userCart.id!!)
?: throw EntityNotFoundException("해당하는 장바구니가 존재하지 않습니다.")

cartItemRepository.deleteAll(userCart.cartItemList)
userCart.cartItemList.clear()

return ClearCartResponseDto(
user.userCart.id!!,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import store.baribari.demo.domain.order.entity.Order
import store.baribari.demo.domain.store.entity.LikeStore
import store.baribari.demo.domain.store.entity.Store
import java.util.UUID
import javax.persistence.CascadeType
import javax.persistence.Column
import javax.persistence.Embedded
import javax.persistence.Entity
Expand Down Expand Up @@ -49,7 +48,7 @@ class User(
@Embedded
var position: Position = Position(0.0, 0.0),
// 장바구니
@OneToOne(fetch = FetchType.LAZY, cascade = [CascadeType.PERSIST], optional = false)
@OneToOne(fetch = FetchType.LAZY, optional = false)
var userCart: Cart,
// 프로필 이미지
var profileImageUrl: String? = null,
Expand Down
Loading