Skip to content
Open
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
30 changes: 0 additions & 30 deletions src/main/java/umc/domain/controller/MissionController.java

This file was deleted.

53 changes: 53 additions & 0 deletions src/main/java/umc/domain/mission/controller/MissionController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package umc.domain.mission.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import umc.domain.mission.dto.req.MissionReqDTO;
import umc.domain.mission.dto.res.MissionResDTO;
import umc.domain.mission.entity.Mission;
import umc.domain.mission.service.command.MissionCommandService;
import umc.domain.mission.service.query.MissionQueryService;
import umc.global.annotation.CheckPage;
import umc.global.apiPayload.ApiResponse;
import umc.global.apiPayload.code.GeneralSuccessCode;

@RestController
@RequiredArgsConstructor
@Validated
@RequestMapping("/api/missions")
public class MissionController {
private final MissionCommandService missionCommandService;
private final MissionQueryService missionQueryService;

@PostMapping("/")
public ApiResponse<MissionResDTO.AddMissionResDTO> addMission(
@RequestBody @Valid MissionReqDTO.AddMissionReqDTO req
){
return ApiResponse.onSuccess(
GeneralSuccessCode.OK,
missionCommandService.addMission(req)
);
}

@GetMapping("/store/{storeId}")
@Operation(summary = "특정 가게의 미션 목록 조회 API", description = "특정 가게의 미션 목록을 조회합니다. page는 1부터 시작합니다.")
@Parameters({
@Parameter(name = "storeId", description = "가게의 아이디, path variable 입니다!"),
@Parameter(name = "page", description = "페이지 번호 (1 이상)")
})
public ApiResponse<MissionResDTO.MissionByStoreListResDTO> getMissionByStore(
@PathVariable Long storeId,
@CheckPage @RequestParam(name = "page") Integer page
){
return ApiResponse.onSuccess(
GeneralSuccessCode.OK,
missionQueryService.getMissionByStoreListResDTO(storeId, page)
);

}
}
32 changes: 32 additions & 0 deletions src/main/java/umc/domain/mission/converter/MissionConverter.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package umc.domain.mission.converter;

import org.springframework.data.domain.Page;
import umc.domain.mission.dto.req.MissionReqDTO;
import umc.domain.mission.dto.res.MissionResDTO;
import umc.domain.mission.entity.Mission;
import umc.domain.store.entity.Store;

import java.util.List;
import java.util.stream.Collectors;

public class MissionConverter {

//DTO -> Entity
Expand All @@ -26,4 +30,32 @@ public static MissionResDTO.AddMissionResDTO toAddMissionResDTO(Mission mission)
.createdAt(mission.getCreatedAt())
.build();
}

//Mission Entity -> MissionByStoreResDTO
public static MissionResDTO.MissionByStoreResDTO toMissionByStoreResDTO(Mission mission){
return MissionResDTO.MissionByStoreResDTO.builder()
.missionId(mission.getId())
.rewardPoint(mission.getRewardPoint())
.baseAmount(mission.getBaseAmount())
.deadline(mission.getDeadline())
.isActive(mission.getIsActive())
.build();
}

//Page<Mission> -> MissionByStoreListResDTO
public static MissionResDTO.MissionByStoreListResDTO toMissionByStoreListResDTO(Page<Mission> missionPage){
List<MissionResDTO.MissionByStoreResDTO> missionList = missionPage.getContent().stream()
.map(MissionConverter::toMissionByStoreResDTO)
.collect(Collectors.toList());

return MissionResDTO.MissionByStoreListResDTO.builder()
.missionList(missionList)
.listSize(missionPage.getSize())
.totalPage(missionPage.getTotalPages())
.totalElements(missionPage.getTotalElements())
.isFirst(missionPage.isFirst())
.isLast(missionPage.isLast())
.build();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ public record AddMissionReqDTO(
LocalDate deadline

){}

}
22 changes: 22 additions & 0 deletions src/main/java/umc/domain/mission/dto/res/MissionResDTO.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import lombok.Builder;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

