Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[9주차/공공] 워크북 제출합니다. #42

Merged
merged 1 commit into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package javalab.umc7th_mission.controller;
package umc.spring.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand All @@ -9,6 +10,7 @@
import umc.spring.service.MissionService.MissionService;

import jakarta.validation.Valid;
import umc.spring.validation.annotation.PageValidation;

@RestController
@RequiredArgsConstructor
Expand All @@ -31,4 +33,65 @@ public ResponseEntity<?> addMission(@RequestBody @Valid MissionRequestDTO reques
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("서버 에러: " + e.getMessage());
}
}
// 특정 가게의 미션 목록 조회
@GetMapping("/store/{storeId}")
public ResponseEntity<Page<MissionResponseDTO>> getMissionsByStore(
@PathVariable Long storeId,
@RequestParam("page") @PageValidation Integer page,
@RequestParam("size") Integer size
) {
Page<MissionResponseDTO> missions = missionService.getMissionsByStore(storeId, page, size);
return ResponseEntity.ok(missions);
}

// 특정 회원이 진행 중인 미션 목록 조회
@GetMapping("/member/{memberId}/missions")
public ResponseEntity<Page<MissionResponseDTO>> getOngoingMissionsByMember(
@PathVariable Long memberId,
@RequestParam("page") @PageValidation Integer page,
@RequestParam("size") Integer size
) {
try {
Page<MissionResponseDTO> missions = missionService.getOngoingMissionsByMember(memberId, page, size);
return ResponseEntity.ok(missions);
} catch (IllegalArgumentException e) {
// 회원 ID가 잘못된 경우
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Page.empty());
} catch (Exception e) {
// 기타 서버 오류
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Page.empty());
}
}

// 회원이 특정 미션에 도전하기
@PostMapping("/{missionId}/start")
public ResponseEntity<?> startMission(
@PathVariable Long missionId,
@RequestParam("memberId") Long memberId) {
try {
missionService.startMission(memberId, missionId);
return ResponseEntity.ok("미션 도전이 성공적으로 시작되었습니다.");
} catch (IllegalArgumentException e) {
// 클라이언트 요청 오류
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
} catch (Exception e) {
// 서버 내부 오류
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("서버 에러: " + e.getMessage());
}
}

