Skip to content

Commit

Permalink
Merge pull request #25 from TeamPINGLE/feat/14
Browse files Browse the repository at this point in the history
[feat] 번개 생성 api
  • Loading branch information
tkdwns414 authored Jan 9, 2024
2 parents 9462fd7 + d86a1a0 commit 2066f7e
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 12 deletions.
11 changes: 11 additions & 0 deletions src/main/java/org/pingle/pingleserver/annotation/GUserId.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.pingle.pingleserver.annotation;

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)
public @interface GUserId {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.pingle.pingleserver.config;

import lombok.RequiredArgsConstructor;
import org.pingle.pingleserver.interceptor.pre.GUserIdArgumentResolver;
import org.pingle.pingleserver.interceptor.pre.UserIdArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
Expand All @@ -15,9 +16,12 @@
public class WebMVCConfig implements WebMvcConfigurer {
private final UserIdArgumentResolver userIdArgumentResolver;

private final GUserIdArgumentResolver gUserIdArgumentResolver;

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
WebMvcConfigurer.super.addArgumentResolvers(resolvers);
resolvers.add(this.userIdArgumentResolver);
resolvers.add(this.gUserIdArgumentResolver);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package org.pingle.pingleserver.controller;


import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.pingle.pingleserver.annotation.UserId;
import org.pingle.pingleserver.annotation.GUserId;
import org.pingle.pingleserver.domain.Meeting;
import org.pingle.pingleserver.domain.Pin;
import org.pingle.pingleserver.dto.common.ApiResponse;
import org.pingle.pingleserver.dto.request.MeetingRequest;
import org.pingle.pingleserver.dto.type.SuccessMessage;
import org.pingle.pingleserver.service.MeetingService;
import org.pingle.pingleserver.service.PinService;

import org.pingle.pingleserver.service.UserMeetingService;
import org.springframework.web.bind.annotation.*;

Expand All @@ -13,8 +22,19 @@
public class MeetingController {

private static final String GROUP_ID = "Group-Id";

private final MeetingService meetingService;
private final UserMeetingService userMeetingService;
private final PinService pinService;

@PostMapping
public ApiResponse<?> createMeeting(@Valid @RequestBody MeetingRequest request, @GUserId Long userId,
@RequestHeader(GROUP_ID) Long groupId) {
Pin pin = pinService.verifyAndReturnPin(request, groupId);//핀 없으면 핀 생성 후 반환, 있다면 핀 생성
Meeting meeting = meetingService.createMeeting(request, pin);//번개 생성
Long userMeetingId = userMeetingService.addOwnerToMeeting(userId, meeting);

return ApiResponse.success(SuccessMessage.CREATED);
}

@PostMapping("/{meetingId}/join")
public ApiResponse<?> participateMeeting (@UserId Long userId, @PathVariable("meetingId") Long meetingId) {
Expand All @@ -27,4 +47,4 @@ public ApiResponse<?> cancelMeeting (@UserId Long userId, @PathVariable("meeting
Long cancelledId = userMeetingService.cancelMeeting(userId, meetingId);
return ApiResponse.success(SuccessMessage.OK);
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/org/pingle/pingleserver/domain/Meeting.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.pingle.pingleserver.domain.enums.MCategory;
Expand Down Expand Up @@ -32,4 +33,15 @@ public class Meeting extends BaseTimeEntity {
private LocalDateTime startAt;

private LocalDateTime endAt;

@Builder
public Meeting(Pin pin, MCategory category, String name, Integer maxParticipants, String chatLink, LocalDateTime startAt, LocalDateTime endAt) {
this.pin = pin;
this.category = category;
this.name = name;
this.maxParticipants = maxParticipants;
this.chatLink = chatLink;
this.startAt = startAt;
this.endAt = endAt;
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/pingle/pingleserver/domain/Pin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -24,4 +25,12 @@ public class Pin extends BaseTimeEntity {
private Address address;

private String name;

@Builder
public Pin(Team team, Point point, Address address, String name) {
this.team = team;
this.point = point;
this.address = address;
this.name = name;
}
}
2 changes: 2 additions & 0 deletions src/main/java/org/pingle/pingleserver/domain/UserMeeting.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.pingle.pingleserver.domain.enums.MRole;
Expand All @@ -26,6 +27,7 @@ public class UserMeeting extends BaseTimeEntity {
@Enumerated(EnumType.STRING)
private MRole meetingRole;

@Builder
public UserMeeting(User user, Meeting meeting, MRole meetingRole) {
this.user = user;
this.meeting = meeting;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.pingle.pingleserver.dto.request;

import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.pingle.pingleserver.domain.enums.MCategory;

import java.time.LocalDateTime;

public record MeetingRequest(MCategory category,
@NotNull
String name,
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")LocalDateTime startAt,
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")LocalDateTime endAt,
@NotNull
Double x,
@NotNull
Double y,
String address,
String roadAddress,
@NotNull
String location,
@Min(2)@Max(99)
Integer maxParticipants,
String chatLink) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,18 @@ public enum ErrorMessage {
INVALID_PARAMETER_ERROR(HttpStatus.BAD_REQUEST, "유효하지 않은 파라미터입니다."),
INVALID_PROVIDER_ERROR(HttpStatus.BAD_REQUEST, "유효하지 않은 소셜 플랫폼입니다."),
MISSING_REQUIRED_HEADER(HttpStatus.BAD_REQUEST, "필수 헤더가 누락되었습니다."),
NO_SUCH_PIN(HttpStatus.BAD_REQUEST, "해당하는 핀이 없습니다."),
NO_SUCH_MEETING(HttpStatus.BAD_REQUEST, "해당하는 미팅이 없습니다." ),
// Authorization Error 401
TOKEN_MALFORMED_ERROR(HttpStatus.UNAUTHORIZED, "유효하지 않은 토큰입니다."),
UNAUTHORIZED_ERROR(HttpStatus.UNAUTHORIZED, "토큰이 제공되지 않았거나 유효하지 않습니다."),
NO_SUCH_USER(HttpStatus.UNAUTHORIZED, "존재하지 않는 사용자입니다."),
// Permission Denied 403
GROUP_PERMISSION_DENIED(HttpStatus.FORBIDDEN, "해당 사용자는 그룹에 속해 있지 않습니다."),
// Not Found Error 404
NOT_FOUND_RESOURCE(HttpStatus.NOT_FOUND, "해당 리소스가 존재하지 않습니다."),
USER_NOT_FOUND_ERROR(HttpStatus.NOT_FOUND, "사용자를 찾을 수 없습니다."),
NOT_FOUND_END_POINT(HttpStatus.NOT_FOUND, "존재하지 않는 API입니다."),
NOT_FOUND_RESOURCE(HttpStatus.NOT_FOUND, "해당 리소스가 존재하지 않습니다."),
// Method Not Allowed Error 405
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED, "지원하지 않는 HTTP 메소드입니다."),
// Conflict Error 409
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.pingle.pingleserver.interceptor.pre;

import lombok.RequiredArgsConstructor;
import org.pingle.pingleserver.annotation.GUserId;
import org.pingle.pingleserver.dto.type.ErrorMessage;
import org.pingle.pingleserver.exception.BusinessException;
import org.pingle.pingleserver.service.UserMeetingService;
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;

import java.security.Principal;

@Component
@RequiredArgsConstructor
public class GUserIdArgumentResolver implements HandlerMethodArgumentResolver {

private final UserMeetingService userMeetingService;

@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().equals(Long.class)
&& parameter.hasParameterAnnotation(GUserId.class);
}

@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
final Principal principal = webRequest.getUserPrincipal();
if (principal == null) {
throw new BusinessException(ErrorMessage.NO_SUCH_USER);
}

if (webRequest.getHeader("Group-Id") == null)
throw new BusinessException(ErrorMessage.INVALID_HEADER_ERROR);
Long groupId = Long.valueOf(webRequest.getHeader("Group-Id"));

userMeetingService.verifyUser(getIdFromPrincipal(principal), groupId);

return Long.valueOf(principal.getName());
}

private Long getIdFromPrincipal (Principal principal) {
return Long.valueOf(principal.getName());
}

}


Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.pingle.pingleserver.repository;

import org.pingle.pingleserver.domain.Pin;
import org.pingle.pingleserver.domain.Point;
import org.springframework.data.jpa.repository.JpaRepository;

public interface PinRepository extends JpaRepository<Pin, Long> {
boolean existsByPoint(Point point);
Pin findByPoint(Point point);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import org.pingle.pingleserver.domain.Meeting;
import org.pingle.pingleserver.domain.UserMeeting;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;


public interface UserMeetingRepository extends JpaRepository<UserMeeting, Long> {
List<UserMeeting> findAllByMeeting(Meeting meeting);
boolean existsByUserIdAndMeeting(Long userId, Meeting meeting);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import org.pingle.pingleserver.domain.UserTeam;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface UserTeamRepository extends JpaRepository<UserTeam, Long> {
Optional<UserTeam> findByUserAndTeam(User user, Team team);

boolean existsByUserAndTeam(User user, Team team);
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
package org.pingle.pingleserver.service;

import lombok.RequiredArgsConstructor;

import org.pingle.pingleserver.domain.Meeting;
import org.pingle.pingleserver.domain.Pin;
import org.pingle.pingleserver.dto.request.MeetingRequest;
import org.pingle.pingleserver.repository.MeetingRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MeetingService {

private final MeetingRepository meetingRepository;

@Transactional
public Meeting createMeeting(MeetingRequest request, Pin pin) {
return meetingRepository.save(
Meeting.builder()
.pin(pin)
.category(request.category())
.name(request.name())
.maxParticipants(request.maxParticipants())
.chatLink(request.chatLink())
.startAt(request.startAt())
.endAt(request.endAt())
.build());

}
}
29 changes: 29 additions & 0 deletions src/main/java/org/pingle/pingleserver/service/PinService.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,40 @@
package org.pingle.pingleserver.service;

import lombok.RequiredArgsConstructor;
import org.pingle.pingleserver.domain.Address;
import org.pingle.pingleserver.domain.Pin;
import org.pingle.pingleserver.domain.Point;
import org.pingle.pingleserver.domain.Team;
import org.pingle.pingleserver.dto.request.MeetingRequest;
import org.pingle.pingleserver.dto.type.ErrorMessage;
import org.pingle.pingleserver.exception.BusinessException;
import org.pingle.pingleserver.repository.PinRepository;
import org.pingle.pingleserver.repository.TeamRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class PinService {
private final PinRepository pinRepository;
private final TeamRepository teamRepository;

@Transactional
public Pin verifyAndReturnPin(MeetingRequest request, Long groupId) {
Team team = teamRepository.findById(groupId).orElseThrow(() -> new BusinessException(ErrorMessage.NOT_FOUND_RESOURCE));
if(!exist(new Point(request.x(), request.y()))) {
return pinRepository.save(Pin.builder()
.address(new Address(request.roadAddress(), request.address()))
.name(request.location())
.point(new Point(request.x(), request.y()))
.team(team)
.build());
}
return pinRepository.findByPoint(new Point(request.x(), request.y()));
}

private boolean exist(Point point) {
return pinRepository.existsByPoint(point);
}
}
Loading

0 comments on commit 2066f7e

Please sign in to comment.