본문 바로가기

카테고리 없음

24.01.25 백오피스4

스토어에도 같은걸 추가해야 한다고 한다.

package com.sparta.dianomi.domain.store.service

import com.sparta.dianomi.common.exception.ModelNotFoundException
import com.sparta.dianomi.domain.store.dto.CreateStoreDto
import com.sparta.dianomi.domain.store.dto.StoreResponseDto
import com.sparta.dianomi.domain.store.dto.UpdateStoreDto
import com.sparta.dianomi.domain.store.model.Store
import com.sparta.dianomi.domain.store.repository.StoreRepository
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class StoreServiceImpl(
    private val storeRepository: StoreRepository,
):StoreService {
    override fun getStore(storeId: Long): StoreResponseDto {
        val findStore = storeRepository.findByIdOrNull(storeId)?: throw ModelNotFoundException("store", storeId)
        //데이터를 찾지 못함
        return findStore.toResponse();
        //dto를 제외하고 클래스를 호출할 생각 X 하면 의존성 증가.
    }
    //접근하는 user가 상점의 특정정보를 들고있어야 대조해보고



    override fun gerStoreList(): List<StoreResponseDto> {
        return storeRepository.findAll().map{it.toResponse()}
    }

    @Transactional
    override fun createStore(createStoreDto: CreateStoreDto, userId: Long): StoreResponseDto {
        return storeRepository.save(
            Store(
                name = createStoreDto.name,
                address = createStoreDto.address,
                businessNum = createStoreDto.businessNum,
                description = createStoreDto.description,
                userId = userId

            )
        ).toResponse()
    }//상호명 같은거 처리해줘야하는지?

    @Transactional
    override fun updateStore(storeId: Long, updateStoreDto: UpdateStoreDto, userId: Long): StoreResponseDto {
        _checkOwner(storeId, userId)
       val findStore = storeRepository.findByIdOrNull(storeId)?: throw ModelNotFoundException("store", storeId)
        //수정하는 user와 store를 해서
        extracted(findStore, updateStoreDto)
        // 함수로 바꾸면 entity안에 ?!

        return storeRepository.save(findStore).toResponse()
    }

    private fun extracted(
        findStore: Store,
        updateStoreDto: UpdateStoreDto
    ) {
        findStore.name = updateStoreDto.name
        findStore.address = updateStoreDto.address
        findStore.businessNum = updateStoreDto.businessNum
        findStore.description = updateStoreDto.description
    }

    @Transactional
    override fun deleteStore(storeId: Long, userId: Long) {
        _checkOwner(storeId, userId)
        val findStore = storeRepository.findByIdOrNull(storeId)?: throw ModelNotFoundException("store", storeId)
        storeRepository.delete(findStore)
    }

    private fun _checkOwner(storeId: Long,userId: Long) {
        val store : Store =storeRepository.findByIdOrNull(storeId) ?: throw Exception("Store not found");
        if (store.userId != userId) {
            throw Exception("User does not have permission to modify this store")
        }


    }
}

이렇게 스토어의 컨트롤러도 메뉴처럼 만든다. 하지만 createstore에는 _checkOwner를 넣지 않는데, 그 이유는 아직 생성이 안됬기 때문이다.

 

package com.sparta.dianomi.domain.store.controller

import com.sparta.dianomi.authority.security.UserPrincipal
import com.sparta.dianomi.domain.store.dto.CreateStoreDto
import com.sparta.dianomi.domain.store.dto.StoreResponseDto
import com.sparta.dianomi.domain.store.dto.UpdateStoreDto
import com.sparta.dianomi.domain.store.service.StoreService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RequestMapping("/stores")
@RestController
class StoreController (
    private val storeService: StoreService
){
    @GetMapping("/{storeId}")
    fun getStore(
        @PathVariable storeId:Long
    ):ResponseEntity<StoreResponseDto>{
        return ResponseEntity
            .status(HttpStatus.OK)
            .body(storeService.getStore(storeId))
    }
    //가게 단건 조회
    @GetMapping
    fun getStoreList():ResponseEntity<List<StoreResponseDto>>{
    return ResponseEntity
        .status(HttpStatus.OK)
        .body(storeService.gerStoreList())
    }
    //가게 리스트

    @PostMapping
    @PreAuthorize("hasRole('STORE')")//스토어인 유저만 만들수있슴
    fun createStore(
        @RequestBody createStoreDto: CreateStoreDto,
        @AuthenticationPrincipal user: UserPrincipal
    ):ResponseEntity<StoreResponseDto>{


        return ResponseEntity
            .status(HttpStatus.CREATED)
            .body(storeService.createStore(createStoreDto))
    }
    //가게 정보 생성

    @PutMapping("/{storeId}")
    @PreAuthorize("hasRole('STORE')")
    fun updateStore(
        @PathVariable storeId: Long,
        @RequestBody updateStoreDto: UpdateStoreDto,
        @AuthenticationPrincipal user: UserPrincipal
    ):ResponseEntity<StoreResponseDto>{
        return ResponseEntity
            .status(HttpStatus.OK)
            .body(storeService.updateStore(storeId,updateStoreDto))
    }
    //가게 정보 수정
    @DeleteMapping("/{storeId}")
    @PreAuthorize("hasRole('STORE')")
    fun deleteStore(
        @PathVariable storeId:Long,
        @AuthenticationPrincipal user: UserPrincipal
    ):ResponseEntity<Unit>{
        storeService.deleteStore(storeId)
        return ResponseEntity
            .status(HttpStatus.NO_CONTENT)
            .build()
    }
    //가게 정보 삭제
}