// 회원이 특정 미션 완료하기
@PostMapping("/{missionId}/complete")
public ResponseEntity<?> completeMission(
@PathVariable Long missionId,
@RequestParam("memberId") Long memberId) {
try {
missionService.completeMission(memberId, missionId);
return ResponseEntity.ok("미션이 성공적으로 완료되었습니다.");
} catch (IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("서버 에러: " + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package javalab.umc7th_mission.controller;
package umc.spring.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import umc.spring.dto.ReviewRequestDTO;
import umc.spring.dto.ReviewResponseDTO;
import umc.spring.service.ReviewService.ReviewService;
import umc.spring.validation.annotation.PageValidation;

@RestController
@RequestMapping("/review")
Expand All @@ -20,4 +22,15 @@ public ResponseEntity<ReviewResponseDTO> addReview(@RequestBody ReviewRequestDTO
ReviewResponseDTO response = reviewService.addReview(request);
return ResponseEntity.ok(response);
}

// 내가 작성한 리뷰 목록 조회 API
@GetMapping("/my")
public ResponseEntity<Page<ReviewResponseDTO>> getMyReviews(
@RequestParam("page") @PageValidation Integer page,
@RequestParam("size") Integer size,
@RequestParam("memberId") Long memberId // 회원 ID를 요청 파라미터로 받음
) {
Page<ReviewResponseDTO> reviews = reviewService.getMyReviews(memberId, page, size);
return ResponseEntity.ok(reviews);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package javalab.umc7th_mission.domain.mapping;
package umc.spring.domain.mapping;



import jakarta.persistence.*;
import lombok.*;
import javalab.umc7th_mission.domain.Member;
import javalab.umc7th_mission.domain.Mission;
import javalab.umc7th_mission.domain.common.BaseEntity;
import javalab.umc7th_mission.domain.enums.MissionStatus;
import umc.spring.domain.Member;
import umc.spring.domain.Mission;
import umc.spring.domain.common.BaseEntity;
import umc.spring.domain.enums.MissionStatus;

@Entity
@Getter
Expand All @@ -25,13 +25,33 @@ public class MemberMission extends BaseEntity {
private MissionStatus status;

//오류 수정
@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;

@ManyToOne
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "mission_id")
private Mission mission;

@Builder
public MemberMission(Member member, Mission mission, MissionStatus status) {
this.member = member;
this.mission = mission;
this.status = status;
}

// Setter를 대신하는 빌더로 새로운 객체 생성
public static MemberMission create(Member member, Mission mission, MissionStatus status) {
return MemberMission.builder()
.member(member)
.mission(mission)
.status(status)
.build();
}

// 미션 완료 처리
public void complete() {
this.status = MissionStatus.COMPLETE;
}

}
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
package umc.spring.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository;
import umc.spring.domain.Member;
import umc.spring.domain.Mission;
import org.springframework.data.domain.Pageable;
import umc.spring.domain.mapping.MemberMission;
import umc.spring.domain.enums.MissionStatus;

public interface MemberMissionRepository extends JpaRepository<MemberMission, Long> {
import java.util.Optional;

public interface MemberMissionRepository extends JpaRepository<MemberMission, Long> {
// 특정 회원이 특정 미션에 도전 중인지 확인
boolean existsByMemberIdAndMissionIdAndStatus(Long memberId, Long missionId, MissionStatus status);

boolean existsByMissionIdAndStatus(Long missionId, MissionStatus missionStatus);

// 회원-미션 관계 조회
Optional<MemberMission> findByMemberIdAndMissionId(Long memberId, Long missionId);
// 특정 회원의 진행 중인 미션 목록 조회
Page<MemberMission> findByMemberIdAndStatus(Long memberId, MissionStatus status, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package umc.spring.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository;
import umc.spring.domain.Mission;
import org.springframework.data.domain.Pageable;
import umc.spring.domain.enums.MissionStatus;

public interface MissionRepository extends JpaRepository<Mission, Long> {
// 미션이 이미 도전 중인지 확인
boolean existsByStatusAndId(MissionStatus status, Long id);
// 특정 가게의 미션 목록 조회
Page<Mission> findByStoreId(Long storeId, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package umc.spring.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import umc.spring.domain.Review;
import org.springframework.data.domain.Pageable;

@Repository
public interface ReviewRepository extends JpaRepository<Review, Long> {

// 회원 ID로 리뷰 조회 (페이징 포함)
Page<Review> findByMemberId(Long memberId, Pageable pageable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import umc.spring.apiPayload.exception.GeneralException;
import umc.spring.domain.Member;
Expand Down Expand Up @@ -58,7 +60,7 @@ public MissionResponseDTO addMission(MissionRequestDTO request) {

@Transactional
public void startMission(Long memberId, Long missionId) {
// 1. Member와 Mission 조회
// 1. 회원 및 미션 조회
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new GeneralException(MEMBER_NOT_FOUND));
Mission mission = missionRepository.findById(missionId)
Expand All @@ -68,13 +70,70 @@ public void startMission(Long memberId, Long missionId) {
boolean isAlreadyChallenging = memberMissionRepository.existsByMemberIdAndMissionIdAndStatus(
memberId, missionId, MissionStatus.CHALLENGING
);

if (isAlreadyChallenging) {
throw new GeneralException(_FORBIDDEN);
throw new GeneralException(MEMBER_NOT_FOUND);
}

// 3. MemberMission 객체 생성 및 저장
// 3. 새로운 도전 상태 추가
MemberMission memberMission = MemberMission.create(member, mission, MissionStatus.CHALLENGING);
memberMissionRepository.save(memberMission);
}

// 특정 가게의 미션 목록 조회
public Page<MissionResponseDTO> getMissionsByStore(Long storeId, Integer page, Integer size) {
// Store 유효성 검사
storeRepository.findById(storeId)
.orElseThrow(() -> new IllegalArgumentException("해당 Store를 찾을 수 없습니다."));

// 페이징 처리
PageRequest pageable = PageRequest.of(page, size);

// 미션 목록 조회 및 DTO 변환
return missionRepository.findByStoreId(storeId, pageable)
.map(mission -> MissionResponseDTO.builder()
.id(mission.getId())
.missionSpec(mission.getMissionSpec())
.reward(mission.getReward())
.deadline(mission.getDeadline())
.storeName(mission.getStore().getName())
.build());
}

@Transactional
public void completeMission(Long memberId, Long missionId) {
// 회원-미션 관계 조회
MemberMission memberMission = memberMissionRepository.findByMemberIdAndMissionId(memberId, missionId)
.orElseThrow(() -> new GeneralException(MEMBER_NOT_FOUND));

// 현재 상태 확인 (CHALLENGING 상태여야 완료 가능)
if (memberMission.getStatus() != MissionStatus.CHALLENGING) {
throw new GeneralException(MEMBER_NOT_FOUND);
}

// 미션 완료 처리
memberMission.complete();
}

@Transactional
public Page<MissionResponseDTO> getOngoingMissionsByMember(Long memberId, Integer page, Integer size) {
// 1. 회원 유효성 검사
memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("해당 회원을 찾을 수 없습니다."));

// 2. 페이징 처리
PageRequest pageable = PageRequest.of(page - 1, size); // Spring Data JPA는 0-based index 사용

// 3. 진행 중인 미션 조회 및 변환
return memberMissionRepository.findByMemberIdAndStatus(memberId, MissionStatus.CHALLENGING, pageable)
.map(memberMission -> {
Mission mission = memberMission.getMission();
return MissionResponseDTO.builder()
.id(mission.getId())
.missionSpec(mission.getMissionSpec())
.reward(mission.getReward())
.deadline(mission.getDeadline())
.storeName(mission.getStore().getName())
.build();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import umc.spring.apiPayload.code.status.ErrorStatus;
import umc.spring.apiPayload.exception.GeneralException;
Expand Down Expand Up @@ -52,4 +54,20 @@ public ReviewResponseDTO addReview(ReviewRequestDTO request) {
.memberId(member.getId())
.build();
}

// 내가 작성한 리뷰 목록 조회
public Page<ReviewResponseDTO> getMyReviews(Long memberId, Integer page, Integer size) {
// 페이징 처리 (PageRequest 생성)
PageRequest pageable = PageRequest.of(page, size);

// 리뷰 조회 및 DTO 변환
return reviewRepository.findByMemberId(memberId, pageable)
.map(review -> ReviewResponseDTO.builder()
.id(review.getId())
.title(review.getTitle())
.score(review.getScore())
.storeId(review.getStore().getId())
.memberId(review.getMember().getId())
.build());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package umc.spring.validation.annotation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import umc.spring.validation.validator.PageValidator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Constraint(validatedBy = PageValidator.class)
@Target({ ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface PageValidation {
String message() default "Page number must be greater than 0.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package umc.spring.validation.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import umc.spring.validation.annotation.PageValidation;

public class PageValidator implements ConstraintValidator<PageValidation, Integer> {

@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return value != null && value > 0; // 페이지 번호는 1 이상이어야 유효
}
}