diff --git a/src/main/java/com/sparta/tdd/domain/store/controller/StoreController.java b/src/main/java/com/sparta/tdd/domain/store/controller/StoreController.java index b87d5e24..50a87e6e 100644 --- a/src/main/java/com/sparta/tdd/domain/store/controller/StoreController.java +++ b/src/main/java/com/sparta/tdd/domain/store/controller/StoreController.java @@ -5,6 +5,7 @@ import com.sparta.tdd.domain.store.dto.StoreResponseDto; import com.sparta.tdd.domain.store.enums.StoreCategory; import com.sparta.tdd.domain.store.service.StoreService; +import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; import java.net.URI; import java.util.UUID; @@ -31,6 +32,10 @@ public class StoreController { private final StoreService storeService; + @Operation( + summary = "가게 검색", + description = "키워드, 카테고리, 페이징 정보로 가게를 검색합니다. 가게의 메뉴 정보도 함께 조회됩니다." + ) @GetMapping public ResponseEntity> searchStores( @RequestParam(required = false) String keyword, @@ -41,6 +46,10 @@ public ResponseEntity> searchStores( return ResponseEntity.ok(responseDto); } + @Operation( + summary = "가게 생성", + description = "새로운 가게를 등록합니다. OWNER, MANAGER, MASTER 권한이 필요합니다." + ) @PreAuthorize("hasAnyRole('OWNER', 'MANAGER', 'MASTER')") @PostMapping public ResponseEntity createStore( @@ -56,12 +65,23 @@ public ResponseEntity createStore( .body(responseDto); } + + @Operation( + summary = "가게 조회", + description = """ + 스토어 ID를 통해 가게를 조회 합니다. + """ + ) @GetMapping("{storeId}") public ResponseEntity getStore(@PathVariable UUID storeId) { StoreResponseDto responseDto = storeService.getStore(storeId); return ResponseEntity.ok(responseDto); } + @Operation( + summary = "가게 상세 조회", + description = "스토어 ID를 통해 가게의 상세 정보를 조회합니다." + ) @PreAuthorize("hasAnyRole('OWNER', 'MANAGER', 'MASTER')") @PatchMapping("{storeId}") public ResponseEntity updateStore( @@ -73,6 +93,10 @@ public ResponseEntity updateStore( return ResponseEntity.ok().build(); } + @Operation( + summary = "가게 정보 수정", + description = "가게 정보를 수정합니다. OWNER, MANAGER, MASTER 권한이 필요합니다." + ) @PreAuthorize("hasAnyRole('OWNER', 'MANAGER', 'MASTER')") @DeleteMapping("{storeId}") public ResponseEntity deleteStore( diff --git a/src/main/java/com/sparta/tdd/domain/store/dto/StoreRequestDto.java b/src/main/java/com/sparta/tdd/domain/store/dto/StoreRequestDto.java index a75dc938..68ccd6c6 100644 --- a/src/main/java/com/sparta/tdd/domain/store/dto/StoreRequestDto.java +++ b/src/main/java/com/sparta/tdd/domain/store/dto/StoreRequestDto.java @@ -3,6 +3,7 @@ import com.sparta.tdd.domain.store.entity.Store; import com.sparta.tdd.domain.store.enums.StoreCategory; import com.sparta.tdd.domain.user.entity.User; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; @@ -10,19 +11,25 @@ import lombok.Builder; @Builder +@Schema(description = "가게 등록 요청DTO") public record StoreRequestDto( - @NotBlank + @NotBlank(message = "가게 이름은 필수입니다.") + @Schema(description = "가게 이름", example = "홍콩반점") String name, - @NotNull + @NotNull(message = "상점 카테고리는 필수입니다.") + @Schema(description = "상점 카테고리", implementation = StoreCategory.class) StoreCategory category, @Size(max = 255) + @Schema(description = "가게 설명 (최대 255자)", example = "정통 중화요리를 판매하는 홍콩반점입니다.") String description, @Pattern(regexp = "^(http|https)://.*$") - String imageUrl) { + @Schema(description = "상점 이미지 URL", example = "https://example.com/image.jpg") + String imageUrl +) { public Store toEntity(User user) { return Store.builder() diff --git a/src/main/java/com/sparta/tdd/domain/store/dto/StoreResponseDto.java b/src/main/java/com/sparta/tdd/domain/store/dto/StoreResponseDto.java index bbc55eaf..3d1f49fe 100644 --- a/src/main/java/com/sparta/tdd/domain/store/dto/StoreResponseDto.java +++ b/src/main/java/com/sparta/tdd/domain/store/dto/StoreResponseDto.java @@ -10,6 +10,8 @@ import com.sparta.tdd.domain.store.entity.QStore; import com.sparta.tdd.domain.store.entity.Store; import com.sparta.tdd.domain.store.enums.StoreCategory; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -17,16 +19,38 @@ import lombok.Builder; @Builder +@Schema(description = "가게 조회 응답 DTO") public record StoreResponseDto( + + @Schema(description = "가게 ID", example = "4f1ed1a0-e7dc-4f7d-a806-412e0e07bfbe") UUID id, + + @Schema(description = "가게 이름", example = "홍콩반점") String name, + + @Schema(description = "점주 이름", example = "TDD") String ownerName, + + @Schema(description = "상점 카테고리", implementation = StoreCategory.class) StoreCategory category, + + @Schema(description = "가게 설명", example = "정통 중화요리를 판매하는 홍콩반점입니다.") String description, + + @Schema(description = "상점 이미지 URL", example = "https://example.com/images/store1.jpg") String imageUrl, + + @Schema(description = "평균 평점", example = "4.5") BigDecimal avgRating, + + @Schema(description = "리뷰 개수", example = "128") Integer reviewCount, + + @Schema(description = "총 주문 수", example = "3456") Long orderCount, + + @ArraySchema(schema = @Schema(implementation = MenuWithStoreResponseDto.class), + arraySchema = @Schema(description = "가게에 등록된 메뉴 목록")) List menus ) { diff --git a/src/main/java/com/sparta/tdd/domain/store/dto/StoreSimpleInfoDto.java b/src/main/java/com/sparta/tdd/domain/store/dto/StoreSimpleInfoDto.java index 08564d53..0a52dd03 100644 --- a/src/main/java/com/sparta/tdd/domain/store/dto/StoreSimpleInfoDto.java +++ b/src/main/java/com/sparta/tdd/domain/store/dto/StoreSimpleInfoDto.java @@ -1,10 +1,15 @@ package com.sparta.tdd.domain.store.dto; import com.sparta.tdd.domain.store.entity.Store; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.UUID; public record StoreSimpleInfoDto( + + @Schema(description = "가게 ID", example = "4f1ed1a0-e7dc-4f7d-a806-412e0e07bfbe") UUID id, + + @Schema(description = "가게 이름", example = "홍콩반점") String storeName ) { diff --git a/src/main/java/com/sparta/tdd/domain/user/controller/UserController.java b/src/main/java/com/sparta/tdd/domain/user/controller/UserController.java index 42c94f4d..479cbb67 100644 --- a/src/main/java/com/sparta/tdd/domain/user/controller/UserController.java +++ b/src/main/java/com/sparta/tdd/domain/user/controller/UserController.java @@ -8,11 +8,9 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.web.PageableDefault; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -25,36 +23,52 @@ public class UserController { private final UserService userService; - // 회원 목록 조회 + @Operation( + summary = "모든 유저 조회", + description = """ + 모든 유저의 id, username, password, nickname, authority를 조회합니다. + MANAGER나 MASTER가 아니면 조회할 수 없습니다. + """) @GetMapping - @PreAuthorize("hasRole('ROLE_MASTER') or hasRole('ROLE_MANAGER')") - @Operation(summary = "모든 유저 조회") + @PreAuthorize("hasAnyRole('MANAGER', 'MASTER')") public ResponseEntity getAllUser(Pageable pageable) { UserPageResponseDto users = new UserPageResponseDto(userService.getAllUsers(pageable)); return ResponseEntity.ok(users); } - // 회원 정보 조회 + @Operation( + summary = "유저 조회", + description = """ + 특정 유저의 id, username, password, nickname, authority를 조회합니다. + """ + ) @GetMapping("/{userId}") - @Operation(summary = "유저 식별자로 유저 조회") public ResponseEntity getUserByUserId(@PathVariable("userId") Long userId) { UserResponseDto user = userService.getUserByUserId(userId); return ResponseEntity.ok(user); } - // 회원 닉네임 수정 + @Operation( + summary = "유저 닉네임 변경", + description = """ + 유저의 닉네임을 변경합니다. + 자신의 닉네임만 변경할 수 있습니다. + """) @PatchMapping("/{userId}/nickname") - @Operation(summary = "유저 닉네임 변경") public ResponseEntity updateUserNickname(@PathVariable("userId") Long userId, - @RequestBody UserNicknameRequestDto requestDto, + @Valid @RequestBody UserNicknameRequestDto requestDto, @AuthenticationPrincipal UserDetailsImpl userDetails) { UserResponseDto responseDto = userService.updateUserNickname(userId, userDetails.getUserId(), requestDto); return ResponseEntity.ok(responseDto); } - // 회원 비밀번호 수정 + @Operation( + summary = "유저 비밀번호 변경", + description = """ + 유저의 비밀번호를 변경합니다. 비밀번호는 8~15자의 대소문자, 숫자, 특수문자를 포함해야 합니다. + 자신의 비밀번호만 변경할 수 있습니다. + """) @PatchMapping("/{userId}/password") - @Operation(summary = "유저 비밀번호 변경") public ResponseEntity updateUserPassword(@PathVariable("userId") Long userId, @Valid @RequestBody UserPasswordRequestDto requestDto, @AuthenticationPrincipal UserDetailsImpl userDetails) { @@ -62,28 +76,38 @@ public ResponseEntity updateUserPassword(@PathVariable("userId" return ResponseEntity.ok(responseDto); } - // 회원 매니저 권한 부여 + @Operation( + summary = "유저 매니저 권한 부여", + description = """ + 유저의 권한을 매니저로 변경합니다. MASTER 권한을 가진 유저만 변경할 수 있습니다. + """) @PatchMapping("/{userId}/authority") - @PreAuthorize("hasRole('ROLE_MASTER')") - @Operation(summary = "유저 매니저 권한 부여") + @PreAuthorize("hasRole('MASTER')") public ResponseEntity updateManagerAuthorityUser(@PathVariable("userId") Long userId) { UserResponseDto responseDto = userService.grantUserManagerAuthority(userId); return ResponseEntity.ok(responseDto); } - // 회원 리뷰 목록 조회 + @Operation( + summary = "유저 리뷰 목록 조회", + description = """ + 특정 유저가 작성한 리뷰 목록을 조회합니다. 삭제한 리뷰는 조회할 수 없습니다. + """) @GetMapping("/{userId}/reviews") - @Operation(summary = "유저 리뷰 목록 조회") public ResponseEntity> getUserReviewsByUserId(@PathVariable("userId") Long userId, - @PageableDefault Pageable pageable) { + Pageable pageable) { Page responseDto = userService.getPersonalReviews(userId, pageable); return ResponseEntity.ok(responseDto); } + @Operation( + summary = "유저 주문 목록 조회", + description = """ + 특정 유저가 주문한 주문 목록을 조회합니다. 삭제한 주문은 조회할 수 없습니다. + """) @GetMapping("/{userId}/orders") - @Operation(summary = "유저 주문 목록 조회") public ResponseEntity> getUserOrdersByUserId(@PathVariable("userId") Long userId, - @PageableDefault Pageable pageable) { + Pageable pageable) { Page responseDto = userService.getPersonalOrders(userId, pageable); return ResponseEntity.ok(responseDto); } diff --git a/src/main/java/com/sparta/tdd/domain/user/dto/UserNicknameRequestDto.java b/src/main/java/com/sparta/tdd/domain/user/dto/UserNicknameRequestDto.java index 25fb1b34..36bcf079 100644 --- a/src/main/java/com/sparta/tdd/domain/user/dto/UserNicknameRequestDto.java +++ b/src/main/java/com/sparta/tdd/domain/user/dto/UserNicknameRequestDto.java @@ -1,6 +1,12 @@ package com.sparta.tdd.domain.user.dto; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; + +@Schema(description = "회원 닉네임 변경 요청 DTO") public record UserNicknameRequestDto( + @NotBlank(message = "닉네임 입력은 필수입니다") + @Schema(description = "닉네임", example = "Nick") String nickname ) { } diff --git a/src/main/java/com/sparta/tdd/domain/user/dto/UserPasswordRequestDto.java b/src/main/java/com/sparta/tdd/domain/user/dto/UserPasswordRequestDto.java index cdba03d4..840a4bb1 100644 --- a/src/main/java/com/sparta/tdd/domain/user/dto/UserPasswordRequestDto.java +++ b/src/main/java/com/sparta/tdd/domain/user/dto/UserPasswordRequestDto.java @@ -4,6 +4,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; +@Schema(description = "회원 비밀번호 변경 요청 DTO") public record UserPasswordRequestDto( @NotBlank(message = "비밀번호는 필수입니다") @Pattern( diff --git a/src/main/java/com/sparta/tdd/domain/user/repository/UserRepository.java b/src/main/java/com/sparta/tdd/domain/user/repository/UserRepository.java index 0e454ae8..8189ce5b 100644 --- a/src/main/java/com/sparta/tdd/domain/user/repository/UserRepository.java +++ b/src/main/java/com/sparta/tdd/domain/user/repository/UserRepository.java @@ -3,8 +3,6 @@ import com.sparta.tdd.domain.user.entity.User; import java.util.Optional; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository {