Skip to content
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
Empty file modified setup-git-hooks.sh
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
@@ -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<ApiResponse<List<GuideResponse>>> {
val guides = guideService.getAllGuides()
return ResponseEntity.ok(ApiResponse("전체 가이드 목록을 조회했습니다.", guides))
}

@Operation(summary = "가이드 단건 조회")
@GetMapping("/{guideId}")
fun getGuideById(
@PathVariable guideId: Long,
): ResponseEntity<ApiResponse<GuideResponse>> {
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<ApiResponse<GuideResponse>> {
val updatedGuideProfile = guideService.updateGuideProfile(guideId, request)
return ResponseEntity.ok(ApiResponse("가이드 정보가 성공적으로 수정되었습니다.", updatedGuideProfile))
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,49 @@
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 io.swagger.v3.oas.annotations.Operation
import org.springframework.http.ResponseEntity
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.PatchMapping
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,
) {
@Operation(summary = "내 정보 조회")
@GetMapping("/me")
fun getMyProfile(
@AuthenticationPrincipal userId: Long,
): ResponseEntity<ApiResponse<UserResponse>> {
val userProfile = userService.getUserProfileById(userId)
return ResponseEntity.ok(ApiResponse("내 정보를 성공적으로 조회했습니다.", userProfile))
}

@Operation(summary = "내 프로필 수정")
@PatchMapping("/me")
fun updateMyProfile(
@AuthenticationPrincipal userId: Long,
@RequestBody request: UserUpdateRequest,
): ResponseEntity<ApiResponse<UserResponse>> {
val updatedProfile = userService.updateUserProfile(userId, request)
return ResponseEntity.ok(ApiResponse("정보가 성공적으로 수정되었습니다.", updatedProfile))
}

@Operation(summary = "회원 탈퇴")
@DeleteMapping("/me")
fun deleteMe(
@AuthenticationPrincipal userId: Long,
): ResponseEntity<ApiResponse<Unit>> {
userService.deleteUser(userId)
return ResponseEntity.ok(ApiResponse("회원 탈퇴가 완료되었습니다."))
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.back.koreaTravelGuide.domain.user.dto.request

data class UserUpdateRequest(
val nickname: String?,
val profileImageUrl: String?,
)
Original file line number Diff line number Diff line change
@@ -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,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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,
) {
companion object {
fun from(user: User): UserResponse {
return UserResponse(
id = user.id!!,
email = user.email,
nickname = user.nickname,
profileImageUrl = user.profileImageUrl,
role = user.role,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,48 @@
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,
@Column
var location: String? = null,
@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(),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.back.koreaTravelGuide.domain.user.enums

enum class UserRole {
USER,
ADMIN,
GUIDE,
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
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<User, Long> {
fun findByRole(role: UserRole): List<User>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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

@Transactional
@Service
class GuideService(
private val userRepository: UserRepository,
) {
@Transactional(readOnly = true)
fun getAllGuides(): List<GuideResponse> {
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))
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
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.repository.UserRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Transactional
@Service
class UserService(
private val userRepository: UserRepository,
) {
@Transactional(readOnly = true)
fun getUserProfileById(id: Long): UserResponse {
val user =
userRepository.findById(id)
.orElseThrow { NoSuchElementException() }
return UserResponse.from(user)
}

fun updateUserProfile(
id: Long,
request: UserUpdateRequest,
): UserResponse {
val user =
userRepository.findById(id)
.orElseThrow { NoSuchElementException() }

user.nickname = request.nickname ?: user.nickname
user.profileImageUrl = request.profileImageUrl ?: user.profileImageUrl

return UserResponse.from(userRepository.save(user))
}

fun deleteUser(id: Long) {
if (!userRepository.existsById(id)) {
throw NoSuchElementException()
}
userRepository.deleteById(id)
}
}