diff --git a/src/main/java/com/uspray/uspray/DTO/category/CategoryRequestDto.java b/src/main/java/com/uspray/uspray/DTO/category/CategoryRequestDto.java new file mode 100644 index 00000000..60d357e0 --- /dev/null +++ b/src/main/java/com/uspray/uspray/DTO/category/CategoryRequestDto.java @@ -0,0 +1,36 @@ +package com.uspray.uspray.DTO.category; + +import com.uspray.uspray.domain.Category; +import com.uspray.uspray.domain.Member; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.jetbrains.annotations.NotNull; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Schema(description = "카테고리 DTO") +public class CategoryRequestDto { + + @NotNull + @Schema(description = "카테고리 이름", example = "카테고리 이름") + private String name; + + @NotNull + @Schema(description = "카테고리 색상", example = "#FFFFFF") + private String color; + + + public Category toEntity(Member member) { + return Category.builder() + .name(name) + .color(color) + .member(member) + .build(); + } + +} diff --git a/src/main/java/com/uspray/uspray/DTO/category/CategoryResponseDto.java b/src/main/java/com/uspray/uspray/DTO/category/CategoryResponseDto.java new file mode 100644 index 00000000..71c4bb50 --- /dev/null +++ b/src/main/java/com/uspray/uspray/DTO/category/CategoryResponseDto.java @@ -0,0 +1,35 @@ +package com.uspray.uspray.DTO.category; + +import com.uspray.uspray.domain.Category; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@Builder +@NoArgsConstructor +public class CategoryResponseDto { + + @Schema(description = "카테고리 ID", example = "1") + private Long id; + + @Schema(description = "카테고리 소유자 ID", example = "1") + private Long memberId; + + @Schema(description = "카테고리 이름", example = "카테고리 이름") + private String name; + + @Schema(description = "카테고리 색상", example = "#FFFFFF") + private String color; + + @Schema(description = "카테고리 순서", example = "1") + private Integer order; + + public static CategoryResponseDto of(Category category) { + return new CategoryResponseDto(category.getId(), category.getMember().getId(), + category.getName(), category.getColor(), category.getOrder()); + } +} diff --git a/src/main/java/com/uspray/uspray/DTO/pray/PrayListResponseDto.java b/src/main/java/com/uspray/uspray/DTO/pray/PrayListResponseDto.java new file mode 100644 index 00000000..c961e08d --- /dev/null +++ b/src/main/java/com/uspray/uspray/DTO/pray/PrayListResponseDto.java @@ -0,0 +1,20 @@ +package com.uspray.uspray.DTO.pray; + +import com.uspray.uspray.DTO.pray.request.PrayResponseDto; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@Builder +@NoArgsConstructor +public class PrayListResponseDto { + + @Schema(description = "카테고리 ID", example = "1") + private Long categoryId; + private List prays; +} diff --git a/src/main/java/com/uspray/uspray/DTO/pray/request/PrayRequestDto.java b/src/main/java/com/uspray/uspray/DTO/pray/request/PrayRequestDto.java index 2f68ad82..01c4c161 100644 --- a/src/main/java/com/uspray/uspray/DTO/pray/request/PrayRequestDto.java +++ b/src/main/java/com/uspray/uspray/DTO/pray/request/PrayRequestDto.java @@ -1,5 +1,7 @@ package com.uspray.uspray.DTO.pray.request; +import com.uspray.uspray.Enums.PrayType; +import com.uspray.uspray.domain.Category; import com.uspray.uspray.domain.Member; import com.uspray.uspray.domain.Pray; import io.swagger.v3.oas.annotations.media.Schema; @@ -25,11 +27,17 @@ public class PrayRequestDto { @Schema(description = "기도제목 마감일", example = "2025-01-01") private LocalDate deadline; - public Pray toEntity(Member member) { + @NotNull + @Schema(description = "기도제목 카테고리", example = "1") + private Long categoryId; + + public Pray toEntity(Member member, Category category, PrayType prayType) { return Pray.builder() .content(content) .deadline(deadline) .member(member) + .category(category) + .prayType(prayType) .build(); } } diff --git a/src/main/java/com/uspray/uspray/DTO/pray/request/PrayResponseDto.java b/src/main/java/com/uspray/uspray/DTO/pray/request/PrayResponseDto.java index 91103c20..04d5bff3 100644 --- a/src/main/java/com/uspray/uspray/DTO/pray/request/PrayResponseDto.java +++ b/src/main/java/com/uspray/uspray/DTO/pray/request/PrayResponseDto.java @@ -32,9 +32,12 @@ public class PrayResponseDto { @Schema(description = "기도제목 생성일", example = "2021-01-01 00:00:00") private LocalDateTime createdAt; + @Schema(description = "기도제목 카테고리", example = "1") + private Long categoryId; + public static PrayResponseDto of(Pray pray) { return new PrayResponseDto(pray.getId(), pray.getContent(), pray.getDeadline(), - pray.getCount(), pray.getCreatedAt()); + pray.getCount(), pray.getCreatedAt(), pray.getCategory().getId()); } } diff --git a/src/main/java/com/uspray/uspray/Enums/PrayType.java b/src/main/java/com/uspray/uspray/Enums/PrayType.java new file mode 100644 index 00000000..44bf9fb2 --- /dev/null +++ b/src/main/java/com/uspray/uspray/Enums/PrayType.java @@ -0,0 +1,15 @@ +package com.uspray.uspray.Enums; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor(access = AccessLevel.PRIVATE) +public enum PrayType { + SHARED("공유 기도"), + PERSONAL("개인 기도"), + GROUP("그룹 기도"), + ; + private final String title; +} diff --git a/src/main/java/com/uspray/uspray/InitDb.java b/src/main/java/com/uspray/uspray/InitDb.java index 4f6a66fb..3fee9f7c 100644 --- a/src/main/java/com/uspray/uspray/InitDb.java +++ b/src/main/java/com/uspray/uspray/InitDb.java @@ -1,5 +1,7 @@ package com.uspray.uspray; +import com.uspray.uspray.Enums.Authority; +import com.uspray.uspray.domain.Category; import com.uspray.uspray.domain.Member; import com.uspray.uspray.domain.Pray; import java.time.LocalDate; @@ -14,40 +16,50 @@ @RequiredArgsConstructor public class InitDb { - private final InitService initService; - @PostConstruct - public void init() { - initService.dbInit(); - } + private final InitService initService; + + @PostConstruct + public void init() { + initService.dbInit(); + } + + @Component + @Transactional + @RequiredArgsConstructor + static class InitService { + + private final EntityManager em; + private final PasswordEncoder passwordEncoder; - @Component @Transactional - @RequiredArgsConstructor - static class InitService { - - private final EntityManager em; - private final PasswordEncoder passwordEncoder; - - @Transactional - public void dbInit() { - Member member = Member.builder() - .userId("test") - .password(passwordEncoder.encode("test")) - .name("홍길동") - .phone("01012345678") - .birth("2002-02-01") - .gender("female") - .build(); - em.persist(member); - - Pray pray = Pray.builder() - .content("테스트 기도") - .deadline(LocalDate.parse("2025-01-01")) - .member(member) - .build(); - - em.persist(pray); - } + public void dbInit() { + Member member = Member.builder() + .userId("test") + .password(passwordEncoder.encode("test")) + .name("홍길동") + .phone("01012345678") + .birth("2002-02-01") + .authority(Authority.ROLE_USER) + .gender("female") + .build(); + em.persist(member); + + Category category = Category.builder() + .name("기타 카테고리") + .color("#FFFFFF") + .member(member) + .build(); + em.persist(category); + Pray pray = Pray.builder() + .content("테스트 기도") + .deadline(LocalDate.parse("2025-01-01")) + .member(member) + .category(category) + .prayType(com.uspray.uspray.Enums.PrayType.PERSONAL) + .build(); + em.persist(pray); } + + } } diff --git a/src/main/java/com/uspray/uspray/controller/AuthController.java b/src/main/java/com/uspray/uspray/controller/AuthController.java index 55fd5914..5fc1da02 100644 --- a/src/main/java/com/uspray/uspray/controller/AuthController.java +++ b/src/main/java/com/uspray/uspray/controller/AuthController.java @@ -1,12 +1,13 @@ package com.uspray.uspray.controller; +import com.uspray.uspray.DTO.ApiResponseDto; +import com.uspray.uspray.DTO.auth.TokenDto; import com.uspray.uspray.DTO.auth.request.FindIdDto; import com.uspray.uspray.DTO.auth.request.FindPwDto; import com.uspray.uspray.DTO.auth.request.MemberDeleteDto; import com.uspray.uspray.DTO.auth.request.MemberLoginRequestDto; import com.uspray.uspray.DTO.auth.request.MemberRequestDto; -import com.uspray.uspray.DTO.ApiResponseDto; -import com.uspray.uspray.DTO.auth.TokenDto; +import com.uspray.uspray.DTO.auth.response.MemberResponseDto; import com.uspray.uspray.exception.SuccessStatus; import com.uspray.uspray.jwt.TokenProvider; import com.uspray.uspray.service.AuthService; @@ -19,6 +20,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirements; import io.swagger.v3.oas.annotations.tags.Tag; import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.userdetails.User; @@ -28,85 +30,84 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import com.uspray.uspray.DTO.auth.response.MemberResponseDto; - -import javax.validation.Valid; @RestController @RequestMapping("/auth") @RequiredArgsConstructor -@Tag(name = "회원 관리", description = "Auth 관련 API docs") +@Tag(name = "회원 관리", description = "Auth 관련 API") public class AuthController { - private final TokenProvider tokenProvider; - private final AuthService authService; + private final TokenProvider tokenProvider; + private final AuthService authService; - @PostMapping("/signup") - @ApiResponse( - responseCode = "201", - description = "회원가입 성공", - content = @Content(schema = @Schema(implementation = MemberResponseDto.class))) - public ApiResponseDto signup( - @RequestBody @Valid MemberRequestDto memberRequestDto) { - return ApiResponseDto.success(SuccessStatus.SIGNUP_SUCCESS, - authService.signup(memberRequestDto)); - } + @PostMapping("/signup") + @ApiResponse( + responseCode = "201", + description = "회원가입 성공", + content = @Content(schema = @Schema(implementation = MemberResponseDto.class))) + public ApiResponseDto signup( + @RequestBody @Valid MemberRequestDto memberRequestDto) { + return ApiResponseDto.success(SuccessStatus.SIGNUP_SUCCESS, + authService.signup(memberRequestDto)); + } - @PostMapping("/login") - @ApiResponse( - responseCode = "200", - description = "로그인 성공", - content = @Content(schema = @Schema(implementation = MemberResponseDto.class))) - public ApiResponseDto login( - @RequestBody MemberLoginRequestDto memberLoginRequestDto) { - return ApiResponseDto.success(SuccessStatus.LOGIN_SUCCESS, - authService.login(memberLoginRequestDto)); - } + @PostMapping("/login") + @ApiResponse( + responseCode = "200", + description = "로그인 성공", + content = @Content(schema = @Schema(implementation = MemberResponseDto.class))) + public ApiResponseDto login( + @RequestBody MemberLoginRequestDto memberLoginRequestDto) { + return ApiResponseDto.success(SuccessStatus.LOGIN_SUCCESS, + authService.login(memberLoginRequestDto)); + } - @PostMapping("/reissue") - @ApiResponse( - responseCode = "200", - description = "토큰 재발급 성공", - content = @Content(schema = @Schema(implementation = TokenDto.class))) - @SecurityRequirements({ - @SecurityRequirement(name = "JWT Auth"), - @SecurityRequirement(name = "Refresh") - }) - public ApiResponseDto reissue(@Parameter(hidden = true) HttpServletRequest request) { - String accessToken = request.getHeader("Authorization").substring(7); - String refreshToken = request.getHeader("Refresh"); - return ApiResponseDto.success(SuccessStatus.REISSUE_SUCCESS, authService.reissue(accessToken, refreshToken)); - } + @PostMapping("/reissue") + @ApiResponse( + responseCode = "200", + description = "토큰 재발급 성공", + content = @Content(schema = @Schema(implementation = TokenDto.class))) + @SecurityRequirements({ + @SecurityRequirement(name = "JWT Auth"), + @SecurityRequirement(name = "Refresh") + }) + public ApiResponseDto reissue(@Parameter(hidden = true) HttpServletRequest request) { + String accessToken = request.getHeader("Authorization").substring(7); + String refreshToken = request.getHeader("Refresh"); + return ApiResponseDto.success(SuccessStatus.REISSUE_SUCCESS, + authService.reissue(accessToken, refreshToken)); + } - @PostMapping("/find-id") - @Operation(summary = "아이디 찾기") - public ApiResponseDto findId(@RequestBody FindIdDto findIdDto) { - return ApiResponseDto.success(SuccessStatus.FIND_USER_ID_SUCCESS, - authService.findId(findIdDto)); - } + @PostMapping("/find-id") + @Operation(summary = "아이디 찾기") + public ApiResponseDto findId(@RequestBody FindIdDto findIdDto) { + return ApiResponseDto.success(SuccessStatus.FIND_USER_ID_SUCCESS, + authService.findId(findIdDto)); + } - @PostMapping("/find-pw") - @Operation(summary = "비밀번호 찾기") - public ApiResponseDto findId(@RequestBody FindPwDto findPwDto) { - authService.findPw(findPwDto); - return ApiResponseDto.success(SuccessStatus.CHANGE_USER_PW_SUCCESS); - } + @PostMapping("/find-pw") + @Operation(summary = "비밀번호 찾기") + public ApiResponseDto findId(@RequestBody FindPwDto findPwDto) { + authService.findPw(findPwDto); + return ApiResponseDto.success(SuccessStatus.CHANGE_USER_PW_SUCCESS); + } - @PostMapping("/withdrawal") - @Operation(summary = "회원 탈퇴") - @SecurityRequirement(name = "JWT Auth") - public ApiResponseDto withdrawal( - @Parameter(hidden = true) @AuthenticationPrincipal User user, @RequestBody MemberDeleteDto memberDeleteDto) { - authService.withdrawal(user.getUsername(), memberDeleteDto); - return ApiResponseDto.success(SuccessStatus.WITHDRAWAL_SUCCESS); - } + @PostMapping("/withdrawal") + @Operation(summary = "회원 탈퇴") + @SecurityRequirement(name = "JWT Auth") + public ApiResponseDto withdrawal( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @RequestBody MemberDeleteDto memberDeleteDto) { + authService.withdrawal(user.getUsername(), memberDeleteDto); + return ApiResponseDto.success(SuccessStatus.WITHDRAWAL_SUCCESS); + } - @GetMapping("/dup-check/{userId}") - @Operation(summary = "아이디 중복 체크") - public ApiResponseDto dupCheck(@PathVariable("userId") String userId) { - authService.dupCheck(userId); - return ApiResponseDto.success(SuccessStatus.CHECK_USER_ID_SUCCESS); + @GetMapping("/dup-check/{userId}") + @Operation(summary = "아이디 중복 체크") + public ApiResponseDto dupCheck(@PathVariable("userId") String userId) { + authService.dupCheck(userId); + return ApiResponseDto.success(SuccessStatus.CHECK_USER_ID_SUCCESS); - } + } } diff --git a/src/main/java/com/uspray/uspray/controller/CategoryController.java b/src/main/java/com/uspray/uspray/controller/CategoryController.java new file mode 100644 index 00000000..65bf15b1 --- /dev/null +++ b/src/main/java/com/uspray/uspray/controller/CategoryController.java @@ -0,0 +1,87 @@ +package com.uspray.uspray.controller; + +import com.uspray.uspray.DTO.ApiResponseDto; +import com.uspray.uspray.DTO.category.CategoryRequestDto; +import com.uspray.uspray.DTO.category.CategoryResponseDto; +import com.uspray.uspray.exception.SuccessStatus; +import com.uspray.uspray.service.CategoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import javax.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.userdetails.User; +import org.springframework.web.bind.annotation.DeleteMapping; +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; + +@RestController +@RequestMapping("/category") +@Tag(name = "Category API", description = "Category 관련 API") +@RequiredArgsConstructor +@SecurityRequirement(name = "JWT Auth") +public class CategoryController { + + private final CategoryService categoryService; + + @Operation(summary = "카테고리 조회") + @ApiResponse( + responseCode = "200", + description = "카테고리 조회", + content = @Content(schema = @Schema(implementation = CategoryResponseDto.class))) + @PostMapping("/{categoryId}") + public ApiResponseDto getCategory( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @Parameter(description = "카테고리 ID", required = true) @PathVariable("categoryId") Long categoryId + ) { + return ApiResponseDto.success(SuccessStatus.GET_CATEGORY_SUCCESS, + categoryService.getCategory(user.getUsername(), categoryId)); + } + + @Operation(summary = "카테고리 생성") + @ApiResponse( + responseCode = "201", + description = "카테고리 생성", + content = @Content(schema = @Schema(implementation = CategoryResponseDto.class))) + @PostMapping() + public ApiResponseDto createCategory( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @RequestBody @Valid CategoryRequestDto categoryRequestDto + ) { + return ApiResponseDto.success(SuccessStatus.CREATE_CATEGORY_SUCCESS, + categoryService.createCategory(user.getUsername(), categoryRequestDto)); + } + + @DeleteMapping("/{categoryId}") + public ApiResponseDto deleteCategory( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @Parameter(description = "카테고리 ID", required = true) @PathVariable("categoryId") Long categoryId + ) { + return ApiResponseDto.success(SuccessStatus.DELETE_CATEGORY_SUCCESS, + categoryService.deleteCategory(user.getUsername(), categoryId)); + } + + @PutMapping("/{categoryId}") + @ApiResponse( + responseCode = "200", + description = "카테고리 수정", + content = @Content(schema = @Schema(implementation = CategoryResponseDto.class))) + @Operation(summary = "카테고리 수정") + public ApiResponseDto updatePray( + @Parameter(description = "카테고리 ID", required = true) @PathVariable("categoryId") Long categoryId, + @RequestBody @Valid CategoryRequestDto categoryRequestDto, + @Parameter(hidden = true) @AuthenticationPrincipal User user + ) { + return ApiResponseDto.success(SuccessStatus.UPDATE_CATEGORY_SUCCESS, + categoryService.updateCategory(user.getUsername(), categoryId, categoryRequestDto)); + } +} diff --git a/src/main/java/com/uspray/uspray/controller/FCMController.java b/src/main/java/com/uspray/uspray/controller/FCMController.java index d99a0b2d..0621cb7a 100644 --- a/src/main/java/com/uspray/uspray/controller/FCMController.java +++ b/src/main/java/com/uspray/uspray/controller/FCMController.java @@ -4,6 +4,7 @@ import com.uspray.uspray.DTO.notification.FCMNotificationRequestDto; import com.uspray.uspray.exception.SuccessStatus; import com.uspray.uspray.service.FCMNotificationService; +import io.swagger.v3.oas.annotations.tags.Tag; import java.io.IOException; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.PostMapping; @@ -12,18 +13,20 @@ @RestController @RequiredArgsConstructor +@Tag(name = "FCM", description = "FCM 관련 API") public class FCMController { - private final FCMNotificationService fcmNotificationService; + private final FCMNotificationService fcmNotificationService; - @PostMapping("/admin/send/push") - public ApiResponseDto pushMessage(@RequestBody FCMNotificationRequestDto requestDto) throws IOException { + @PostMapping("/admin/send/push") + public ApiResponseDto pushMessage(@RequestBody FCMNotificationRequestDto requestDto) + throws IOException { - fcmNotificationService.sendMessageTo( - requestDto.getToken(), - requestDto.getTitle(), - requestDto.getBody()); - return ApiResponseDto.success(SuccessStatus.PUSH_SUCCESS); - } + fcmNotificationService.sendMessageTo( + requestDto.getToken(), + requestDto.getTitle(), + requestDto.getBody()); + return ApiResponseDto.success(SuccessStatus.PUSH_SUCCESS); + } } diff --git a/src/main/java/com/uspray/uspray/controller/PrayController.java b/src/main/java/com/uspray/uspray/controller/PrayController.java index ae304f68..cc8231df 100644 --- a/src/main/java/com/uspray/uspray/controller/PrayController.java +++ b/src/main/java/com/uspray/uspray/controller/PrayController.java @@ -2,6 +2,7 @@ import com.uspray.uspray.DTO.ApiResponseDto; +import com.uspray.uspray.DTO.pray.PrayListResponseDto; import com.uspray.uspray.DTO.pray.request.PrayRequestDto; import com.uspray.uspray.DTO.pray.request.PrayResponseDto; import com.uspray.uspray.exception.SuccessStatus; @@ -31,7 +32,7 @@ @RestController @RequestMapping("/pray") -@Tag(name = "Pray", description = "기도제목 API") +@Tag(name = "Pray", description = "기도제목 관련 API") @RequiredArgsConstructor @SecurityRequirement(name = "JWT Auth") public class PrayController { @@ -46,7 +47,7 @@ public class PrayController { content = @Content(schema = @Schema(implementation = PrayResponseDto.class))) @GetMapping() - public ApiResponseDto> getPrayList( + public ApiResponseDto> getPrayList( @Parameter(hidden = true) @AuthenticationPrincipal User user, @Parameter(description = "정렬 기준 (date, count)", required = true, example = "date") String orderType ) { diff --git a/src/main/java/com/uspray/uspray/controller/ShareController.java b/src/main/java/com/uspray/uspray/controller/ShareController.java index 4361d7af..d6eb9372 100644 --- a/src/main/java/com/uspray/uspray/controller/ShareController.java +++ b/src/main/java/com/uspray/uspray/controller/ShareController.java @@ -31,62 +31,63 @@ @RequestMapping("/share") @RequiredArgsConstructor @SecurityRequirement(name = "JWT Auth") -@Tag(name = "shared pray", description = "기도제목 공유") +@Tag(name = "shared pray", description = "기도제목 공유 관련 API") public class ShareController { - private final ShareService shareService; + private final ShareService shareService; - @GetMapping() - @ApiResponse( - responseCode = "200", - description = "공유받은 기도제목 조회 (보관함 조회)", - content = @Content(schema = @Schema(implementation = SharedPrayListResponseDto.class)) - ) - @Operation(summary = "공유받은 기도제목 조회 (보관함 조회)") - public ApiResponseDto> getSharedPrayList( - @Parameter(hidden = true) @AuthenticationPrincipal User user) { - return ApiResponseDto.success(SuccessStatus.GET_PRAY_LIST_SUCCESS, shareService.getSharedPrayList(user.getUsername())); - } + @GetMapping() + @ApiResponse( + responseCode = "200", + description = "공유받은 기도제목 조회 (보관함 조회)", + content = @Content(schema = @Schema(implementation = SharedPrayListResponseDto.class)) + ) + @Operation(summary = "공유받은 기도제목 조회 (보관함 조회)") + public ApiResponseDto> getSharedPrayList( + @Parameter(hidden = true) @AuthenticationPrincipal User user) { + return ApiResponseDto.success(SuccessStatus.GET_PRAY_LIST_SUCCESS, + shareService.getSharedPrayList(user.getUsername())); + } - @PostMapping() - @ApiResponse( - responseCode = "201", - description = "기도제목 공유", - content = @Content(schema = @Schema(implementation = PrayResponseDto.class)) - ) - @Operation(summary = "기도제목 공유") - public ApiResponseDto sharePray( - @Parameter(hidden = true) @AuthenticationPrincipal User user, - @RequestBody SharedPrayRequestDto sharedPrayRequestDto) { - shareService.sharePray(user.getUsername(), sharedPrayRequestDto); - return ApiResponseDto.success(SuccessStatus.SHARE_PRAY_SUCCESS); - } + @PostMapping() + @ApiResponse( + responseCode = "201", + description = "기도제목 공유", + content = @Content(schema = @Schema(implementation = PrayResponseDto.class)) + ) + @Operation(summary = "기도제목 공유") + public ApiResponseDto sharePray( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @RequestBody SharedPrayRequestDto sharedPrayRequestDto) { + shareService.sharePray(user.getUsername(), sharedPrayRequestDto); + return ApiResponseDto.success(SuccessStatus.SHARE_PRAY_SUCCESS); + } - @DeleteMapping() - @ApiResponse( - responseCode = "204", - description = "공유받은 기도제목 삭제", - content = @Content(schema = @Schema(implementation = PrayResponseDto.class)) - ) - @Operation(summary = "공유받은 기도제목 삭제") - public ApiResponseDto deletePray( - @Parameter(hidden = true) @AuthenticationPrincipal User user, - @RequestParam Long sharedPrayId) { - shareService.deleteSharedPray(user.getUsername(), sharedPrayId); - return ApiResponseDto.success(SuccessStatus.DELETE_PRAY_SUCCESS); - } + @DeleteMapping() + @ApiResponse( + responseCode = "204", + description = "공유받은 기도제목 삭제", + content = @Content(schema = @Schema(implementation = PrayResponseDto.class)) + ) + @Operation(summary = "공유받은 기도제목 삭제") + public ApiResponseDto deletePray( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @RequestParam Long sharedPrayId) { + shareService.deleteSharedPray(user.getUsername(), sharedPrayId); + return ApiResponseDto.success(SuccessStatus.DELETE_PRAY_SUCCESS); + } - @PostMapping("/save") - @ApiResponse( - responseCode = "201", - description = "공유받은 기도제목 저장", - content = @Content(schema = @Schema(implementation = PrayResponseDto.class)) - ) - @Operation(summary = "공유받은 기도제목 저장") - public ApiResponseDto savePray( - @Parameter(hidden = true) @AuthenticationPrincipal User user, - @RequestParam Long sharedPrayId) { - shareService.saveSharedPray(user.getUsername(), sharedPrayId); - return ApiResponseDto.success(SuccessStatus.SHARE_PRAY_AGREE_SUCCESS); - } + @PostMapping("/save") + @ApiResponse( + responseCode = "201", + description = "공유받은 기도제목 저장", + content = @Content(schema = @Schema(implementation = PrayResponseDto.class)) + ) + @Operation(summary = "공유받은 기도제목 저장") + public ApiResponseDto savePray( + @Parameter(hidden = true) @AuthenticationPrincipal User user, + @RequestParam Long sharedPrayId) { + shareService.saveSharedPray(user.getUsername(), sharedPrayId); + return ApiResponseDto.success(SuccessStatus.SHARE_PRAY_AGREE_SUCCESS); + } } diff --git a/src/main/java/com/uspray/uspray/controller/SmsController.java b/src/main/java/com/uspray/uspray/controller/SmsController.java index 74365e34..01e92046 100644 --- a/src/main/java/com/uspray/uspray/controller/SmsController.java +++ b/src/main/java/com/uspray/uspray/controller/SmsController.java @@ -20,7 +20,7 @@ @RequiredArgsConstructor @RestController -@Tag(name = "sms 전송", description = "전화번호 인증 관련 api") +@Tag(name = "sms 전송", description = "SMS 관련 API") public class SmsController { private final SmsService smsService; diff --git a/src/main/java/com/uspray/uspray/domain/Category.java b/src/main/java/com/uspray/uspray/domain/Category.java new file mode 100644 index 00000000..8147bf21 --- /dev/null +++ b/src/main/java/com/uspray/uspray/domain/Category.java @@ -0,0 +1,60 @@ +package com.uspray.uspray.domain; + +import com.uspray.uspray.DTO.category.CategoryRequestDto; +import com.uspray.uspray.common.domain.AuditingTimeEntity; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@SQLDelete(sql = "UPDATE category SET deleted = true WHERE id = ?") +@Where(clause = "deleted=false") +public class Category extends AuditingTimeEntity { + + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "category_id") + @Id + private Long id; + private String name; + private String color; + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @Column(name = "category_order") + private int order; + private final Boolean deleted = false; + + @ManyToOne + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @Builder + public Category( + Long id, + String name, + String color, + int order, + Member member + ) { + this.id = id; + this.name = name; + this.color = color; + this.order = order; + this.member = member; + } + + public void update(CategoryRequestDto categoryRequestDto) { + this.name = categoryRequestDto.getName(); + this.color = categoryRequestDto.getColor(); + } +} diff --git a/src/main/java/com/uspray/uspray/domain/Pray.java b/src/main/java/com/uspray/uspray/domain/Pray.java index b77990af..ffa0dd84 100644 --- a/src/main/java/com/uspray/uspray/domain/Pray.java +++ b/src/main/java/com/uspray/uspray/domain/Pray.java @@ -1,14 +1,19 @@ package com.uspray.uspray.domain; import com.uspray.uspray.DTO.pray.request.PrayRequestDto; +import com.uspray.uspray.Enums.PrayType; import com.uspray.uspray.common.domain.AuditingTimeEntity; import java.time.LocalDate; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; +import javax.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -24,34 +29,43 @@ public class Pray extends AuditingTimeEntity { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "pray_id") private Long id; + @ManyToOne @JoinColumn(name = "member_id", nullable = false) private Member member; private String content; - private Integer count; - private LocalDate deadline; - private final Boolean deleted = false; - private Boolean isShared = false; @Column(name = "origin_pray_id") private Long originPrayId; + @NotNull + @Enumerated(EnumType.STRING) + private PrayType prayType; + + @NotNull + @ManyToOne + @JoinColumn(name = "category_id") + private Category category; + @Builder - public Pray(Member member, String content, LocalDate deadline, Long originPrayId) { + public Pray(Member member, String content, LocalDate deadline, Long originPrayId, + Category category, PrayType prayType) { this.member = member; this.content = content; this.count = 0; this.deadline = deadline; this.originPrayId = originPrayId; this.isShared = (originPrayId != null); + this.category = category; + this.prayType = prayType; } public void update(PrayRequestDto prayRequestDto) { diff --git a/src/main/java/com/uspray/uspray/exception/ErrorStatus.java b/src/main/java/com/uspray/uspray/exception/ErrorStatus.java index 23688952..896a462f 100644 --- a/src/main/java/com/uspray/uspray/exception/ErrorStatus.java +++ b/src/main/java/com/uspray/uspray/exception/ErrorStatus.java @@ -8,31 +8,33 @@ @Getter @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public enum ErrorStatus { - - /* - * 400 BAD_REQUEST - */ - VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), - ALREADY_EXIST_ID_EXCEPTION(HttpStatus.BAD_REQUEST, "이미 사용중인 아이디입니다."), - INVALID_TOKEN_INFO_EXCEPTION(HttpStatus.BAD_REQUEST, "토큰 혹은 만료시간 설정이 잘못되었습니다."), - SENDER_RECEIVER_SAME_EXCEPTION(HttpStatus.BAD_REQUEST, "자신에게는 기도제목을 공유할 수 없습니다."), - /* - * 401 UNAUTHORIZED - */ - PRAY_UNAUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED, "해당 기도제목에 대한 권한이 없습니다."), - TOKEN_NOT_VALID_EXCEPTION(HttpStatus.UNAUTHORIZED, "유효하지 않는 토큰입니다."), - SHARE_NOT_AUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED, "기도제목을 공유할 권한이 없습니다."), - DELETE_NOT_AUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED, "기도제목을 삭제할 권한이 없습니다."), + /* + * 400 BAD_REQUEST + */ + VALIDATION_EXCEPTION(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), + ALREADY_EXIST_ID_EXCEPTION(HttpStatus.BAD_REQUEST, "이미 사용중인 아이디입니다."), + INVALID_TOKEN_INFO_EXCEPTION(HttpStatus.BAD_REQUEST, "토큰 혹은 만료시간 설정이 잘못되었습니다."), + SENDER_RECEIVER_SAME_EXCEPTION(HttpStatus.BAD_REQUEST, "자신에게는 기도제목을 공유할 수 없습니다."), + CATEGORY_ALREADY_EXIST_EXCEPTION(HttpStatus.BAD_REQUEST, "이미 존재하는 카테고리입니다."), + CATEGORY_LIMIT_EXCEPTION(HttpStatus.BAD_REQUEST, "카테고리는 최대 7개까지 생성 가능합니다."), - /** - * 404 NOT FOUND - */ - NOT_FOUND_USER_EXCEPTION(HttpStatus.NOT_FOUND, "존재하지 않는 유저입니다"), - PRAY_NOT_FOUND_EXCEPTION(HttpStatus.NOT_FOUND, "해당 기도제목을 찾을 수 없습니다."), - PRAY_ALREADY_DELETED_EXCEPTION(HttpStatus.NOT_FOUND, "원본 기도제목이 삭제되었습니다."), - NOT_FOUND_SHARED_PRAY_EXCEPTION(HttpStatus.NOT_FOUND, "해당 공유기도제목을 찾을 수 없습니다."), - ; + /* + * 401 UNAUTHORIZED + */ + PRAY_UNAUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED, "해당 기도제목에 대한 권한이 없습니다."), + TOKEN_NOT_VALID_EXCEPTION(HttpStatus.UNAUTHORIZED, "유효하지 않는 토큰입니다."), + SHARE_NOT_AUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED, "기도제목을 공유할 권한이 없습니다."), + DELETE_NOT_AUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED, "기도제목을 삭제할 권한이 없습니다."), + CATEGORY_UNAUTHORIZED_EXCEPTION(HttpStatus.UNAUTHORIZED, "해당 카테고리에 대한 권한이 없습니다."), + + /** + * 404 NOT FOUND + */ + NOT_FOUND_USER_EXCEPTION(HttpStatus.NOT_FOUND, "존재하지 않는 유저입니다"), + PRAY_NOT_FOUND_EXCEPTION(HttpStatus.NOT_FOUND, "해당 기도제목을 찾을 수 없습니다."), + PRAY_ALREADY_DELETED_EXCEPTION(HttpStatus.NOT_FOUND, "원본 기도제목이 삭제되었습니다."), + NOT_FOUND_SHARED_PRAY_EXCEPTION(HttpStatus.NOT_FOUND, "해당 공유기도제목을 찾을 수 없습니다."); private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/com/uspray/uspray/exception/SuccessStatus.java b/src/main/java/com/uspray/uspray/exception/SuccessStatus.java index 1eae018c..b2ffa490 100644 --- a/src/main/java/com/uspray/uspray/exception/SuccessStatus.java +++ b/src/main/java/com/uspray/uspray/exception/SuccessStatus.java @@ -25,13 +25,15 @@ public enum SuccessStatus { REISSUE_SUCCESS(HttpStatus.OK, "토큰 재발급에 성공했습니다."), PUSH_SUCCESS(HttpStatus.OK, "푸쉬 알림을 성공적으로 전송했습니다."), CHANGE_PUSH_AGREE_SUCCESS(HttpStatus.OK, "푸쉬 알림 설정을 성공적으로 변경했습니다."), - + UPDATE_CATEGORY_SUCCESS(HttpStatus.OK, "카테고리 수정에 성공했습니다."), + GET_CATEGORY_SUCCESS(HttpStatus.OK, "카테고리 조회에 성공했습니다."), /* * 201 created */ SIGNUP_SUCCESS(HttpStatus.CREATED, "회원가입이 완료되었습니다."), CREATE_PRAY_SUCCESS(HttpStatus.CREATED, "기도제목 생성에 성공했습니다."), + CREATE_CATEGORY_SUCCESS(HttpStatus.CREATED, "카테고리 생성에 성공했습니다."), SHARE_PRAY_SUCCESS(HttpStatus.CREATED, "기도제목 공유에 성공했습니다."), SHARE_PRAY_AGREE_SUCCESS(HttpStatus.CREATED, "기도제목 공유 수락에 성공했습니다."), @@ -40,7 +42,8 @@ public enum SuccessStatus { */ DELETE_PRAY_SUCCESS(HttpStatus.NO_CONTENT, "기도제목 삭제에 성공했습니다."), WITHDRAWAL_SUCCESS(HttpStatus.NO_CONTENT, "회원 탈퇴에 성공했습니다."), - ; + DELETE_CATEGORY_SUCCESS(HttpStatus.NO_CONTENT, "카테고리 삭제에 성공했습니다."); + private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/com/uspray/uspray/infrastructure/CategoryRepository.java b/src/main/java/com/uspray/uspray/infrastructure/CategoryRepository.java new file mode 100644 index 00000000..0b0180e7 --- /dev/null +++ b/src/main/java/com/uspray/uspray/infrastructure/CategoryRepository.java @@ -0,0 +1,18 @@ +package com.uspray.uspray.infrastructure; + +import com.uspray.uspray.domain.Category; +import com.uspray.uspray.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CategoryRepository extends JpaRepository { + + Category getCategoryById(Long categoryId); + + boolean existsCategoryByNameAndMember(String name, Member member); + + int countCategoryByMember(Member member); + + boolean existsCategoryByIdAndMember(Long categoryId, Member member); +} diff --git a/src/main/java/com/uspray/uspray/infrastructure/querydsl/PrayRepositoryImpl.java b/src/main/java/com/uspray/uspray/infrastructure/querydsl/PrayRepositoryImpl.java index 3e22ef7c..a65efd9d 100644 --- a/src/main/java/com/uspray/uspray/infrastructure/querydsl/PrayRepositoryImpl.java +++ b/src/main/java/com/uspray/uspray/infrastructure/querydsl/PrayRepositoryImpl.java @@ -1,5 +1,6 @@ package com.uspray.uspray.infrastructure.querydsl; +import static com.uspray.uspray.domain.QCategory.category; import static com.uspray.uspray.domain.QPray.pray; import com.querydsl.jpa.impl.JPAQueryFactory; @@ -17,9 +18,11 @@ public class PrayRepositoryImpl implements PrayRepositoryCustom { @Override public List findAllWithOrder(String orderType, String username) { - return queryFactory. - selectFrom(pray) - .where(pray.member.userId.eq(username), pray.deleted.eq(false)) + return queryFactory + .select(pray) + .from(pray) + .join(pray.category, category) + .where(category.member.userId.eq(username)) .orderBy(orderType.equals("date") ? pray.createdAt.desc() : pray.count.asc()) .fetch(); } diff --git a/src/main/java/com/uspray/uspray/service/AuthService.java b/src/main/java/com/uspray/uspray/service/AuthService.java index 5298d557..6a401441 100644 --- a/src/main/java/com/uspray/uspray/service/AuthService.java +++ b/src/main/java/com/uspray/uspray/service/AuthService.java @@ -8,12 +8,13 @@ import com.uspray.uspray.DTO.auth.request.MemberRequestDto; import com.uspray.uspray.DTO.auth.response.MemberResponseDto; import com.uspray.uspray.Enums.WithdrawReason; -import com.uspray.uspray.domain.Withdraw; import com.uspray.uspray.domain.Member; +import com.uspray.uspray.domain.Withdraw; import com.uspray.uspray.exception.ErrorStatus; import com.uspray.uspray.exception.model.ExistIdException; -import com.uspray.uspray.infrastructure.WithdrawRepository; +import com.uspray.uspray.exception.model.NotFoundException; import com.uspray.uspray.infrastructure.MemberRepository; +import com.uspray.uspray.infrastructure.WithdrawRepository; import com.uspray.uspray.jwt.TokenProvider; import java.util.Objects; import java.util.concurrent.TimeUnit; @@ -30,118 +31,119 @@ @RequiredArgsConstructor public class AuthService { - private final AuthenticationManagerBuilder authenticationManagerBuilder; - private final MemberRepository memberRepository; - private final PasswordEncoder passwordEncoder; - private final TokenProvider tokenProvider; - private final RedisTemplate redisTemplate; - private final WithdrawRepository withdrawRepository; - - @Transactional - public MemberResponseDto signup(MemberRequestDto memberRequestDto) { - // 핸드폰번호가 존재하거나 아이디가 존재하면 에러 - // 핸드폰 번호 또는 아이디가 이미 존재하는지 확인 - if (memberRepository.existsByUserId(memberRequestDto.getUserId())) { - throw new RuntimeException("이미 가입되어 있는 유저입니다"); - } - - Member member = memberRequestDto.toMember(passwordEncoder); - return MemberResponseDto.of(memberRepository.save(member)); + private final AuthenticationManagerBuilder authenticationManagerBuilder; + private final MemberRepository memberRepository; + private final PasswordEncoder passwordEncoder; + private final TokenProvider tokenProvider; + private final RedisTemplate redisTemplate; + private final WithdrawRepository withdrawRepository; + + @Transactional + public MemberResponseDto signup(MemberRequestDto memberRequestDto) { + // 핸드폰번호가 존재하거나 아이디가 존재하면 에러 + // 핸드폰 번호 또는 아이디가 이미 존재하는지 확인 + if (memberRepository.existsByUserId(memberRequestDto.getUserId())) { + throw new NotFoundException(ErrorStatus.ALREADY_EXIST_ID_EXCEPTION, + ErrorStatus.ALREADY_EXIST_ID_EXCEPTION.getMessage()); } - - @Transactional - public TokenDto login(MemberLoginRequestDto memberLoginRequestDto) { - // 1. Login ID/PW 를 기반으로 AuthenticationToken 생성 - UsernamePasswordAuthenticationToken authenticationToken = memberLoginRequestDto.toAuthentication(); - - // 2. 실제로 검증 (사용자 비밀번호 체크) 이 이루어지는 부분 - // authenticate 메서드가 실행이 될 때 CustomUserDetailsService 에서 만들었던 loadUserByUsername 메서드가 실행됨 - Authentication authentication = authenticationManagerBuilder.getObject() - .authenticate(authenticationToken); - - // 3. 인증 정보를 기반으로 JWT 토큰 생성 - TokenDto tokenDto = tokenProvider.generateTokenDto(authentication); - - // 4. RefreshToken 저장 - redisTemplate.opsForValue().set("RT:" + authentication.getName(), - tokenDto.getRefreshToken(), - tokenProvider.getRefreshTokenExpireTime(), - TimeUnit.MILLISECONDS); - return tokenDto; + Member member = memberRequestDto.toMember(passwordEncoder); + return MemberResponseDto.of(memberRepository.save(member)); + } + + + @Transactional + public TokenDto login(MemberLoginRequestDto memberLoginRequestDto) { + // 1. Login ID/PW 를 기반으로 AuthenticationToken 생성 + UsernamePasswordAuthenticationToken authenticationToken = memberLoginRequestDto.toAuthentication(); + + // 2. 실제로 검증 (사용자 비밀번호 체크) 이 이루어지는 부분 + // authenticate 메서드가 실행이 될 때 CustomUserDetailsService 에서 만들었던 loadUserByUsername 메서드가 실행됨 + Authentication authentication = authenticationManagerBuilder.getObject() + .authenticate(authenticationToken); + + // 3. 인증 정보를 기반으로 JWT 토큰 생성 + TokenDto tokenDto = tokenProvider.generateTokenDto(authentication); + + // 4. RefreshToken 저장 + redisTemplate.opsForValue().set("RT:" + authentication.getName(), + tokenDto.getRefreshToken(), + tokenProvider.getRefreshTokenExpireTime(), + TimeUnit.MILLISECONDS); + return tokenDto; + } + + @Transactional + public TokenDto reissue(String accessToken, String refreshToken) { + // 1. Refresh Token 검증 + if (!tokenProvider.validateToken(refreshToken)) { + throw new RuntimeException("Refresh Token 이 유효하지 않습니다"); } - @Transactional - public TokenDto reissue(String accessToken, String refreshToken) { - // 1. Refresh Token 검증 - if (!tokenProvider.validateToken(refreshToken)) { - throw new RuntimeException("Refresh Token 이 유효하지 않습니다"); - } + // 2. Access Token 에서 Member ID 가져오기 + Authentication authentication = tokenProvider.getAuthentication(accessToken); - // 2. Access Token 에서 Member ID 가져오기 - Authentication authentication = tokenProvider.getAuthentication(accessToken); - - // 3. 저장소에서 Member ID 를 기반으로 Refresh Token 값 가져오기 - String refreshTokenValue = redisTemplate.opsForValue() - .get("RT:" + authentication.getName()); - - // 4. Refresh Token 일치하는지 검사 - if (!refreshToken.equals(refreshTokenValue)) { - throw new RuntimeException("Refresh Token 이 일치하지 않습니다"); - } - - // 5. 새로운 토큰 생성 - TokenDto tokenDto = tokenProvider.generateTokenDto(authentication); - - // 6. 저장소 정보 업데이트 - redisTemplate.opsForValue().set("RT:" + authentication.getName(), - tokenDto.getRefreshToken(), - tokenProvider.getRefreshTokenExpireTime(), - TimeUnit.MILLISECONDS); - - // 토큰 발급 - return tokenDto; - } - - //Custom exception merge된 후 예외처리 하기 - public String findId(FindIdDto findIdDto) { - return memberRepository.findByNameAndPhone(findIdDto.getName(), findIdDto.getPhone()) - .getUserId(); - } + // 3. 저장소에서 Member ID 를 기반으로 Refresh Token 값 가져오기 + String refreshTokenValue = redisTemplate.opsForValue() + .get("RT:" + authentication.getName()); - @Transactional - public void findPw(FindPwDto findPwDto) { - memberRepository.findByNameAndPhoneAndUserId( - findPwDto.getName(), findPwDto.getPhone(), - findPwDto.getUserId()).changePw(passwordEncoder.encode(findPwDto.getPassword())); + // 4. Refresh Token 일치하는지 검사 + if (!refreshToken.equals(refreshTokenValue)) { + throw new RuntimeException("Refresh Token 이 일치하지 않습니다"); } - @Transactional - public void withdrawal(String userId, MemberDeleteDto memberDeleteDto) { - Member member = memberRepository.getMemberByUserId(userId); - for (WithdrawReason withdrawReason : memberDeleteDto.getWithdrawReason()) { - if (Objects.equals(withdrawReason, WithdrawReason.ETC)) { - withdrawRepository.save(Withdraw.builder() - .memberId(member.getId()) - .withdrawReason(withdrawReason) - .description(memberDeleteDto.getDescription()) - .build()); - } - withdrawRepository.save(Withdraw.builder() - .memberId(member.getId()) - .withdrawReason(withdrawReason) - .build()); - } - memberRepository.delete(member); + // 5. 새로운 토큰 생성 + TokenDto tokenDto = tokenProvider.generateTokenDto(authentication); + + // 6. 저장소 정보 업데이트 + redisTemplate.opsForValue().set("RT:" + authentication.getName(), + tokenDto.getRefreshToken(), + tokenProvider.getRefreshTokenExpireTime(), + TimeUnit.MILLISECONDS); + + // 토큰 발급 + return tokenDto; + } + + //Custom exception merge된 후 예외처리 하기 + public String findId(FindIdDto findIdDto) { + return memberRepository.findByNameAndPhone(findIdDto.getName(), findIdDto.getPhone()) + .getUserId(); + } + + @Transactional + public void findPw(FindPwDto findPwDto) { + memberRepository.findByNameAndPhoneAndUserId( + findPwDto.getName(), findPwDto.getPhone(), + findPwDto.getUserId()).changePw(passwordEncoder.encode(findPwDto.getPassword())); + } + + @Transactional + public void withdrawal(String userId, MemberDeleteDto memberDeleteDto) { + Member member = memberRepository.getMemberByUserId(userId); + for (WithdrawReason withdrawReason : memberDeleteDto.getWithdrawReason()) { + if (Objects.equals(withdrawReason, WithdrawReason.ETC)) { + withdrawRepository.save(Withdraw.builder() + .memberId(member.getId()) + .withdrawReason(withdrawReason) + .description(memberDeleteDto.getDescription()) + .build()); + } + withdrawRepository.save(Withdraw.builder() + .memberId(member.getId()) + .withdrawReason(withdrawReason) + .build()); } + memberRepository.delete(member); + } - public void dupCheck(String userId) { + public void dupCheck(String userId) { - if (memberRepository.existsByUserId(userId)) { - throw new ExistIdException(ErrorStatus.ALREADY_EXIST_ID_EXCEPTION, - ErrorStatus.ALREADY_EXIST_ID_EXCEPTION.getMessage()); - } + if (memberRepository.existsByUserId(userId)) { + throw new ExistIdException(ErrorStatus.ALREADY_EXIST_ID_EXCEPTION, + ErrorStatus.ALREADY_EXIST_ID_EXCEPTION.getMessage()); } + } } diff --git a/src/main/java/com/uspray/uspray/service/CategoryService.java b/src/main/java/com/uspray/uspray/service/CategoryService.java new file mode 100644 index 00000000..b183d208 --- /dev/null +++ b/src/main/java/com/uspray/uspray/service/CategoryService.java @@ -0,0 +1,68 @@ +package com.uspray.uspray.service; + +import com.uspray.uspray.DTO.category.CategoryRequestDto; +import com.uspray.uspray.DTO.category.CategoryResponseDto; +import com.uspray.uspray.domain.Category; +import com.uspray.uspray.domain.Member; +import com.uspray.uspray.exception.ErrorStatus; +import com.uspray.uspray.exception.model.NotFoundException; +import com.uspray.uspray.infrastructure.CategoryRepository; +import com.uspray.uspray.infrastructure.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CategoryService { + + private final MemberRepository memberRepository; + private final CategoryRepository categoryRepository; + + public CategoryResponseDto createCategory(String username, + CategoryRequestDto categoryRequestDto) { + Member member = memberRepository.getMemberByUserId(username); + if (categoryRepository.existsCategoryByNameAndMember(categoryRequestDto.getName(), member)) { + throw new NotFoundException(ErrorStatus.CATEGORY_ALREADY_EXIST_EXCEPTION, + ErrorStatus.CATEGORY_ALREADY_EXIST_EXCEPTION.getMessage()); + } + if (categoryRepository.countCategoryByMember(member) > 7) { + throw new NotFoundException(ErrorStatus.CATEGORY_LIMIT_EXCEPTION, + ErrorStatus.CATEGORY_LIMIT_EXCEPTION.getMessage()); + } + Category category = categoryRequestDto.toEntity(member); + categoryRepository.save(category); + return CategoryResponseDto.of(category); + } + + public CategoryResponseDto deleteCategory(String username, Long categoryId) { + Category category = categoryRepository.getCategoryById(categoryId); + if (categoryRepository.existsCategoryByIdAndMember(categoryId, + memberRepository.getMemberByUserId(username))) { + throw new NotFoundException(ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION, + ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION.getMessage()); + } + return CategoryResponseDto.of(category); + } + + public CategoryResponseDto updateCategory(String username, Long categoryId, + CategoryRequestDto categoryRequestDto) { + Category category = categoryRepository.getCategoryById(categoryId); + if (categoryRepository.existsCategoryByIdAndMember(categoryId, + memberRepository.getMemberByUserId(username))) { + throw new NotFoundException(ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION, + ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION.getMessage()); + } + category.update(categoryRequestDto); + return CategoryResponseDto.of(category); + } + + public CategoryResponseDto getCategory(String username, Long categoryId) { + Category category = categoryRepository.getCategoryById(categoryId); + if (categoryRepository.existsCategoryByIdAndMember(categoryId, + memberRepository.getMemberByUserId(username))) { + throw new NotFoundException(ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION, + ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION.getMessage()); + } + return CategoryResponseDto.of(category); + } +} diff --git a/src/main/java/com/uspray/uspray/service/PrayService.java b/src/main/java/com/uspray/uspray/service/PrayService.java index 797bf269..14e627d5 100644 --- a/src/main/java/com/uspray/uspray/service/PrayService.java +++ b/src/main/java/com/uspray/uspray/service/PrayService.java @@ -1,14 +1,20 @@ package com.uspray.uspray.service; +import com.uspray.uspray.DTO.pray.PrayListResponseDto; import com.uspray.uspray.DTO.pray.request.PrayRequestDto; import com.uspray.uspray.DTO.pray.request.PrayResponseDto; +import com.uspray.uspray.Enums.PrayType; +import com.uspray.uspray.domain.Category; import com.uspray.uspray.domain.Member; import com.uspray.uspray.domain.Pray; import com.uspray.uspray.exception.ErrorStatus; import com.uspray.uspray.exception.model.NotFoundException; +import com.uspray.uspray.infrastructure.CategoryRepository; import com.uspray.uspray.infrastructure.MemberRepository; import com.uspray.uspray.infrastructure.PrayRepository; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.stream.Collectors; import javax.transaction.Transactional; import lombok.RequiredArgsConstructor; @@ -20,11 +26,17 @@ public class PrayService { private final PrayRepository prayRepository; private final MemberRepository memberRepository; + private final CategoryRepository categoryRepository; @Transactional public PrayResponseDto createPray(PrayRequestDto prayRequestDto, String username) { Member member = memberRepository.getMemberByUserId(username); - Pray pray = prayRequestDto.toEntity(member); + Category category = categoryRepository.getCategoryById(prayRequestDto.getCategoryId()); + if (!Objects.equals(category.getMember().getId(), member.getId())) { + throw new NotFoundException(ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION, + ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION.getMessage()); + } + Pray pray = prayRequestDto.toEntity(member, category, PrayType.PERSONAL); prayRepository.save(pray); return PrayResponseDto.of(pray); } @@ -61,14 +73,29 @@ public PrayResponseDto updatePray(Long prayId, String username, PrayRequestDto p throw new NotFoundException(ErrorStatus.PRAY_UNAUTHORIZED_EXCEPTION, ErrorStatus.PRAY_UNAUTHORIZED_EXCEPTION.getMessage()); } + Category category = categoryRepository.getCategoryById(prayRequestDto.getCategoryId()); + if (!Objects.equals(category.getMember().getId(), pray.getMember().getId())) { + throw new NotFoundException(ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION, + ErrorStatus.CATEGORY_UNAUTHORIZED_EXCEPTION.getMessage()); + } pray.update(prayRequestDto); return PrayResponseDto.of(pray); } @Transactional - public List getPrayList(String username, String orderType) { - return prayRepository.findAllWithOrder(orderType, username).stream() - .map(PrayResponseDto::of) + public List getPrayList(String username, String orderType) { + List prays = prayRepository.findAllWithOrder(orderType, username); + + // Pray 엔티티를 categoryId를 기준으로 그룹화한 맵 생성 + Map> prayMap = prays.stream() + .collect(Collectors.groupingBy(pray -> pray.getCategory().getId())); + + // 그룹화된 맵을 PrayListResponseDto 변환하여 반환 + return prayMap.entrySet().stream() + .map(entry -> new PrayListResponseDto(entry.getKey(), + entry.getValue().stream() + .map(PrayResponseDto::of) + .collect(Collectors.toList()))) .collect(Collectors.toList()); } }