diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/controller/MeetUpController.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/controller/MeetUpController.java index 5fdd36c..df69256 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/meeting/controller/MeetUpController.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/controller/MeetUpController.java @@ -1,6 +1,7 @@ package com.bapMate.bapMateServer.domain.meeting.controller; import com.bapMate.bapMateServer.domain.meeting.dto.request.MeetUpRequestDto; +import com.bapMate.bapMateServer.domain.meeting.entity.MeetUp; import com.bapMate.bapMateServer.domain.meeting.service.MeetUpService; import com.bapMate.bapMateServer.domain.participation.entity.Participation; import com.bapMate.bapMateServer.domain.participation.repository.ParticipationRepository; @@ -14,8 +15,12 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -28,6 +33,19 @@ public class MeetUpController { private final MeetUpService meetUpService; private final ParticipationService participationService; private final AuthentiatedUserUtils authentiatedUserUtils; + + @Operation(description = "모임 대표 이미지") + @PostMapping(value = "/image/{meetUpId}", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.APPLICATION_JSON_VALUE}) + public SuccessResponse uploadImage(@PathVariable Long meetUpId,@RequestParam("file") MultipartFile file) throws IOException { + try { + meetUpService.uploadImage(meetUpId, file); + SuccessResponse successResponse = SuccessResponse.onSuccess(200); + return successResponse; + } catch (IOException e) { + throw new IllegalArgumentException("오류"); + } + } + @Operation(summary = "모임을 생성합니다.") @PostMapping("/host") public SuccessResponse createMeetUp(@RequestBody MeetUpRequestDto meetUpRequestDto) { @@ -40,13 +58,40 @@ public SuccessResponse createMeetUp(@RequestBody MeetUpRequestDto meetUp } @Operation(summary = "생성한 모임을 확인합니다.") @GetMapping("/host") - public SuccessResponse getCreatedMeetUp() { + public SuccessResponse> getCreatedMeetUp() { User user = authentiatedUserUtils.getCurrentUser(); - Participation participation = participationService.getParticipation(user); - SuccessResponse successResponse = SuccessResponse.onSuccess(200,participation); + List participation = participationService.getParticipation(user); + System.out.println(participation.size()); + System.out.println(participation.get(0).getName()); + SuccessResponse> successResponse = SuccessResponse.onSuccess(200, participation); + System.out.println(successResponse.getData()); + return successResponse; + } + @Operation(summary = "참여한 모임을 확인합니다.") + @GetMapping("/participate") + public SuccessResponse> getParticipatedMeetUp() { + User user = authentiatedUserUtils.getCurrentUser(); + + List participation = participationService.getParticipations(user); + + SuccessResponse> successResponse = SuccessResponse.onSuccess(200, participation); + System.out.println(successResponse.getData()); return successResponse; } + @Operation(summary = "모임에 참여합니다") + @PostMapping("/participate/{meetUpId}") + public SuccessResponse participateMeetUp(@RequestParam Long meetUpId){ + User user = authentiatedUserUtils.getCurrentUser(); + + meetUpService.participateMeetUp(user,meetUpId); + + SuccessResponse successResponse = SuccessResponse.onSuccess(200); + return successResponse; + } + + + } diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/dto/request/MeetUpRequestDto.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/dto/request/MeetUpRequestDto.java index 094cebc..9a51970 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/meeting/dto/request/MeetUpRequestDto.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/dto/request/MeetUpRequestDto.java @@ -18,20 +18,22 @@ public class MeetUpRequestDto { private LocalDateTime date; private String restaurant; private int numberOfPeople; - private MeetUpAtmosphere meetUpAtmosphere; - private RegionAtmosphere regionAtmosphere; + private String meetUpAtmosphere; + private String regionAtmosphere; private String representationImage; public MeetUp toEntity() { + RegionAtmosphere regionAtmosphereEnum = RegionAtmosphere.fromTitle(regionAtmosphere); + MeetUpAtmosphere meetUpAtmosphereEnum = MeetUpAtmosphere.fromTitle(meetUpAtmosphere); return MeetUp.builder() .chatRoomLink(chatRoomLink) .date(date) - .meetUpAtmosphere(meetUpAtmosphere) + .meetUpAtmosphere(meetUpAtmosphereEnum) .introduce(introduce) .restaurant(restaurant) .numberOfPeople(numberOfPeople) .region(region) - .regionAtmosphere(regionAtmosphere) + .regionAtmosphere(regionAtmosphereEnum) .representationImage(representationImage) .name(name) .build(); diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/dto/response/MeetUpResponseDto.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/dto/response/MeetUpResponseDto.java new file mode 100644 index 0000000..de5b13e --- /dev/null +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/dto/response/MeetUpResponseDto.java @@ -0,0 +1,22 @@ +package com.bapMate.bapMateServer.domain.meeting.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; + +import java.time.LocalDateTime; + +@AllArgsConstructor +@Builder +public class MeetUpResponseDto { + private Long id; + private String name; + private String introduce; + private String chatRoomLink; + private String region; + private LocalDateTime date; + private String restaurant; + private int numberOfPeople; + private String meetUpAtmosphere; + private String regionAtmosphere; + private String representationImage; +} diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/entity/MeetUp.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/entity/MeetUp.java index 54d8483..a73848b 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/meeting/entity/MeetUp.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/entity/MeetUp.java @@ -2,11 +2,15 @@ import com.bapMate.bapMateServer.domain.meeting.enums.MeetUpAtmosphere; import com.bapMate.bapMateServer.domain.meeting.enums.RegionAtmosphere; +import com.bapMate.bapMateServer.domain.participation.entity.Participation; +import com.bapMate.bapMateServer.domain.user.entity.User; import com.bapMate.bapMateServer.global.entity.BaseTimeEntity; import jakarta.persistence.*; import lombok.*; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -15,16 +19,16 @@ @Builder public class MeetUp extends BaseTimeEntity { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "meeting_id") private Long id; - private String name; private String introduce; private String chatRoomLink; private String region; private LocalDateTime date; private String restaurant; + private int currentNumberOfPeople; private int numberOfPeople; @Enumerated(EnumType.STRING) private MeetUpAtmosphere meetUpAtmosphere; @@ -32,4 +36,12 @@ public class MeetUp extends BaseTimeEntity { private RegionAtmosphere regionAtmosphere; private String representationImage; + public int updateNumberOfPeople(int currentNumber) { + this.currentNumberOfPeople = currentNumber + 1; + return currentNumberOfPeople; + } + public void updateImgUrl(String imageUrl) { + this.representationImage = imageUrl; + } + } diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/enums/MeetUpAtmosphere.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/enums/MeetUpAtmosphere.java index eccb604..588a017 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/meeting/enums/MeetUpAtmosphere.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/enums/MeetUpAtmosphere.java @@ -16,4 +16,13 @@ public enum MeetUpAtmosphere { public String getTitle() {return title;} public int getCheck() {return check;} + + public static MeetUpAtmosphere fromTitle(String title) { + for (MeetUpAtmosphere atmosphere : MeetUpAtmosphere.values()) { + if (atmosphere.title.equals(title)) { + return atmosphere; + } + } + return null; // 매칭되는 값이 없을 경우 + } } diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/enums/RegionAtmosphere.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/enums/RegionAtmosphere.java index cd5a91b..26a568d 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/meeting/enums/RegionAtmosphere.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/enums/RegionAtmosphere.java @@ -11,4 +11,14 @@ public enum RegionAtmosphere { public String getTitle() {return title;} public int getCheck() {return check;} + + // String 값을 Enum으로 변환하는 메서드 + public static RegionAtmosphere fromTitle(String title) { + for (RegionAtmosphere atmosphere : RegionAtmosphere.values()) { + if (atmosphere.title.equals(title)) { + return atmosphere; + } + } + throw new IllegalArgumentException("Unknown title: " + title); + } } diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/AlreadyParticipatedException.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/AlreadyParticipatedException.java new file mode 100644 index 0000000..eb07afa --- /dev/null +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/AlreadyParticipatedException.java @@ -0,0 +1,7 @@ +package com.bapMate.bapMateServer.domain.meeting.exception; + +import com.bapMate.bapMateServer.global.exception.base.BaseException; + +public class AlreadyParticipatedException extends BaseException { + public AlreadyParticipatedException() {super(MeetingErrorCode.ALREADY_PARTICIPATED_ERROR);} +} diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/FullCapacityException.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/FullCapacityException.java new file mode 100644 index 0000000..f128883 --- /dev/null +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/FullCapacityException.java @@ -0,0 +1,9 @@ +package com.bapMate.bapMateServer.domain.meeting.exception; + +import com.bapMate.bapMateServer.global.exception.base.BaseException; + +public class FullCapacityException extends BaseException { + public FullCapacityException() { + super(MeetingErrorCode.FULL_CAPACITY_ERROR); + } +} diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/MeetingErrorCode.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/MeetingErrorCode.java new file mode 100644 index 0000000..e7eaba8 --- /dev/null +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/MeetingErrorCode.java @@ -0,0 +1,27 @@ +package com.bapMate.bapMateServer.domain.meeting.exception; + +import com.bapMate.bapMateServer.global.exception.base.BaseErrorCode; +import com.bapMate.bapMateServer.global.exception.dto.ErrorReason; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import static com.bapMate.bapMateServer.global.constant.StaticValue.*; + +@Getter +@AllArgsConstructor +public enum MeetingErrorCode implements BaseErrorCode { + FULL_CAPACITY_ERROR(BAD_REQUEST,"MEETING_404","인원이 다 찼습니다"), + MEETING_NOT_FOUND(BAD_REQUEST,"MEETING_404","모임이 존재하지 않습니다"), + ALREADY_PARTICIPATED_ERROR(BAD_REQUEST,"MEETING_404","이미 참여한 모임입니다"), + NOT_ALLOWED_TO_PARTICIPATE_ERROR(BAD_REQUEST,"MEETING_404","생성한 모임에는 참여할 수 없습니다"); + + private final int status; + private final String code; + private final String reason; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.of(status, code, reason); + } +} diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/MeetingNotFoundException.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/MeetingNotFoundException.java new file mode 100644 index 0000000..60ae4da --- /dev/null +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/MeetingNotFoundException.java @@ -0,0 +1,9 @@ +package com.bapMate.bapMateServer.domain.meeting.exception; + +import com.bapMate.bapMateServer.global.exception.base.BaseException; + +public class MeetingNotFoundException extends BaseException { + public MeetingNotFoundException() { + super(MeetingErrorCode.MEETING_NOT_FOUND); + } +} diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/NotAllowedToParticipateException.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/NotAllowedToParticipateException.java new file mode 100644 index 0000000..7aad6be --- /dev/null +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/exception/NotAllowedToParticipateException.java @@ -0,0 +1,7 @@ +package com.bapMate.bapMateServer.domain.meeting.exception; + +import com.bapMate.bapMateServer.global.exception.base.BaseException; + +public class NotAllowedToParticipateException extends BaseException { + public NotAllowedToParticipateException() {super(MeetingErrorCode.NOT_ALLOWED_TO_PARTICIPATE_ERROR);} +} diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/repository/MeetUpRepository.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/repository/MeetUpRepository.java index fcf48a8..6b233ea 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/meeting/repository/MeetUpRepository.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/repository/MeetUpRepository.java @@ -2,6 +2,14 @@ import com.bapMate.bapMateServer.domain.meeting.entity.MeetUp; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; public interface MeetUpRepository extends JpaRepository { + @Modifying + @Query("UPDATE MeetUp m SET m.currentNumberOfPeople = m.currentNumberOfPeople + 1 WHERE m.id = :meetUpId") + void incrementCurrentNumberOfPeople(@Param("meetUpId") Long meetUpId); } diff --git a/src/main/java/com/bapMate/bapMateServer/domain/meeting/service/MeetUpService.java b/src/main/java/com/bapMate/bapMateServer/domain/meeting/service/MeetUpService.java index 69c7104..1bfcb2d 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/meeting/service/MeetUpService.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/meeting/service/MeetUpService.java @@ -3,21 +3,46 @@ import com.bapMate.bapMateServer.domain.meeting.dto.request.MeetUpRequestDto; import com.bapMate.bapMateServer.domain.meeting.entity.MeetUp; import com.bapMate.bapMateServer.domain.meeting.enums.MeetUpStatus; +import com.bapMate.bapMateServer.domain.meeting.exception.FullCapacityException; +import com.bapMate.bapMateServer.domain.meeting.exception.MeetingNotFoundException; +import com.bapMate.bapMateServer.domain.meeting.exception.NotAllowedToParticipateException; import com.bapMate.bapMateServer.domain.meeting.repository.MeetUpRepository; import com.bapMate.bapMateServer.domain.participation.entity.Participation; import com.bapMate.bapMateServer.domain.participation.repository.ParticipationRepository; +import com.bapMate.bapMateServer.domain.participation.service.ParticipationService; import com.bapMate.bapMateServer.domain.user.entity.User; +import com.bapMate.bapMateServer.domain.user.exception.UserNotFoundException; +import com.bapMate.bapMateServer.global.S3.S3Service; +import com.bapMate.bapMateServer.global.exception.handler.GlobalExceptionHandler; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.ErrorResponseException; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; @RequiredArgsConstructor -@Transactional @Service public class MeetUpService { private final ParticipationRepository participationRepository; private final MeetUpRepository meetUpRepository; - public void uploadMeetUp(User user, MeetUpRequestDto requestDto) { + private final ParticipationService participationService; + private final S3Service s3UploadService; + + @Transactional + public void uploadImage(Long meetUpId, MultipartFile file) throws IOException { + // S3에 이미지 파일 업로드 및 업로드된 파일의 URL 생성 + String imageUrl = s3UploadService.upload(file); + MeetUp meetUp = meetUpRepository.findById(meetUpId).orElseThrow(UserNotFoundException::new); + + meetUp.updateImgUrl(imageUrl); + meetUpRepository.save(meetUp); + } + + public MeetUp uploadMeetUp(User user, MeetUpRequestDto requestDto) { MeetUp meetUp = turnCheckToOne(requestDto); Participation participation = Participation.builder() .meetUp(meetUp) @@ -25,11 +50,81 @@ public void uploadMeetUp(User user, MeetUpRequestDto requestDto) { .meetUpStatus(MeetUpStatus.HOST.getStatus()) .build(); participationRepository.save(participation); + //addParticipant(meetUp, participation); + return meetUp; } private MeetUp turnCheckToOne(MeetUpRequestDto requestDto) { MeetUp meetUp = requestDto.toEntity(); meetUpRepository.save(meetUp); + System.out.println(meetUp.getName()); return meetUp; } + + + + @Transactional + public Participation participateMeetUp(User user, Long meetUpId) { + //meetUpId를 통해 meetUp가져오기 - NotFound + MeetUp meetUp = meetUpRepository.findById(meetUpId).orElseThrow(() -> new MeetingNotFoundException()); + + //meetUp과 User랑 연결된 Participant가져오고 중복 검증까지 하기 + validate(user,meetUpId,meetUp); + + Participation participation = addParticipation(meetUp,user); + + return participation; + } + + @Transactional + public Participation addParticipation(MeetUp meetUp, User user) { + //participation을 meetUp에 추가하기 + Participation participation = Participation.builder() + .meetUp(meetUp) + .user(user) + .meetUpStatus(MeetUpStatus.PARTICIPANT.getStatus()) + .build(); + participationRepository.save(participation); + return participation; + } + + private void validate(User user,Long meetUpId,MeetUp meetUp) { + + validateDuplicateHost(user, meetUpId); + validateDuplicateParticipant(user,meetUpId); + validateCapacity(meetUp,meetUpId); + } + + @Transactional + void validateCapacity(MeetUp meetUp, Long meetUpId) { + //meetUp의 count 갱신하기 다 찼으면 에러 발생시키기 -FullCapacity + int number = meetUp.getCurrentNumberOfPeople(); + if(!(number < meetUp.getNumberOfPeople())){ + throw new FullCapacityException(); + } + participateMeetUp(meetUpId); + } + + private void validateDuplicateParticipant(User user, Long meetUpId) { + participationService.checkExistence(user,meetUpId); + } + + private void validateDuplicateHost(User user, Long meetUpId) { + List participations = participationRepository.findAllByUserId(user.getId()); + //meetUp을 가져와 meetUp과 매핑된 participant가져와 +//meetUp을 가져와 meetUp과 매핑된 participant를 가져옵니다. + Optional hostUserId = participationService.getHostUserId(participations, meetUpId); + +// hostUserId가 존재하고, 그 값이 현재 사용자의 ID와 같다면 예외를 던집니다. + hostUserId.ifPresent(id -> { + if (id.equals(user.getId())) { + throw new NotAllowedToParticipateException(); + } + }); + } + + @Transactional + public void participateMeetUp(Long meetUpId) { + meetUpRepository.incrementCurrentNumberOfPeople(meetUpId); + } } diff --git a/src/main/java/com/bapMate/bapMateServer/domain/participation/entity/Participation.java b/src/main/java/com/bapMate/bapMateServer/domain/participation/entity/Participation.java index 8563d42..923016d 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/participation/entity/Participation.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/participation/entity/Participation.java @@ -14,7 +14,7 @@ @Entity public class Participation extends BaseTimeEntity { @Id - @GeneratedValue + @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "participation_id") private Long id; diff --git a/src/main/java/com/bapMate/bapMateServer/domain/participation/repository/ParticipationRepository.java b/src/main/java/com/bapMate/bapMateServer/domain/participation/repository/ParticipationRepository.java index d8e9d65..07587b1 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/participation/repository/ParticipationRepository.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/participation/repository/ParticipationRepository.java @@ -3,6 +3,11 @@ import com.bapMate.bapMateServer.domain.participation.entity.Participation; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface ParticipationRepository extends JpaRepository { + List findAllByUserId(Long id); + + //Participation findByMeetUpId(Long meetUpId); } diff --git a/src/main/java/com/bapMate/bapMateServer/domain/participation/service/ParticipationService.java b/src/main/java/com/bapMate/bapMateServer/domain/participation/service/ParticipationService.java index dab2106..5defad9 100644 --- a/src/main/java/com/bapMate/bapMateServer/domain/participation/service/ParticipationService.java +++ b/src/main/java/com/bapMate/bapMateServer/domain/participation/service/ParticipationService.java @@ -1,8 +1,12 @@ package com.bapMate.bapMateServer.domain.participation.service; +import com.bapMate.bapMateServer.domain.meeting.entity.MeetUp; +import com.bapMate.bapMateServer.domain.meeting.enums.MeetUpStatus; +import com.bapMate.bapMateServer.domain.meeting.exception.AlreadyParticipatedException; import com.bapMate.bapMateServer.domain.participation.entity.Participation; import com.bapMate.bapMateServer.domain.participation.repository.ParticipationRepository; import com.bapMate.bapMateServer.domain.user.entity.User; +import com.bapMate.bapMateServer.domain.user.exception.UserNotFoundException; import com.bapMate.bapMateServer.global.exception.GlobalErrorCode; import com.bapMate.bapMateServer.global.exception.base.BaseException; import com.bapMate.bapMateServer.global.response.ErrorResponse; @@ -10,7 +14,9 @@ import org.springframework.stereotype.Service; import org.springframework.web.ErrorResponseException; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import static com.bapMate.bapMateServer.global.exception.GlobalErrorCode.INVALID_TOKEN; @@ -18,11 +24,54 @@ @AllArgsConstructor public class ParticipationService { private final ParticipationRepository participationRepository; - public Participation getParticipation(User user) { - Optional participation = participationRepository.findById(user.getId()); + public List getParticipation(User user) { + List participation = validateUser(user); + List hostMeetUps = participation.stream() + .filter(meet -> "HOST".equals(meet.getMeetUpStatus())) + .map(Participation::getMeetUp) + .distinct() // Ensure uniqueness + .collect(Collectors.toList()); + return hostMeetUps; + } + + private List validateUser(User user) { + List participation = participationRepository.findAllByUserId(user.getId()); if(user.getId() == null) { - throw new IllegalArgumentException("존재하지 않는 유저입니다"); + throw new UserNotFoundException(); + } + return participation; + } + + public List getParticipations(User user) { + List participation = validateUser(user); + List hostMeetUps = participation.stream() + .filter(meet -> "PARTICIPANT".equals(meet.getMeetUpStatus())) + .map(Participation::getMeetUp) + .distinct() // Ensure uniqueness + .collect(Collectors.toList()); + System.out.println(hostMeetUps.size()); + return hostMeetUps; + } + + public Optional getHostUserId(List participationList, Long meetUpId) { + return participationList.stream() + .filter(participation -> meetUpId.equals(participation.getMeetUp().getId())) + .filter(participation -> "HOST".equals(participation.getMeetUpStatus())) + .map(participation -> participation.getUser().getId()) + .findFirst(); + } + + public void checkExistence(User user, Long meetUpId) { + List participation = validateUser(user); + + // Check if userId is in the list of participating MeetUps + boolean userIdExists = participation.stream() + .filter(meet -> meetUpId.equals(meet.getMeetUp().getId())) + .filter(meet -> "PARTICIPANT".equals(meet.getMeetUpStatus())) + .anyMatch(meet -> meet.getUser().getId().equals(user.getId())); + + if (userIdExists) { + throw new AlreadyParticipatedException(); } - return participation.get(); } } diff --git a/src/main/java/com/bapMate/bapMateServer/global/S3/S3Config.java b/src/main/java/com/bapMate/bapMateServer/global/S3/S3Config.java new file mode 100644 index 0000000..5369562 --- /dev/null +++ b/src/main/java/com/bapMate/bapMateServer/global/S3/S3Config.java @@ -0,0 +1,33 @@ +package com.bapMate.bapMateServer.global.S3; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class S3Config { + + @Value("${cloud.aws.credentials.access-key}") + private String accessKey; + + @Value("${cloud.aws.credentials.secret-key}") + private String secretKey; + + @Value("${cloud.aws.region.static}") + private String region; + + @Bean + public AmazonS3Client amazonS3Client() { + BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey); + + return (AmazonS3Client) AmazonS3ClientBuilder + .standard() + .withRegion(region) + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/bapMate/bapMateServer/global/S3/S3Service.java b/src/main/java/com/bapMate/bapMateServer/global/S3/S3Service.java new file mode 100644 index 0000000..d39ff5e --- /dev/null +++ b/src/main/java/com/bapMate/bapMateServer/global/S3/S3Service.java @@ -0,0 +1,96 @@ +package com.bapMate.bapMateServer.global.S3; + +import com.amazonaws.AmazonServiceException; +import com.amazonaws.HttpMethod; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3Client; +import com.amazonaws.services.s3.Headers; +import com.amazonaws.services.s3.model.CannedAccessControlList; +import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLDecoder; +import java.util.Date; +import java.util.UUID; + +@Slf4j +@RequiredArgsConstructor +@Service +public class S3Service { + @Value("${cloud.aws.s3.bucket}") + private String bucket; + + private final AmazonS3 amazonS3; + private final AmazonS3Client amazonS3Client; + + + /* 1. 파일 업로드 */ + public String upload(MultipartFile multipartFile) { + // 메타데이터 설정 + ObjectMetadata objectMetadata = new ObjectMetadata(); + objectMetadata.setContentType(multipartFile.getContentType()); + objectMetadata.setContentLength(multipartFile.getSize()); + + // 실제 S3 bucket 디렉토리명 설정 + // 파일명 중복을 방지하기 위한 UUID 추가 + String fileName = bucket + "/" + UUID.randomUUID() + "." + multipartFile.getOriginalFilename(); + + try (InputStream inputStream = multipartFile.getInputStream()) { + amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, inputStream, objectMetadata)); + } catch (IOException e) { + log.error("S3 파일 업로드에 실패했습니다. {}", e.getMessage()); + throw new IllegalStateException("S3 파일 업로드에 실패했습니다."); + } + return amazonS3Client.getUrl(bucket, fileName).toString(); + } + + + /* 2. 파일 삭제 */ + public void delete (String keyName) { + try { + // deleteObject(버킷명, 키값)으로 객체 삭제 + amazonS3.deleteObject(bucket, keyName); + } catch (AmazonServiceException e) { + log.error(e.toString()); + } + } + + /* 3. 파일의 presigned URL 반환 */ + public String getPreSignedUrl(String bucket, String prefix, String fileName) { + if (!prefix.equals("")) { + fileName = prefix + "/" + fileName; + } + GeneratePresignedUrlRequest generatePresignedUrlRequest = getGeneratePreSignedUrlRequest(bucket, fileName); + URL url = amazonS3.generatePresignedUrl(generatePresignedUrlRequest); + return url.toString(); + } + + private GeneratePresignedUrlRequest getGeneratePreSignedUrlRequest(String bucket, String fileName) { + GeneratePresignedUrlRequest generatePresignedUrlRequest = + new GeneratePresignedUrlRequest(bucket, fileName) + .withMethod(HttpMethod.PUT) + .withExpiration(getPreSignedUrlExpiration()); + generatePresignedUrlRequest.addRequestParameter( + Headers.S3_CANNED_ACL, + CannedAccessControlList.PublicRead.toString()); + return generatePresignedUrlRequest; + } + + private Date getPreSignedUrlExpiration() { + Date expiration = new Date(); + long expTimeMillis = expiration.getTime(); + expTimeMillis += 1000 * 60 * 2; + expiration.setTime(expTimeMillis); + log.info(expiration.toString()); + return expiration; + } +} diff --git a/src/main/java/com/bapMate/bapMateServer/global/config/CorsConfig.java b/src/main/java/com/bapMate/bapMateServer/global/config/CorsConfig.java index f66749b..4deb518 100644 --- a/src/main/java/com/bapMate/bapMateServer/global/config/CorsConfig.java +++ b/src/main/java/com/bapMate/bapMateServer/global/config/CorsConfig.java @@ -16,6 +16,7 @@ public void addCorsMappings(CorsRegistry registry) { ArrayList allowedOriginPatterns = new ArrayList<>(); allowedOriginPatterns.add("http://localhost:3000"); allowedOriginPatterns.add("http://localhost:8080"); + allowedOriginPatterns.add("http://52.79.201.97:8080"); String[] patterns = allowedOriginPatterns.toArray(String[]::new); registry.addMapping("/**") diff --git a/src/main/java/com/bapMate/bapMateServer/global/response/SuccessResponse.java b/src/main/java/com/bapMate/bapMateServer/global/response/SuccessResponse.java index 267bac0..8478ff9 100644 --- a/src/main/java/com/bapMate/bapMateServer/global/response/SuccessResponse.java +++ b/src/main/java/com/bapMate/bapMateServer/global/response/SuccessResponse.java @@ -7,6 +7,8 @@ import lombok.Getter; import lombok.ToString; +import java.util.List; + @Getter public class SuccessResponse { @JsonProperty("status") diff --git a/src/test/java/com/bapMate/bapMateServer/domain/MeetUpServiceTest.java b/src/test/java/com/bapMate/bapMateServer/domain/MeetUpServiceTest.java new file mode 100644 index 0000000..68f7f87 --- /dev/null +++ b/src/test/java/com/bapMate/bapMateServer/domain/MeetUpServiceTest.java @@ -0,0 +1,290 @@ +package com.bapMate.bapMateServer.domain; + +import com.bapMate.bapMateServer.domain.meeting.dto.request.MeetUpRequestDto; +import com.bapMate.bapMateServer.domain.meeting.entity.MeetUp; +import com.bapMate.bapMateServer.domain.meeting.enums.MeetUpAtmosphere; +import com.bapMate.bapMateServer.domain.meeting.enums.MeetUpStatus; +import com.bapMate.bapMateServer.domain.meeting.enums.RegionAtmosphere; +import com.bapMate.bapMateServer.domain.meeting.exception.FullCapacityException; +import com.bapMate.bapMateServer.domain.meeting.exception.MeetingNotFoundException; +import com.bapMate.bapMateServer.domain.meeting.repository.MeetUpRepository; +import com.bapMate.bapMateServer.domain.meeting.service.MeetUpService; +import com.bapMate.bapMateServer.domain.participation.entity.Participation; +import com.bapMate.bapMateServer.domain.participation.repository.ParticipationRepository; +import com.bapMate.bapMateServer.domain.user.entity.AuthInfo; +import com.bapMate.bapMateServer.domain.user.entity.LoginType; +import com.bapMate.bapMateServer.domain.user.entity.User; +import com.bapMate.bapMateServer.domain.user.repositroy.UserRepository; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static com.bapMate.bapMateServer.domain.meeting.enums.MeetUpStatus.PARTICIPANT; +import static org.assertj.core.api.ClassBasedNavigableIterableAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +@Transactional +class MeetUpServiceTest { + +// @InjectMocks +// private MeetUpService meetUpService; +// +// @Mock +// private MeetUpRepository meetUpRepository; +// +// @Mock +// private UserRepository userRepository; +// +// @Mock +// private ParticipationRepository participationRepository; +// +// @Test +// void 모임_생성_성공() { +// User user = User.builder() +// .id(1L) // Set the ID as needed +// .name("John Doe") // Set the name as needed +// .authInfo(AuthInfo.builder() +// .email("john@example.com") // Set the email as needed +// .loginType(LoginType.KAKAO) // Set the login type as needed +// .build()) +// .universityName("Example University") // Set the university name as needed +// .build(); // You may need to create a valid user for testing +// userRepository.save(user); +// +// MeetUpRequestDto meetUpRequestDto = MeetUpRequestDto.builder() +// .name("Sample MeetUp") // Set the name as needed +// .introduce("A sample meet-up for testing") // Set the introduce as needed +// .chatRoomLink("https://example.com/chat-room") // Set the chat room link as needed +// .region("Sample Region") // Set the region as needed +// .date(LocalDateTime.now()) // Set the date as needed +// .restaurant("Sample Restaurant") // Set the restaurant as needed +// .numberOfPeople(10) // Set the total number of people as needed +// .meetUpAtmosphere("#조용한식사") // Set the meet-up atmosphere as needed +// .regionAtmosphere("#인스타감성") // Set the region atmosphere as needed +// .representationImage("sample-image.jpg") // Set the representation image as needed +// .build(); +// +// MeetUp meetUp = meetUpService.uploadMeetUp(user,meetUpRequestDto); +// System.out.println(meetUp.getId()); +// System.out.println(meetUp.getAllUsers().size()); +// List participations = participationRepository.findAll(); +// //System.out.println(participations.size()); +// Optional participationA = participationRepository.findByUserId(user.getId()); +// Assertions.assertThat(participationA.isPresent()); +// String meetId = participationA.get().getMeetUpStatus(); +// System.out.println(meetId); +// //.get().getMeetUp().getId(); +// //Long meetUpId = 1L; +//// System.out.println(meetId); +//// +//// Optional meet = meetUpRepository.findById(meetId); +//// meet.isPresent(); +//// System.out.println(meet.get().getCurrentNumberOfPeople()); +//// Assertions.assertThat(meet.get().getAllUsers().get(0).getMeetUpStatus()).isEqualTo("HOST"); +// //질문.. 왜 이렇게 test를 작성하면 null로 print?? +// } +// +// @Test +// void participateMeetUp_Success() { +// // Arrange +// Long meetUpId = 1L; +// //Participation participation = new Participation(); +// User user = User.builder() +// .id(1L) // Set the ID as needed +// .name("John Doe") // Set the name as needed +// .authInfo(AuthInfo.builder() +// .email("john@example.com") // Set the email as needed +// .loginType(LoginType.KAKAO) // Set the login type as needed +// .build()) +// .universityName("Example University") // Set the university name as needed +// .build(); // You may need to create a valid user for testing +// +// //when(userRepository.save(any())).thenReturn(user); +// MeetUp meetUp = MeetUp.builder() +// .id(1L) // Set the ID as needed +// .name("Sample MeetUp") // Set the name as needed +// .introduce("A sample meet-up for testing") // Set the introduce as needed +// .chatRoomLink("https://example.com/chat-room") // Set the chat room link as needed +// .region("Sample Region") // Set the region as needed +// .date(LocalDateTime.now()) // Set the date as needed +// .restaurant("Sample Restaurant") // Set the restaurant as needed +// .currentNumberOfPeople(0) // Initial current number of people +// .numberOfPeople(10) // Set the total number of people as needed +// .meetUpAtmosphere(MeetUpAtmosphere.QUIET) // Set the meet-up atmosphere as needed +// .regionAtmosphere(RegionAtmosphere.INSTA) // Set the region atmosphere as needed +// .representationImage("sample-image.jpg") // Set the representation image as needed +// .build(); +// when(meetUpRepository.findById(meetUpId)).thenReturn(Optional.of(meetUp)); +// +// Participation participation = Participation.builder() +// .meetUp(meetUp) +// .user(user) +// .meetUpStatus(MeetUpStatus.HOST.getStatus()) +// .build(); +// when(participationRepository.save(any())).thenReturn(participation); +// +//// when(meetUpRepository.findById(meetUpId)).thenReturn(Optional.of(meetUp)); +//// System.out.println(meetUp.getName()); +// User userA = User.builder() +// .id(2L) // Set the ID as needed +// .name("John") // Set the name as needed +// .authInfo(AuthInfo.builder() +// .email("john1@example.com") // Set the email as needed +// .loginType(LoginType.KAKAO) // Set the login type as needed +// .build()) +// .universityName("Example University") // Set the university name as needed +// .build(); // You may need to create a valid user for testing +// +// // Act +// Participation participationA = meetUpService.participateMeetUp(userA, meetUpId); +// System.out.println(meetUp.getCurrentNumberOfPeople()); +// +// Assertions.assertThat(participation.getMeetUpStatus()).isEqualTo("HOST"); +// Assertions.assertThat(participationA.getMeetUpStatus()).isEqualTo("PARTICIPANT"); +// } +// +// @Test +// void participateMeetUp_Many_Success() { +// // Arrange +// Long meetUpId = 1L; +// //Participation participation = new Participation(); +// User user = User.builder() +// .id(1L) // Set the ID as needed +// .name("John Doe") // Set the name as needed +// .authInfo(AuthInfo.builder() +// .email("john@example.com") // Set the email as needed +// .loginType(LoginType.KAKAO) // Set the login type as needed +// .build()) +// .universityName("Example University") // Set the university name as needed +// .build(); // You may need to create a valid user for testing +// +// //when(userRepository.save(any())).thenReturn(user); +// MeetUp meetUp = MeetUp.builder() +// .id(1L) // Set the ID as needed +// .name("Sample MeetUp") // Set the name as needed +// .introduce("A sample meet-up for testing") // Set the introduce as needed +// .chatRoomLink("https://example.com/chat-room") // Set the chat room link as needed +// .region("Sample Region") // Set the region as needed +// .date(LocalDateTime.now()) // Set the date as needed +// .restaurant("Sample Restaurant") // Set the restaurant as needed +// .currentNumberOfPeople(0) // Initial current number of people +// .numberOfPeople(10) // Set the total number of people as needed +// .meetUpAtmosphere(MeetUpAtmosphere.QUIET) // Set the meet-up atmosphere as needed +// .regionAtmosphere(RegionAtmosphere.INSTA) // Set the region atmosphere as needed +// .representationImage("sample-image.jpg") // Set the representation image as needed +// .build(); +// when(meetUpRepository.findById(meetUpId)).thenReturn(Optional.of(meetUp)); +// +// Participation participation = Participation.builder() +// .meetUp(meetUp) +// .user(user) +// .meetUpStatus(MeetUpStatus.HOST.getStatus()) +// .build(); +// when(participationRepository.save(any())).thenReturn(participation); +// +//// when(meetUpRepository.findById(meetUpId)).thenReturn(Optional.of(meetUp)); +//// System.out.println(meetUp.getName()); +// User userA = User.builder() +// .id(2L) // Set the ID as needed +// .name("John") // Set the name as needed +// .authInfo(AuthInfo.builder() +// .email("john1@example.com") // Set the email as needed +// .loginType(LoginType.KAKAO) // Set the login type as needed +// .build()) +// .universityName("Example University") // Set the university name as needed +// .build(); // You may need to create a valid user for testing +// User userB = User.builder() +// .id(3L) // Set the ID as needed +// .name("JohnA") // Set the name as needed +// .authInfo(AuthInfo.builder() +// .email("john1A@example.com") // Set the email as needed +// .loginType(LoginType.KAKAO) // Set the login type as needed +// .build()) +// .universityName("Example University") // Set the university name as needed +// .build(); // You may need to create a valid user for testing +// +// +// // Act +// Participation participationA = meetUpService.participateMeetUp(userA, meetUpId); +// Participation participationB = meetUpService.participateMeetUp(userB, meetUpId); +// System.out.println(meetUp.getCurrentNumberOfPeople()); +// +// +// Assertions.assertThat(participation.getMeetUpStatus()).isEqualTo("HOST"); +// Assertions.assertThat(participationA.getMeetUpStatus()).isEqualTo("PARTICIPANT"); +// } +// +// @Test +// void participateMeetUp_MeetingNotFound() { +// // Arrange +// Long meetUpId = 2L; // Assuming this meet-up does not exist +// User user = User.builder() +// .id(1L) // Set the ID as needed +// .name("John Doe") // Set the name as needed +// .authInfo(AuthInfo.builder() +// .email("john@example.com") // Set the email as needed +// .loginType(LoginType.KAKAO) // Set the login type as needed +// .build()) +// .universityName("Example University") // Set the university name as needed +// .build(); // You may need to create a valid user for testing +// +// // Act and Assert +// assertThrows(MeetingNotFoundException.class, +// () -> meetUpService.participateMeetUp(user, meetUpId)); +// +// verify(meetUpRepository, times(1)).findById(meetUpId); +// verifyNoMoreInteractions(meetUpRepository, participationRepository); +// } +// +// @Test +// void participateMeetUp_FullCapacity() { +// // Arrange +// Long meetUpId = 3L; +// User user = User.builder() +// .id(1L) // Set the ID as needed +// .name("John Doe") // Set the name as needed +// .authInfo(AuthInfo.builder() +// .email("john@example.com") // Set the email as needed +// .loginType(LoginType.KAKAO) // Set the login type as needed +// .build()) +// .universityName("Example University") // Set the university name as needed +// .build(); // You may need to create a valid user for testing +// +// //when(userRepository.save(any())).thenReturn(user); +// MeetUp meetUp = MeetUp.builder() +// .id(3L) // Set the ID as needed +// .name("Sample MeetUp") // Set the name as needed +// .introduce("A sample meet-up for testing") // Set the introduce as needed +// .chatRoomLink("https://example.com/chat-room") // Set the chat room link as needed +// .region("Sample Region") // Set the region as needed +// .date(LocalDateTime.now()) // Set the date as needed +// .restaurant("Sample Restaurant") // Set the restaurant as needed +// .currentNumberOfPeople(10) // Initial current number of people +// .numberOfPeople(10) // Set the total number of people as needed +// .meetUpAtmosphere(MeetUpAtmosphere.QUIET) // Set the meet-up atmosphere as needed +// .regionAtmosphere(RegionAtmosphere.INSTA) // Set the region atmosphere as needed +// .representationImage("sample-image.jpg") // Set the representation image as needed +// .allUsers(new ArrayList<>()) // Initialize the list of users +// .build(); +// +// when(meetUpRepository.findById(meetUpId)).thenReturn(Optional.of(meetUp)); +// +// // Act and Assert +// assertThrows(FullCapacityException.class, +// () -> meetUpService.participateMeetUp(user, meetUpId)); +// +// verify(meetUpRepository, times(1)).findById(meetUpId); +// verifyNoMoreInteractions(meetUpRepository, participationRepository); +// } +}