이랬던 녀석은

 

package com.sparta.dianomi.domain.store.controller

import com.sparta.dianomi.authority.security.UserPrincipal
import com.sparta.dianomi.domain.store.dto.CreateStoreDto
import com.sparta.dianomi.domain.store.dto.StoreResponseDto
import com.sparta.dianomi.domain.store.dto.UpdateStoreDto
import com.sparta.dianomi.domain.store.service.StoreService
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RequestMapping("/stores")
@RestController
class StoreController (
    private val storeService: StoreService
){
    @GetMapping("/{storeId}")
    fun getStore(
        @PathVariable storeId:Long
    ):ResponseEntity<StoreResponseDto>{
        return ResponseEntity
            .status(HttpStatus.OK)
            .body(storeService.getStore(storeId))
    }
    //가게 단건 조회
    @GetMapping
    fun getStoreList():ResponseEntity<List<StoreResponseDto>>{
    return ResponseEntity
        .status(HttpStatus.OK)
        .body(storeService.gerStoreList())
    }
    //가게 리스트

    @PostMapping
    @PreAuthorize("hasRole('STORE')")//스토어인 유저만 만들수있슴
    fun createStore(
        @RequestBody createStoreDto: CreateStoreDto,
        @AuthenticationPrincipal user: UserPrincipal
    ):ResponseEntity<StoreResponseDto>{


        return ResponseEntity
            .status(HttpStatus.CREATED)
            .body(storeService.createStore(createStoreDto,user.id))
    }
    //가게 정보 생성

    @PutMapping("/{storeId}")
    @PreAuthorize("hasRole('STORE')")
    fun updateStore(
        @PathVariable storeId: Long,
        @RequestBody updateStoreDto: UpdateStoreDto,
        @AuthenticationPrincipal user: UserPrincipal
    ):ResponseEntity<StoreResponseDto>{
        return ResponseEntity
            .status(HttpStatus.OK)
            .body(storeService.updateStore(storeId,updateStoreDto, user.id))
    }
    //가게 정보 수정
    @DeleteMapping("/{storeId}")
    @PreAuthorize("hasRole('STORE')")
    fun deleteStore(
        @PathVariable storeId:Long,
        @AuthenticationPrincipal user: UserPrincipal
    ):ResponseEntity<Unit>{
        storeService.deleteStore(storeId, user.id)
        return ResponseEntity
            .status(HttpStatus.NO_CONTENT)
            .build()
    }
    //가게 정보 삭제
}

이렇게 곳곳에 user.id를 집어넣는다.

 

interface StoreService {
    fun getStore(storeId:Long):StoreResponseDto
    //가게정보조회
    fun gerStoreList():List<StoreResponseDto>
    fun createStore(createStoreDto: CreateStoreDto, userId: Long):StoreResponseDto
    //신규상점입점
    fun updateStore(storeId: Long,updateStoreDto: UpdateStoreDto, userId: Long):StoreResponseDto
    //상점정보수정
    fun deleteStore(storeId: Long, userId: Long)

서비스도 이렇게 userid를 넣는다. 하지만 dto는 건드리지 않는다!

 

 

 

그러자 정상작동한다!!! 안됬던 이유는, store랑 menu는 일심동체라서 둘다 바꿔야만 하는거였다. 하나만 바꾸니 안돴던거다.

 

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMyIsImlzcyI6InRlYW0uc3BhcnRhLmNvbSIsImlhdCI6MTcwNjE3OTM4MiwiZXhwIjoxNzA3MDQzMzgyLCJyb2xlIjoiVVNFUiIsIm5pY2tOYW1lIjoibmlja25hbWUifQ.gkEkJPzVWkvZvNb5YsrxGO3T91LthvRBlbjcph38O2I

유저 저장용

 

그런데 get store가 Can not set long field cohttp://m.sparta.dianomi.domain.store.model.Store.userId to null value라는 오류를 뱉어내는게 아닌가? 머리를 싸매고 궁리한 결과, 팀장님이 userid는 나중에 추가한거라 데이터베이스에 userid부분이 그 전에 생성한 녀석들이 null값을 가지고 있어서 그런거라고 하면서 그전에 생성한 목록들을 날려버리자 정상작동했다.

 

카테고리 추가법

enum class Category {
    족발가게, 햄버거가게, 치킨가게
}

이걸 추가하고

스토어컨트롤러에

@GetMapping("/categories")
fun getCategories(): List<Category> {
    return Category.entries
}

그러면

[
  "족발가게",
  "햄버거가게",
  "치킨가게"
]

 

 

@GetMapping("/categories")
fun getCategories(): Map<String, List<String>> {
    return mapOf("data" to Category.entries.map { it.name })
}

 

 

 

 

@ManyToOne이나 @OneToMany는 @Column 필요없음!

 

cart작업을 했는데,  OndToMany, ManyToOne을 넣었더니

 

2024-01-25T17:28:07.210+09:00 ERROR 15816 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost]           : Exception Processing ErrorPage[errorCode=0, location=/error]

