diff --git a/bean/umc10th/.gitignore b/bean/umc10th/.gitignore index ac2055b..5cbf6be 100644 --- a/bean/umc10th/.gitignore +++ b/bean/umc10th/.gitignore @@ -1,7 +1,6 @@ HELP.md .gradle -.gradle-user-home/ -.gradle-user-home-2/ +.gradle-user-home*/ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/controller/MemberController.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/controller/MemberController.java index a223397..06db769 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/controller/MemberController.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/controller/MemberController.java @@ -2,31 +2,30 @@ import com.example.umc10th.domain.member.dto.MemberReqDto; import com.example.umc10th.domain.member.dto.MemberResDto; +import com.example.umc10th.domain.member.exception.code.MemberSuccessCode; import com.example.umc10th.domain.member.service.MemberService; +import com.example.umc10th.global.apiPayload.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/members") +@RequestMapping("/api/v1/users") @RequiredArgsConstructor public class MemberController { private final MemberService memberService; - @GetMapping("/{memberId}") - public ResponseEntity getMember(@PathVariable Long memberId) { - return ResponseEntity.ok(memberService.getMember(memberId)); - } + @PostMapping("/me") + public ResponseEntity> getMyPage( + @Valid @RequestBody MemberReqDto.GetMyPageRequest request + ) { + MemberResDto.GetMyPageResponse result = memberService.getMyPage(request.id()); - @PostMapping - public ResponseEntity createMember(@Valid @RequestBody MemberReqDto.Create request) { - return ResponseEntity.ok(memberService.createMember(request)); + return ResponseEntity.ok(ApiResponse.of(MemberSuccessCode.GET_MY_PAGE_SUCCESS, result)); } -} \ No newline at end of file +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/converter/MemberConverter.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/converter/MemberConverter.java index 479a2d4..edc3bf4 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/converter/MemberConverter.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/converter/MemberConverter.java @@ -29,4 +29,14 @@ public static MemberResDto.MemberInfo toMemberInfo(Member member) { member.getNickname() ); } -} \ No newline at end of file + + public static MemberResDto.GetMyPageResponse toMyPageResponse(Member member, Integer point) { + return new MemberResDto.GetMyPageResponse( + member.getName(), + null, + null, + null, + point + ); + } +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/dto/MemberReqDto.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/dto/MemberReqDto.java index 71fad6a..adfafa5 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/dto/MemberReqDto.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/dto/MemberReqDto.java @@ -3,6 +3,7 @@ import com.example.umc10th.domain.member.enums.Gender; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.Size; import java.time.LocalDate; @@ -16,4 +17,9 @@ public record Create( @NotBlank @Size(max = 20) String nickname ) { } + + public record GetMyPageRequest( + @NotNull @Positive Long id + ) { + } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/dto/MemberResDto.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/dto/MemberResDto.java index 26d1817..8ba3e08 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/dto/MemberResDto.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/dto/MemberResDto.java @@ -14,4 +14,13 @@ public record MemberInfo( String nickname ) { } + + public record GetMyPageResponse( + String name, + String profileUrl, + String email, + String phoneNumber, + Integer point + ) { + } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/MemberException.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/MemberException.java index be0bbb2..5e0d1f9 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/MemberException.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/MemberException.java @@ -1,15 +1,10 @@ package com.example.umc10th.domain.member.exception; import com.example.umc10th.domain.member.exception.code.MemberErrorCode; -import lombok.Getter; - -@Getter -public class MemberException extends RuntimeException { - - private final MemberErrorCode errorCode; +import com.example.umc10th.global.apiPayload.exception.GeneralException; +public class MemberException extends GeneralException { public MemberException(MemberErrorCode errorCode) { - super(errorCode.getMessage()); - this.errorCode = errorCode; + super(errorCode); } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/code/MemberErrorCode.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/code/MemberErrorCode.java index 8d59239..cc28733 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/code/MemberErrorCode.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/code/MemberErrorCode.java @@ -1,15 +1,27 @@ package com.example.umc10th.domain.member.exception.code; +import com.example.umc10th.global.apiPayload.code.BaseErrorCode; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @Getter @RequiredArgsConstructor -public enum MemberErrorCode { - MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER404", "����ڸ� ã�� �� �����ϴ�."); +public enum MemberErrorCode implements BaseErrorCode { + MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER404", "User not found."); - private final HttpStatus status; + private final HttpStatus httpStatus; private final String code; private final String message; + + @Override + public ReasonDto getReason() { + return new ReasonDto(httpStatus, false, code, message); + } + + @Override + public ReasonDto getReasonHttpStatus() { + return getReason(); + } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/code/MemberSuccessCode.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/code/MemberSuccessCode.java new file mode 100644 index 0000000..11a1c29 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/exception/code/MemberSuccessCode.java @@ -0,0 +1,27 @@ +package com.example.umc10th.domain.member.exception.code; + +import com.example.umc10th.global.apiPayload.code.BaseSuccessCode; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum MemberSuccessCode implements BaseSuccessCode { + GET_MY_PAGE_SUCCESS(HttpStatus.OK, "MEMBER2001", "Successfully retrieved user information."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ReasonDto getReason() { + return new ReasonDto(httpStatus, true, code, message); + } + + @Override + public ReasonDto getReasonHttpStatus() { + return getReason(); + } +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/service/MemberService.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/service/MemberService.java index 86dbcad..970c6a8 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/service/MemberService.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/service/MemberService.java @@ -7,4 +7,6 @@ public interface MemberService { MemberResDto.MemberInfo getMember(Long memberId); MemberResDto.MemberInfo createMember(MemberReqDto.Create request); -} \ No newline at end of file + + MemberResDto.GetMyPageResponse getMyPage(Long memberId); +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/service/MemberServiceImpl.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/service/MemberServiceImpl.java index 76794fe..552b8ea 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/member/service/MemberServiceImpl.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/member/service/MemberServiceImpl.java @@ -7,6 +7,8 @@ import com.example.umc10th.domain.member.exception.MemberException; import com.example.umc10th.domain.member.exception.code.MemberErrorCode; import com.example.umc10th.domain.member.repository.MemberRepository; +import com.example.umc10th.domain.mission.enums.MemberMissionStatus; +import com.example.umc10th.domain.mission.repository.MemberMissionRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -17,6 +19,7 @@ public class MemberServiceImpl implements MemberService { private final MemberRepository memberRepository; + private final MemberMissionRepository memberMissionRepository; @Override public MemberResDto.MemberInfo getMember(Long memberId) { @@ -32,4 +35,17 @@ public MemberResDto.MemberInfo createMember(MemberReqDto.Create request) { Member savedMember = memberRepository.save(MemberConverter.toEntity(request)); return MemberConverter.toMemberInfo(savedMember); } -} \ No newline at end of file + + @Override + public MemberResDto.GetMyPageResponse getMyPage(Long memberId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MemberErrorCode.MEMBER_NOT_FOUND)); + + Integer totalPoint = memberMissionRepository.sumRewardPointByMemberIdAndStatus( + memberId, + MemberMissionStatus.COMPLETE + ); + + return MemberConverter.toMyPageResponse(member, totalPoint); + } +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/controller/MissionController.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/controller/MissionController.java index f26e710..6774cc3 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/controller/MissionController.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/controller/MissionController.java @@ -2,31 +2,56 @@ import com.example.umc10th.domain.mission.dto.MissionReqDto; import com.example.umc10th.domain.mission.dto.MissionResDto; +import com.example.umc10th.domain.mission.enums.MemberMissionStatus; import com.example.umc10th.domain.mission.service.MissionService; +import com.example.umc10th.global.apiPayload.ApiResponse; import jakarta.validation.Valid; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/api/missions") +@RequestMapping("/api/v1/missions") @RequiredArgsConstructor +@Validated public class MissionController { private final MissionService missionService; @GetMapping("/{missionId}") - public ResponseEntity getMission(@PathVariable Long missionId) { - return ResponseEntity.ok(missionService.getMission(missionId)); + public ResponseEntity> getMission(@PathVariable Long missionId) { + return ResponseEntity.ok(ApiResponse.onSuccess(missionService.getMission(missionId))); } @PostMapping - public ResponseEntity createMission(@Valid @RequestBody MissionReqDto.Create request) { - return ResponseEntity.ok(missionService.createMission(request)); + public ResponseEntity> createMission(@Valid @RequestBody MissionReqDto.Create request) { + return ResponseEntity.ok(ApiResponse.onSuccess(missionService.createMission(request))); } -} \ No newline at end of file + + @GetMapping("/members/{memberId}") + public ResponseEntity> getMemberMissions( + @PathVariable Long memberId, + @RequestParam MemberMissionStatus status, + @RequestParam(defaultValue = "1") @Min(1) Integer page + ) { + return ResponseEntity.ok(ApiResponse.onSuccess(missionService.getMemberMissions(memberId, status, page))); + } + + @GetMapping("/home") + public ResponseEntity> getHomeMissions( + @RequestParam @NotNull Long regionId, + @RequestParam(required = false) Long memberId, + @RequestParam(defaultValue = "1") @Min(1) Integer page + ) { + return ResponseEntity.ok(ApiResponse.onSuccess(missionService.getHomeMissions(regionId, memberId, page))); + } +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/converter/MissionConverter.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/converter/MissionConverter.java index c3c7f68..4baf8a4 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/converter/MissionConverter.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/converter/MissionConverter.java @@ -2,8 +2,11 @@ import com.example.umc10th.domain.mission.dto.MissionReqDto; import com.example.umc10th.domain.mission.dto.MissionResDto; +import com.example.umc10th.domain.mission.entity.MemberMission; import com.example.umc10th.domain.mission.entity.Mission; +import org.springframework.data.domain.Page; import com.example.umc10th.domain.store.entity.Store; +import java.util.List; public final class MissionConverter { @@ -26,4 +29,59 @@ public static MissionResDto.MissionInfo toMissionInfo(Mission mission) { mission.getRewardPoint() ); } -} \ No newline at end of file + + public static MissionResDto.MemberMissionPageItem toMemberMissionPageItem(MemberMission memberMission) { + Mission mission = memberMission.getMission(); + + return new MissionResDto.MemberMissionPageItem( + memberMission.getId(), + mission.getId(), + mission.getStore().getName(), + mission.getContent(), + mission.getRewardPoint(), + memberMission.getStatus() + ); + } + + public static MissionResDto.HomeMissionPageItem toHomeMissionPageItem(Mission mission) { + return new MissionResDto.HomeMissionPageItem( + mission.getId(), + mission.getStore().getName(), + mission.getContent(), + mission.getRewardPoint(), + mission.getStore().getScore() + ); + } + + public static MissionResDto.MemberMissionPageResponse toMemberMissionPageResponse(Page page) { + List missions = page.getContent() + .stream() + .map(MissionConverter::toMemberMissionPageItem) + .toList(); + + return new MissionResDto.MemberMissionPageResponse( + missions, + missions.size(), + page.getTotalPages(), + page.getTotalElements(), + page.isFirst(), + page.isLast() + ); + } + + public static MissionResDto.HomeMissionPageResponse toHomeMissionPageResponse(Page page) { + List missions = page.getContent() + .stream() + .map(MissionConverter::toHomeMissionPageItem) + .toList(); + + return new MissionResDto.HomeMissionPageResponse( + missions, + missions.size(), + page.getTotalPages(), + page.getTotalElements(), + page.isFirst(), + page.isLast() + ); + } +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/dto/MissionResDto.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/dto/MissionResDto.java index fc37061..dce8553 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/dto/MissionResDto.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/dto/MissionResDto.java @@ -1,5 +1,8 @@ package com.example.umc10th.domain.mission.dto; +import com.example.umc10th.domain.mission.enums.MemberMissionStatus; +import java.util.List; + public class MissionResDto { public record MissionInfo( @@ -9,4 +12,43 @@ public record MissionInfo( int rewardPoint ) { } -} \ No newline at end of file + + public record MemberMissionPageItem( + Long memberMissionId, + Long missionId, + String storeName, + String missionContent, + Integer rewardPoint, + MemberMissionStatus status + ) { + } + + public record HomeMissionPageItem( + Long missionId, + String storeName, + String missionContent, + Integer rewardPoint, + Float storeScore + ) { + } + + public record MemberMissionPageResponse( + List missions, + Integer listSize, + Integer totalPage, + Long totalElements, + Boolean isFirst, + Boolean isLast + ) { + } + + public record HomeMissionPageResponse( + List missions, + Integer listSize, + Integer totalPage, + Long totalElements, + Boolean isFirst, + Boolean isLast + ) { + } +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/exception/MissionException.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/exception/MissionException.java index 26a61fe..41fdeaa 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/exception/MissionException.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/exception/MissionException.java @@ -1,15 +1,10 @@ package com.example.umc10th.domain.mission.exception; import com.example.umc10th.domain.mission.exception.code.MissionErrorCode; -import lombok.Getter; - -@Getter -public class MissionException extends RuntimeException { - - private final MissionErrorCode errorCode; +import com.example.umc10th.global.apiPayload.exception.GeneralException; +public class MissionException extends GeneralException { public MissionException(MissionErrorCode errorCode) { - super(errorCode.getMessage()); - this.errorCode = errorCode; + super(errorCode); } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/exception/code/MissionErrorCode.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/exception/code/MissionErrorCode.java index 4d71999..f3087ba 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/exception/code/MissionErrorCode.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/exception/code/MissionErrorCode.java @@ -1,16 +1,28 @@ package com.example.umc10th.domain.mission.exception.code; +import com.example.umc10th.global.apiPayload.code.BaseErrorCode; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @Getter @RequiredArgsConstructor -public enum MissionErrorCode { - MISSION_NOT_FOUND(HttpStatus.NOT_FOUND, "MISSION404", "�̼��� ã�� �� �����ϴ�."), - STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "STORE404", "���Ը� ã�� �� �����ϴ�."); +public enum MissionErrorCode implements BaseErrorCode { + MISSION_NOT_FOUND(HttpStatus.NOT_FOUND, "MISSION404", "Mission not found."), + STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "STORE404", "Store not found."); - private final HttpStatus status; + private final HttpStatus httpStatus; private final String code; private final String message; + + @Override + public ReasonDto getReason() { + return new ReasonDto(httpStatus, false, code, message); + } + + @Override + public ReasonDto getReasonHttpStatus() { + return getReason(); + } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/repository/MemberMissionRepository.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/repository/MemberMissionRepository.java index d9d5164..28bd763 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/repository/MemberMissionRepository.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/repository/MemberMissionRepository.java @@ -1,7 +1,38 @@ package com.example.umc10th.domain.mission.repository; import com.example.umc10th.domain.mission.entity.MemberMission; +import com.example.umc10th.domain.mission.enums.MemberMissionStatus; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface MemberMissionRepository extends JpaRepository { + + @EntityGraph(attributePaths = {"mission", "mission.store"}) + @Query(""" + select mm + from MemberMission mm + where mm.member.id = :memberId + and mm.status = :status + order by mm.id desc + """) + Page findMemberMissionsByStatus( + @Param("memberId") Long memberId, + @Param("status") MemberMissionStatus status, + Pageable pageable + ); + + @Query(""" + select coalesce(sum(mm.mission.rewardPoint), 0) + from MemberMission mm + where mm.member.id = :memberId + and mm.status = :status + """) + Integer sumRewardPointByMemberIdAndStatus( + @Param("memberId") Long memberId, + @Param("status") MemberMissionStatus status + ); } diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/repository/MissionRepository.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/repository/MissionRepository.java index 71f7528..73cdabf 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/repository/MissionRepository.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/repository/MissionRepository.java @@ -1,7 +1,33 @@ package com.example.umc10th.domain.mission.repository; import com.example.umc10th.domain.mission.entity.Mission; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface MissionRepository extends JpaRepository { + + @EntityGraph(attributePaths = {"store"}) + @Query(""" + select m + from Mission m + where m.store.region.id = :regionId + and ( + :memberId is null + or m.id not in ( + select mm.mission.id + from MemberMission mm + where mm.member.id = :memberId + ) + ) + order by m.id desc + """) + Page findChallengeableMissionsByRegion( + @Param("regionId") Long regionId, + @Param("memberId") Long memberId, + Pageable pageable + ); } diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/service/MissionService.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/service/MissionService.java index 594e659..a4d76d9 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/service/MissionService.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/service/MissionService.java @@ -2,9 +2,14 @@ import com.example.umc10th.domain.mission.dto.MissionReqDto; import com.example.umc10th.domain.mission.dto.MissionResDto; +import com.example.umc10th.domain.mission.enums.MemberMissionStatus; public interface MissionService { MissionResDto.MissionInfo getMission(Long missionId); MissionResDto.MissionInfo createMission(MissionReqDto.Create request); -} \ No newline at end of file + + MissionResDto.MemberMissionPageResponse getMemberMissions(Long memberId, MemberMissionStatus status, Integer page); + + MissionResDto.HomeMissionPageResponse getHomeMissions(Long regionId, Long memberId, Integer page); +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/service/MissionServiceImpl.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/service/MissionServiceImpl.java index 42873ba..602cee6 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/service/MissionServiceImpl.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/mission/service/MissionServiceImpl.java @@ -3,13 +3,18 @@ import com.example.umc10th.domain.mission.converter.MissionConverter; import com.example.umc10th.domain.mission.dto.MissionReqDto; import com.example.umc10th.domain.mission.dto.MissionResDto; +import com.example.umc10th.domain.mission.entity.MemberMission; import com.example.umc10th.domain.mission.entity.Mission; +import com.example.umc10th.domain.mission.enums.MemberMissionStatus; import com.example.umc10th.domain.store.entity.Store; import com.example.umc10th.domain.mission.exception.MissionException; import com.example.umc10th.domain.mission.exception.code.MissionErrorCode; +import com.example.umc10th.domain.mission.repository.MemberMissionRepository; import com.example.umc10th.domain.mission.repository.MissionRepository; import com.example.umc10th.domain.store.repository.StoreRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,6 +24,7 @@ public class MissionServiceImpl implements MissionService { private final MissionRepository missionRepository; + private final MemberMissionRepository memberMissionRepository; private final StoreRepository storeRepository; @Override @@ -38,4 +44,27 @@ public MissionResDto.MissionInfo createMission(MissionReqDto.Create request) { Mission savedMission = missionRepository.save(MissionConverter.toEntity(request, store)); return MissionConverter.toMissionInfo(savedMission); } -} \ No newline at end of file + + @Override + public MissionResDto.MemberMissionPageResponse getMemberMissions(Long memberId, MemberMissionStatus status, Integer page) { + int pageNumber = Math.max(page, 1) - 1; + PageRequest pageRequest = PageRequest.of(pageNumber, 10); + + Page memberMissionPage = memberMissionRepository.findMemberMissionsByStatus( + memberId, + status, + pageRequest + ); + + return MissionConverter.toMemberMissionPageResponse(memberMissionPage); + } + + @Override + public MissionResDto.HomeMissionPageResponse getHomeMissions(Long regionId, Long memberId, Integer page) { + int pageNumber = Math.max(page, 1) - 1; + PageRequest pageRequest = PageRequest.of(pageNumber, 10); + + Page missionPage = missionRepository.findChallengeableMissionsByRegion(regionId, memberId, pageRequest); + return MissionConverter.toHomeMissionPageResponse(missionPage); + } +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/review/controller/ReviewController.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/review/controller/ReviewController.java index f3cfdfd..57cfa3e 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/review/controller/ReviewController.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/review/controller/ReviewController.java @@ -3,6 +3,7 @@ import com.example.umc10th.domain.review.dto.ReviewReqDto; import com.example.umc10th.domain.review.dto.ReviewResDto; import com.example.umc10th.domain.review.service.ReviewService; +import com.example.umc10th.global.apiPayload.ApiResponse; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -21,12 +22,12 @@ public class ReviewController { private final ReviewService reviewService; @GetMapping("/{reviewId}") - public ResponseEntity getReview(@PathVariable Long reviewId) { - return ResponseEntity.ok(reviewService.getReview(reviewId)); + public ResponseEntity> getReview(@PathVariable Long reviewId) { + return ResponseEntity.ok(ApiResponse.onSuccess(reviewService.getReview(reviewId))); } @PostMapping - public ResponseEntity createReview(@Valid @RequestBody ReviewReqDto.Create request) { - return ResponseEntity.ok(reviewService.createReview(request)); + public ResponseEntity> createReview(@Valid @RequestBody ReviewReqDto.Create request) { + return ResponseEntity.ok(ApiResponse.onSuccess(reviewService.createReview(request))); } -} \ No newline at end of file +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/review/exception/ReviewException.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/review/exception/ReviewException.java index e4d509f..10f3491 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/review/exception/ReviewException.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/review/exception/ReviewException.java @@ -1,15 +1,10 @@ package com.example.umc10th.domain.review.exception; import com.example.umc10th.domain.review.exception.code.ReviewErrorCode; -import lombok.Getter; - -@Getter -public class ReviewException extends RuntimeException { - - private final ReviewErrorCode errorCode; +import com.example.umc10th.global.apiPayload.exception.GeneralException; +public class ReviewException extends GeneralException { public ReviewException(ReviewErrorCode errorCode) { - super(errorCode.getMessage()); - this.errorCode = errorCode; + super(errorCode); } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/review/exception/code/ReviewErrorCode.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/review/exception/code/ReviewErrorCode.java index 84140b6..115b466 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/review/exception/code/ReviewErrorCode.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/review/exception/code/ReviewErrorCode.java @@ -1,17 +1,29 @@ package com.example.umc10th.domain.review.exception.code; +import com.example.umc10th.global.apiPayload.code.BaseErrorCode; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @Getter @RequiredArgsConstructor -public enum ReviewErrorCode { - REVIEW_NOT_FOUND(HttpStatus.NOT_FOUND, "REVIEW404", "���並 ã�� �� �����ϴ�."), - MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER404", "����ڸ� ã�� �� �����ϴ�."), - STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "STORE404", "���Ը� ã�� �� �����ϴ�."); +public enum ReviewErrorCode implements BaseErrorCode { + REVIEW_NOT_FOUND(HttpStatus.NOT_FOUND, "REVIEW404", "Review not found."), + MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "MEMBER404", "User not found."), + STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "STORE404", "Store not found."); - private final HttpStatus status; + private final HttpStatus httpStatus; private final String code; private final String message; + + @Override + public ReasonDto getReason() { + return new ReasonDto(httpStatus, false, code, message); + } + + @Override + public ReasonDto getReasonHttpStatus() { + return getReason(); + } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/store/controller/StoreController.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/store/controller/StoreController.java index 22afbfa..ed3fdb5 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/store/controller/StoreController.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/store/controller/StoreController.java @@ -2,6 +2,7 @@ import com.example.umc10th.domain.store.dto.StoreResDto; import com.example.umc10th.domain.store.service.StoreService; +import com.example.umc10th.global.apiPayload.ApiResponse; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -17,7 +18,7 @@ public class StoreController { private final StoreService storeService; @GetMapping("/{storeId}") - public ResponseEntity getStore(@PathVariable Long storeId) { - return ResponseEntity.ok(storeService.getStore(storeId)); + public ResponseEntity> getStore(@PathVariable Long storeId) { + return ResponseEntity.ok(ApiResponse.onSuccess(storeService.getStore(storeId))); } -} \ No newline at end of file +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/store/exception/StoreException.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/store/exception/StoreException.java index 3d33f25..6574d25 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/store/exception/StoreException.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/store/exception/StoreException.java @@ -1,15 +1,10 @@ package com.example.umc10th.domain.store.exception; import com.example.umc10th.domain.store.exception.code.StoreErrorCode; -import lombok.Getter; - -@Getter -public class StoreException extends RuntimeException { - - private final StoreErrorCode errorCode; +import com.example.umc10th.global.apiPayload.exception.GeneralException; +public class StoreException extends GeneralException { public StoreException(StoreErrorCode errorCode) { - super(errorCode.getMessage()); - this.errorCode = errorCode; + super(errorCode); } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/store/exception/code/StoreErrorCode.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/store/exception/code/StoreErrorCode.java index dffd006..bcac3ca 100644 --- a/bean/umc10th/src/main/java/com/example/umc10th/domain/store/exception/code/StoreErrorCode.java +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/store/exception/code/StoreErrorCode.java @@ -1,15 +1,27 @@ package com.example.umc10th.domain.store.exception.code; +import com.example.umc10th.global.apiPayload.code.BaseErrorCode; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; import lombok.Getter; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @Getter @RequiredArgsConstructor -public enum StoreErrorCode { +public enum StoreErrorCode implements BaseErrorCode { STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "STORE404", "Store not found."); - private final HttpStatus status; + private final HttpStatus httpStatus; private final String code; private final String message; + + @Override + public ReasonDto getReason() { + return new ReasonDto(httpStatus, false, code, message); + } + + @Override + public ReasonDto getReasonHttpStatus() { + return getReason(); + } } \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/test/controller/TestController.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/test/controller/TestController.java new file mode 100644 index 0000000..7fcc448 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/test/controller/TestController.java @@ -0,0 +1,20 @@ +package com.example.umc10th.domain.test.controller; + +import com.example.umc10th.domain.test.dto.TestResDto; +import com.example.umc10th.global.apiPayload.ApiResponse; +import com.example.umc10th.global.apiPayload.code.status.SuccessStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/test") +public class TestController { + + @GetMapping("/health") + public ResponseEntity> health() { + TestResDto.HealthResponse result = new TestResDto.HealthResponse("UP", "Test API is working."); + return ResponseEntity.ok(ApiResponse.of(SuccessStatus.OK, result)); + } +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/domain/test/dto/TestResDto.java b/bean/umc10th/src/main/java/com/example/umc10th/domain/test/dto/TestResDto.java new file mode 100644 index 0000000..bd56486 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/domain/test/dto/TestResDto.java @@ -0,0 +1,10 @@ +package com.example.umc10th.domain.test.dto; + +public class TestResDto { + + public record HealthResponse( + String status, + String message + ) { + } +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/ApiResponse.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/ApiResponse.java new file mode 100644 index 0000000..7912d32 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/ApiResponse.java @@ -0,0 +1,50 @@ +package com.example.umc10th.global.apiPayload; + +import com.example.umc10th.global.apiPayload.code.BaseErrorCode; +import com.example.umc10th.global.apiPayload.code.BaseSuccessCode; +import com.example.umc10th.global.apiPayload.code.status.SuccessStatus; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@JsonPropertyOrder({"isSuccess", "code", "message", "result"}) +public class ApiResponse { + + @JsonProperty("isSuccess") + private final Boolean isSuccess; + private final String code; + private final String message; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private T result; + + public static ApiResponse onSuccess(T result) { + return of(SuccessStatus.OK, result); + } + + public static ApiResponse of(BaseSuccessCode code, T result) { + return new ApiResponse<>( + true, + code.getReason().code(), + code.getReason().message(), + result + ); + } + + public static ApiResponse onFailure(BaseErrorCode code, T result) { + return new ApiResponse<>( + false, + code.getReason().code(), + code.getReason().message(), + result + ); + } + + public static ApiResponse onFailure(String code, String message, T result) { + return new ApiResponse<>(false, code, message, result); + } +} diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseCode.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseCode.java new file mode 100644 index 0000000..07573cd --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseCode.java @@ -0,0 +1,9 @@ +package com.example.umc10th.global.apiPayload.code; + +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; + +public interface BaseCode { + ReasonDto getReason(); + + ReasonDto getReasonHttpStatus(); +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseErrorCode.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseErrorCode.java new file mode 100644 index 0000000..da096a5 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseErrorCode.java @@ -0,0 +1,4 @@ +package com.example.umc10th.global.apiPayload.code; + +public interface BaseErrorCode extends BaseCode { +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseSuccessCode.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseSuccessCode.java new file mode 100644 index 0000000..35b18d3 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/BaseSuccessCode.java @@ -0,0 +1,4 @@ +package com.example.umc10th.global.apiPayload.code; + +public interface BaseSuccessCode extends BaseCode { +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/dto/ReasonDto.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/dto/ReasonDto.java new file mode 100644 index 0000000..92b3568 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/dto/ReasonDto.java @@ -0,0 +1,11 @@ +package com.example.umc10th.global.apiPayload.code.dto; + +import org.springframework.http.HttpStatus; + +public record ReasonDto( + HttpStatus httpStatus, + boolean isSuccess, + String code, + String message +) { +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/status/ErrorStatus.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/status/ErrorStatus.java new file mode 100644 index 0000000..202e2eb --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/status/ErrorStatus.java @@ -0,0 +1,29 @@ +package com.example.umc10th.global.apiPayload.code.status; + +import com.example.umc10th.global.apiPayload.code.BaseErrorCode; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum ErrorStatus implements BaseErrorCode { + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON500", "Server error."), + BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON400", "Bad request."), + VALIDATION_ERROR(HttpStatus.BAD_REQUEST, "COMMON4001", "Validation failed."); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ReasonDto getReason() { + return new ReasonDto(httpStatus, false, code, message); + } + + @Override + public ReasonDto getReasonHttpStatus() { + return getReason(); + } +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/status/SuccessStatus.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/status/SuccessStatus.java new file mode 100644 index 0000000..75662f9 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/code/status/SuccessStatus.java @@ -0,0 +1,27 @@ +package com.example.umc10th.global.apiPayload.code.status; + +import com.example.umc10th.global.apiPayload.code.BaseSuccessCode; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@RequiredArgsConstructor +public enum SuccessStatus implements BaseSuccessCode { + OK(HttpStatus.OK, "COMMON200", "Success"); + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ReasonDto getReason() { + return new ReasonDto(httpStatus, true, code, message); + } + + @Override + public ReasonDto getReasonHttpStatus() { + return getReason(); + } +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/exception/GeneralException.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/exception/GeneralException.java new file mode 100644 index 0000000..2021214 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/exception/GeneralException.java @@ -0,0 +1,21 @@ +package com.example.umc10th.global.apiPayload.exception; + +import com.example.umc10th.global.apiPayload.code.BaseErrorCode; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class GeneralException extends RuntimeException { + + private final BaseErrorCode code; + + public ReasonDto getErrorReason() { + return code.getReason(); + } + + public ReasonDto getErrorReasonHttpStatus() { + return code.getReasonHttpStatus(); + } +} \ No newline at end of file diff --git a/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/exception/GeneralExceptionHandler.java b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/exception/GeneralExceptionHandler.java new file mode 100644 index 0000000..b997923 --- /dev/null +++ b/bean/umc10th/src/main/java/com/example/umc10th/global/apiPayload/exception/GeneralExceptionHandler.java @@ -0,0 +1,46 @@ +package com.example.umc10th.global.apiPayload.exception; + +import com.example.umc10th.global.apiPayload.ApiResponse; +import com.example.umc10th.global.apiPayload.code.dto.ReasonDto; +import com.example.umc10th.global.apiPayload.code.status.ErrorStatus; +import java.util.LinkedHashMap; +import java.util.Map; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GeneralExceptionHandler { + + @ExceptionHandler(GeneralException.class) + public ResponseEntity> handleGeneralException(GeneralException exception) { + ReasonDto reason = exception.getErrorReasonHttpStatus(); + return ResponseEntity + .status(reason.httpStatus()) + .body(ApiResponse.onFailure(reason.code(), reason.message(), null)); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidationException(MethodArgumentNotValidException exception) { + Map errors = new LinkedHashMap<>(); + + for (FieldError fieldError : exception.getBindingResult().getFieldErrors()) { + errors.put(fieldError.getField(), fieldError.getDefaultMessage()); + } + + ErrorStatus status = ErrorStatus.VALIDATION_ERROR; + return ResponseEntity + .status(status.getHttpStatus()) + .body(ApiResponse.onFailure(status.getCode(), status.getMessage(), errors)); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity> handleException(Exception exception) { + ErrorStatus status = ErrorStatus.INTERNAL_SERVER_ERROR; + return ResponseEntity + .status(status.getHttpStatus()) + .body(ApiResponse.onFailure(status.getCode(), status.getMessage(), exception.getMessage())); + } +} \ No newline at end of file