public class MissionResDTO {

Expand All @@ -11,4 +13,24 @@ public record AddMissionResDTO(
Long missionId,
LocalDateTime createdAt
){}

@Builder
public record MissionByStoreListResDTO(
List<MissionByStoreResDTO> missionList,
Integer listSize, //페이징 단위
Integer totalPage,
Long totalElements,
Boolean isFirst,
Boolean isLast

){}

@Builder
public record MissionByStoreResDTO(
Long missionId,
Integer rewardPoint,
Integer baseAmount,
LocalDate deadline,
Boolean isActive
){}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import umc.domain.mission.entity.Mission;
import umc.domain.store.entity.Store;
import umc.domain.user.enums.MissionStatus;

import java.util.List;
Expand Down Expand Up @@ -40,4 +41,6 @@ Page<Mission> findAvailableMissionsByRegion(
@Param("statuses")List<MissionStatus> statuses,
Pageable pageable
);

Page<Mission> findAllByStore(Store store, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package umc.domain.mission.service.query;

import umc.domain.mission.dto.res.MissionResDTO;

public interface MissionQueryService {
MissionResDTO.MissionByStoreListResDTO getMissionByStoreListResDTO(Long storeId, Integer page);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package umc.domain.mission.service.query;

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;
import org.springframework.validation.annotation.Validated;
import umc.domain.mission.converter.MissionConverter;
import umc.domain.mission.dto.res.MissionResDTO;
import umc.domain.mission.entity.Mission;
import umc.domain.mission.repository.MissionRepository;
import umc.domain.store.entity.Store;
import umc.domain.store.exception.StoreException;
import umc.domain.store.exception.code.StoreErrorCode;
import umc.domain.store.repository.StoreRepository;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MissionQueryServiceImpl implements MissionQueryService{
private final MissionRepository missionRepository;
private final StoreRepository storeRepository;

@Override
public MissionResDTO.MissionByStoreListResDTO getMissionByStoreListResDTO(Long storeId, Integer page){

Store store = storeRepository.findById(storeId).orElseThrow(()-> new StoreException(StoreErrorCode.NOT_FOUND));
PageRequest pageRequest = PageRequest.of(page-1,10);
Page<Mission> missionPage = missionRepository.findAllByStore(store, pageRequest);

return MissionConverter.toMissionByStoreListResDTO(missionPage);

}
}
36 changes: 32 additions & 4 deletions src/main/java/umc/domain/review/controller/ReviewController.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
package umc.domain.review.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import umc.domain.review.dto.req.ReviewReqDTO;
import umc.domain.review.dto.res.ReviewResDTO;
import umc.domain.review.exception.code.ReviewSuccessCode;
import umc.domain.review.service.command.ReviewCommandService;
import umc.domain.review.service.query.ReviewQueryService;
import umc.global.annotation.CheckPage;
import umc.global.apiPayload.ApiResponse;
import umc.global.apiPayload.code.GeneralSuccessCode;

import java.util.List;

@RestController
@RequiredArgsConstructor
@Validated
@RequestMapping("/api/reviews")
public class ReviewController {
public class ReviewController implements ReviewControllerDocs{
private final ReviewQueryService reviewQueryService;
private final ReviewCommandService reviewCommandService;

@GetMapping("/search") //워크북 실습용 API
public ApiResponse<List<ReviewResDTO.ReviewPreviewDTO>> searchReview(
public ApiResponse<List<ReviewResDTO.ReviewPreviewWorkbookDTO>> searchReview(
@RequestParam String query,
@RequestParam String type
){
Expand All @@ -30,13 +36,24 @@ public ApiResponse<List<ReviewResDTO.ReviewPreviewDTO>> searchReview(
);
}

//가게의 리뷰 목록 조회
@GetMapping("/reviews")
@Override
public ApiResponse<ReviewResDTO.ReviewPreViewListDTO> getReviews(
@RequestParam String storeName,
@RequestParam(defaultValue = "1") Integer page
){
ReviewSuccessCode code = ReviewSuccessCode.FOUND;
return ApiResponse.onSuccess(code,reviewQueryService.findReview(storeName,page));
}


//과제(미션)용 API
@GetMapping("/my")
public ApiResponse<List<ReviewResDTO.ReviewPreviewDTO>>getMyReviews(
public ApiResponse<List<ReviewResDTO.ReviewPreviewWorkbookDTO>>getMyReviews(
@RequestParam(name = "userId")Long userId,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

스터디 때 userId 어떻게 받아오냐고 여쭤보셨던 거 같아요!
방법은 찾아보시면 여러가지 있는데, 제가 알고있는 방식만 대략 설명드릴게요!

  • JWT에서 userId를 추출하고 ArgumentResolver를 커스텀하여 컨트롤러 인자로 받기

스프링 시큐리티 단에서 JWT 유효성 검증을 마치면, JWT로 부터 userId를 추출하여 컨트롤러의 인자로 바로 넘겨주는 방식이에요.
이때 컨트롤러가 필요로 하는 인자를 만들어주는건 ArgumentResolver 의 역할이잖아요? 그래서 이걸 직접 커스텀해야해요.

// 1. 커스텀 어노테이션 정의 (@UserId)
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Parameter(hidden = true) // Swagger UI hidden
public @interface UserId {
}
// 2. ArgumentResolver 커스텀
@Component
public class UserIdArgumentResolver implements HandlerMethodArgumentResolver {

    // 어떤 어노테이션에 적용될 리졸버인지 (위에서 정의한 @UserId)
	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(UserId.class);
	}

    // 어떤 값을 전달하면 되는지
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
		NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
		return (Long)authentication.getDetails();
	}
}
// 3. 사용하는 컨트롤러 쪽에서는 어노테이션만 적용
public ApiResponse<...> method(
        @UserId Long userId
) {...}

보통 경험 많으신 분들이 위처럼 만들어주실 거예요.
물론 다른 방법도 있겠지만, '이런 방법이 있다!' 로만 이해해주세요!

@RequestParam(name = "storeId", required = false) Long storeId,
@RequestParam(name = "starFloor", required = false) Integer starFloor
@RequestParam(name = "starFloor", required = false) Float starFloor
) {
return ApiResponse.onSuccess(
GeneralSuccessCode.OK,
Expand All @@ -55,6 +72,17 @@ public ApiResponse<List<ReviewResDTO.ReviewPreviewDTO>> searchReview(
);
}

@GetMapping("/my/list")
@Override
public ApiResponse<ReviewResDTO.MyReviewPreviewListDTO> getMyReviewList(
@CheckPage @RequestParam(name="page") Integer page
){
return ApiResponse.onSuccess(
GeneralSuccessCode.OK,
reviewQueryService.getMyReviewList(page)
);
}

}


Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package umc.domain.review.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.web.bind.annotation.RequestParam;
import umc.domain.review.dto.res.ReviewResDTO;
import umc.global.annotation.CheckPage;
import umc.global.apiPayload.ApiResponse;
public interface ReviewControllerDocs {
@Operation(
summary = "가게의 리뷰 목록 조회 API By 박콩(개발 중)",
description = "특정 가게의 리뷰를 모두 조회합니다. 페이지네이션으로 제공합니다."
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200",description = "성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "실패")
})
@Parameters({
@Parameter(name="storeName", description = "가게 이름"),
@Parameter(name="page",description = "페이지 번호")
})
ApiResponse<ReviewResDTO.ReviewPreViewListDTO> getReviews(
@RequestParam(name="storeName") String storeName,
@RequestParam(name="page") Integer page
);
Comment on lines +11 to +27
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옹 이렇게 인터페이스 만들어서 docs 관리하는 거 진짜 좋아보이네요


@Operation(summary = "내가 작성한 리뷰 목록 조회 API", description = "나의 리뷰 목록을 조회합니다. page는 1부터 시작")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "페이지 번호가 1보다 작을 때 발생")
})
@Parameters({
@Parameter(name = "page", description = "페이지 번호 (1 이상)")
})
public ApiResponse<ReviewResDTO.MyReviewPreviewListDTO> getMyReviewList(
@CheckPage @RequestParam(name = "page") Integer page // 커스텀 어노테이션 적용!
);
}
Loading