jakarta.servlet.ServletException: Unable to handle the Spring Security Exception because the response is already committed. 이거랑 2024-01-25T17:28:07.209+09:00 ERROR 15816 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] threw exception

org.springframework.security.access.AccessDeniedException: Access Denied이거랑 2024-01-25T17:28:07.205+09:00 ERROR 15816 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Infinite recursion (StackOverflowError)] with root cause

java.lang.StackOverflowError: null이거랑 2024-01-25T17:28:07.203+09:00  WARN 15816 --- [nio-8080-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Failure while trying to resolve exception [org.springframework.http.converter.HttpMessageNotWritableException]

java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.checkCommitted(ResponseFacade.java:485) ~[tomcat-embed-core-10.1.17.jar:10.1.17]
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:337) ~[tomcat-embed-core-10.1.17.jar:10.1.17]

 

이렇게 에러가 너무 많이 떠서 기겁하고 어떻게든 해결하려고 애쓴결과, 무한참조가 발생해서 생긴 문제였다.무한참조는 

 

@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator::class, property = "id")

 

이녀석을 @ManyToOne이나 @OneToMany을 한곳마다 박아넣어서 해결했다.

그리고

data class CartDto(
    val id: Long,
    val menuId: Long,
    val storeId: Long,
    val memberId: Long,
    val count: Int
) {
    companion object {
        fun from(cart: Cart): CartDto {
            return CartDto(
                id = cart.id,
                menuId = cart.menu.id,
                storeId = cart.store.id,
                memberId = cart.member.id,
                count = cart.count
            )
        }
    }
}

 

에도 오류가 발생했는데, 컴페니언 오브젝트에서 id쪽이 빨간줄이 떳는데, 필요한 항목 Long 발견된 항목 Long?이라길래 저위의 long를 전부 ?를 붙였다.

 

그 다음에는 카트 삭제나 조회에 다른 번호를 입력해 실패했을때 인증실패란 문구가 나오길래 인증 실패가 아닌데 그런 문구가 나오는게 어색해서 

@RestController
@RequestMapping("/carts")
class CartController(private val cartService: CartService) {

    @PostMapping
    fun addToCart(@AuthenticationPrincipal user: UserPrincipal, @RequestBody cartCreateDto: CartCreateDto): ResponseEntity<CartDto> {
        val memberId = user.id
        val cart = cartService.addToCart(cartCreateDto.menuId, cartCreateDto.storeId, memberId, cartCreateDto.count)
        return ResponseEntity.status(HttpStatus.CREATED).body(CartDto.from(cart))
    }

    @DeleteMapping("/{cartId}")
    fun removeFromCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable cartId: Long): ResponseEntity<String> {
        val memberId = user.id
        return try {
            cartService.removeFromCart(memberId, cartId)
            ResponseEntity.ok("성공적으로 제거하였습니다.")
        } catch (e: Exception) {
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("제거에 실패했습니다.")
        }
    }

    @DeleteMapping("/member/{memberId}")
    fun clearCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable memberId: String): ResponseEntity<String> {
        val memberId = user.id
        return try {
            cartService.clearCart(memberId)
            ResponseEntity.ok("성공적으로 제거하였습니다.")
        } catch (e: Exception) {
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("제거에 실패했습니다.")
        }
    }
    @GetMapping("/{cartId}")
    fun getCart(@AuthenticationPrincipal user: UserPrincipal, @PathVariable cartId: Long): ResponseEntity<out Any> {
        val memberId = user.id
        return try {
            val cart = cartService.getCart(memberId, cartId)
            if (cart.member.id != memberId) {
                ResponseEntity.status(HttpStatus.FORBIDDEN).body("You do not have permission to view this cart.")
            } else {
                ResponseEntity.ok(CartDto.from(cart))
            }
        } catch (e: Exception) {
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("조회할 수 없습니다.")
        }
    }

}

 

try catch로 성공, 실패 문구를 넣었다. 이게 훨씬 보기에도 깔끔하고 이제 인증 실패도 나오지 않는다.