From c82a5aee93feff40487852220bbb4803e25978cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=8F=99=ED=95=98?= Date: Thu, 25 Sep 2025 12:04:32 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat(be):User=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?1=EC=B0=A8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup-git-hooks.sh | 0 .../domain/user/controller/UserController.kt | 64 +++++++++++++++++- .../domain/user/dto/UserRequest.kt | 7 -- .../domain/user/dto/UserResponse.kt | 8 --- .../user/dto/request/UserUpdateRequest.kt | 8 +++ .../domain/user/dto/response/UserResponse.kt | 30 +++++++++ .../domain/user/entity/User.kt | 50 +++++++++++++- .../domain/user/enums/UserRole.kt | 7 ++ .../domain/user/repository/UserRepository.kt | 18 ++++- .../domain/user/service/UserService.kt | 66 ++++++++++++++++++- 10 files changed, 235 insertions(+), 23 deletions(-) mode change 100644 => 100755 setup-git-hooks.sh delete mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/UserRequest.kt delete mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/UserResponse.kt create mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt create mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt create mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/enums/UserRole.kt diff --git a/setup-git-hooks.sh b/setup-git-hooks.sh old mode 100644 new mode 100755 diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt index 851fc2e..17be85c 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt @@ -1,4 +1,64 @@ package com.back.koreaTravelGuide.domain.user.controller -// TODO: 사용자 컨트롤러 - 사용자 관련 API 엔드포인트 (향후 구현) -class UserController +import com.back.koreaTravelGuide.common.ApiResponse +import com.back.koreaTravelGuide.domain.user.dto.request.UserUpdateRequest +import com.back.koreaTravelGuide.domain.user.dto.response.UserResponse +import com.back.koreaTravelGuide.domain.user.service.UserService +import org.springframework.http.ResponseEntity +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.security.core.userdetails.UserDetails +import org.springframework.web.bind.annotation.DeleteMapping +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RestController + +@RestController +class UserController( + private val userService: UserService, +) { + @GetMapping("/api/users/me") + fun getMyProfile( + @AuthenticationPrincipal userDetails: UserDetails, + ): ResponseEntity> { + // username 이메일로 설정할것임 + val userProfile = userService.getUserProfileByEmail(userDetails.username) + val response = ApiResponse("내 정보를 성공적으로 조회했습니다.", userProfile) + return ResponseEntity.ok(response) + } + + @PatchMapping("/api/users/me") + fun updateMyProfile( + @AuthenticationPrincipal userDetails: UserDetails, + @RequestBody request: UserUpdateRequest, + ): ResponseEntity> { + val updatedProfile = userService.updateProfile(userDetails.username, request) + val response = ApiResponse("정보가 성공적으로 수정되었습니다.", updatedProfile) + return ResponseEntity.ok(response) + } + + @DeleteMapping("/api/users/me") + fun deleteUser( + @AuthenticationPrincipal userDetails: UserDetails, + ): ResponseEntity> { + userService.deleteUser(userDetails.username) + return ResponseEntity.ok(ApiResponse("회원 탈퇴가 완료되었습니다.")) + } + + @GetMapping("/api/guides") + fun getAllGuides(): ResponseEntity>> { + val guides = userService.getAllGuides() + val response = ApiResponse("전체 가이드 목록을 조회했습니다.", guides) + return ResponseEntity.ok(response) + } + + @GetMapping("/api/guides/{guideId}") + fun getGuideById( + @PathVariable guideId: Long, + ): ResponseEntity> { + val guide = userService.getGuideById(guideId) + val response = ApiResponse("가이드 정보를 성공적으로 조회했습니다.", guide) + return ResponseEntity.ok(response) + } +} diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/UserRequest.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/UserRequest.kt deleted file mode 100644 index 5bbc2a3..0000000 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/UserRequest.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.back.koreaTravelGuide.domain.user.dto - -// TODO: 사용자 요청 DTO - 사용자 등록/수정 요청 (향후 구현) -data class UserRequest( - val username: String, - val email: String, -) diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/UserResponse.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/UserResponse.kt deleted file mode 100644 index 5011cbc..0000000 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/UserResponse.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.back.koreaTravelGuide.domain.user.dto - -// TODO: 사용자 응답 DTO - 사용자 정보 반환 (향후 구현) -data class UserResponse( - val id: Long, - val username: String, - val email: String, -) diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt new file mode 100644 index 0000000..a5f099f --- /dev/null +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt @@ -0,0 +1,8 @@ +package com.back.koreaTravelGuide.domain.user.dto.request + +data class UserUpdateRequest( + val nickname: String?, + val profileImageUrl: String?, + val location: String?, + val description: String?, +) diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt new file mode 100644 index 0000000..91e8219 --- /dev/null +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt @@ -0,0 +1,30 @@ +package com.back.koreaTravelGuide.domain.user.dto.response + +import com.back.koreaTravelGuide.domain.user.entity.User +import com.back.koreaTravelGuide.domain.user.enums.UserRole + +data class UserResponse( + val id: Long, + val email: String, + val nickname: String, + val profileImageUrl: String?, + val role: UserRole, + // Guide + val location: String?, + // Guide + val description: String?, +) { + companion object { + fun from(user: User): UserResponse { + return UserResponse( + id = user.id!!, + email = user.email, + nickname = user.nickname, + profileImageUrl = user.profileImageUrl, + role = user.role, + location = user.location, + description = user.description, + ) + } + } +} diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt index bff2382..42d41a4 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt @@ -1,4 +1,50 @@ package com.back.koreaTravelGuide.domain.user.entity -// TODO: 사용자 엔티티 - 사용자 정보 저장 (향후 구현) -class User +import com.back.koreaTravelGuide.domain.user.enums.UserRole +import jakarta.persistence.Column +import jakarta.persistence.Entity +import jakarta.persistence.EntityListeners +import jakarta.persistence.EnumType +import jakarta.persistence.Enumerated +import jakarta.persistence.GeneratedValue +import jakarta.persistence.GenerationType +import jakarta.persistence.Id +import jakarta.persistence.Table +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedDate +import org.springframework.data.jpa.domain.support.AuditingEntityListener +import java.time.LocalDateTime + +@Entity +@Table(name = "users") +@EntityListeners(AuditingEntityListener::class) +class User( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + val id: Long? = null, + @Column(name = "oauth_provider", nullable = false) + val oauthProvider: String, + @Column(name = "oauth_id", nullable = false) + val oauthId: String, + @Column(unique = true, nullable = false) + val email: String, + @Column(nullable = false) + var nickname: String, + @Column(name = "profile_image_url") + var profileImageUrl: String? = null, + @Enumerated(EnumType.STRING) + @Column(nullable = false) + var role: UserRole = UserRole.USER, + // GUIDE + @Column + var location: String? = null, + // GUIDE + @Column(columnDefinition = "TEXT") + var description: String? = null, + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + val createdAt: LocalDateTime = LocalDateTime.now(), + @LastModifiedDate + @Column(name = "last_login_at") + var lastLoginAt: LocalDateTime = LocalDateTime.now(), +) diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/enums/UserRole.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/enums/UserRole.kt new file mode 100644 index 0000000..a77bba5 --- /dev/null +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/enums/UserRole.kt @@ -0,0 +1,7 @@ +package com.back.koreaTravelGuide.domain.user.enums + +enum class UserRole { + USER, + ADMIN, + GUIDE, +} diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt index f3a9f9a..8f9456f 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt @@ -1,4 +1,18 @@ package com.back.koreaTravelGuide.domain.user.repository -// TODO: 사용자 리포지토리 - 사용자 데이터 저장/조회 (향후 구현) -interface UserRepository +import com.back.koreaTravelGuide.domain.user.entity.User +import com.back.koreaTravelGuide.domain.user.enums.UserRole +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.stereotype.Repository + +@Repository +interface UserRepository : JpaRepository { + fun findByOauthProviderAndOauthId( + oauthProvider: String, + oauthId: String, + ): User? + + fun findByEmail(email: String): User? + + fun findByRole(role: UserRole): List +} diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt index 4b029d0..2fa626f 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt @@ -1,4 +1,66 @@ package com.back.koreaTravelGuide.domain.user.service -// TODO: 사용자 서비스 - 사용자 관리 비즈니스 로직 (향후 구현) -class UserService +import com.back.koreaTravelGuide.domain.user.dto.request.UserUpdateRequest +import com.back.koreaTravelGuide.domain.user.dto.response.UserResponse +import com.back.koreaTravelGuide.domain.user.enums.UserRole +import com.back.koreaTravelGuide.domain.user.repository.UserRepository +import jakarta.persistence.EntityNotFoundException +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional +class UserService( + private val userRepository: UserRepository, +) { + // 회원 이메일 단건 조회 + @Transactional(readOnly = true) + fun getUserProfileByEmail(email: String): UserResponse { + val user = + userRepository.findByEmail(email) + ?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다") + return UserResponse.from(user) + } + + // 회원 업데이트 + fun updateProfile( + email: String, + request: UserUpdateRequest, + ): UserResponse { + val user = + userRepository.findByEmail(email) + ?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다") + + user.nickname = request.nickname ?: user.nickname + user.profileImageUrl = request.profileImageUrl ?: user.profileImageUrl + user.location = request.location ?: user.location + user.description = request.description ?: user.description + + val updatedUser = userRepository.save(user) + return UserResponse.from(updatedUser) + } + + // 회원 삭제 + fun deleteUser(email: String) { + val user = + userRepository.findByEmail(email) + ?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다") + userRepository.delete(user) + } + + // 전체 가이드 목록 조회 + @Transactional(readOnly = true) + fun getAllGuides(): List { + return userRepository.findByRole(UserRole.GUIDE) + .map { UserResponse.from(it) } + } + + // 가이드 정보 단건 조회 + @Transactional(readOnly = true) + fun getGuideById(guideId: Long): UserResponse { + val user = + userRepository.findById(guideId) + .orElseThrow { EntityNotFoundException("해당 ID의 사용자를 찾을 수 없습니다") } + return UserResponse.from(user) + } +} From 87aad8c135e1a61401190f5b8ccd5de43529bd09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=8F=99=ED=95=98?= Date: Thu, 25 Sep 2025 17:43:48 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat(be):User=EB=8F=84=EB=A9=94=EC=9D=B8=20?= =?UTF-8?q?1=EC=B0=A8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/user/controller/GuideController.kt | 51 +++++++++++++++++++ .../domain/user/controller/UserController.kt | 51 +++++++------------ .../user/dto/request/GuideUpdateRequest.kt | 8 +++ .../user/dto/request/UserUpdateRequest.kt | 2 - .../domain/user/dto/response/GuideResponse.kt | 28 ++++++++++ .../domain/user/dto/response/UserResponse.kt | 6 --- .../domain/user/entity/User.kt | 2 - .../domain/user/repository/UserRepository.kt | 7 --- .../domain/user/service/GuideService.kt | 51 +++++++++++++++++++ .../domain/user/service/UserService.kt | 51 +++++-------------- 10 files changed, 169 insertions(+), 88 deletions(-) create mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/GuideController.kt create mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/GuideUpdateRequest.kt create mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/GuideResponse.kt create mode 100644 src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/GuideService.kt diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/GuideController.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/GuideController.kt new file mode 100644 index 0000000..5944fb7 --- /dev/null +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/GuideController.kt @@ -0,0 +1,51 @@ +package com.back.koreaTravelGuide.domain.guide.controller + +import com.back.koreaTravelGuide.common.ApiResponse +import com.back.koreaTravelGuide.domain.guide.service.GuideService +import com.back.koreaTravelGuide.domain.user.dto.request.GuideUpdateRequest +import com.back.koreaTravelGuide.domain.user.dto.response.GuideResponse +import io.swagger.v3.oas.annotations.Operation +import io.swagger.v3.oas.annotations.tags.Tag +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.GetMapping +import org.springframework.web.bind.annotation.PatchMapping +import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@Tag(name = "가이드 API", description = "가이드 조회 및 프로필 관리에 대한 API") +@RestController +@RequestMapping("/api/guides") +class GuideController( + private val guideService: GuideService, +) { + @Operation(summary = "가이드 목록 조회") + @GetMapping + fun getAllGuides(): ResponseEntity>> { + val guides = guideService.getAllGuides() + return ResponseEntity.ok(ApiResponse("전체 가이드 목록을 조회했습니다.", guides)) + } + + @Operation(summary = "가이드 단건 조회") + @GetMapping("/{guideId}") + fun getGuideById( + @PathVariable guideId: Long, + ): ResponseEntity> { + val guide = guideService.getGuideById(guideId) + return ResponseEntity.ok(ApiResponse("가이드 정보를 성공적으로 조회했습니다.", guide)) + } + + @Operation(summary = "가이드 프로필 수정") + @PreAuthorize("hasRole('GUIDE')") + @PatchMapping("/me") + fun updateMyGuideProfile( + @AuthenticationPrincipal guideId: Long, + @RequestBody request: GuideUpdateRequest, + ): ResponseEntity> { + val updatedGuideProfile = guideService.updateGuideProfile(guideId, request) + return ResponseEntity.ok(ApiResponse("가이드 정보가 성공적으로 수정되었습니다.", updatedGuideProfile)) + } +} diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt index 17be85c..32d1fab 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt @@ -4,61 +4,46 @@ import com.back.koreaTravelGuide.common.ApiResponse import com.back.koreaTravelGuide.domain.user.dto.request.UserUpdateRequest import com.back.koreaTravelGuide.domain.user.dto.response.UserResponse import com.back.koreaTravelGuide.domain.user.service.UserService +import io.swagger.v3.oas.annotations.Operation import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal -import org.springframework.security.core.userdetails.UserDetails import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PatchMapping -import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController @RestController +@RequestMapping("/api/users") class UserController( private val userService: UserService, ) { - @GetMapping("/api/users/me") + @Operation(summary = "내 정보 조회") + @GetMapping("/me") fun getMyProfile( - @AuthenticationPrincipal userDetails: UserDetails, + @AuthenticationPrincipal userId: Long, ): ResponseEntity> { - // username 이메일로 설정할것임 - val userProfile = userService.getUserProfileByEmail(userDetails.username) - val response = ApiResponse("내 정보를 성공적으로 조회했습니다.", userProfile) - return ResponseEntity.ok(response) + val userProfile = userService.getUserProfileById(userId) + return ResponseEntity.ok(ApiResponse("내 정보를 성공적으로 조회했습니다.", userProfile)) } - @PatchMapping("/api/users/me") + @Operation(summary = "내 프로필 수정") + @PatchMapping("/me") fun updateMyProfile( - @AuthenticationPrincipal userDetails: UserDetails, + @AuthenticationPrincipal userId: Long, @RequestBody request: UserUpdateRequest, ): ResponseEntity> { - val updatedProfile = userService.updateProfile(userDetails.username, request) - val response = ApiResponse("정보가 성공적으로 수정되었습니다.", updatedProfile) - return ResponseEntity.ok(response) + val updatedProfile = userService.updateUserProfile(userId, request) + return ResponseEntity.ok(ApiResponse("정보가 성공적으로 수정되었습니다.", updatedProfile)) } - @DeleteMapping("/api/users/me") - fun deleteUser( - @AuthenticationPrincipal userDetails: UserDetails, + @Operation(summary = "회원 탈퇴") + @DeleteMapping("/me") + fun deleteMe( + @AuthenticationPrincipal userId: Long, ): ResponseEntity> { - userService.deleteUser(userDetails.username) + userService.deleteUser(userId) return ResponseEntity.ok(ApiResponse("회원 탈퇴가 완료되었습니다.")) } - - @GetMapping("/api/guides") - fun getAllGuides(): ResponseEntity>> { - val guides = userService.getAllGuides() - val response = ApiResponse("전체 가이드 목록을 조회했습니다.", guides) - return ResponseEntity.ok(response) - } - - @GetMapping("/api/guides/{guideId}") - fun getGuideById( - @PathVariable guideId: Long, - ): ResponseEntity> { - val guide = userService.getGuideById(guideId) - val response = ApiResponse("가이드 정보를 성공적으로 조회했습니다.", guide) - return ResponseEntity.ok(response) - } } diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/GuideUpdateRequest.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/GuideUpdateRequest.kt new file mode 100644 index 0000000..11c5200 --- /dev/null +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/GuideUpdateRequest.kt @@ -0,0 +1,8 @@ +package com.back.koreaTravelGuide.domain.user.dto.request + +data class GuideUpdateRequest( + val nickname: String?, + val profileImageUrl: String?, + val location: String?, + val description: String?, +) diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt index a5f099f..ba4e046 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt @@ -3,6 +3,4 @@ package com.back.koreaTravelGuide.domain.user.dto.request data class UserUpdateRequest( val nickname: String?, val profileImageUrl: String?, - val location: String?, - val description: String?, ) diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/GuideResponse.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/GuideResponse.kt new file mode 100644 index 0000000..c6e9b2e --- /dev/null +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/GuideResponse.kt @@ -0,0 +1,28 @@ +package com.back.koreaTravelGuide.domain.user.dto.response + +import com.back.koreaTravelGuide.domain.user.entity.User +import com.back.koreaTravelGuide.domain.user.enums.UserRole + +data class GuideResponse( + val id: Long, + val email: String, + val nickname: String, + val profileImageUrl: String?, + val role: UserRole, + val location: String?, + val description: String?, +) { + companion object { + fun from(user: User): GuideResponse { + return GuideResponse( + id = user.id!!, + email = user.email, + nickname = user.nickname, + profileImageUrl = user.profileImageUrl, + role = user.role, + location = user.location, + description = user.description, + ) + } + } +} diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt index 91e8219..925cf20 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt @@ -9,10 +9,6 @@ data class UserResponse( val nickname: String, val profileImageUrl: String?, val role: UserRole, - // Guide - val location: String?, - // Guide - val description: String?, ) { companion object { fun from(user: User): UserResponse { @@ -22,8 +18,6 @@ data class UserResponse( nickname = user.nickname, profileImageUrl = user.profileImageUrl, role = user.role, - location = user.location, - description = user.description, ) } } diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt index 42d41a4..7124b57 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt @@ -35,10 +35,8 @@ class User( @Enumerated(EnumType.STRING) @Column(nullable = false) var role: UserRole = UserRole.USER, - // GUIDE @Column var location: String? = null, - // GUIDE @Column(columnDefinition = "TEXT") var description: String? = null, @CreatedDate diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt index 8f9456f..35e6d6e 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt @@ -7,12 +7,5 @@ import org.springframework.stereotype.Repository @Repository interface UserRepository : JpaRepository { - fun findByOauthProviderAndOauthId( - oauthProvider: String, - oauthId: String, - ): User? - - fun findByEmail(email: String): User? - fun findByRole(role: UserRole): List } diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/GuideService.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/GuideService.kt new file mode 100644 index 0000000..7572c9b --- /dev/null +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/GuideService.kt @@ -0,0 +1,51 @@ +package com.back.koreaTravelGuide.domain.guide.service + +import com.back.koreaTravelGuide.domain.user.dto.request.GuideUpdateRequest +import com.back.koreaTravelGuide.domain.user.dto.response.GuideResponse +import com.back.koreaTravelGuide.domain.user.enums.UserRole +import com.back.koreaTravelGuide.domain.user.repository.UserRepository +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +class GuideService( + private val userRepository: UserRepository, +) { + @Transactional(readOnly = true) + fun getAllGuides(): List { + return userRepository.findByRole(UserRole.GUIDE) + .map { GuideResponse.from(it) } + } + + @Transactional(readOnly = true) + fun getGuideById(guideId: Long): GuideResponse { + val user = + userRepository.findById(guideId) + .orElseThrow { NoSuchElementException() } + + if (user.role != UserRole.GUIDE) { + throw IllegalArgumentException() + } + return GuideResponse.from(user) + } + + fun updateGuideProfile( + guideId: Long, + request: GuideUpdateRequest, + ): GuideResponse { + val user = + userRepository.findById(guideId) + .orElseThrow { NoSuchElementException() } + + if (user.role != UserRole.GUIDE) { + throw IllegalAccessException() + } + + user.nickname = request.nickname ?: user.nickname + user.profileImageUrl = request.profileImageUrl ?: user.profileImageUrl + user.location = request.location ?: user.location + user.description = request.description ?: user.description + + return GuideResponse.from(userRepository.save(user)) + } +} diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt index 2fa626f..6b50abb 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt @@ -2,65 +2,40 @@ package com.back.koreaTravelGuide.domain.user.service import com.back.koreaTravelGuide.domain.user.dto.request.UserUpdateRequest import com.back.koreaTravelGuide.domain.user.dto.response.UserResponse -import com.back.koreaTravelGuide.domain.user.enums.UserRole import com.back.koreaTravelGuide.domain.user.repository.UserRepository -import jakarta.persistence.EntityNotFoundException import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @Service -@Transactional class UserService( private val userRepository: UserRepository, ) { - // 회원 이메일 단건 조회 @Transactional(readOnly = true) - fun getUserProfileByEmail(email: String): UserResponse { + fun getUserProfileById(id: Long): UserResponse { val user = - userRepository.findByEmail(email) - ?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다") + userRepository.findById(id) + .orElseThrow { NoSuchElementException() } return UserResponse.from(user) } - // 회원 업데이트 - fun updateProfile( - email: String, + fun updateUserProfile( + id: Long, request: UserUpdateRequest, ): UserResponse { val user = - userRepository.findByEmail(email) - ?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다") + userRepository.findById(id) + .orElseThrow { NoSuchElementException() } user.nickname = request.nickname ?: user.nickname user.profileImageUrl = request.profileImageUrl ?: user.profileImageUrl - user.location = request.location ?: user.location - user.description = request.description ?: user.description - val updatedUser = userRepository.save(user) - return UserResponse.from(updatedUser) + return UserResponse.from(userRepository.save(user)) } - // 회원 삭제 - fun deleteUser(email: String) { - val user = - userRepository.findByEmail(email) - ?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다") - userRepository.delete(user) - } - - // 전체 가이드 목록 조회 - @Transactional(readOnly = true) - fun getAllGuides(): List { - return userRepository.findByRole(UserRole.GUIDE) - .map { UserResponse.from(it) } - } - - // 가이드 정보 단건 조회 - @Transactional(readOnly = true) - fun getGuideById(guideId: Long): UserResponse { - val user = - userRepository.findById(guideId) - .orElseThrow { EntityNotFoundException("해당 ID의 사용자를 찾을 수 없습니다") } - return UserResponse.from(user) + fun deleteUser(id: Long) { + if (!userRepository.existsById(id)) { + throw NoSuchElementException() + } + userRepository.deleteById(id) } } From 2fbde5d10f0b329d885d0f35c73896a3f1816da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EB=8F=99=ED=95=98?= Date: Thu, 25 Sep 2025 17:58:24 +0900 Subject: [PATCH 3/3] work --- .../back/koreaTravelGuide/domain/user/service/GuideService.kt | 1 + .../com/back/koreaTravelGuide/domain/user/service/UserService.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/GuideService.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/GuideService.kt index 7572c9b..4f254d7 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/GuideService.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/GuideService.kt @@ -7,6 +7,7 @@ import com.back.koreaTravelGuide.domain.user.repository.UserRepository import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional +@Transactional @Service class GuideService( private val userRepository: UserRepository, diff --git a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt index 6b50abb..027eb45 100644 --- a/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt +++ b/src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt @@ -6,6 +6,7 @@ import com.back.koreaTravelGuide.domain.user.repository.UserRepository import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional +@Transactional @Service class UserService( private val userRepository: UserRepository,