Skip to content

Commit d1e5cce

Browse files
정동하정동하
authored andcommitted
feat(be):User도메인 1차 구현
1 parent 890e709 commit d1e5cce

File tree

10 files changed

+169
-88
lines changed

10 files changed

+169
-88
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.back.koreaTravelGuide.domain.guide.controller
2+
3+
import com.back.koreaTravelGuide.common.ApiResponse
4+
import com.back.koreaTravelGuide.domain.guide.service.GuideService
5+
import com.back.koreaTravelGuide.domain.user.dto.request.GuideUpdateRequest
6+
import com.back.koreaTravelGuide.domain.user.dto.response.GuideResponse
7+
import io.swagger.v3.oas.annotations.Operation
8+
import io.swagger.v3.oas.annotations.tags.Tag
9+
import org.springframework.http.ResponseEntity
10+
import org.springframework.security.access.prepost.PreAuthorize
11+
import org.springframework.security.core.annotation.AuthenticationPrincipal
12+
import org.springframework.web.bind.annotation.GetMapping
13+
import org.springframework.web.bind.annotation.PatchMapping
14+
import org.springframework.web.bind.annotation.PathVariable
15+
import org.springframework.web.bind.annotation.RequestBody
16+
import org.springframework.web.bind.annotation.RequestMapping
17+
import org.springframework.web.bind.annotation.RestController
18+
19+
@Tag(name = "가이드 API", description = "가이드 조회 및 프로필 관리에 대한 API")
20+
@RestController
21+
@RequestMapping("/api/guides")
22+
class GuideController(
23+
private val guideService: GuideService,
24+
) {
25+
@Operation(summary = "가이드 목록 조회")
26+
@GetMapping
27+
fun getAllGuides(): ResponseEntity<ApiResponse<List<GuideResponse>>> {
28+
val guides = guideService.getAllGuides()
29+
return ResponseEntity.ok(ApiResponse("전체 가이드 목록을 조회했습니다.", guides))
30+
}
31+
32+
@Operation(summary = "가이드 단건 조회")
33+
@GetMapping("/{guideId}")
34+
fun getGuideById(
35+
@PathVariable guideId: Long,
36+
): ResponseEntity<ApiResponse<GuideResponse>> {
37+
val guide = guideService.getGuideById(guideId)
38+
return ResponseEntity.ok(ApiResponse("가이드 정보를 성공적으로 조회했습니다.", guide))
39+
}
40+
41+
@Operation(summary = "가이드 프로필 수정")
42+
@PreAuthorize("hasRole('GUIDE')")
43+
@PatchMapping("/me")
44+
fun updateMyGuideProfile(
45+
@AuthenticationPrincipal guideId: Long,
46+
@RequestBody request: GuideUpdateRequest,
47+
): ResponseEntity<ApiResponse<GuideResponse>> {
48+
val updatedGuideProfile = guideService.updateGuideProfile(guideId, request)
49+
return ResponseEntity.ok(ApiResponse("가이드 정보가 성공적으로 수정되었습니다.", updatedGuideProfile))
50+
}
51+
}

src/main/kotlin/com/back/koreaTravelGuide/domain/user/controller/UserController.kt

Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,46 @@ import com.back.koreaTravelGuide.common.ApiResponse
44
import com.back.koreaTravelGuide.domain.user.dto.request.UserUpdateRequest
55
import com.back.koreaTravelGuide.domain.user.dto.response.UserResponse
66
import com.back.koreaTravelGuide.domain.user.service.UserService
7+
import io.swagger.v3.oas.annotations.Operation
78
import org.springframework.http.ResponseEntity
89
import org.springframework.security.core.annotation.AuthenticationPrincipal
9-
import org.springframework.security.core.userdetails.UserDetails
1010
import org.springframework.web.bind.annotation.DeleteMapping
1111
import org.springframework.web.bind.annotation.GetMapping
1212
import org.springframework.web.bind.annotation.PatchMapping
13-
import org.springframework.web.bind.annotation.PathVariable
1413
import org.springframework.web.bind.annotation.RequestBody
14+
import org.springframework.web.bind.annotation.RequestMapping
1515
import org.springframework.web.bind.annotation.RestController
1616

1717
@RestController
18+
@RequestMapping("/api/users")
1819
class UserController(
1920
private val userService: UserService,
2021
) {
21-
@GetMapping("/api/users/me")
22+
@Operation(summary = "내 정보 조회")
23+
@GetMapping("/me")
2224
fun getMyProfile(
23-
@AuthenticationPrincipal userDetails: UserDetails,
25+
@AuthenticationPrincipal userId: Long,
2426
): ResponseEntity<ApiResponse<UserResponse>> {
25-
// username 이메일로 설정할것임
26-
val userProfile = userService.getUserProfileByEmail(userDetails.username)
27-
val response = ApiResponse("내 정보를 성공적으로 조회했습니다.", userProfile)
28-
return ResponseEntity.ok(response)
27+
val userProfile = userService.getUserProfileById(userId)
28+
return ResponseEntity.ok(ApiResponse("내 정보를 성공적으로 조회했습니다.", userProfile))
2929
}
3030

31-
@PatchMapping("/api/users/me")
31+
@Operation(summary = "내 프로필 수정")
32+
@PatchMapping("/me")
3233
fun updateMyProfile(
33-
@AuthenticationPrincipal userDetails: UserDetails,
34+
@AuthenticationPrincipal userId: Long,
3435
@RequestBody request: UserUpdateRequest,
3536
): ResponseEntity<ApiResponse<UserResponse>> {
36-
val updatedProfile = userService.updateProfile(userDetails.username, request)
37-
val response = ApiResponse("정보가 성공적으로 수정되었습니다.", updatedProfile)
38-
return ResponseEntity.ok(response)
37+
val updatedProfile = userService.updateUserProfile(userId, request)
38+
return ResponseEntity.ok(ApiResponse("정보가 성공적으로 수정되었습니다.", updatedProfile))
3939
}
4040

41-
@DeleteMapping("/api/users/me")
42-
fun deleteUser(
43-
@AuthenticationPrincipal userDetails: UserDetails,
41+
@Operation(summary = "회원 탈퇴")
42+
@DeleteMapping("/me")
43+
fun deleteMe(
44+
@AuthenticationPrincipal userId: Long,
4445
): ResponseEntity<ApiResponse<Unit>> {
45-
userService.deleteUser(userDetails.username)
46+
userService.deleteUser(userId)
4647
return ResponseEntity.ok(ApiResponse("회원 탈퇴가 완료되었습니다."))
4748
}
48-
49-
@GetMapping("/api/guides")
50-
fun getAllGuides(): ResponseEntity<ApiResponse<List<UserResponse>>> {
51-
val guides = userService.getAllGuides()
52-
val response = ApiResponse("전체 가이드 목록을 조회했습니다.", guides)
53-
return ResponseEntity.ok(response)
54-
}
55-
56-
@GetMapping("/api/guides/{guideId}")
57-
fun getGuideById(
58-
@PathVariable guideId: Long,
59-
): ResponseEntity<ApiResponse<UserResponse>> {
60-
val guide = userService.getGuideById(guideId)
61-
val response = ApiResponse("가이드 정보를 성공적으로 조회했습니다.", guide)
62-
return ResponseEntity.ok(response)
63-
}
6449
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.back.koreaTravelGuide.domain.user.dto.request
2+
3+
data class GuideUpdateRequest(
4+
val nickname: String?,
5+
val profileImageUrl: String?,
6+
val location: String?,
7+
val description: String?,
8+
)

src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/request/UserUpdateRequest.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,4 @@ package com.back.koreaTravelGuide.domain.user.dto.request
33
data class UserUpdateRequest(
44
val nickname: String?,
55
val profileImageUrl: String?,
6-
val location: String?,
7-
val description: String?,
86
)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.back.koreaTravelGuide.domain.user.dto.response
2+
3+
import com.back.koreaTravelGuide.domain.user.entity.User
4+
import com.back.koreaTravelGuide.domain.user.enums.UserRole
5+
6+
data class GuideResponse(
7+
val id: Long,
8+
val email: String,
9+
val nickname: String,
10+
val profileImageUrl: String?,
11+
val role: UserRole,
12+
val location: String?,
13+
val description: String?,
14+
) {
15+
companion object {
16+
fun from(user: User): GuideResponse {
17+
return GuideResponse(
18+
id = user.id!!,
19+
email = user.email,
20+
nickname = user.nickname,
21+
profileImageUrl = user.profileImageUrl,
22+
role = user.role,
23+
location = user.location,
24+
description = user.description,
25+
)
26+
}
27+
}
28+
}

src/main/kotlin/com/back/koreaTravelGuide/domain/user/dto/response/UserResponse.kt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@ data class UserResponse(
99
val nickname: String,
1010
val profileImageUrl: String?,
1111
val role: UserRole,
12-
// Guide
13-
val location: String?,
14-
// Guide
15-
val description: String?,
1612
) {
1713
companion object {
1814
fun from(user: User): UserResponse {
@@ -22,8 +18,6 @@ data class UserResponse(
2218
nickname = user.nickname,
2319
profileImageUrl = user.profileImageUrl,
2420
role = user.role,
25-
location = user.location,
26-
description = user.description,
2721
)
2822
}
2923
}

src/main/kotlin/com/back/koreaTravelGuide/domain/user/entity/User.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,8 @@ class User(
3535
@Enumerated(EnumType.STRING)
3636
@Column(nullable = false)
3737
var role: UserRole = UserRole.USER,
38-
// GUIDE
3938
@Column
4039
var location: String? = null,
41-
// GUIDE
4240
@Column(columnDefinition = "TEXT")
4341
var description: String? = null,
4442
@CreatedDate

src/main/kotlin/com/back/koreaTravelGuide/domain/user/repository/UserRepository.kt

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,5 @@ import org.springframework.stereotype.Repository
77

88
@Repository
99
interface UserRepository : JpaRepository<User, Long> {
10-
fun findByOauthProviderAndOauthId(
11-
oauthProvider: String,
12-
oauthId: String,
13-
): User?
14-
15-
fun findByEmail(email: String): User?
16-
1710
fun findByRole(role: UserRole): List<User>
1811
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.back.koreaTravelGuide.domain.guide.service
2+
3+
import com.back.koreaTravelGuide.domain.user.dto.request.GuideUpdateRequest
4+
import com.back.koreaTravelGuide.domain.user.dto.response.GuideResponse
5+
import com.back.koreaTravelGuide.domain.user.enums.UserRole
6+
import com.back.koreaTravelGuide.domain.user.repository.UserRepository
7+
import org.springframework.stereotype.Service
8+
import org.springframework.transaction.annotation.Transactional
9+
10+
@Service
11+
class GuideService(
12+
private val userRepository: UserRepository,
13+
) {
14+
@Transactional(readOnly = true)
15+
fun getAllGuides(): List<GuideResponse> {
16+
return userRepository.findByRole(UserRole.GUIDE)
17+
.map { GuideResponse.from(it) }
18+
}
19+
20+
@Transactional(readOnly = true)
21+
fun getGuideById(guideId: Long): GuideResponse {
22+
val user =
23+
userRepository.findById(guideId)
24+
.orElseThrow { NoSuchElementException() }
25+
26+
if (user.role != UserRole.GUIDE) {
27+
throw IllegalArgumentException()
28+
}
29+
return GuideResponse.from(user)
30+
}
31+
32+
fun updateGuideProfile(
33+
guideId: Long,
34+
request: GuideUpdateRequest,
35+
): GuideResponse {
36+
val user =
37+
userRepository.findById(guideId)
38+
.orElseThrow { NoSuchElementException() }
39+
40+
if (user.role != UserRole.GUIDE) {
41+
throw IllegalAccessException()
42+
}
43+
44+
user.nickname = request.nickname ?: user.nickname
45+
user.profileImageUrl = request.profileImageUrl ?: user.profileImageUrl
46+
user.location = request.location ?: user.location
47+
user.description = request.description ?: user.description
48+
49+
return GuideResponse.from(userRepository.save(user))
50+
}
51+
}

src/main/kotlin/com/back/koreaTravelGuide/domain/user/service/UserService.kt

Lines changed: 13 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,65 +2,40 @@ package com.back.koreaTravelGuide.domain.user.service
22

33
import com.back.koreaTravelGuide.domain.user.dto.request.UserUpdateRequest
44
import com.back.koreaTravelGuide.domain.user.dto.response.UserResponse
5-
import com.back.koreaTravelGuide.domain.user.enums.UserRole
65
import com.back.koreaTravelGuide.domain.user.repository.UserRepository
7-
import jakarta.persistence.EntityNotFoundException
86
import org.springframework.stereotype.Service
97
import org.springframework.transaction.annotation.Transactional
108

119
@Service
12-
@Transactional
1310
class UserService(
1411
private val userRepository: UserRepository,
1512
) {
16-
// 회원 이메일 단건 조회
1713
@Transactional(readOnly = true)
18-
fun getUserProfileByEmail(email: String): UserResponse {
14+
fun getUserProfileById(id: Long): UserResponse {
1915
val user =
20-
userRepository.findByEmail(email)
21-
?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다")
16+
userRepository.findById(id)
17+
.orElseThrow { NoSuchElementException() }
2218
return UserResponse.from(user)
2319
}
2420

25-
// 회원 업데이트
26-
fun updateProfile(
27-
email: String,
21+
fun updateUserProfile(
22+
id: Long,
2823
request: UserUpdateRequest,
2924
): UserResponse {
3025
val user =
31-
userRepository.findByEmail(email)
32-
?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다")
26+
userRepository.findById(id)
27+
.orElseThrow { NoSuchElementException() }
3328

3429
user.nickname = request.nickname ?: user.nickname
3530
user.profileImageUrl = request.profileImageUrl ?: user.profileImageUrl
36-
user.location = request.location ?: user.location
37-
user.description = request.description ?: user.description
3831

39-
val updatedUser = userRepository.save(user)
40-
return UserResponse.from(updatedUser)
32+
return UserResponse.from(userRepository.save(user))
4133
}
4234

43-
// 회원 삭제
44-
fun deleteUser(email: String) {
45-
val user =
46-
userRepository.findByEmail(email)
47-
?: throw EntityNotFoundException("해당 이메일을 가진 사용자를 찾을 수 없습니다")
48-
userRepository.delete(user)
49-
}
50-
51-
// 전체 가이드 목록 조회
52-
@Transactional(readOnly = true)
53-
fun getAllGuides(): List<UserResponse> {
54-
return userRepository.findByRole(UserRole.GUIDE)
55-
.map { UserResponse.from(it) }
56-
}
57-
58-
// 가이드 정보 단건 조회
59-
@Transactional(readOnly = true)
60-
fun getGuideById(guideId: Long): UserResponse {
61-
val user =
62-
userRepository.findById(guideId)
63-
.orElseThrow { EntityNotFoundException("해당 ID의 사용자를 찾을 수 없습니다") }
64-
return UserResponse.from(user)
35+
fun deleteUser(id: Long) {
36+
if (!userRepository.existsById(id)) {
37+
throw NoSuchElementException()
38+
}
39+
userRepository.deleteById(id)
6540
}
6641
}

0 commit comments

Comments
 (0)