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

[BE] feat: 방 신청 구현(#32) #58

Merged
merged 11 commits into from
Jul 18, 2024
13 changes: 13 additions & 0 deletions backend/src/main/java/corea/WebConfig.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package corea;

import corea.auth.resolver.LoginMemberArgumentResolver;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public class WebConfig implements WebMvcConfigurer {
public class WebConfig implements WebMvcConfigurer {


private final LoginMemberArgumentResolver loginMemberArgumentResolver;

@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
Expand All @@ -15,4 +23,9 @@ public void addCorsMappings(CorsRegistry registry) {
.allowCredentials(true)
.maxAge(3000);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
}
}


@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(loginMemberArgumentResolver);
}
}
14 changes: 14 additions & 0 deletions backend/src/main/java/corea/auth/RequestHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package corea.auth;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;

@Component
public class RequestHandler {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public class RequestHandler {
public class RequestHandler {


private static final String AUTHORIZATION_HEADER = "Authorization";
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String AUTHORIZATION_HEADER = "Authorization";

개행하세요 ^^


public String extract(HttpServletRequest request) {
return request.getHeader(AUTHORIZATION_HEADER);
}
}
14 changes: 14 additions & 0 deletions backend/src/main/java/corea/auth/annotation/LoginUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package corea.auth.annotation;

import io.swagger.v3.oas.annotations.Hidden;

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

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Hidden()
public @interface LoginUser {
}
34 changes: 34 additions & 0 deletions backend/src/main/java/corea/auth/domain/AuthInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package corea.auth.domain;

import corea.member.domain.Member;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Objects;

@RequiredArgsConstructor
@Getter
public class AuthInfo {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public class AuthInfo {
public class AuthInfo {


private final Long id;

private final String name;

private final String email;

public static AuthInfo from(Member member){
return new AuthInfo(member.getId(), member.getUserName(), member.getEmail());
}

@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (!(o instanceof AuthInfo authInfo)) return false;
return Objects.equals(id, authInfo.id);
}

@Override
public int hashCode() {
return Objects.hashCode(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package corea.auth.resolver;

import corea.auth.RequestHandler;
import corea.auth.annotation.LoginUser;
import corea.auth.domain.AuthInfo;
import corea.exception.CoreaException;
import corea.exception.ExceptionType;
import corea.member.domain.Member;
import corea.member.repository.MemberRepository;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

@Component
@RequiredArgsConstructor
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {
public class LoginMemberArgumentResolver implements HandlerMethodArgumentResolver {


private final RequestHandler requestHandler;
private final MemberRepository memberRepository;

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(LoginUser.class);
}

@Override
public AuthInfo resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
Member member = memberRepository.findByEmail(requestHandler.extract(request))
.orElseThrow(()-> new CoreaException(ExceptionType.AUTHORIZATION_ERROR));
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
.orElseThrow(()-> new CoreaException(ExceptionType.AUTHORIZATION_ERROR));
.orElseThrow(() -> new CoreaException(ExceptionType.AUTHORIZATION_ERROR));

return AuthInfo.from(member);
}
}
20 changes: 17 additions & 3 deletions backend/src/main/java/corea/exception/CoreaException.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package corea.exception;

public class CoreaException extends Exception {
import org.springframework.http.HttpStatus;

public class CoreaException extends RuntimeException {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public class CoreaException extends RuntimeException {
public class CoreaException extends RuntimeException {


private final ExceptionType exceptionType;
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private final ExceptionType exceptionType;
private final ExceptionType exceptionType;


public CoreaException(ExceptionType exceptionType) {
super(exceptionType.getMessage());
this.exceptionType = exceptionType;
}

public CoreaException(ExceptionType exceptionType, String message) {
super(message);
this.exceptionType = exceptionType;
}

Expand All @@ -12,7 +21,12 @@ public CoreaException(ExceptionType exceptionType, Throwable cause) {
this.exceptionType = exceptionType;
}

public ExceptionType getExceptionType() {
return exceptionType;
public HttpStatus getHttpStatus() {
return exceptionType.getHttpStatus();
}

@Override
public String getMessage() {
return exceptionType.getMessage();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package corea.exception;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@Slf4j
@ControllerAdvice
public class ExceptionResponseHandler {

@ExceptionHandler(CoreaException.class)
public ResponseEntity<ErrorResponse> handleCoreaException(final CoreaException e) {
log.debug("Corea exception [statusCode = {}, errorMessage = {}, cause = {}]", e.getHttpStatus(), e.getMessage(), e.getCause());
return ResponseEntity.status(e.getHttpStatus())
.body(new ErrorResponse(e.getMessage()));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
}
}


@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleException(final Exception e) {
log.debug("Server exception [errorMessage = {}, cause = {}]", e.getMessage(), e.getCause());
return ResponseEntity.internalServerError()
.body(new ErrorResponse(e.getMessage()));
}
}
4 changes: 4 additions & 0 deletions backend/src/main/java/corea/exception/ExceptionType.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import org.springframework.http.HttpStatus;

public enum ExceptionType {

NOT_FOUND_ERROR(HttpStatus.NOT_FOUND,"해당하는 값이 없습니다."),
AUTHORIZATION_ERROR(HttpStatus.UNAUTHORIZED,"인증에 실패했습니다."),
ALREADY_APPLY(HttpStatus.BAD_REQUEST,"해당 방에 이미 참여했습니다"),
SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,"서버에 문제가 발생했습니다.");
private final HttpStatus httpStatus;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package corea.matching.controller;

import corea.auth.annotation.LoginUser;
import corea.auth.domain.AuthInfo;
import corea.matching.dto.ParticipationRequest;
import corea.matching.service.ParticipationService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/participate")
@RequiredArgsConstructor
public class ParticipateController implements ParticipationControllerSpecification {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public class ParticipateController implements ParticipationControllerSpecification {
public class ParticipateController implements ParticipationControllerSpecification {


private final ParticipationService participationService;

@PostMapping("/{id}")
public ResponseEntity<Void> participate(@PathVariable long id, @LoginUser AuthInfo authInfo) {
participationService.participate(new ParticipationRequest(id, authInfo.getId()));
return ResponseEntity.ok()
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package corea.matching.controller;

import corea.auth.domain.AuthInfo;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import org.springframework.http.ResponseEntity;

public interface ParticipationControllerSpecification {

@ApiResponses(
value = {
@ApiResponse(responseCode = "404", content = @Content(mediaType = "application/json", examples = {
@ExampleObject(name = "해당하는 방이 없는 경우", value = """
{
"message": "1에 해당하는 방 없습니다."
}
""")
})),
}
)
ResponseEntity<Void> participate(long id, AuthInfo authInfo);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@
import java.util.List;

public interface ParticipationRepository extends JpaRepository<Participation, Long> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public interface ParticipationRepository extends JpaRepository<Participation, Long> {
public interface ParticipationRepository extends JpaRepository<Participation, Long> {


List<Participation> findAllByRoomId(long roomId);

boolean existsByRoomIdAndMemberId(long roomId, long memberId);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package corea.matching.service;

import corea.exception.CoreaException;
import corea.exception.ExceptionType;
import corea.matching.domain.Participation;
import corea.matching.dto.ParticipationRequest;
import corea.matching.dto.ParticipationResponse;
import corea.member.repository.MemberRepository;
import corea.matching.repository.ParticipationRepository;
import corea.member.repository.MemberRepository;
import corea.room.repository.RoomRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand All @@ -28,10 +30,13 @@ public ParticipationResponse participate(final ParticipationRequest request) {

private void validateIdExist(final long roomId, final long memberId) {
if (!roomRepository.existsById(roomId)) {
throw new IllegalArgumentException(String.format("해당 Id의 방이 없어 참여할 수 없습니다. 입력된 방 Id=%d", roomId));
throw new CoreaException(ExceptionType.NOT_FOUND_ERROR, String.format("%d에 해당하는 방이 없습니다.", roomId));
}
if (!memberRepository.existsById(memberId)) {
throw new IllegalArgumentException(String.format("해당 Id의 멤버가 없어 방에 참여할 수 없습니다. 입력된 멤버 Id=%d", memberId));
throw new CoreaException(ExceptionType.NOT_FOUND_ERROR, String.format("%d에 해당하는 멤버가 없습니다.", memberId));
}
if (participationRepository.existsByRoomIdAndMemberId(roomId, memberId)) {
throw new CoreaException(ExceptionType.ALREADY_APPLY);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/rooms")
@RequiredArgsConstructor
public class RoomController {

private final RoomService roomService;

@GetMapping("/rooms/{id}")
@GetMapping("/{id}")
public ResponseEntity<RoomResponse> room(@PathVariable final long id) {
final RoomResponse response = roomService.findOne(id);
return ResponseEntity.ok(response);
}

@GetMapping("/rooms")
@GetMapping
public ResponseEntity<RoomResponses> rooms() {
final RoomResponses response = roomService.findAll();
return ResponseEntity.ok(response);
Expand Down
1 change: 1 addition & 0 deletions backend/src/main/java/corea/room/domain/Room.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class Room {
private int limitedParticipantsSize;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "manager_id",foreignKey = @ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Member manager;

private LocalDateTime recruitmentDeadline;
Expand Down
9 changes: 6 additions & 3 deletions backend/src/main/java/corea/room/dto/RoomResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ public record RoomResponse(
LocalDateTime reviewDeadline
) {

//TODO 해당 객체를 사용한다면 반영
public static RoomResponse of(final Room room, final String author) {
return null;
public static RoomResponse of(final Room room) {
return new RoomResponse(
room.getId(), room.getTitle(), room.getContent(), room.getManager().getEmail(),
room.getRepositoryLink(), room.getThumbnailLink(), room.getMatchingSize(), List.of(room.getKeyword()),
room.getCurrentParticipantsSize(), room.getLimitedParticipantsSize(), room.getRecruitmentDeadline(), room.getReviewDeadline()
);
}
}
10 changes: 1 addition & 9 deletions backend/src/main/java/corea/room/service/RoomService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package corea.room.service;

import corea.member.domain.Member;
import corea.room.domain.Room;
import corea.room.dto.RoomCreateRequest;
import corea.room.dto.RoomResponse;
Expand Down Expand Up @@ -42,15 +41,8 @@ public RoomResponses findAll() {
.collect(collectingAndThen(toList(), RoomResponses::new));
}

//TODO 해당 객체를 사용한다면 반영
private RoomResponse toRoomResponse(final Room room) {
final Member member = null;
return RoomResponse.of(room, member.getEmail());
}

private Member getMember(final long memberId) {
return memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException(String.format("해당 Id의 멤버가 없습니다. 입력된 Id=%d", memberId)));
return RoomResponse.of(room);
}

private Room getRoom(final long roomId) {
Expand Down
Loading
Loading