diff --git a/src/docs/asciidoc/game.adoc b/src/docs/asciidoc/game.adoc index aaea787b..c29a2e3c 100644 --- a/src/docs/asciidoc/game.adoc +++ b/src/docs/asciidoc/game.adoc @@ -9,27 +9,27 @@ --- === 4.3. 게스트 모집글 상세 조회 -operation::find-game[snippets='http-request,http-response'] +operation::find-gameEntity[snippets='http-request,http-response'] --- === 4.4. 게스트 모집 참여 신청 -operation::register-gameMember[snippets='http-request,http-response'] +operation::register-gameMemberEntity[snippets='http-request,http-response'] --- === 4.5. 게스트 모집에 참여 신청된 혹은 확정된 사용자 정보 목록 조회 -operation::find-all-waiting-or-confirmed-gameMembers[snippets='http-request,http-response'] +operation::find-all-waiting-or-confirmed-gameMemberEntities[snippets='http-request,http-response'] --- === 4.6. 게스트 모집 참여 신청 수락 -operation::confirm-gameMember[snippets='http-request,http-response'] +operation::confirm-gameMemberEntity[snippets='http-request,http-response'] --- === 4.7. 게스트 모집 참여 신청 거절/취소 -operation::delete-gameMember[snippets='http-request,http-response'] +operation::delete-gameMemberEntity[snippets='http-request,http-response'] --- diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 2ce6fdb9..6337dee1 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -21,7 +21,7 @@ include::auth.adoc[] include::member.adoc[] include::crew.adoc[] -include::game.adoc[] +include::gameEntity.adoc[] include::address.adoc[] include::position.adoc[] include::chat.adoc[] diff --git a/src/docs/asciidoc/member.adoc b/src/docs/asciidoc/member.adoc index 72b436d8..1eb3145c 100644 --- a/src/docs/asciidoc/member.adoc +++ b/src/docs/asciidoc/member.adoc @@ -11,12 +11,12 @@ operation::find-member[snippets='http-request,http-response'] --- === 2.3. 사용자의 참여 확정 게스트 모집글 목록 조회 -operation::find-all-member-games[snippets='http-request,http-response'] +operation::find-all-member-gameEntities[snippets='http-request,http-response'] --- === 2.4. 사용자가 만든 게스트 모집글 목록 조회 -operation::find-all-created-games[snippets='http-request,http-response'] +operation::find-all-created-gameEntities[snippets='http-request,http-response'] --- diff --git a/src/main/http/alarm/game-alarm.http b/src/main/http/alarm/game-alarm.http index 71ae82c4..ea67406e 100644 --- a/src/main/http/alarm/game-alarm.http +++ b/src/main/http/alarm/game-alarm.http @@ -1,6 +1,6 @@ ### 게임 관련 알람 상태 수정 -PATCH http://localhost:8080/game-alarms/{gameAlarmId} +PATCH http://localhost:8080/gameEntity-alarms/{gameAlarmId} Authorization: Content-Type: application/json diff --git a/src/main/http/game/game.http b/src/main/http/game/game.http index cbb15120..b90386ee 100644 --- a/src/main/http/game/game.http +++ b/src/main/http/game/game.http @@ -5,11 +5,11 @@ Authorization: { "content": "재밌는 농구 경기 해요~! 다 초보입니다", - "playDate": "2023-02-01", - "playStartTime": "11:30", + "playDate": "2024-03-20", + "playStartTime": "12:30", "playTimeMinutes": 90, - "mainAddress": "서울 영등포구 도림동 254", - "detailAddress": "영등포 다목적 체육관 2층 201호", + "mainAddress": "서울 구로구 새말로 19", + "detailAddress": "구로역 옆 농구장", "cost": 100, "maxMemberCount": "5", "positions": [ @@ -19,15 +19,15 @@ Authorization: } ### 게스트 모집 상세 조회 -GET http://localhost:8080/games/1 +GET http://localhost:8080/games/3 ### 게스트 모집 참여 신청 -POST http://localhost:8080/games/38/members +POST http://localhost:8080/games/4/members Content-Type: application/json Authorization: ### 게스트 모집 참여 신청 수락 -PATCH http://localhost:8080/games/2/members/3 +PATCH http://localhost:8080/games/3/members/2 Content-Type: application/json Authorization: @@ -36,11 +36,11 @@ Authorization: } ### 게스트 모집 참여 신청 거절/취소 -DELETE http://localhost:8080/games/2/members/3 +DELETE http://localhost:8080/games/4/members/1 Authorization: ### 게스트 모집에 참여 신청된 사용자 정보 목록 조회 -GET http://localhost:8080/games/2/members?status=대기 +GET http://localhost:8080/games/3/members?status=대기 Authorization: ### 게스트 모집에 확정된 사용자 정보 목록 조회 @@ -48,28 +48,24 @@ GET http://localhost:8080/games/2/members?status=확정 Authorization: ### 다른 사용자(호스트, 게스트) 매너 스코어 리뷰 -PATCH http://localhost:8080/games/1/members/manner-scores +PATCH http://localhost:8080/games/3/members/manner-scores Content-Type: application/json Authorization: { "mannerScoreReviews": [ - { - "memberId": 1, - "mannerScore": 1 - }, { "memberId": 2, - "mannerScore": -1 + "mannerScore": 1 } ] } ### 조건별 게스트 모집글 조회(장소) -GET http://localhost:8080/games?category=location&value=서울시+영등포구&page=0&size=3 +GET http://localhost:8080/games?category=location&value=서울시+구로구&page=0&size=3 ### 위도, 경도, 거리를 통해 해당하는 게스트 모집글 조회 -GET http://localhost:8080/games/by-location?latitude=37.5066680941127&longitude=126.897412723839&distance=1000 +GET http://localhost:8080/games/by-location?latitude=37.5066680941127&longitude=126.897412723839&distance=1500 ### 사용자의 주 활동지역에 해당하는 게스트 모집글 조회 -GET http://localhost:8080/games/by-address?addressDepth1=서울시&addressDepth2=영등포구 +GET http://localhost:8080/games/by-address?addressDepth1=서울시&addressDepth2=구로구 diff --git a/src/main/http/member/member.http b/src/main/http/member/member.http index 01a2cf3d..1a8986c2 100644 --- a/src/main/http/member/member.http +++ b/src/main/http/member/member.http @@ -4,15 +4,15 @@ Content-Type: application/json Authorization: { - "email": "changhyeon.h@kakao.com", - "nickname": "창현", + "email": "test2@test.com", + "nickname": "테스트2", "profileImageUrl": "http://k.kakaocdn.net/dn/dpk9l1/btqmGhA2lKL/Oz0wDuJn1YV2DIn92f6DVK/img_640x640.jpg", "positions": [ - "SG" + "C","PG" ], "addressDepth1": "서울시", - "addressDepth2": "영등포구", - "oauthId": 32014123, + "addressDepth2": "구로구", + "oauthId": 32014120, "oauthProvider": "KAKAO" } @@ -20,7 +20,7 @@ Authorization: GET http://localhost:8080/members/1 ### 사용자가 가입한 크루 목록 -GET http://localhost:8080/members/1/crews?status=확정 +GET http://localhost:8080/members/3/crews?status=확정 Authorization: ### 사용자가 만든 크루 목록 조회 @@ -28,18 +28,26 @@ GET http://localhost:8080/members/1/created-crews Authorization: ### 사용자의 참여 확정 게스트 모집글 목록 조회 -GET http://localhost:8080/members/1/games?status=확정 +GET http://localhost:8080/members/2/games?status=대기 Authorization: ### 사용자가 만든 게스트 모집글 목록 조회 -GET http://localhost:8080/members/1/created-games +GET http://localhost:8080/members/2/created-games Authorization: ### 사용자의 게스트 모집 참여 여부 조회 -GET http://localhost:8080/members/5/games/1/registration-status +GET http://localhost:8080/members/2/games/4/registration-status Authorization: ### 사용자의 크루 가입 여부 조회 GET http://localhost:8080/members/1/crews/2/registration-status -Authorization \ No newline at end of file +Authorization: + +### AccessToken 재발급 +POST http://localhost:8080/auth/refresh +Authorization: + +### AccessToken 재발급 +DELETE http://localhost:8080/auth/logout +Authorization: diff --git a/src/main/java/kr/pickple/back/address/domain/Address.java b/src/main/java/kr/pickple/back/address/domain/Address.java new file mode 100644 index 00000000..26e5b5c4 --- /dev/null +++ b/src/main/java/kr/pickple/back/address/domain/Address.java @@ -0,0 +1,17 @@ +package kr.pickple.back.address.domain; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@EqualsAndHashCode +public class Address { + + private Long id; + private String name; +} diff --git a/src/main/java/kr/pickple/back/address/domain/AllAddress.java b/src/main/java/kr/pickple/back/address/domain/AllAddress.java new file mode 100644 index 00000000..6d434b58 --- /dev/null +++ b/src/main/java/kr/pickple/back/address/domain/AllAddress.java @@ -0,0 +1,19 @@ +package kr.pickple.back.address.domain; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@EqualsAndHashCode +public class AllAddress { + + private String addressDepth1Name; + private List addressDepth2Names; +} diff --git a/src/main/java/kr/pickple/back/address/domain/MainAddress.java b/src/main/java/kr/pickple/back/address/domain/MainAddress.java new file mode 100644 index 00000000..846078f6 --- /dev/null +++ b/src/main/java/kr/pickple/back/address/domain/MainAddress.java @@ -0,0 +1,31 @@ +package kr.pickple.back.address.domain; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; + +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@EqualsAndHashCode +public class MainAddress { + + private Address addressDepth1; + private Address addressDepth2; + + public Long getAddressDepth1Id() { + return this.addressDepth1.getId(); + } + + public String getAddressDepth1Name() { + return this.addressDepth1.getName(); + } + + public Long getAddressDepth2Id() { + return this.addressDepth2.getId(); + } + + public String getAddressDepth2Name() { + return this.addressDepth2.getName(); + } +} diff --git a/src/main/java/kr/pickple/back/address/dto/kakao/KakaoAddressResponse.java b/src/main/java/kr/pickple/back/address/dto/kakao/KakaoAddressResponse.java index 9cc163cc..8bd7c667 100644 --- a/src/main/java/kr/pickple/back/address/dto/kakao/KakaoAddressResponse.java +++ b/src/main/java/kr/pickple/back/address/dto/kakao/KakaoAddressResponse.java @@ -17,6 +17,12 @@ public class KakaoAddressResponse { private List documents; + public Point toPoint() { + final GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326); + + return geometryFactory.createPoint(new Coordinate(documents.get(0).x, documents.get(0).y)); + } + @Getter @NoArgsConstructor(access = AccessLevel.PRIVATE) static class Document { @@ -25,10 +31,4 @@ static class Document { private Double x; private Double y; } - - public Point toPoint() { - final GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326); - - return geometryFactory.createPoint(new Coordinate(documents.get(0).x, documents.get(0).y)); - } } diff --git a/src/main/java/kr/pickple/back/address/dto/response/MainAddressResponse.java b/src/main/java/kr/pickple/back/address/dto/response/MainAddressResponse.java deleted file mode 100644 index 849a6413..00000000 --- a/src/main/java/kr/pickple/back/address/dto/response/MainAddressResponse.java +++ /dev/null @@ -1,17 +0,0 @@ -package kr.pickple.back.address.dto.response; - -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class MainAddressResponse { - - private AddressDepth1 addressDepth1; - private AddressDepth2 addressDepth2; -} diff --git a/src/main/java/kr/pickple/back/address/implement/AddressMapper.java b/src/main/java/kr/pickple/back/address/implement/AddressMapper.java new file mode 100644 index 00000000..32463d27 --- /dev/null +++ b/src/main/java/kr/pickple/back/address/implement/AddressMapper.java @@ -0,0 +1,38 @@ +package kr.pickple.back.address.implement; + +import java.util.List; + +import kr.pickple.back.address.domain.Address; +import kr.pickple.back.address.domain.AllAddress; +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.repository.entity.AddressEntity; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class AddressMapper { + + public static AllAddress mapToAllAddressDomain( + final String addressDepth1Name, + final List addressDepth2Names + ) { + return AllAddress.builder() + .addressDepth1Name(addressDepth1Name) + .addressDepth2Names(addressDepth2Names) + .build(); + } + + public static MainAddress mapToMainAddressDomain(final Address addressDepth1, final Address addressDepth2) { + return MainAddress.builder() + .addressDepth1(addressDepth1) + .addressDepth2(addressDepth2) + .build(); + } + + public static Address mapAddressEntityToDomain(final AddressEntity addressEntity) { + return Address.builder() + .id(addressEntity.getId()) + .name(addressEntity.getName()) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/address/implement/AddressReader.java b/src/main/java/kr/pickple/back/address/implement/AddressReader.java new file mode 100644 index 00000000..49867baa --- /dev/null +++ b/src/main/java/kr/pickple/back/address/implement/AddressReader.java @@ -0,0 +1,87 @@ +package kr.pickple.back.address.implement; + +import static kr.pickple.back.address.exception.AddressExceptionCode.*; + +import java.util.List; + +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.address.domain.AllAddress; +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.exception.AddressException; +import kr.pickple.back.address.repository.AddressDepth1Repository; +import kr.pickple.back.address.repository.AddressDepth2Repository; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; +import kr.pickple.back.address.util.AddressParser; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class AddressReader { + + private final AddressDepth1Repository addressDepth1Repository; + private final AddressDepth2Repository addressDepth2Repository; + + /** + * 지역 목록 조회 + */ + @Cacheable(cacheManager = "caffeineCacheManager", cacheNames = "address", key = "'all'") + public AllAddress readAllAddress() { + final AddressDepth1Entity addressDepth1Entity = addressDepth1Repository.findAll().get(0); + final List addressDepth2Names = addressDepth2Repository.findAllByAddressDepth1Id( + addressDepth1Entity.getId()) + .stream() + .map(AddressDepth2Entity::getName) + .toList(); + + return AddressMapper.mapToAllAddressDomain(addressDepth1Entity.getName(), addressDepth2Names); + } + + /** + * 주소1과 주소2의 ID를 통해 MainAddress 조회 + */ + public MainAddress readMainAddressByIds(final Long addressDepth1Id, final Long addressDepth2Id) { + final AddressDepth1Entity addressDepth1Entity = addressDepth1Repository.findById(addressDepth1Id) + .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, addressDepth1Id)); + + final AddressDepth2Entity addressDepth2Entity = addressDepth2Repository.findById(addressDepth2Id) + .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, addressDepth2Id)); + + return AddressMapper.mapToMainAddressDomain( + AddressMapper.mapAddressEntityToDomain(addressDepth1Entity), + AddressMapper.mapAddressEntityToDomain(addressDepth2Entity) + ); + } + + /** + * 주소1과 주소2의 이름을 통해 MainAddress 조회 + */ + public MainAddress readMainAddressByNames(final String addressDepth1Name, final String addressDepth2Name) { + final AddressDepth1Entity addressDepth1Entity = addressDepth1Repository.findByName(addressDepth1Name) + .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, addressDepth1Name)); + + final AddressDepth2Entity addressDepth2Entity = addressDepth2Repository.findByNameAndAddressDepth1Id( + addressDepth2Name, addressDepth1Entity.getId()) + .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, addressDepth1Name, addressDepth2Name)); + + return AddressMapper.mapToMainAddressDomain( + AddressMapper.mapAddressEntityToDomain(addressDepth1Entity), + AddressMapper.mapAddressEntityToDomain(addressDepth2Entity) + ); + } + + /** + * 전체 주소를 주소1과 주소2로 나누고, 이를 이용해 MainAddress 조회 + */ + public MainAddress readMainAddressFromFullAddress(final String mainAddressName) { + final List addressDepth1And2Names = AddressParser.splitToAddressDepth1And2(mainAddressName); + final String addressDepth1Name = addressDepth1And2Names.get(0); + final String addressDepth2Name = addressDepth1And2Names.get(1); + + return readMainAddressByNames(addressDepth1Name, addressDepth2Name); + } +} diff --git a/src/main/java/kr/pickple/back/address/repository/AddressDepth1Repository.java b/src/main/java/kr/pickple/back/address/repository/AddressDepth1Repository.java index 42a0679c..97a77e27 100644 --- a/src/main/java/kr/pickple/back/address/repository/AddressDepth1Repository.java +++ b/src/main/java/kr/pickple/back/address/repository/AddressDepth1Repository.java @@ -4,9 +4,9 @@ import org.springframework.data.jpa.repository.JpaRepository; -import kr.pickple.back.address.domain.AddressDepth1; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; -public interface AddressDepth1Repository extends JpaRepository { +public interface AddressDepth1Repository extends JpaRepository { - Optional findByName(final String name); + Optional findByName(final String name); } diff --git a/src/main/java/kr/pickple/back/address/repository/AddressDepth2Repository.java b/src/main/java/kr/pickple/back/address/repository/AddressDepth2Repository.java index 5721c897..01f1b567 100644 --- a/src/main/java/kr/pickple/back/address/repository/AddressDepth2Repository.java +++ b/src/main/java/kr/pickple/back/address/repository/AddressDepth2Repository.java @@ -5,12 +5,11 @@ import org.springframework.data.jpa.repository.JpaRepository; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; -public interface AddressDepth2Repository extends JpaRepository { +public interface AddressDepth2Repository extends JpaRepository { - List findByAddressDepth1(final AddressDepth1 addressDepth1); + List findAllByAddressDepth1Id(final Long addressDepth1Id); - Optional findByNameAndAddressDepth1(final String name, final AddressDepth1 addressDepth1); + Optional findByNameAndAddressDepth1Id(final String name, final Long addressDepth1Id); } diff --git a/src/main/java/kr/pickple/back/address/domain/AddressDepth1.java b/src/main/java/kr/pickple/back/address/repository/entity/AddressDepth1Entity.java similarity index 69% rename from src/main/java/kr/pickple/back/address/domain/AddressDepth1.java rename to src/main/java/kr/pickple/back/address/repository/entity/AddressDepth1Entity.java index e2f50da4..588a4afa 100644 --- a/src/main/java/kr/pickple/back/address/domain/AddressDepth1.java +++ b/src/main/java/kr/pickple/back/address/repository/entity/AddressDepth1Entity.java @@ -1,10 +1,11 @@ -package kr.pickple.back.address.domain; +package kr.pickple.back.address.repository.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import kr.pickple.back.common.domain.BaseEntity; import lombok.AccessLevel; @@ -13,20 +14,22 @@ import lombok.NoArgsConstructor; @Entity +@Getter +@Table(name = "address_depth1") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class AddressDepth1 extends BaseEntity { +public class AddressDepth1Entity extends BaseEntity implements AddressEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Getter @NotNull @Column(unique = true, length = 10) private String name; @Builder - private AddressDepth1(final String name) { + private AddressDepth1Entity(final Long id, final String name) { + this.id = id; this.name = name; } } diff --git a/src/main/java/kr/pickple/back/address/domain/AddressDepth2.java b/src/main/java/kr/pickple/back/address/repository/entity/AddressDepth2Entity.java similarity index 59% rename from src/main/java/kr/pickple/back/address/domain/AddressDepth2.java rename to src/main/java/kr/pickple/back/address/repository/entity/AddressDepth2Entity.java index d940c47d..00ec53cd 100644 --- a/src/main/java/kr/pickple/back/address/domain/AddressDepth2.java +++ b/src/main/java/kr/pickple/back/address/repository/entity/AddressDepth2Entity.java @@ -1,13 +1,11 @@ -package kr.pickple.back.address.domain; +package kr.pickple.back.address.repository.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import kr.pickple.back.common.domain.BaseEntity; import lombok.AccessLevel; @@ -16,26 +14,27 @@ import lombok.NoArgsConstructor; @Entity +@Getter +@Table(name = "address_depth2") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class AddressDepth2 extends BaseEntity { +public class AddressDepth2Entity extends BaseEntity implements AddressEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Getter @NotNull @Column(unique = true, length = 10) private String name; @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "address_depth1_id") - private AddressDepth1 addressDepth1; + @Column(name = "address_depth1_id") + private Long addressDepth1Id; @Builder - private AddressDepth2(final String name, final AddressDepth1 addressDepth1) { + private AddressDepth2Entity(final Long id, final String name, final Long addressDepth1Id) { + this.id = id; this.name = name; - this.addressDepth1 = addressDepth1; + this.addressDepth1Id = addressDepth1Id; } } diff --git a/src/main/java/kr/pickple/back/address/repository/entity/AddressEntity.java b/src/main/java/kr/pickple/back/address/repository/entity/AddressEntity.java new file mode 100644 index 00000000..2647f9a2 --- /dev/null +++ b/src/main/java/kr/pickple/back/address/repository/entity/AddressEntity.java @@ -0,0 +1,8 @@ +package kr.pickple.back.address.repository.entity; + +public interface AddressEntity { + + Long getId(); + + String getName(); +} diff --git a/src/main/java/kr/pickple/back/address/service/AddressService.java b/src/main/java/kr/pickple/back/address/service/AddressService.java index f23bb004..d3b861f0 100644 --- a/src/main/java/kr/pickple/back/address/service/AddressService.java +++ b/src/main/java/kr/pickple/back/address/service/AddressService.java @@ -1,21 +1,11 @@ package kr.pickple.back.address.service; -import static kr.pickple.back.address.exception.AddressExceptionCode.*; - -import java.util.List; - -import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; +import kr.pickple.back.address.domain.AllAddress; import kr.pickple.back.address.dto.response.AllAddressResponse; -import kr.pickple.back.address.dto.response.MainAddressResponse; -import kr.pickple.back.address.exception.AddressException; -import kr.pickple.back.address.repository.AddressDepth1Repository; -import kr.pickple.back.address.repository.AddressDepth2Repository; -import kr.pickple.back.address.util.AddressParser; +import kr.pickple.back.address.implement.AddressReader; import lombok.RequiredArgsConstructor; @Service @@ -23,44 +13,17 @@ @Transactional(readOnly = true) public class AddressService { - private final AddressDepth1Repository addressDepth1Repository; - private final AddressDepth2Repository addressDepth2Repository; + private final AddressReader addressReader; - @Cacheable(cacheManager = "caffeineCacheManager", cacheNames = "address", key = "'all'") + /** + * 지역 목록 조회 + */ public AllAddressResponse findAllAddress() { - final AddressDepth1 addressDepth1 = addressDepth1Repository.findAll() - .get(0); - - final List addressDepth2List = addressDepth2Repository.findByAddressDepth1(addressDepth1) - .stream() - .map(AddressDepth2::getName) - .toList(); + final AllAddress allAddress = addressReader.readAllAddress(); return AllAddressResponse.builder() - .addressDepth1(addressDepth1.getName()) - .addressDepth2List(addressDepth2List) + .addressDepth1(allAddress.getAddressDepth1Name()) + .addressDepth2List(allAddress.getAddressDepth2Names()) .build(); } - - public MainAddressResponse findMainAddressByNames(final String addressDepth1Name, final String addressDepth2Name) { - final AddressDepth1 addressDepth1 = addressDepth1Repository.findByName(addressDepth1Name) - .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, addressDepth1Name)); - - final AddressDepth2 addressDepth2 = addressDepth2Repository.findByNameAndAddressDepth1(addressDepth2Name, - addressDepth1) - .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, addressDepth1Name, addressDepth2Name)); - - return MainAddressResponse.builder() - .addressDepth1(addressDepth1) - .addressDepth2(addressDepth2) - .build(); - } - - //기존 메서드 네이밍 및 시그니처 유지를 위해 임시적으로 아래와 구현했습니다. - //todo 현호:서로 다른 입력을 받아 MainAddressResponse를 반환하는 두 메서드를 어떻게 통합하면 좋을 지 논의해보면 좋겠습니다. - public MainAddressResponse findMainAddressByAddressStrings(final String mainAddress) { - final List depthedAddress = AddressParser.splitToAddressDepth1And2(mainAddress); - - return findMainAddressByNames(depthedAddress.get(0), depthedAddress.get(1)); - } } diff --git a/src/main/java/kr/pickple/back/address/util/AddressParser.java b/src/main/java/kr/pickple/back/address/util/AddressParser.java index f34b92fc..1143fa31 100644 --- a/src/main/java/kr/pickple/back/address/util/AddressParser.java +++ b/src/main/java/kr/pickple/back/address/util/AddressParser.java @@ -3,19 +3,23 @@ import java.util.Arrays; import java.util.List; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) public final class AddressParser { public static final int ADDRESS_DEPTH_SIZE = 2; private static final String ADDRESS_DELIMITER_REGEX = "[\\s+]"; - public static List splitToAddressDepth1And2(final String mainAddress) { - return Arrays.stream(mainAddress.split(ADDRESS_DELIMITER_REGEX)) + public static List splitToAddressDepth1And2(final String mainAddressName) { + return Arrays.stream(mainAddressName.split(ADDRESS_DELIMITER_REGEX)) .limit(ADDRESS_DEPTH_SIZE) .map(AddressParser::addCitySuffixToFirstAddress) .toList(); } - private static String addCitySuffixToFirstAddress(final String addressDepth1) { - return addressDepth1.equals("서울") ? "서울시" : addressDepth1; + private static String addCitySuffixToFirstAddress(final String firstAddressName) { + return firstAddressName.equals("서울") ? "서울시" : firstAddressName; } } diff --git a/src/main/java/kr/pickple/back/alarm/config/AsynConfig.java b/src/main/java/kr/pickple/back/alarm/config/AsynConfig.java index 2ff06fd1..da891ec0 100644 --- a/src/main/java/kr/pickple/back/alarm/config/AsynConfig.java +++ b/src/main/java/kr/pickple/back/alarm/config/AsynConfig.java @@ -1,13 +1,14 @@ package kr.pickple.back.alarm.config; -import kr.pickple.back.common.config.property.AsyncProperties; -import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import kr.pickple.back.common.config.property.AsyncProperties; +import lombok.RequiredArgsConstructor; + @Configuration @EnableAsync @RequiredArgsConstructor @@ -25,6 +26,7 @@ public TaskExecutor taskExecutor() { } static class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor { + private CustomThreadPoolTaskExecutor(final int corePoolSize, final int maxPoolSize, final int queueCapacity) { super(); this.setCorePoolSize(corePoolSize); @@ -38,6 +40,7 @@ public static Builder builder() { } public static class Builder { + private int corePoolSize; private int maxPoolSize; private int queueCapacity; diff --git a/src/main/java/kr/pickple/back/alarm/controller/AlarmController.java b/src/main/java/kr/pickple/back/alarm/controller/AlarmController.java index b64318fd..25e84ef7 100644 --- a/src/main/java/kr/pickple/back/alarm/controller/AlarmController.java +++ b/src/main/java/kr/pickple/back/alarm/controller/AlarmController.java @@ -1,18 +1,22 @@ package kr.pickple.back.alarm.controller; +import static org.springframework.http.HttpStatus.*; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + import kr.pickple.back.alarm.dto.response.AlarmExistStatusResponse; import kr.pickple.back.alarm.dto.response.AlarmResponse; import kr.pickple.back.alarm.service.AlarmService; import kr.pickple.back.alarm.util.CursorResult; import kr.pickple.back.auth.config.resolver.Login; import lombok.RequiredArgsConstructor; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -import static org.springframework.http.HttpStatus.NO_CONTENT; -import static org.springframework.http.HttpStatus.OK; @RestController @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/controller/CrewAlarmController.java b/src/main/java/kr/pickple/back/alarm/controller/CrewAlarmController.java index 12c5d502..cccf8a24 100644 --- a/src/main/java/kr/pickple/back/alarm/controller/CrewAlarmController.java +++ b/src/main/java/kr/pickple/back/alarm/controller/CrewAlarmController.java @@ -1,14 +1,19 @@ package kr.pickple.back.alarm.controller; +import static org.springframework.http.HttpStatus.*; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + import jakarta.validation.Valid; import kr.pickple.back.alarm.dto.request.CrewAlarmUpdateStatusRequest; import kr.pickple.back.alarm.service.CrewAlarmService; import kr.pickple.back.auth.config.resolver.Login; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import static org.springframework.http.HttpStatus.NO_CONTENT; @RestController @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/controller/GameAlarmController.java b/src/main/java/kr/pickple/back/alarm/controller/GameAlarmController.java index 34be13fe..d6344452 100644 --- a/src/main/java/kr/pickple/back/alarm/controller/GameAlarmController.java +++ b/src/main/java/kr/pickple/back/alarm/controller/GameAlarmController.java @@ -1,14 +1,19 @@ package kr.pickple.back.alarm.controller; +import static org.springframework.http.HttpStatus.*; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + import jakarta.validation.Valid; import kr.pickple.back.alarm.dto.request.GameAlarmUpdateStatusRequest; import kr.pickple.back.alarm.service.GameAlarmService; import kr.pickple.back.auth.config.resolver.Login; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import static org.springframework.http.HttpStatus.NO_CONTENT; @RestController @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/domain/AlarmExistsStatus.java b/src/main/java/kr/pickple/back/alarm/domain/AlarmExistsStatus.java index 0d3114c9..6cfb7263 100644 --- a/src/main/java/kr/pickple/back/alarm/domain/AlarmExistsStatus.java +++ b/src/main/java/kr/pickple/back/alarm/domain/AlarmExistsStatus.java @@ -1,10 +1,6 @@ package kr.pickple.back.alarm.domain; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; -import kr.pickple.back.crew.exception.CrewException; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import static kr.pickple.back.alarm.exception.AlarmExceptionCode.*; import java.util.Collections; import java.util.Map; @@ -12,7 +8,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static kr.pickple.back.alarm.exception.AlarmExceptionCode.ALARM_EXISTS_STATUS_NOT_FOUND; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import kr.pickple.back.crew.exception.CrewException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor @@ -21,8 +22,9 @@ public enum AlarmExistsStatus { EXISTS("읽지 않은 알람이 있음", true), NOT_EXISTS("읽지 않은 알람이 없음", false); - private static final Map alarmExistsStatusMap = Collections.unmodifiableMap(Stream.of(values()) - .collect(Collectors.toMap(AlarmExistsStatus::getDescription, Function.identity()))); + private static final Map alarmExistsStatusMap = Collections.unmodifiableMap( + Stream.of(values()) + .collect(Collectors.toMap(AlarmExistsStatus::getDescription, Function.identity()))); private final String description; private final Boolean booleanValue; diff --git a/src/main/java/kr/pickple/back/alarm/domain/CrewAlarm.java b/src/main/java/kr/pickple/back/alarm/domain/CrewAlarm.java index 97d58542..c8d8d77b 100644 --- a/src/main/java/kr/pickple/back/alarm/domain/CrewAlarm.java +++ b/src/main/java/kr/pickple/back/alarm/domain/CrewAlarm.java @@ -1,11 +1,19 @@ package kr.pickple.back.alarm.domain; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.NotNull; import kr.pickple.back.alarm.util.CrewAlarmTypeConverter; import kr.pickple.back.common.domain.BaseEntity; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.member.repository.entity.MemberEntity; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -30,17 +38,17 @@ public class CrewAlarm extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "crew_id") - private Crew crew; + private CrewEntity crew; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") - private Member member; + private MemberEntity member; @Builder private CrewAlarm( final CrewAlarmType crewAlarmType, - final Crew crew, - final Member member + final CrewEntity crew, + final MemberEntity member ) { this.crewAlarmType = crewAlarmType; this.crew = crew; diff --git a/src/main/java/kr/pickple/back/alarm/domain/CrewAlarmType.java b/src/main/java/kr/pickple/back/alarm/domain/CrewAlarmType.java index 5c289a5b..5a730a73 100644 --- a/src/main/java/kr/pickple/back/alarm/domain/CrewAlarmType.java +++ b/src/main/java/kr/pickple/back/alarm/domain/CrewAlarmType.java @@ -1,10 +1,6 @@ package kr.pickple.back.alarm.domain; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; -import kr.pickple.back.alarm.exception.AlarmException; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import static kr.pickple.back.alarm.exception.AlarmExceptionCode.*; import java.util.Collections; import java.util.Map; @@ -12,7 +8,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static kr.pickple.back.alarm.exception.AlarmExceptionCode.ALARM_TYPE_NOT_FOUND; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import kr.pickple.back.alarm.exception.AlarmException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/domain/GameAlarm.java b/src/main/java/kr/pickple/back/alarm/domain/GameAlarm.java index b5738882..43bf1436 100644 --- a/src/main/java/kr/pickple/back/alarm/domain/GameAlarm.java +++ b/src/main/java/kr/pickple/back/alarm/domain/GameAlarm.java @@ -1,11 +1,19 @@ package kr.pickple.back.alarm.domain; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.NotNull; import kr.pickple.back.alarm.util.GameAlarmTypeConverter; import kr.pickple.back.common.domain.BaseEntity; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.game.repository.entity.GameEntity; +import kr.pickple.back.member.repository.entity.MemberEntity; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -30,17 +38,17 @@ public class GameAlarm extends BaseEntity { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "game_id") - private Game game; + private GameEntity game; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "member_id") - private Member member; + private MemberEntity member; @Builder private GameAlarm( final GameAlarmType gameAlarmType, - final Game game, - final Member member + final GameEntity game, + final MemberEntity member ) { this.gameAlarmType = gameAlarmType; this.game = game; diff --git a/src/main/java/kr/pickple/back/alarm/domain/GameAlarmType.java b/src/main/java/kr/pickple/back/alarm/domain/GameAlarmType.java index 7ef70be8..f40d98de 100644 --- a/src/main/java/kr/pickple/back/alarm/domain/GameAlarmType.java +++ b/src/main/java/kr/pickple/back/alarm/domain/GameAlarmType.java @@ -1,10 +1,6 @@ package kr.pickple.back.alarm.domain; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; -import kr.pickple.back.alarm.exception.AlarmException; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import static kr.pickple.back.alarm.exception.AlarmExceptionCode.*; import java.util.Collections; import java.util.Map; @@ -12,7 +8,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static kr.pickple.back.alarm.exception.AlarmExceptionCode.ALARM_TYPE_NOT_FOUND; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import kr.pickple.back.alarm.exception.AlarmException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/dto/response/CrewAlarmResponse.java b/src/main/java/kr/pickple/back/alarm/dto/response/CrewAlarmResponse.java index 678f1d60..41b4e745 100644 --- a/src/main/java/kr/pickple/back/alarm/dto/response/CrewAlarmResponse.java +++ b/src/main/java/kr/pickple/back/alarm/dto/response/CrewAlarmResponse.java @@ -1,15 +1,16 @@ package kr.pickple.back.alarm.dto.response; +import java.time.LocalDateTime; + import com.fasterxml.jackson.databind.annotation.JsonSerialize; + import kr.pickple.back.alarm.domain.CrewAlarm; import kr.pickple.back.alarm.domain.CrewAlarmType; -import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.crew.repository.entity.CrewEntity; import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.time.LocalDateTime; - @Getter @Builder @JsonSerialize @@ -25,7 +26,7 @@ public class CrewAlarmResponse implements AlarmResponse { private final CrewAlarmType crewAlarmMessage; public static CrewAlarmResponse from(final CrewAlarm crewAlarm) { - final Crew crew = crewAlarm.getCrew(); + final CrewEntity crew = crewAlarm.getCrew(); return CrewAlarmResponse.builder() .crewAlarmId(crewAlarm.getId()) diff --git a/src/main/java/kr/pickple/back/alarm/dto/response/GameAlarmResponse.java b/src/main/java/kr/pickple/back/alarm/dto/response/GameAlarmResponse.java index a6c35fba..e5fc24ed 100644 --- a/src/main/java/kr/pickple/back/alarm/dto/response/GameAlarmResponse.java +++ b/src/main/java/kr/pickple/back/alarm/dto/response/GameAlarmResponse.java @@ -1,17 +1,18 @@ package kr.pickple.back.alarm.dto.response; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + import com.fasterxml.jackson.databind.annotation.JsonSerialize; + import kr.pickple.back.alarm.domain.GameAlarm; import kr.pickple.back.alarm.domain.GameAlarmType; -import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.repository.entity.GameEntity; import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; - @Getter @Builder @JsonSerialize @@ -29,16 +30,16 @@ public class GameAlarmResponse implements AlarmResponse { private final GameAlarmType gameAlarmMessage; public static GameAlarmResponse from(final GameAlarm gameAlarm) { - final Game game = gameAlarm.getGame(); + final GameEntity gameEntity = gameAlarm.getGame(); return GameAlarmResponse.builder() .gameAlarmId(gameAlarm.getId()) - .gameId(game.getId()) - .mainAddress(gameAlarm.getGame().getMainAddress()) + .gameId(gameEntity.getId()) + .mainAddress(gameEntity.getMainAddress()) .createdAt(gameAlarm.getCreatedAt()) - .playDate(gameAlarm.getGame().getPlayDate()) - .playStartTime(gameAlarm.getGame().getPlayStartTime()) - .playTimeMinutes(gameAlarm.getGame().getPlayTimeMinutes()) + .playDate(gameEntity.getPlayDate()) + .playStartTime(gameEntity.getPlayStartTime()) + .playTimeMinutes(gameEntity.getPlayTimeMinutes()) .isRead(gameAlarm.getIsRead()) .gameAlarmMessage(gameAlarm.getGameAlarmType()) .build(); diff --git a/src/main/java/kr/pickple/back/alarm/exception/AlarmExceptionCode.java b/src/main/java/kr/pickple/back/alarm/exception/AlarmExceptionCode.java index 11e742a1..baf1a1f2 100644 --- a/src/main/java/kr/pickple/back/alarm/exception/AlarmExceptionCode.java +++ b/src/main/java/kr/pickple/back/alarm/exception/AlarmExceptionCode.java @@ -1,9 +1,10 @@ package kr.pickple.back.alarm.exception; +import org.springframework.http.HttpStatus; + import kr.pickple.back.common.exception.ExceptionCode; import lombok.Getter; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; @Getter @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/handler/CrewAlarmEventHandler.java b/src/main/java/kr/pickple/back/alarm/handler/CrewAlarmEventHandler.java index 474d5f30..6768bcca 100644 --- a/src/main/java/kr/pickple/back/alarm/handler/CrewAlarmEventHandler.java +++ b/src/main/java/kr/pickple/back/alarm/handler/CrewAlarmEventHandler.java @@ -1,13 +1,14 @@ package kr.pickple.back.alarm.handler; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; + import kr.pickple.back.alarm.event.crew.CrewJoinRequestNotificationEvent; import kr.pickple.back.alarm.event.crew.CrewMemberJoinedEvent; import kr.pickple.back.alarm.event.crew.CrewMemberRejectedEvent; import kr.pickple.back.alarm.service.CrewAlarmService; import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; -import org.springframework.transaction.event.TransactionalEventListener; @Component @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/handler/GameAlarmEventHandler.java b/src/main/java/kr/pickple/back/alarm/handler/GameAlarmEventHandler.java index e7a7b674..ec827f5d 100644 --- a/src/main/java/kr/pickple/back/alarm/handler/GameAlarmEventHandler.java +++ b/src/main/java/kr/pickple/back/alarm/handler/GameAlarmEventHandler.java @@ -1,13 +1,14 @@ package kr.pickple.back.alarm.handler; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; + import kr.pickple.back.alarm.event.game.GameJoinRequestNotificationEvent; import kr.pickple.back.alarm.event.game.GameMemberJoinedEvent; import kr.pickple.back.alarm.event.game.GameMemberRejectedEvent; import kr.pickple.back.alarm.service.GameAlarmService; import lombok.RequiredArgsConstructor; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Component; -import org.springframework.transaction.event.TransactionalEventListener; @Component @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/repository/CrewAlarmRepository.java b/src/main/java/kr/pickple/back/alarm/repository/CrewAlarmRepository.java index 51c0ab2a..2e82b7cc 100644 --- a/src/main/java/kr/pickple/back/alarm/repository/CrewAlarmRepository.java +++ b/src/main/java/kr/pickple/back/alarm/repository/CrewAlarmRepository.java @@ -1,13 +1,14 @@ package kr.pickple.back.alarm.repository; -import kr.pickple.back.alarm.domain.CrewAlarm; +import java.util.List; +import java.util.Optional; + import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.List; -import java.util.Optional; +import kr.pickple.back.alarm.domain.CrewAlarm; public interface CrewAlarmRepository extends JpaRepository { diff --git a/src/main/java/kr/pickple/back/alarm/repository/GameAlarmRepository.java b/src/main/java/kr/pickple/back/alarm/repository/GameAlarmRepository.java index 22823773..f8de4cc7 100644 --- a/src/main/java/kr/pickple/back/alarm/repository/GameAlarmRepository.java +++ b/src/main/java/kr/pickple/back/alarm/repository/GameAlarmRepository.java @@ -1,13 +1,14 @@ package kr.pickple.back.alarm.repository; -import kr.pickple.back.alarm.domain.GameAlarm; +import java.util.List; +import java.util.Optional; + import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import java.util.List; -import java.util.Optional; +import kr.pickple.back.alarm.domain.GameAlarm; public interface GameAlarmRepository extends JpaRepository { diff --git a/src/main/java/kr/pickple/back/alarm/repository/RedisEventCacheRepository.java b/src/main/java/kr/pickple/back/alarm/repository/RedisEventCacheRepository.java index b2125f77..154fd0bd 100644 --- a/src/main/java/kr/pickple/back/alarm/repository/RedisEventCacheRepository.java +++ b/src/main/java/kr/pickple/back/alarm/repository/RedisEventCacheRepository.java @@ -1,15 +1,16 @@ package kr.pickple.back.alarm.repository; -import kr.pickple.back.alarm.dto.response.AlarmResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Repository; - import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Repository; + +import kr.pickple.back.alarm.dto.response.AlarmResponse; +import lombok.RequiredArgsConstructor; + @Repository @RequiredArgsConstructor public class RedisEventCacheRepository { @@ -27,7 +28,7 @@ public Map findAllEventCacheByMemberId(final Long memberId) { public List findLatestEventCacheByMemberId(final Long memberId, final int count) { final Map eventCache = findAllEventCacheByMemberId(memberId); return eventCache.values().stream() - .sorted(Comparator.comparing(event -> ((AlarmResponse) event).getCreatedAt()).reversed()) + .sorted(Comparator.comparing(event -> ((AlarmResponse)event).getCreatedAt()).reversed()) .limit(count) .toList(); } diff --git a/src/main/java/kr/pickple/back/alarm/repository/SseEmitterLocalRepository.java b/src/main/java/kr/pickple/back/alarm/repository/SseEmitterLocalRepository.java index 84ed1b9e..4e6931c8 100644 --- a/src/main/java/kr/pickple/back/alarm/repository/SseEmitterLocalRepository.java +++ b/src/main/java/kr/pickple/back/alarm/repository/SseEmitterLocalRepository.java @@ -1,14 +1,15 @@ package kr.pickple.back.alarm.repository; -import lombok.NoArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.stereotype.Repository; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import org.springframework.stereotype.Repository; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import lombok.NoArgsConstructor; +import lombok.extern.log4j.Log4j2; + @Log4j2 @Repository @NoArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/repository/SseEmitterRepository.java b/src/main/java/kr/pickple/back/alarm/repository/SseEmitterRepository.java index a72bcbc8..2d72df05 100644 --- a/src/main/java/kr/pickple/back/alarm/repository/SseEmitterRepository.java +++ b/src/main/java/kr/pickple/back/alarm/repository/SseEmitterRepository.java @@ -1,9 +1,9 @@ package kr.pickple.back.alarm.repository; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - import java.util.Optional; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + public interface SseEmitterRepository { SseEmitter save(final String emitterId, final SseEmitter sseEmitter); diff --git a/src/main/java/kr/pickple/back/alarm/service/AlarmService.java b/src/main/java/kr/pickple/back/alarm/service/AlarmService.java index 5804f2bd..4664057e 100644 --- a/src/main/java/kr/pickple/back/alarm/service/AlarmService.java +++ b/src/main/java/kr/pickple/back/alarm/service/AlarmService.java @@ -1,28 +1,30 @@ package kr.pickple.back.alarm.service; +import static kr.pickple.back.member.exception.MemberExceptionCode.*; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + import kr.pickple.back.alarm.dto.response.AlarmExistStatusResponse; import kr.pickple.back.alarm.dto.response.AlarmResponse; import kr.pickple.back.alarm.dto.response.CrewAlarmResponse; import kr.pickple.back.alarm.dto.response.GameAlarmResponse; import kr.pickple.back.alarm.util.CursorResult; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.repository.entity.MemberEntity; import kr.pickple.back.member.exception.MemberException; import kr.pickple.back.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; - -import static kr.pickple.back.member.exception.MemberExceptionCode.MEMBER_NOT_FOUND; @Service @RequiredArgsConstructor public class AlarmService { + private final GameAlarmService gameAlarmService; private final CrewAlarmService crewAlarmService; private final MemberRepository memberRepository; @@ -32,12 +34,15 @@ public SseEmitter subscribeToSse(final Long loggedInMemberId) { return sseEmitterService.subscribeToSse(loggedInMemberId); } - public CursorResult findAllAlarms(final Long loggedInMemberId, final Long cursorId, final Integer size) { + public CursorResult findAllAlarms(final Long loggedInMemberId, final Long cursorId, + final Integer size) { final List alarms = new ArrayList<>(); final Integer checkSizeForNextPage = size + 1; - final List crewAlarms = crewAlarmService.findByMemberId(loggedInMemberId, Optional.ofNullable(cursorId), checkSizeForNextPage); - final List gameAlarms = gameAlarmService.findByMemberId(loggedInMemberId, Optional.ofNullable(cursorId), checkSizeForNextPage); + final List crewAlarms = crewAlarmService.findByMemberId(loggedInMemberId, + Optional.ofNullable(cursorId), checkSizeForNextPage); + final List gameAlarms = gameAlarmService.findByMemberId(loggedInMemberId, + Optional.ofNullable(cursorId), checkSizeForNextPage); alarms.addAll(crewAlarms); alarms.addAll(gameAlarms); @@ -72,13 +77,13 @@ public AlarmExistStatusResponse checkUnReadAlarms(final Long loggedInMemberId) { @Transactional public void deleteAllAlarms(final Long loggedInMemberId) { - final Member member = findMemberById(loggedInMemberId); + final MemberEntity member = findMemberById(loggedInMemberId); crewAlarmService.deleteAllCrewAlarms(member.getId()); gameAlarmService.deleteAllGameAlarms(member.getId()); } - private Member findMemberById(final Long memberId) { + private MemberEntity findMemberById(final Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); } diff --git a/src/main/java/kr/pickple/back/alarm/service/CrewAlarmService.java b/src/main/java/kr/pickple/back/alarm/service/CrewAlarmService.java index 07777dce..4b4cb0ea 100644 --- a/src/main/java/kr/pickple/back/alarm/service/CrewAlarmService.java +++ b/src/main/java/kr/pickple/back/alarm/service/CrewAlarmService.java @@ -1,6 +1,19 @@ package kr.pickple.back.alarm.service; +import static kr.pickple.back.alarm.domain.CrewAlarmType.*; +import static kr.pickple.back.alarm.exception.AlarmExceptionCode.*; +import static kr.pickple.back.crew.exception.CrewExceptionCode.*; +import static kr.pickple.back.member.exception.MemberExceptionCode.*; + +import java.util.List; +import java.util.Optional; + +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import kr.pickple.back.alarm.domain.CrewAlarm; +import kr.pickple.back.alarm.domain.CrewAlarmType; import kr.pickple.back.alarm.dto.request.CrewAlarmUpdateStatusRequest; import kr.pickple.back.alarm.dto.response.CrewAlarmResponse; import kr.pickple.back.alarm.event.crew.CrewJoinRequestNotificationEvent; @@ -8,25 +21,13 @@ import kr.pickple.back.alarm.event.crew.CrewMemberRejectedEvent; import kr.pickple.back.alarm.exception.AlarmException; import kr.pickple.back.alarm.repository.CrewAlarmRepository; -import kr.pickple.back.crew.domain.Crew; import kr.pickple.back.crew.exception.CrewException; import kr.pickple.back.crew.repository.CrewRepository; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.crew.repository.entity.CrewEntity; import kr.pickple.back.member.exception.MemberException; import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.repository.entity.MemberEntity; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.PageRequest; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Optional; - -import static kr.pickple.back.alarm.domain.CrewAlarmType.*; -import static kr.pickple.back.alarm.exception.AlarmExceptionCode.ALARM_NOT_FOUND; -import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_IS_NOT_LEADER; -import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_NOT_FOUND; -import static kr.pickple.back.member.exception.MemberExceptionCode.MEMBER_NOT_FOUND; @Service @RequiredArgsConstructor @@ -39,87 +40,58 @@ public class CrewAlarmService { @Transactional public void createCrewJoinAlarm(final CrewJoinRequestNotificationEvent crewJoinRequestNotificationEvent) { - - validateIsLeader(crewJoinRequestNotificationEvent); - final Long crewId = crewJoinRequestNotificationEvent.getCrewId(); - final Crew crew = getCrewInfo(crewId); - final Member leader = crew.getLeader(); + final Long leaderId = crewJoinRequestNotificationEvent.getMemberId(); + final CrewEntity crew = getCrewById(crewId); - final CrewAlarm crewAlarm = CrewAlarm.builder() - .crew(crew) - .member(leader) - .crewAlarmType(CREW_LEADER_WAITING) - .build(); - - crewAlarmRepository.save(crewAlarm); + if (!crew.isLeader(leaderId)) { + throw new CrewException(CREW_IS_NOT_LEADER, crewId, leaderId); + } - final CrewAlarmResponse response = CrewAlarmResponse.from(crewAlarm); - sseEmitterService.sendAlarm(leader.getId(), response); + createCrewMemberAlarm(crewId, leaderId, CREW_LEADER_WAITING); } @Transactional public void createCrewMemberApproveAlarm(final CrewMemberJoinedEvent crewMemberJoinedEvent) { final Long crewId = crewMemberJoinedEvent.getCrewId(); - final Crew crew = getCrewInfo(crewId); final Long memberId = crewMemberJoinedEvent.getMemberId(); - final Member member = getMemberInfo(memberId); - - final CrewAlarm crewAlarm = CrewAlarm.builder() - .crew(crew) - .member(member) - .crewAlarmType(CREW_ACCEPT) - .build(); - crewAlarmRepository.save(crewAlarm); - - final CrewAlarmResponse response = CrewAlarmResponse.from(crewAlarm); - sseEmitterService.sendAlarm(member.getId(), response); + createCrewMemberAlarm(crewId, memberId, CREW_ACCEPT); } @Transactional public void createCrewMemberDeniedAlarm(final CrewMemberRejectedEvent crewMemberRejectedEvent) { final Long crewId = crewMemberRejectedEvent.getCrewId(); - final Crew crew = getCrewInfo(crewId); final Long memberId = crewMemberRejectedEvent.getMemberId(); - final Member member = getMemberInfo(memberId); + + createCrewMemberAlarm(crewId, memberId, CREW_DENIED); + } + + private void createCrewMemberAlarm(final Long crewId, final Long memberId, final CrewAlarmType crewAlarmType) { + final CrewEntity crew = getCrewById(crewId); + final MemberEntity member = memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); final CrewAlarm crewAlarm = CrewAlarm.builder() .crew(crew) .member(member) - .crewAlarmType(CREW_DENIED) + .crewAlarmType(crewAlarmType) .build(); crewAlarmRepository.save(crewAlarm); - - final CrewAlarmResponse response = CrewAlarmResponse.from(crewAlarm); - sseEmitterService.sendAlarm(member.getId(), response); + sseEmitterService.sendAlarm(memberId, CrewAlarmResponse.from(crewAlarm)); } - private Crew getCrewInfo(final Long crewId) { - final Crew crew = crewRepository.findById(crewId) + private CrewEntity getCrewById(final Long crewId) { + return crewRepository.findById(crewId) .orElseThrow(() -> new CrewException(CREW_NOT_FOUND, crewId)); - - return crew; } - private Member getMemberInfo(final Long memberId) { - final Member member = memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); - - return member; - } - - private void validateIsLeader(final CrewJoinRequestNotificationEvent crewAlarmEvent) { - final Long crewId = crewAlarmEvent.getCrewId(); - final Crew crew = crewRepository.findById(crewId).orElseThrow(() -> new CrewException(CREW_NOT_FOUND, crewId)); - - if (!crew.isLeader(crewAlarmEvent.getMemberId())) { - throw new CrewException(CREW_IS_NOT_LEADER, crewId, crew.getLeader()); - } - } - - public List findByMemberId(final Long loggedInMemberId, final Optional optionalCursorId, final Integer size) { + public List findByMemberId( + final Long loggedInMemberId, + final Optional optionalCursorId, + final Integer size + ) { final List crewAlarms = optionalCursorId .map(cursorId -> crewAlarmRepository.findByMemberIdAndIdLessThanOrderByCreatedAtDesc( loggedInMemberId, @@ -137,9 +109,7 @@ public List findByMemberId(final Long loggedInMemberId, final } public boolean checkUnreadCrewAlarm(final Long memberId) { - final boolean existsUnreadCrewAlarm = crewAlarmRepository.existsByMemberIdAndIsRead(memberId, false); - - return existsUnreadCrewAlarm; + return crewAlarmRepository.existsByMemberIdAndIsRead(memberId, false); } @Transactional @@ -153,19 +123,9 @@ public void updateCrewAlarmById( final Long crewAlarmId, final CrewAlarmUpdateStatusRequest crewAlarmUpdateStatusRequest ) { - final Member member = findMemberById(loggedInMemberId); - final CrewAlarm crewAlarm = checkExistCrewAlarm(loggedInMemberId, crewAlarmId); + final CrewAlarm crewAlarm = crewAlarmRepository.findByMemberIdAndId(loggedInMemberId, crewAlarmId) + .orElseThrow(() -> new AlarmException(ALARM_NOT_FOUND, loggedInMemberId, crewAlarmId)); crewAlarm.updateStatus(crewAlarmUpdateStatusRequest.getIsRead()); } - - private Member findMemberById(final Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); - } - - public CrewAlarm checkExistCrewAlarm(final Long memberId, final Long crewAlarmId) { - return crewAlarmRepository.findByMemberIdAndId(memberId, crewAlarmId) - .orElseThrow(() -> new AlarmException(ALARM_NOT_FOUND, memberId, crewAlarmId)); - } } diff --git a/src/main/java/kr/pickple/back/alarm/service/GameAlarmService.java b/src/main/java/kr/pickple/back/alarm/service/GameAlarmService.java index 3f8ab231..838d2699 100644 --- a/src/main/java/kr/pickple/back/alarm/service/GameAlarmService.java +++ b/src/main/java/kr/pickple/back/alarm/service/GameAlarmService.java @@ -20,10 +20,10 @@ import kr.pickple.back.alarm.event.game.GameMemberRejectedEvent; import kr.pickple.back.alarm.exception.AlarmException; import kr.pickple.back.alarm.repository.GameAlarmRepository; -import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.repository.entity.GameEntity; import kr.pickple.back.game.exception.GameException; import kr.pickple.back.game.repository.GameRepository; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.repository.entity.MemberEntity; import kr.pickple.back.member.exception.MemberException; import kr.pickple.back.member.repository.MemberRepository; import lombok.RequiredArgsConstructor; @@ -43,11 +43,11 @@ public void createGameJoinAlarm(final GameJoinRequestNotificationEvent gameJoinR validateIsHost(gameJoinRequestNotificationEvent); final Long gameId = gameJoinRequestNotificationEvent.getGameId(); - final Game game = getGameInfo(gameId); - final Member host = game.getHost(); + final GameEntity gameEntity = getGameInfo(gameId); + final MemberEntity host = getMemberInfo(gameEntity.getHostId()); final GameAlarm gameAlarm = GameAlarm.builder() - .game(game) + .game(gameEntity) .member(host) .gameAlarmType(HOST_WAITING) .build(); @@ -61,12 +61,12 @@ public void createGameJoinAlarm(final GameJoinRequestNotificationEvent gameJoinR @Transactional public void createGuestApproveAlarm(final GameMemberJoinedEvent gameMemberJoinedEvent) { final Long gameId = gameMemberJoinedEvent.getGameId(); - final Game game = getGameInfo(gameId); + final GameEntity gameEntity = getGameInfo(gameId); final Long memberId = gameMemberJoinedEvent.getMemberId(); - final Member member = getMemberInfo(memberId); + final MemberEntity member = getMemberInfo(memberId); final GameAlarm gameAlarm = GameAlarm.builder() - .game(game) + .game(gameEntity) .member(member) .gameAlarmType(GUEST_ACCEPT) .build(); @@ -81,12 +81,12 @@ public void createGuestApproveAlarm(final GameMemberJoinedEvent gameMemberJoined public void createGuestDeniedAlarm(final GameMemberRejectedEvent gameMemberRejectedEvent) { final Long gameId = gameMemberRejectedEvent.getGameId(); - final Game game = getGameInfo(gameId); + final GameEntity gameEntity = getGameInfo(gameId); final Long memberId = gameMemberRejectedEvent.getMemberId(); - final Member member = getMemberInfo(memberId); + final MemberEntity member = getMemberInfo(memberId); final GameAlarm gameAlarm = GameAlarm.builder() - .game(game) + .game(gameEntity) .member(member) .gameAlarmType(GUEST_DENIED) .build(); @@ -99,20 +99,20 @@ public void createGuestDeniedAlarm(final GameMemberRejectedEvent gameMemberRejec private void validateIsHost(final GameJoinRequestNotificationEvent gameJoinRequestNotificationEvent) { final Long gameId = gameJoinRequestNotificationEvent.getGameId(); - final Game game = gameRepository.findById(gameId).orElseThrow(() -> new GameException(GAME_NOT_FOUND, gameId)); + final GameEntity gameEntity = gameRepository.findById(gameId).orElseThrow(() -> new GameException(GAME_NOT_FOUND, gameId)); - if (!game.isHost(gameJoinRequestNotificationEvent.getMemberId())) { - throw new GameException(GAME_MEMBER_IS_NOT_HOST, gameId, game.getHost()); + if (!gameEntity.isHost(gameJoinRequestNotificationEvent.getMemberId())) { + throw new GameException(GAME_MEMBER_IS_NOT_HOST, gameId, gameEntity.getHostId()); } } - private Game getGameInfo(final Long gameId) { - final Game game = gameRepository.findById(gameId) + private GameEntity getGameInfo(final Long gameId) { + final GameEntity gameEntity = gameRepository.findById(gameId) .orElseThrow(() -> new GameException(GAME_NOT_FOUND, gameId)); - return game; + return gameEntity; } - private Member getMemberInfo(final Long memberId) { + private MemberEntity getMemberInfo(final Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); } @@ -155,13 +155,13 @@ public void updateGameAlarmById( final Long gameAlarmId, final GameAlarmUpdateStatusRequest gameAlarmUpdateStatusRequest ) { - final Member member = findMemberById(loggedInMemberId); + final MemberEntity member = findMemberById(loggedInMemberId); final GameAlarm gameAlarm = checkExistGameAlarm(loggedInMemberId, gameAlarmId); gameAlarm.updateStatus(gameAlarmUpdateStatusRequest.getIsRead()); } - private Member findMemberById(final Long memberId) { + private MemberEntity findMemberById(final Long memberId) { return memberRepository.findById(memberId) .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); } diff --git a/src/main/java/kr/pickple/back/alarm/service/RedisAlarmPublisher.java b/src/main/java/kr/pickple/back/alarm/service/RedisAlarmPublisher.java index af235813..30325f6f 100644 --- a/src/main/java/kr/pickple/back/alarm/service/RedisAlarmPublisher.java +++ b/src/main/java/kr/pickple/back/alarm/service/RedisAlarmPublisher.java @@ -1,9 +1,10 @@ package kr.pickple.back.alarm.service; -import lombok.RequiredArgsConstructor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import lombok.RequiredArgsConstructor; + @Service @RequiredArgsConstructor public class RedisAlarmPublisher implements AlarmPublisher { diff --git a/src/main/java/kr/pickple/back/alarm/service/RedisAlarmSubscriber.java b/src/main/java/kr/pickple/back/alarm/service/RedisAlarmSubscriber.java index cde597e2..d4ba2b15 100644 --- a/src/main/java/kr/pickple/back/alarm/service/RedisAlarmSubscriber.java +++ b/src/main/java/kr/pickple/back/alarm/service/RedisAlarmSubscriber.java @@ -1,20 +1,21 @@ package kr.pickple.back.alarm.service; +import static kr.pickple.back.alarm.exception.AlarmExceptionCode.*; + +import java.util.Map; + +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.stereotype.Service; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; + import kr.pickple.back.alarm.dto.response.AlarmResponse; import kr.pickple.back.alarm.dto.response.CrewAlarmResponse; import kr.pickple.back.alarm.dto.response.GameAlarmResponse; import kr.pickple.back.alarm.exception.AlarmException; import lombok.RequiredArgsConstructor; -import org.springframework.data.redis.connection.Message; -import org.springframework.data.redis.connection.MessageListener; -import org.springframework.stereotype.Service; - -import java.util.Map; - -import static kr.pickple.back.alarm.exception.AlarmExceptionCode.ALARM_CONVERT_TYPE_NOT_FOUND; -import static kr.pickple.back.alarm.exception.AlarmExceptionCode.ALARM_TYPE_NOT_FOUND; @Service @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/alarm/service/SseEmitterService.java b/src/main/java/kr/pickple/back/alarm/service/SseEmitterService.java index dc57c127..ad2a322d 100644 --- a/src/main/java/kr/pickple/back/alarm/service/SseEmitterService.java +++ b/src/main/java/kr/pickple/back/alarm/service/SseEmitterService.java @@ -1,15 +1,16 @@ package kr.pickple.back.alarm.service; +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + import kr.pickple.back.alarm.repository.RedisEventCacheRepository; import kr.pickple.back.alarm.repository.SseEmitterRepository; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; - -import java.io.IOException; -import java.util.List; -import java.util.Optional; @Slf4j @Service diff --git a/src/main/java/kr/pickple/back/alarm/util/CursorResult.java b/src/main/java/kr/pickple/back/alarm/util/CursorResult.java index 358e2bdc..ed0ddade 100644 --- a/src/main/java/kr/pickple/back/alarm/util/CursorResult.java +++ b/src/main/java/kr/pickple/back/alarm/util/CursorResult.java @@ -1,12 +1,12 @@ package kr.pickple.back.alarm.util; +import java.util.List; + import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import java.util.List; - @Getter @Builder @NoArgsConstructor diff --git a/src/main/java/kr/pickple/back/auth/controller/OauthController.java b/src/main/java/kr/pickple/back/auth/controller/OauthController.java index 111dc8fb..a8e581ec 100644 --- a/src/main/java/kr/pickple/back/auth/controller/OauthController.java +++ b/src/main/java/kr/pickple/back/auth/controller/OauthController.java @@ -1,5 +1,22 @@ package kr.pickple.back.auth.controller; +import static org.springframework.http.HttpHeaders.*; +import static org.springframework.http.HttpStatus.*; + +import java.io.IOException; + +import org.springframework.http.ResponseCookie; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + import jakarta.servlet.http.HttpServletResponse; import kr.pickple.back.auth.config.property.JwtProperties; import kr.pickple.back.auth.config.resolver.Login; @@ -8,14 +25,6 @@ import kr.pickple.back.auth.service.OauthService; import kr.pickple.back.member.dto.response.AuthenticatedMemberResponse; import lombok.RequiredArgsConstructor; -import org.springframework.http.ResponseCookie; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.io.IOException; - -import static org.springframework.http.HttpHeaders.SET_COOKIE; -import static org.springframework.http.HttpStatus.*; @RequiredArgsConstructor @RestController diff --git a/src/main/java/kr/pickple/back/auth/implement/TokenManager.java b/src/main/java/kr/pickple/back/auth/implement/TokenManager.java new file mode 100644 index 00000000..b0b5bdcc --- /dev/null +++ b/src/main/java/kr/pickple/back/auth/implement/TokenManager.java @@ -0,0 +1,42 @@ +package kr.pickple.back.auth.implement; + +import java.time.LocalDateTime; + +import org.springframework.stereotype.Component; + +import kr.pickple.back.auth.config.property.JwtProperties; +import kr.pickple.back.auth.domain.token.AuthTokens; +import kr.pickple.back.auth.domain.token.JwtProvider; +import kr.pickple.back.auth.domain.token.RefreshToken; +import kr.pickple.back.auth.repository.RedisRepository; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class TokenManager { + + private static final String REFRESH_TOKEN_KEY = "refresh_token"; + + private final JwtProvider jwtProvider; + private final JwtProperties jwtProperties; + private final RedisRepository redisRepository; + + public AuthTokens create(final Long memberId) { + final AuthTokens loginTokens = jwtProvider.createLoginToken(String.valueOf(memberId)); + + final RefreshToken refreshToken = RefreshToken.builder() + .token(loginTokens.getRefreshToken()) + .memberId(memberId) + .createdAt(LocalDateTime.now()) + .build(); + + redisRepository.saveHash( + REFRESH_TOKEN_KEY, + refreshToken.getToken(), + refreshToken, + jwtProperties.getRefreshTokenExpirationTime() + ); + + return loginTokens; + } +} diff --git a/src/main/java/kr/pickple/back/auth/service/OauthService.java b/src/main/java/kr/pickple/back/auth/service/OauthService.java index aa368188..5a62a1b7 100644 --- a/src/main/java/kr/pickple/back/auth/service/OauthService.java +++ b/src/main/java/kr/pickple/back/auth/service/OauthService.java @@ -1,5 +1,15 @@ package kr.pickple.back.auth.service; +import static kr.pickple.back.auth.exception.AuthExceptionCode.*; + +import java.time.LocalDateTime; +import java.util.Optional; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.implement.AddressReader; import kr.pickple.back.auth.config.property.JwtProperties; import kr.pickple.back.auth.config.resolver.TokenExtractor; import kr.pickple.back.auth.domain.oauth.OauthMember; @@ -12,18 +22,10 @@ import kr.pickple.back.auth.repository.RedisRepository; import kr.pickple.back.auth.service.authcode.AuthCodeRequestUrlProviderComposite; import kr.pickple.back.auth.service.memberclient.OauthMemberClientComposite; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.repository.entity.MemberEntity; import kr.pickple.back.member.dto.response.AuthenticatedMemberResponse; -import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.implement.MemberReader; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.LocalDateTime; -import java.util.Optional; - -import static kr.pickple.back.auth.exception.AuthExceptionCode.AUTH_FAIL_TO_VALIDATE_TOKEN; -import static kr.pickple.back.auth.exception.AuthExceptionCode.AUTH_INVALID_REFRESH_TOKEN; @Service @RequiredArgsConstructor @@ -32,13 +34,14 @@ public class OauthService { private static final String REFRESH_TOKEN_KEY = "refresh_token"; - private final MemberRepository memberRepository; + private final MemberReader memberReader; private final AuthCodeRequestUrlProviderComposite authCodeRequestUrlProviderComposite; private final OauthMemberClientComposite oauthMemberClientComposite; private final TokenExtractor tokenExtractor; private final JwtProvider jwtProvider; private final JwtProperties jwtProperties; private final RedisRepository redisRepository; + private final AddressReader addressReader; public String getAuthCodeRequestUrl(final OauthProvider oauthProvider) { return authCodeRequestUrlProviderComposite.provide(oauthProvider); @@ -50,11 +53,11 @@ public AuthenticatedMemberResponse processLoginOrRegistration( final String authCode ) { final OauthMember oauthMember = oauthMemberClientComposite.fetch(oauthProvider, authCode); - final Optional member = memberRepository.findByOauthId(oauthMember.getOauthId()); + final Optional member = memberReader.readByOauthId(oauthMember.getOauthId()); // 사용자가 로그인 하는 경우 if (member.isPresent()) { - final Member loginMember = member.get(); + final MemberEntity loginMember = member.get(); final AuthTokens loginTokens = jwtProvider.createLoginToken(String.valueOf(loginMember.getId())); final RefreshToken refreshToken = RefreshToken.builder() @@ -70,7 +73,12 @@ public AuthenticatedMemberResponse processLoginOrRegistration( jwtProperties.getRefreshTokenExpirationTime() ); - return AuthenticatedMemberResponse.of(loginMember, loginTokens); + final MainAddress mainAddress = addressReader.readMainAddressByIds( + loginMember.getAddressDepth1Id(), + loginMember.getAddressDepth2Id() + ); + + return AuthenticatedMemberResponse.of(loginMember, loginTokens, mainAddress); } // 사용자가 회원 가입 시, 추가 정보(주 활동지역, 포지션)를 입력하는 경우 @@ -84,8 +92,7 @@ public AccessTokenResponse regenerateAccessToken(final String refreshToken, fina final String accessToken = tokenExtractor.extractAccessToken(authorizationHeader); if (jwtProvider.isValidRefreshAndInvalidAccess(refreshToken, accessToken)) { - final RefreshToken validRefreshToken = redisRepository.findHash(REFRESH_TOKEN_KEY, - refreshToken); + final RefreshToken validRefreshToken = redisRepository.findHash(REFRESH_TOKEN_KEY, refreshToken); if (validRefreshToken == null) { throw new AuthException(AUTH_INVALID_REFRESH_TOKEN); diff --git a/src/main/java/kr/pickple/back/chat/controller/ChatRoomController.java b/src/main/java/kr/pickple/back/chat/controller/ChatRoomController.java index 1b7a7a27..6320085b 100644 --- a/src/main/java/kr/pickple/back/chat/controller/ChatRoomController.java +++ b/src/main/java/kr/pickple/back/chat/controller/ChatRoomController.java @@ -17,8 +17,8 @@ import kr.pickple.back.chat.domain.RoomType; import kr.pickple.back.chat.dto.request.PersonalChatRoomCreateRequest; import kr.pickple.back.chat.dto.response.ChatRoomDetailResponse; -import kr.pickple.back.chat.dto.response.PersonalChatRoomExistedResponse; import kr.pickple.back.chat.dto.response.ChatRoomResponse; +import kr.pickple.back.chat.dto.response.PersonalChatRoomStatusResponse; import kr.pickple.back.chat.service.ChatRoomFindService; import kr.pickple.back.chat.service.ChatRoomService; import lombok.RequiredArgsConstructor; @@ -37,25 +37,25 @@ public ResponseEntity createPersonalRoom( @RequestBody final PersonalChatRoomCreateRequest personalChatRoomCreateRequest ) { return ResponseEntity.status(CREATED) - .body(chatRoomService.createPersonalRoom(senderId, personalChatRoomCreateRequest)); + .body(chatRoomService.createPersonalRoom(senderId, personalChatRoomCreateRequest.getReceiverId())); } @GetMapping("/personal") - public ResponseEntity findActivePersonalChatRoomWithReceiver( + public ResponseEntity findPersonalChatRoomStatus( @Login final Long senderId, @RequestParam("receiver") final Long receiverId ) { return ResponseEntity.status(OK) - .body(chatRoomService.findActivePersonalChatRoomWithReceiver(senderId, receiverId)); + .body(chatRoomService.findPersonalChatRoomStatus(senderId, receiverId)); } @GetMapping - public ResponseEntity> findAllActiveChatRoomsByType( + public ResponseEntity> findAllEnteringChatRoomsByType( @Login final Long loggedInMemberId, @RequestParam final RoomType type ) { return ResponseEntity.status(OK) - .body(chatRoomFindService.findAllActiveChatRoomsByType(loggedInMemberId, type)); + .body(chatRoomFindService.findAllEnteringChatRoomsByType(loggedInMemberId, type)); } @GetMapping("/{roomId}") diff --git a/src/main/java/kr/pickple/back/chat/domain/ChatMessage.java b/src/main/java/kr/pickple/back/chat/domain/ChatMessage.java index c558ea2e..23324f60 100644 --- a/src/main/java/kr/pickple/back/chat/domain/ChatMessage.java +++ b/src/main/java/kr/pickple/back/chat/domain/ChatMessage.java @@ -1,66 +1,24 @@ package kr.pickple.back.chat.domain; -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.validation.constraints.NotNull; -import kr.pickple.back.chat.util.MessageTypeAttributeConverter; -import kr.pickple.back.common.domain.BaseEntity; +import java.time.LocalDateTime; + import kr.pickple.back.member.domain.Member; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ChatMessage extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class ChatMessage { - @Getter - @NotNull - @Convert(converter = MessageTypeAttributeConverter.class) + private Long chatMessageId; private MessageType type; - - @Getter - @NotNull - @Column(length = 500) private String content; - - @Getter - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "sender_id") private Member sender; - - @Getter - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "chat_room_id") private ChatRoom chatRoom; - - @Builder - private ChatMessage(final ChatRoom chatRoom, final Member sender, final String content, final MessageType type) { - this.type = type; - this.content = content; - this.chatRoom = chatRoom; - this.sender = sender; - } - - public void updateContent(final String content) { - this.content = content; - } - - public Boolean isMatchedMessageType(final MessageType type) { - return this.type == type; - } + private LocalDateTime createdAt; } diff --git a/src/main/java/kr/pickple/back/chat/domain/ChatMessages.java b/src/main/java/kr/pickple/back/chat/domain/ChatMessages.java deleted file mode 100644 index 9ea0f430..00000000 --- a/src/main/java/kr/pickple/back/chat/domain/ChatMessages.java +++ /dev/null @@ -1,42 +0,0 @@ -package kr.pickple.back.chat.domain; - -import static kr.pickple.back.chat.domain.MessageType.*; -import static kr.pickple.back.chat.exception.ChatExceptionCode.*; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.OneToMany; -import kr.pickple.back.chat.exception.ChatException; -import kr.pickple.back.member.domain.Member; -import lombok.Getter; - -@Embeddable -public class ChatMessages { - - @Getter - @OneToMany(mappedBy = "chatRoom", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) - private List chatMessages = new ArrayList<>(); - - void addChatMessage(final ChatMessage chatMessage) { - chatMessages.add(chatMessage); - } - - ChatMessage getLastChatMessage() { - return chatMessages.get(chatMessages.size() - 1); - } - - ChatMessage getLastEnteringChatMessageByMember(final Member member) { - return chatMessages.stream() - .filter(chatMessage -> isEnteringMessageByMember(member, chatMessage)) - .max(Comparator.comparing(ChatMessage::getCreatedAt)) - .orElseThrow(() -> new ChatException(CHAT_MEMBER_IS_NOT_IN_ROOM, member.getId())); - } - - private Boolean isEnteringMessageByMember(final Member member, final ChatMessage chatMessage) { - return member.equals(chatMessage.getSender()) && chatMessage.isMatchedMessageType(ENTER); - } -} diff --git a/src/main/java/kr/pickple/back/chat/domain/ChatRoom.java b/src/main/java/kr/pickple/back/chat/domain/ChatRoom.java index d0630084..8bf49679 100644 --- a/src/main/java/kr/pickple/back/chat/domain/ChatRoom.java +++ b/src/main/java/kr/pickple/back/chat/domain/ChatRoom.java @@ -2,141 +2,49 @@ import static kr.pickple.back.chat.exception.ChatExceptionCode.*; -import java.util.List; +import java.time.LocalDateTime; -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.validation.constraints.NotNull; import kr.pickple.back.chat.exception.ChatException; -import kr.pickple.back.chat.util.RoomTypeAttributeConverter; -import kr.pickple.back.common.domain.BaseEntity; -import kr.pickple.back.member.domain.Member; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class ChatRoom extends BaseEntity { +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class ChatRoom { - @Id - @Getter - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Getter - @NotNull - @Column(length = 20) + private Long chatRoomId; private String name; - - @Getter - @NotNull - @Convert(converter = RoomTypeAttributeConverter.class) private RoomType type; - - @Getter - @NotNull - private Integer memberCount = 0; - - @Getter - @NotNull - private Integer maxMemberCount = 2; - - @Embedded - private ChatRoomMembers chatRoomMembers = new ChatRoomMembers(); - - @Embedded - private ChatMessages chatMessages = new ChatMessages(); - - @Builder - private ChatRoom(final String name, final RoomType type) { - this.name = name; - this.type = type; - } + private Integer memberCount; + private Integer maxMemberCount; + private LocalDateTime createdAt; public void increaseMemberCount() { - if (isFullRoom()) { + if (memberCount.equals(maxMemberCount)) { throw new ChatException(CHAT_ROOM_IS_FULL, memberCount); } memberCount += 1; } - private Boolean isFullRoom() { - return memberCount.equals(maxMemberCount); - } - public void decreaseMemberCount() { - if (isEmptyRoom()) { + if (isEmpty()) { throw new ChatException(CHAT_ROOM_IS_EMPTY, memberCount); } memberCount -= 1; } - public Boolean isEmptyRoom() { + public Boolean isEmpty() { return memberCount == 0; } - public Boolean isEntered(final Member member) { - return chatRoomMembers.isMemberEnteredRoom(member); - } - public Boolean isMatchedRoomType(final RoomType type) { return this.type == type; } - - public void updateMaxMemberCount(final Integer maxMemberCount) { - if (maxMemberCount < memberCount) { - throw new ChatException(CHAT_MAX_MEMBER_COUNT_SHOULD_BE_BIGGER_THAN_MEMBER_COUNT, maxMemberCount); - } - - this.maxMemberCount = maxMemberCount; - } - - public void sendMessage(final ChatMessage chatMessage) { - chatMessages.addChatMessage(chatMessage); - } - - public void enterRoom(final ChatMessage chatMessage) { - final Member newMember = chatMessage.getSender(); - chatRoomMembers.activateChatRoomMember(this, newMember); - - sendMessage(chatMessage); - increaseMemberCount(); - } - - public void leaveRoom(final ChatMessage chatMessage) { - final Member member = chatMessage.getSender(); - chatRoomMembers.deactivateChatRoomMember(this, member); - - sendMessage(chatMessage); - decreaseMemberCount(); - } - - public List getChatMessages() { - return chatMessages.getChatMessages(); - } - - public List getActiveMembersInRoom() { - return chatRoomMembers.getActiveMembers(); - } - - public List getAllMembersInRoom() { - return chatRoomMembers.getAllMembers(); - } - - public ChatMessage getLastChatMessage() { - return chatMessages.getLastChatMessage(); - } - - public ChatMessage getLastEnteringChatMessageByMember(final Member member) { - return chatMessages.getLastEnteringChatMessageByMember(member); - } } diff --git a/src/main/java/kr/pickple/back/chat/domain/ChatRoomMember.java b/src/main/java/kr/pickple/back/chat/domain/ChatRoomMember.java deleted file mode 100644 index 3efc12b8..00000000 --- a/src/main/java/kr/pickple/back/chat/domain/ChatRoomMember.java +++ /dev/null @@ -1,65 +0,0 @@ -package kr.pickple.back.chat.domain; - -import static java.lang.Boolean.*; - -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import jakarta.persistence.UniqueConstraint; -import jakarta.validation.constraints.NotNull; -import kr.pickple.back.common.domain.BaseEntity; -import kr.pickple.back.member.domain.Member; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Entity -@Table(uniqueConstraints = @UniqueConstraint(columnNames = {"member_id", "chat_room_id"})) -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@EqualsAndHashCode(of = {"member", "chatRoom"}, callSuper = false) -public class ChatRoomMember extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - private Boolean active = TRUE; - - @Getter - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; - - @Getter - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "chat_room_id") - private ChatRoom chatRoom; - - @Builder - private ChatRoomMember(final Member member, final ChatRoom chatRoom) { - this.member = member; - this.chatRoom = chatRoom; - } - - public void activate() { - active = TRUE; - } - - public void deactivate() { - active = FALSE; - } - - public Boolean isActive() { - return active; - } -} diff --git a/src/main/java/kr/pickple/back/chat/domain/ChatRoomMembers.java b/src/main/java/kr/pickple/back/chat/domain/ChatRoomMembers.java deleted file mode 100644 index 28f7ae93..00000000 --- a/src/main/java/kr/pickple/back/chat/domain/ChatRoomMembers.java +++ /dev/null @@ -1,71 +0,0 @@ -package kr.pickple.back.chat.domain; - -import static kr.pickple.back.chat.exception.ChatExceptionCode.*; - -import java.util.ArrayList; -import java.util.List; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.OneToMany; -import kr.pickple.back.chat.exception.ChatException; -import kr.pickple.back.member.domain.Member; - -@Embeddable -public class ChatRoomMembers { - - @OneToMany(mappedBy = "chatRoom", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) - private List chatRoomMembers = new ArrayList<>(); - - void activateChatRoomMember(final ChatRoom chatRoom, final Member member) { - final ChatRoomMember chatRoomMember = buildChatRoom(chatRoom, member); - - if (chatRoomMembers.contains(chatRoomMember)) { - final ChatRoomMember foundChatRoomMember = findChatRoomMember(chatRoom, member); - foundChatRoomMember.activate(); - - return; - } - - chatRoomMembers.add(chatRoomMember); - } - - void deactivateChatRoomMember(final ChatRoom chatRoom, final Member member) { - final ChatRoomMember foundChatRoomMember = findChatRoomMember(chatRoom, member); - foundChatRoomMember.deactivate(); - } - - private ChatRoomMember findChatRoomMember(final ChatRoom chatRoom, final Member member) { - final ChatRoomMember chatRoomMember = buildChatRoom(chatRoom, member); - - return chatRoomMembers.stream() - .filter(chatRoomMember::equals) - .findFirst() - .orElseThrow(() -> new ChatException(CHAT_MEMBER_IS_NOT_IN_ROOM, member.getId(), chatRoom.getId())); - } - - private ChatRoomMember buildChatRoom(final ChatRoom chatRoom, final Member member) { - return ChatRoomMember.builder() - .member(member) - .chatRoom(chatRoom) - .build(); - } - - Boolean isMemberEnteredRoom(final Member member) { - return chatRoomMembers.stream() - .anyMatch(chatRoomMember -> member.equals(chatRoomMember.getMember())); - } - - List getActiveMembers() { - return chatRoomMembers.stream() - .filter(ChatRoomMember::isActive) - .map(ChatRoomMember::getMember) - .toList(); - } - - List getAllMembers() { - return chatRoomMembers.stream() - .map(ChatRoomMember::getMember) - .toList(); - } -} diff --git a/src/main/java/kr/pickple/back/chat/domain/PersonalChatRoomStatus.java b/src/main/java/kr/pickple/back/chat/domain/PersonalChatRoomStatus.java new file mode 100644 index 00000000..ae74a745 --- /dev/null +++ b/src/main/java/kr/pickple/back/chat/domain/PersonalChatRoomStatus.java @@ -0,0 +1,15 @@ +package kr.pickple.back.chat.domain; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PersonalChatRoomStatus { + + private Long roomId; + private Boolean isSenderActive; +} diff --git a/src/main/java/kr/pickple/back/chat/dto/mapper/ChatResponseMapper.java b/src/main/java/kr/pickple/back/chat/dto/mapper/ChatResponseMapper.java new file mode 100644 index 00000000..27bf9917 --- /dev/null +++ b/src/main/java/kr/pickple/back/chat/dto/mapper/ChatResponseMapper.java @@ -0,0 +1,135 @@ +package kr.pickple.back.chat.dto.mapper; + +import java.util.List; + +import kr.pickple.back.chat.domain.ChatMessage; +import kr.pickple.back.chat.domain.ChatRoom; +import kr.pickple.back.chat.domain.PersonalChatRoomStatus; +import kr.pickple.back.chat.dto.response.ChatMemberResponse; +import kr.pickple.back.chat.dto.response.ChatMessageResponse; +import kr.pickple.back.chat.dto.response.ChatRoomDetailResponse; +import kr.pickple.back.chat.dto.response.ChatRoomResponse; +import kr.pickple.back.chat.dto.response.PersonalChatRoomStatusResponse; +import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.member.domain.Member; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class ChatResponseMapper { + + public static ChatRoomResponse mapToChatRoomResponseDto( + final ChatMessage lastChatMessage, + final ChatRoomDetailResponse chatRoomDetailResponse + ) { + return ChatRoomResponse.builder() + .id(chatRoomDetailResponse.getId()) + .roomName(chatRoomDetailResponse.getRoomName()) + .roomIconImageUrl(chatRoomDetailResponse.getRoomIconImageUrl()) + .type(chatRoomDetailResponse.getType()) + .memberCount(chatRoomDetailResponse.getMemberCount()) + .maxMemberCount(chatRoomDetailResponse.getMaxMemberCount()) + .playStartTime(chatRoomDetailResponse.getPlayStartTime()) + .playTimeMinutes(chatRoomDetailResponse.getPlayTimeMinutes()) + .lastMessageContent(lastChatMessage.getContent()) + .lastMessageCreatedAt(lastChatMessage.getCreatedAt()) + .createdAt(chatRoomDetailResponse.getCreatedAt()) + .build(); + } + + public static ChatRoomDetailResponse mapToPersonalChatRoomDetailResponseDto( + final Member sender, + final Member receiver, + final ChatRoom chatRoom + ) { + final List chatMemberResponses = List.of( + mapToChatMemberResponseDto(sender), + mapToChatMemberResponseDto(receiver) + ); + + return ChatRoomDetailResponse.builder() + .id(chatRoom.getChatRoomId()) + .roomName(receiver.getNickname()) + .roomIconImageUrl(receiver.getProfileImageUrl()) + .type(chatRoom.getType()) + .domainId(receiver.getMemberId()) + .memberCount(chatRoom.getMemberCount()) + .maxMemberCount(chatRoom.getMaxMemberCount()) + .members(chatMemberResponses) + .createdAt(chatRoom.getCreatedAt()) + .build(); + } + + public static ChatRoomDetailResponse mapToCrewChatRoomDetailResponseDto( + final Crew crew, + final ChatRoom chatRoom, + final List members + ) { + final List chatMemberResponses = members.stream() + .map(ChatResponseMapper::mapToChatMemberResponseDto) + .toList(); + + return ChatRoomDetailResponse.builder() + .id(chatRoom.getChatRoomId()) + .roomName(chatRoom.getName()) + .roomIconImageUrl(crew.getProfileImageUrl()) + .type(chatRoom.getType()) + .domainId(crew.getCrewId()) + .memberCount(chatRoom.getMemberCount()) + .maxMemberCount(chatRoom.getMaxMemberCount()) + .members(chatMemberResponses) + .createdAt(chatRoom.getCreatedAt()) + .build(); + } + + public static ChatRoomDetailResponse mapToGameChatRoomDetailResponseDto( + final Game game, + final ChatRoom chatRoom, + final List members + ) { + final List chatMemberResponses = members.stream() + .map(ChatResponseMapper::mapToChatMemberResponseDto) + .toList(); + + return ChatRoomDetailResponse.builder() + .id(chatRoom.getChatRoomId()) + .roomName(chatRoom.getName()) + .type(chatRoom.getType()) + .domainId(game.getGameId()) + .memberCount(chatRoom.getMemberCount()) + .maxMemberCount(chatRoom.getMaxMemberCount()) + .playStartTime(game.getPlayStartTime()) + .playTimeMinutes(game.getPlayTimeMinutes()) + .members(chatMemberResponses) + .createdAt(chatRoom.getCreatedAt()) + .build(); + } + + public static ChatMemberResponse mapToChatMemberResponseDto(final Member member) { + return ChatMemberResponse.builder() + .id(member.getMemberId()) + .nickname(member.getNickname()) + .profileImageUrl(member.getProfileImageUrl()) + .build(); + } + + public static PersonalChatRoomStatusResponse mapToPersonalChatRoomStatusResponseDto( + final PersonalChatRoomStatus personalChatRoomStatus + ) { + return PersonalChatRoomStatusResponse.builder() + .roomId(personalChatRoomStatus.getRoomId()) + .isSenderActive(personalChatRoomStatus.getIsSenderActive()) + .build(); + } + + public static ChatMessageResponse mapToChatMessageResponseDto(final ChatMessage chatMessage) { + return ChatMessageResponse.builder() + .type(chatMessage.getType()) + .content(chatMessage.getContent()) + .sender(mapToChatMemberResponseDto(chatMessage.getSender())) + .roomId(chatMessage.getChatRoom().getChatRoomId()) + .createdAt(chatMessage.getCreatedAt()) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/chat/dto/response/ChatMemberResponse.java b/src/main/java/kr/pickple/back/chat/dto/response/ChatMemberResponse.java index b778d70d..6a4d0b80 100644 --- a/src/main/java/kr/pickple/back/chat/dto/response/ChatMemberResponse.java +++ b/src/main/java/kr/pickple/back/chat/dto/response/ChatMemberResponse.java @@ -1,6 +1,5 @@ package kr.pickple.back.chat.dto.response; -import kr.pickple.back.member.domain.Member; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -14,12 +13,4 @@ public class ChatMemberResponse { private Long id; private String nickname; private String profileImageUrl; - - public static ChatMemberResponse from(final Member member) { - return ChatMemberResponse.builder() - .id(member.getId()) - .nickname(member.getNickname()) - .profileImageUrl(member.getProfileImageUrl()) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/chat/dto/response/ChatMessageResponse.java b/src/main/java/kr/pickple/back/chat/dto/response/ChatMessageResponse.java index 5f515797..de33a808 100644 --- a/src/main/java/kr/pickple/back/chat/dto/response/ChatMessageResponse.java +++ b/src/main/java/kr/pickple/back/chat/dto/response/ChatMessageResponse.java @@ -2,7 +2,6 @@ import java.time.LocalDateTime; -import kr.pickple.back.chat.domain.ChatMessage; import kr.pickple.back.chat.domain.MessageType; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -19,14 +18,4 @@ public class ChatMessageResponse { private ChatMemberResponse sender; private Long roomId; private LocalDateTime createdAt; - - public static ChatMessageResponse from(final ChatMessage chatMessage) { - return ChatMessageResponse.builder() - .type(chatMessage.getType()) - .content(chatMessage.getContent()) - .sender(ChatMemberResponse.from(chatMessage.getSender())) - .roomId(chatMessage.getChatRoom().getId()) - .createdAt(chatMessage.getCreatedAt()) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/chat/dto/response/ChatRoomDetailResponse.java b/src/main/java/kr/pickple/back/chat/dto/response/ChatRoomDetailResponse.java index 689be77f..6356c802 100644 --- a/src/main/java/kr/pickple/back/chat/dto/response/ChatRoomDetailResponse.java +++ b/src/main/java/kr/pickple/back/chat/dto/response/ChatRoomDetailResponse.java @@ -4,11 +4,7 @@ import java.time.LocalTime; import java.util.List; -import kr.pickple.back.chat.domain.ChatRoom; import kr.pickple.back.chat.domain.RoomType; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.member.domain.Member; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -30,54 +26,4 @@ public class ChatRoomDetailResponse { private Integer playTimeMinutes; private List members; private LocalDateTime createdAt; - - public static ChatRoomDetailResponse of(final ChatRoom chatRoom, final Member receiver) { - return ChatRoomDetailResponse.builder() - .id(chatRoom.getId()) - .roomName(receiver.getNickname()) - .roomIconImageUrl(receiver.getProfileImageUrl()) - .type(chatRoom.getType()) - .domainId(receiver.getId()) - .memberCount(chatRoom.getMemberCount()) - .maxMemberCount(chatRoom.getMaxMemberCount()) - .members(getChatMemberResponses(chatRoom)) - .createdAt(chatRoom.getCreatedAt()) - .build(); - } - - public static ChatRoomDetailResponse of(final ChatRoom chatRoom, final Game game) { - return ChatRoomDetailResponse.builder() - .id(chatRoom.getId()) - .roomName(chatRoom.getName()) - .type(chatRoom.getType()) - .domainId(game.getId()) - .memberCount(chatRoom.getMemberCount()) - .maxMemberCount(chatRoom.getMaxMemberCount()) - .playStartTime(game.getPlayStartTime()) - .playTimeMinutes(game.getPlayTimeMinutes()) - .members(getChatMemberResponses(chatRoom)) - .createdAt(chatRoom.getCreatedAt()) - .build(); - } - - public static ChatRoomDetailResponse of(final ChatRoom chatRoom, final Crew crew) { - return ChatRoomDetailResponse.builder() - .id(chatRoom.getId()) - .roomName(chatRoom.getName()) - .roomIconImageUrl(crew.getProfileImageUrl()) - .type(chatRoom.getType()) - .domainId(crew.getId()) - .memberCount(chatRoom.getMemberCount()) - .maxMemberCount(chatRoom.getMaxMemberCount()) - .members(getChatMemberResponses(chatRoom)) - .createdAt(chatRoom.getCreatedAt()) - .build(); - } - - private static List getChatMemberResponses(final ChatRoom chatRoom) { - return chatRoom.getActiveMembersInRoom() - .stream() - .map(ChatMemberResponse::from) - .toList(); - } } diff --git a/src/main/java/kr/pickple/back/chat/dto/response/ChatRoomResponse.java b/src/main/java/kr/pickple/back/chat/dto/response/ChatRoomResponse.java index 8e65ecfd..2b5d0d80 100644 --- a/src/main/java/kr/pickple/back/chat/dto/response/ChatRoomResponse.java +++ b/src/main/java/kr/pickple/back/chat/dto/response/ChatRoomResponse.java @@ -25,24 +25,4 @@ public class ChatRoomResponse { private String lastMessageContent; private LocalDateTime lastMessageCreatedAt; private LocalDateTime createdAt; - - public static ChatRoomResponse of( - final ChatRoomDetailResponse chatRoomDetail, - final String lastMessageContent, - final LocalDateTime lastMessageCreatedAt - ) { - return ChatRoomResponse.builder() - .id(chatRoomDetail.getId()) - .roomName(chatRoomDetail.getRoomName()) - .roomIconImageUrl(chatRoomDetail.getRoomIconImageUrl()) - .type(chatRoomDetail.getType()) - .memberCount(chatRoomDetail.getMemberCount()) - .maxMemberCount(chatRoomDetail.getMaxMemberCount()) - .playStartTime(chatRoomDetail.getPlayStartTime()) - .playTimeMinutes(chatRoomDetail.getPlayTimeMinutes()) - .lastMessageContent(lastMessageContent) - .lastMessageCreatedAt(lastMessageCreatedAt) - .createdAt(chatRoomDetail.getCreatedAt()) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/chat/dto/response/PersonalChatRoomExistedResponse.java b/src/main/java/kr/pickple/back/chat/dto/response/PersonalChatRoomStatusResponse.java similarity index 52% rename from src/main/java/kr/pickple/back/chat/dto/response/PersonalChatRoomExistedResponse.java rename to src/main/java/kr/pickple/back/chat/dto/response/PersonalChatRoomStatusResponse.java index 6c701354..80ebfc5a 100644 --- a/src/main/java/kr/pickple/back/chat/dto/response/PersonalChatRoomExistedResponse.java +++ b/src/main/java/kr/pickple/back/chat/dto/response/PersonalChatRoomStatusResponse.java @@ -1,11 +1,14 @@ package kr.pickple.back.chat.dto.response; +import lombok.AccessLevel; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; @Getter -@AllArgsConstructor(staticName = "of") -public class PersonalChatRoomExistedResponse { +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class PersonalChatRoomStatusResponse { private Long roomId; private Boolean isSenderActive; diff --git a/src/main/java/kr/pickple/back/chat/exception/ChatExceptionCode.java b/src/main/java/kr/pickple/back/chat/exception/ChatExceptionCode.java index 5b7299fd..ad06ed06 100644 --- a/src/main/java/kr/pickple/back/chat/exception/ChatExceptionCode.java +++ b/src/main/java/kr/pickple/back/chat/exception/ChatExceptionCode.java @@ -18,7 +18,8 @@ public enum ChatExceptionCode implements ExceptionCode { CHAT_MEMBER_IS_ALREADY_IN_ROOM(HttpStatus.BAD_REQUEST, "CHT-006", "해당 채팅방에 이미 존재하는 사용자"), CHAT_MEMBER_IS_NOT_IN_ROOM(HttpStatus.BAD_REQUEST, "CHT-007", "해당 채팅방에 존재하지 않는 사용자"), CHAT_MEMBER_CANNOT_CHAT_SELF(HttpStatus.BAD_REQUEST, "CHT-008", "사용자는 자기 자신과 채팅할 수 없음"), - CHAT_MAX_MEMBER_COUNT_SHOULD_BE_BIGGER_THAN_MEMBER_COUNT(HttpStatus.BAD_REQUEST, "CHT-009", "채팅방 인원제한은 현재 인원수 이상이어야함"), + CHAT_MAX_MEMBER_COUNT_SHOULD_BE_BIGGER_THAN_MEMBER_COUNT(HttpStatus.BAD_REQUEST, "CHT-009", + "채팅방 인원제한은 현재 인원수 이상이어야함"), CHAT_CREW_NOT_FOUND(HttpStatus.NOT_FOUND, "CHT-010", "해당 채팅방을 소유한 크루 정보를 찾을 수 없음"), CHAT_GAME_NOT_FOUND(HttpStatus.NOT_FOUND, "CHT-011", "해당 채팅방을 소유한 게스트 모집글 정보를 찾을 수 없음"), CHAT_RECEIVER_NOT_FOUND(HttpStatus.NOT_FOUND, "CHT-012", "해당 1:1 채팅방에 나를 제외한 사용자가 존재하지 않음"), diff --git a/src/main/java/kr/pickple/back/chat/implement/ChatMapper.java b/src/main/java/kr/pickple/back/chat/implement/ChatMapper.java new file mode 100644 index 00000000..aa8c11d3 --- /dev/null +++ b/src/main/java/kr/pickple/back/chat/implement/ChatMapper.java @@ -0,0 +1,39 @@ +package kr.pickple.back.chat.implement; + +import kr.pickple.back.chat.repository.entity.ChatMessageEntity; +import kr.pickple.back.chat.domain.ChatMessage; +import kr.pickple.back.chat.repository.entity.ChatRoomEntity; +import kr.pickple.back.chat.domain.ChatRoom; +import kr.pickple.back.member.domain.Member; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class ChatMapper { + + public static ChatRoom mapChatRoomEntityToDomain(final ChatRoomEntity chatRoomEntity) { + return ChatRoom.builder() + .chatRoomId(chatRoomEntity.getId()) + .type(chatRoomEntity.getType()) + .name(chatRoomEntity.getName()) + .memberCount(chatRoomEntity.getMemberCount()) + .maxMemberCount(chatRoomEntity.getMaxMemberCount()) + .createdAt(chatRoomEntity.getCreatedAt()) + .build(); + } + + public static ChatMessage mapChatMessageEntityToDomain( + final ChatMessageEntity chatMessageEntity, + final Member sender, + final ChatRoom chatRoom + ) { + return ChatMessage.builder() + .chatMessageId(chatMessageEntity.getId()) + .type(chatMessageEntity.getType()) + .content(chatMessageEntity.getContent()) + .sender(sender) + .chatRoom(chatRoom) + .createdAt(chatMessageEntity.getCreatedAt()) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/chat/implement/ChatReader.java b/src/main/java/kr/pickple/back/chat/implement/ChatReader.java new file mode 100644 index 00000000..88a5b08e --- /dev/null +++ b/src/main/java/kr/pickple/back/chat/implement/ChatReader.java @@ -0,0 +1,132 @@ +package kr.pickple.back.chat.implement; + +import static kr.pickple.back.chat.domain.RoomType.PERSONAL; +import static kr.pickple.back.chat.exception.ChatExceptionCode.CHAT_MEMBER_IS_NOT_IN_ROOM; +import static kr.pickple.back.chat.exception.ChatExceptionCode.CHAT_RECEIVER_NOT_FOUND; +import static kr.pickple.back.chat.exception.ChatExceptionCode.CHAT_ROOM_NOT_FOUND; + +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.chat.domain.ChatMessage; +import kr.pickple.back.chat.domain.ChatRoom; +import kr.pickple.back.chat.domain.PersonalChatRoomStatus; +import kr.pickple.back.chat.domain.RoomType; +import kr.pickple.back.chat.exception.ChatException; +import kr.pickple.back.chat.repository.ChatMessageRepository; +import kr.pickple.back.chat.repository.ChatRoomMemberRepository; +import kr.pickple.back.chat.repository.ChatRoomRepository; +import kr.pickple.back.chat.repository.entity.ChatMessageEntity; +import kr.pickple.back.chat.repository.entity.ChatRoomEntity; +import kr.pickple.back.chat.repository.entity.ChatRoomMemberEntity; +import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.game.repository.GameRepository; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.implement.MemberReader; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ChatReader { + + private final MemberReader memberReader; + private final CrewRepository crewRepository; + private final GameRepository gameRepository; + private final ChatRoomRepository chatRoomRepository; + private final ChatRoomMemberRepository chatRoomMemberRepository; + private final ChatMessageRepository chatMessageRepository; + + public ChatRoom readRoom(final Long chatRoomId) { + final ChatRoomEntity chatRoomEntity = chatRoomRepository.findById(chatRoomId) + .orElseThrow(() -> new ChatException(CHAT_ROOM_NOT_FOUND, chatRoomId)); + + return ChatMapper.mapChatRoomEntityToDomain(chatRoomEntity); + } + + public ChatRoom readRoomByCrewId(final Long crewId) { + return readRoom(crewRepository.findChatRoomId(crewId)); + } + + public ChatRoom readRoomByGameId(final Long gameId) { + return readRoom(gameRepository.findChatRoomId(gameId)); + } + + public PersonalChatRoomStatus readPersonalRoomStatus(final Long senderId, final Long receiverId) { + final ChatRoomMemberEntity receiverEntity = chatRoomMemberRepository.findAllByMemberId(senderId) + .stream() + .filter(chatRoomMemberEntity -> { + final ChatRoom chatRoom = readRoom(chatRoomMemberEntity.getChatRoomId()); + final Long chatRoomId = chatRoom.getChatRoomId(); + + return chatRoom.isMatchedRoomType(PERSONAL) + && chatRoomMemberRepository.existsByChatRoomIdAndMemberId(chatRoomId, receiverId); + }) + .findFirst() + .orElseThrow(() -> new ChatException(CHAT_ROOM_NOT_FOUND)); + + return PersonalChatRoomStatus.builder() + .roomId(receiverEntity.getChatRoomId()) + .isSenderActive(receiverEntity.getActive()) + .build(); + } + + public List readEnteringRoomsByType(final Long memberId, final RoomType type) { + return chatRoomMemberRepository.findAllByActiveTrueAndMemberId(memberId) + .stream() + .map(chatRoomMemberEntity -> readRoom(chatRoomMemberEntity.getChatRoomId())) + .filter(chatRoom -> chatRoom.isMatchedRoomType(type)) + .toList(); + } + + public Member readReceiver(final Long senderId, final Long chatRoomId) { + final ChatRoomMemberEntity receiverEntity = chatRoomMemberRepository.findByChatRoomIdAndMemberIdNot( + chatRoomId, senderId) + .orElseThrow(() -> new ChatException(CHAT_RECEIVER_NOT_FOUND)); + + return memberReader.readByMemberId(receiverEntity.getMemberId()); + } + + public List readRoomMembers(final Long chatRoomId) { + return chatRoomMemberRepository.findAllByActiveTrueAndChatRoomId(chatRoomId) + .stream() + .map(chatRoomMember -> memberReader.readByMemberId(chatRoomMember.getMemberId())) + .toList(); + } + + public ChatMessage readLastMessage(final ChatRoom chatRoom) { + final ChatMessageEntity lastMessageEntity = chatMessageRepository.findTopByChatRoomIdOrderByCreatedAtDesc( + chatRoom.getChatRoomId()); + final Member sender = memberReader.readByMemberId(lastMessageEntity.getSenderId()); + + return ChatMapper.mapChatMessageEntityToDomain(lastMessageEntity, sender, chatRoom); + } + + public List readMessagesAfterEntrance(final Long memberId, final Long chatRoomId) { + final ChatRoom chatRoom = readRoom(chatRoomId); + + if (!chatRoomMemberRepository.existsByActiveTrueAndChatRoomIdAndMemberId(chatRoomId, memberId)) { + throw new ChatException(CHAT_MEMBER_IS_NOT_IN_ROOM, chatRoomId, memberId); + } + + final LocalDateTime entranceDatetime = chatMessageRepository.findChatRoomLastEntranceMessageCreatedAt( + memberId, + chatRoomId + ); + final List chatMessageEntities = chatMessageRepository.findAllByChatRoomIdAndCreatedAtGreaterThanEqual( + chatRoomId, + entranceDatetime + ); + + return chatMessageEntities.stream() + .map(chatMessageEntity -> ChatMapper.mapChatMessageEntityToDomain( + chatMessageEntity, + memberReader.readByMemberId(chatMessageEntity.getSenderId()), + chatRoom + )) + .toList(); + } +} diff --git a/src/main/java/kr/pickple/back/chat/implement/ChatWriter.java b/src/main/java/kr/pickple/back/chat/implement/ChatWriter.java new file mode 100644 index 00000000..c48d40f6 --- /dev/null +++ b/src/main/java/kr/pickple/back/chat/implement/ChatWriter.java @@ -0,0 +1,191 @@ +package kr.pickple.back.chat.implement; + +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; +import static kr.pickple.back.chat.domain.MessageType.ENTER; +import static kr.pickple.back.chat.domain.MessageType.LEAVE; +import static kr.pickple.back.chat.domain.RoomType.CREW; +import static kr.pickple.back.chat.domain.RoomType.GAME; +import static kr.pickple.back.chat.domain.RoomType.PERSONAL; +import static kr.pickple.back.chat.exception.ChatExceptionCode.CHAT_CREW_CHATROOM_NOT_ALLOWED_TO_LEAVE; +import static kr.pickple.back.chat.exception.ChatExceptionCode.CHAT_GAME_CHATROOM_NOT_ALLOWED_TO_LEAVE; +import static kr.pickple.back.chat.exception.ChatExceptionCode.CHAT_MEMBER_IS_ALREADY_IN_ROOM; +import static kr.pickple.back.chat.exception.ChatExceptionCode.CHAT_MEMBER_IS_NOT_IN_ROOM; +import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; + +import java.time.LocalDateTime; +import java.util.Optional; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.chat.domain.ChatMessage; +import kr.pickple.back.chat.domain.ChatRoom; +import kr.pickple.back.chat.domain.MessageType; +import kr.pickple.back.chat.domain.RoomType; +import kr.pickple.back.chat.exception.ChatException; +import kr.pickple.back.chat.repository.ChatMessageRepository; +import kr.pickple.back.chat.repository.ChatRoomMemberRepository; +import kr.pickple.back.chat.repository.ChatRoomRepository; +import kr.pickple.back.chat.repository.entity.ChatMessageEntity; +import kr.pickple.back.chat.repository.entity.ChatRoomEntity; +import kr.pickple.back.chat.repository.entity.ChatRoomMemberEntity; +import kr.pickple.back.common.util.DateTimeUtil; +import kr.pickple.back.crew.repository.CrewMemberRepository; +import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.game.repository.GameRepository; +import kr.pickple.back.game.repository.entity.GameEntity; +import kr.pickple.back.member.domain.Member; +import lombok.RequiredArgsConstructor; + +@Component +@Transactional +@RequiredArgsConstructor +public class ChatWriter { + + private static final Integer PERSONAL_ROOM_MAX_MEMBER_COUNT = 2; + + private final CrewRepository crewRepository; + private final CrewMemberRepository crewMemberRepository; + private final GameRepository gameRepository; + private final ChatRoomRepository chatRoomRepository; + private final ChatRoomMemberRepository chatRoomMemberRepository; + private final ChatMessageRepository chatMessageRepository; + + public ChatRoom createNewPersonalRoom(final String name) { + final ChatRoomEntity chatRoomEntity = ChatRoomEntity.builder() + .name(name) + .type(PERSONAL) + .maxMemberCount(PERSONAL_ROOM_MAX_MEMBER_COUNT) + .build(); + final ChatRoomEntity savedChatRoomEntity = chatRoomRepository.save(chatRoomEntity); + + return ChatMapper.mapChatRoomEntityToDomain(savedChatRoomEntity); + } + + public ChatRoom createNewGroupRoom(final RoomType type, final String name, final Integer maxMemberCount) { + final ChatRoomEntity chatRoomEntity = ChatRoomEntity.builder() + .name(name) + .type(type) + .maxMemberCount(maxMemberCount) + .build(); + final ChatRoomEntity savedChatRoomEntity = chatRoomRepository.save(chatRoomEntity); + + return ChatMapper.mapChatRoomEntityToDomain(savedChatRoomEntity); + } + + public ChatMessage enterRoom(final Member member, final ChatRoom chatRoom) { + final Long memberId = member.getMemberId(); + final Long chatRoomId = chatRoom.getChatRoomId(); + + if (chatRoomMemberRepository.existsByActiveTrueAndChatRoomIdAndMemberId(chatRoomId, memberId)) { + throw new ChatException(CHAT_MEMBER_IS_ALREADY_IN_ROOM, chatRoomId, memberId); + } + + activateRoom(chatRoomId, memberId); + chatRoom.increaseMemberCount(); + chatRoomRepository.updateMemberCount(chatRoomId, chatRoom.getMemberCount()); + + return sendMessage(ENTER, MessageType.makeEnterMessage(member.getNickname()), member, chatRoom); + } + + private void activateRoom(final Long chatRoomId, final Long memberId) { + if (chatRoomMemberRepository.existsByChatRoomIdAndMemberId(chatRoomId, memberId)) { + chatRoomMemberRepository.updateChatRoomMemberActiveStatus(chatRoomId, memberId, TRUE); + + return; + } + + chatRoomMemberRepository.save(ChatRoomMemberEntity.builder() + .memberId(memberId) + .chatRoomId(chatRoomId) + .build()); + } + + public ChatMessage sendMessage( + final MessageType type, + final String content, + final Member sender, + final ChatRoom chatRoom + ) { + final Long chatRoomId = chatRoom.getChatRoomId(); + final Long senderId = sender.getMemberId(); + + if (!chatRoomMemberRepository.existsByActiveTrueAndChatRoomIdAndMemberId(chatRoomId, senderId)) { + throw new ChatException(CHAT_MEMBER_IS_NOT_IN_ROOM, chatRoomId, senderId); + } + + final ChatMessageEntity chatMessageEntity = ChatMessageEntity.builder() + .type(type) + .content(content) + .senderId(senderId) + .chatRoomId(chatRoomId) + .build(); + final ChatMessageEntity savedChatMessageEntity = chatMessageRepository.save(chatMessageEntity); + + return ChatMapper.mapChatMessageEntityToDomain(savedChatMessageEntity, sender, chatRoom); + } + + public ChatMessage leaveRoom(final Member member, final ChatRoom chatRoom) { + final Long memberId = member.getMemberId(); + final Long chatRoomId = chatRoom.getChatRoomId(); + + if (!chatRoomMemberRepository.existsByActiveTrueAndChatRoomIdAndMemberId(chatRoomId, memberId)) { + throw new ChatException(CHAT_MEMBER_IS_NOT_IN_ROOM, chatRoomId, memberId); + } + + validateCanLeaveChatRoom(memberId, chatRoom); + + final ChatMessage leaveMessage = sendMessage( + LEAVE, + MessageType.makeLeaveMessage(member.getNickname()), + member, + chatRoom + ); + + chatRoomMemberRepository.updateChatRoomMemberActiveStatus(chatRoomId, memberId, FALSE); + chatRoom.decreaseMemberCount(); + chatRoomRepository.updateMemberCount(chatRoomId, chatRoom.getMemberCount()); + + if (chatRoom.isEmpty()) { + chatRoomRepository.deleteById(chatRoomId); + } + + return leaveMessage; + } + + private void validateCanLeaveChatRoom(final Long memberId, final ChatRoom chatRoom) { + if (chatRoom.getType() == CREW) { + validateCanLeaveCrewChatRoom(memberId, chatRoom); + } + + if (chatRoom.getType() == GAME) { + validateCanLeaveGameChatRoom(chatRoom); + } + } + + private void validateCanLeaveCrewChatRoom(final Long memberId, final ChatRoom chatRoom) { + final Optional crewEntity = crewRepository.findByChatRoomId(chatRoom.getChatRoomId()); + + if (crewEntity.isPresent() && existsMemberInCrew(crewEntity.get().getId(), memberId)) { + throw new ChatException(CHAT_CREW_CHATROOM_NOT_ALLOWED_TO_LEAVE); + } + } + + private Boolean existsMemberInCrew(final Long crewId, final Long memberId) { + return crewMemberRepository.existsByCrewIdAndMemberIdAndStatus(crewId, memberId, CONFIRMED); + } + + private void validateCanLeaveGameChatRoom(final ChatRoom chatRoom) { + final Optional gameEntity = gameRepository.findByChatRoomId(chatRoom.getChatRoomId()); + + if (gameEntity.isPresent() && isGameNotEnded(gameEntity.get().getPlayEndDatetime())) { + throw new ChatException(CHAT_GAME_CHATROOM_NOT_ALLOWED_TO_LEAVE); + } + } + + private Boolean isGameNotEnded(final LocalDateTime gameEndDatetime) { + return DateTimeUtil.isAfterThanNow(gameEndDatetime); + } +} diff --git a/src/main/java/kr/pickple/back/chat/repository/ChatMessageRepository.java b/src/main/java/kr/pickple/back/chat/repository/ChatMessageRepository.java index 672cbd47..f51b5c8b 100644 --- a/src/main/java/kr/pickple/back/chat/repository/ChatMessageRepository.java +++ b/src/main/java/kr/pickple/back/chat/repository/ChatMessageRepository.java @@ -1,9 +1,30 @@ package kr.pickple.back.chat.repository; +import java.time.LocalDateTime; +import java.util.List; + import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import kr.pickple.back.chat.repository.entity.ChatMessageEntity; + +public interface ChatMessageRepository extends JpaRepository { -import kr.pickple.back.chat.domain.ChatMessage; + List findAllByChatRoomIdAndCreatedAtGreaterThanEqual( + final Long chatRoomId, + final LocalDateTime createdAt + ); -public interface ChatMessageRepository extends JpaRepository { + ChatMessageEntity findTopByChatRoomIdOrderByCreatedAtDesc(final Long chatRoomId); + @Query(""" + select cm.createdAt + from ChatMessageEntity cm + where cm.senderId= :memberId + and cm.chatRoomId = :chatRoomId + and cm.type = kr.pickple.back.chat.domain.MessageType.ENTER + order by cm.createdAt desc + limit 1 + """) + LocalDateTime findChatRoomLastEntranceMessageCreatedAt(final Long memberId, final Long chatRoomId); } diff --git a/src/main/java/kr/pickple/back/chat/repository/ChatRoomMemberRepository.java b/src/main/java/kr/pickple/back/chat/repository/ChatRoomMemberRepository.java index f178eb3d..7eaa640c 100644 --- a/src/main/java/kr/pickple/back/chat/repository/ChatRoomMemberRepository.java +++ b/src/main/java/kr/pickple/back/chat/repository/ChatRoomMemberRepository.java @@ -1,18 +1,37 @@ package kr.pickple.back.chat.repository; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.domain.ChatRoomMember; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.chat.repository.entity.ChatRoomMemberEntity; -public interface ChatRoomMemberRepository extends JpaRepository { +public interface ChatRoomMemberRepository extends JpaRepository { - Boolean existsByActiveTrueAndChatRoomAndMember(final ChatRoom chatRoom, final Member member); + Boolean existsByChatRoomIdAndMemberId(final Long chatRoomId, final Long memberId); - List findAllByMember(final Member member); + Boolean existsByActiveTrueAndChatRoomIdAndMemberId(final Long chatRoomId, final Long memberId); - List findAllByActiveTrueAndMember(final Member member); + List findAllByMemberId(final Long memberId); + + List findAllByActiveTrueAndMemberId(final Long memberId); + + List findAllByActiveTrueAndChatRoomId(final Long chatRoomId); + + // 개인 채팅방에서 상대방에 대한 ChatRoomMember 데이터 조회용 + Optional findByChatRoomIdAndMemberIdNot(final Long chatRoomId, final Long memberId); + + @Modifying(clearAutomatically = true) + @Query(""" + update ChatRoomMemberEntity crm + set crm.active = :activeStatus + where crm.chatRoomId = :chatRoomId and crm.memberId = :memberId""") + void updateChatRoomMemberActiveStatus( + final Long chatRoomId, + final Long memberId, + final Boolean activeStatus + ); } diff --git a/src/main/java/kr/pickple/back/chat/repository/ChatRoomRepository.java b/src/main/java/kr/pickple/back/chat/repository/ChatRoomRepository.java index bf075cda..a898a8b5 100644 --- a/src/main/java/kr/pickple/back/chat/repository/ChatRoomRepository.java +++ b/src/main/java/kr/pickple/back/chat/repository/ChatRoomRepository.java @@ -1,9 +1,14 @@ package kr.pickple.back.chat.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; -import kr.pickple.back.chat.domain.ChatRoom; +import kr.pickple.back.chat.repository.entity.ChatRoomEntity; -public interface ChatRoomRepository extends JpaRepository { +public interface ChatRoomRepository extends JpaRepository { + @Modifying(clearAutomatically = true) + @Query("update ChatRoomEntity cr set cr.memberCount = :memberCount where cr.id = :chatRoomId") + void updateMemberCount(final Long chatRoomId, final Integer memberCount); } diff --git a/src/main/java/kr/pickple/back/chat/repository/entity/ChatMessageEntity.java b/src/main/java/kr/pickple/back/chat/repository/entity/ChatMessageEntity.java new file mode 100644 index 00000000..28b771a4 --- /dev/null +++ b/src/main/java/kr/pickple/back/chat/repository/entity/ChatMessageEntity.java @@ -0,0 +1,51 @@ +package kr.pickple.back.chat.repository.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import kr.pickple.back.chat.domain.MessageType; +import kr.pickple.back.chat.util.MessageTypeAttributeConverter; +import kr.pickple.back.common.domain.BaseEntity; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@Table(name = "chat_message") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ChatMessageEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Convert(converter = MessageTypeAttributeConverter.class) + private MessageType type; + + @NotNull + @Column(length = 500) + private String content; + + @NotNull + private Long senderId; + + @NotNull + private Long chatRoomId; + + @Builder + private ChatMessageEntity(final MessageType type, final String content, final Long senderId, + final Long chatRoomId) { + this.type = type; + this.content = content; + this.senderId = senderId; + this.chatRoomId = chatRoomId; + } +} diff --git a/src/main/java/kr/pickple/back/chat/repository/entity/ChatRoomEntity.java b/src/main/java/kr/pickple/back/chat/repository/entity/ChatRoomEntity.java new file mode 100644 index 00000000..6f6adda5 --- /dev/null +++ b/src/main/java/kr/pickple/back/chat/repository/entity/ChatRoomEntity.java @@ -0,0 +1,49 @@ +package kr.pickple.back.chat.repository.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import kr.pickple.back.chat.domain.RoomType; +import kr.pickple.back.chat.util.RoomTypeAttributeConverter; +import kr.pickple.back.common.domain.BaseEntity; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@Table(name = "chat_room") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class ChatRoomEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Column(length = 20) + private String name; + + @NotNull + @Convert(converter = RoomTypeAttributeConverter.class) + private RoomType type; + + @NotNull + private Integer memberCount = 0; + + @NotNull + private Integer maxMemberCount = 2; + + @Builder + private ChatRoomEntity(final String name, final RoomType type, final Integer maxMemberCount) { + this.name = name; + this.type = type; + this.maxMemberCount = maxMemberCount; + } +} diff --git a/src/main/java/kr/pickple/back/chat/repository/entity/ChatRoomMemberEntity.java b/src/main/java/kr/pickple/back/chat/repository/entity/ChatRoomMemberEntity.java new file mode 100644 index 00000000..7aefd90a --- /dev/null +++ b/src/main/java/kr/pickple/back/chat/repository/entity/ChatRoomMemberEntity.java @@ -0,0 +1,47 @@ +package kr.pickple.back.chat.repository.entity; + +import static java.lang.Boolean.*; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.UniqueConstraint; +import jakarta.validation.constraints.NotNull; +import kr.pickple.back.common.domain.BaseEntity; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "chat_room_member", uniqueConstraints = @UniqueConstraint(columnNames = {"member_id", "chat_room_id"})) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = {"memberId", "chatRoomId"}, callSuper = false) +public class ChatRoomMemberEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + private Boolean active = TRUE; + + @NotNull + @Column(name = "member_id") + private Long memberId; + + @NotNull + @Column(name = "chat_room_id") + private Long chatRoomId; + + @Builder + private ChatRoomMemberEntity(final Long memberId, final Long chatRoomId) { + this.memberId = memberId; + this.chatRoomId = chatRoomId; + } +} diff --git a/src/main/java/kr/pickple/back/chat/service/ChatMessageService.java b/src/main/java/kr/pickple/back/chat/service/ChatMessageService.java index a3073243..7823b860 100644 --- a/src/main/java/kr/pickple/back/chat/service/ChatMessageService.java +++ b/src/main/java/kr/pickple/back/chat/service/ChatMessageService.java @@ -1,8 +1,6 @@ package kr.pickple.back.chat.service; import static kr.pickple.back.chat.domain.MessageType.*; -import static kr.pickple.back.chat.exception.ChatExceptionCode.*; -import static kr.pickple.back.member.exception.MemberExceptionCode.*; import java.util.List; @@ -11,16 +9,13 @@ import kr.pickple.back.chat.domain.ChatMessage; import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.domain.MessageType; +import kr.pickple.back.chat.dto.mapper.ChatResponseMapper; import kr.pickple.back.chat.dto.request.ChatMessageCreateRequest; import kr.pickple.back.chat.dto.response.ChatMessageResponse; -import kr.pickple.back.chat.exception.ChatException; -import kr.pickple.back.chat.repository.ChatMessageRepository; -import kr.pickple.back.chat.repository.ChatRoomRepository; -import kr.pickple.back.common.util.DateTimeUtil; +import kr.pickple.back.chat.implement.ChatReader; +import kr.pickple.back.chat.implement.ChatWriter; import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.implement.MemberReader; import lombok.RequiredArgsConstructor; @Service @@ -28,119 +23,67 @@ @Transactional(readOnly = true) public class ChatMessageService { - private final ChatValidator chatValidator; - - private final ChatMessageRepository chatMessageRepository; - private final ChatRoomRepository chatRoomRepository; - private final MemberRepository memberRepository; + private final MemberReader memberReader; + private final ChatReader chatReader; + private final ChatWriter chatWriter; + /** + * 채팅방 입장 + */ @Transactional public ChatMessageResponse enterChatRoom( - final Long roomId, + final Long chatRoomId, final ChatMessageCreateRequest chatMessageCreateRequest ) { - final Member member = findMemberById(chatMessageCreateRequest.getSenderId()); - final ChatRoom chatRoom = findRoomById(roomId); - final ChatMessage enteringMessage = enterRoomAndSaveEnteringMessages(chatRoom, member); + final ChatRoom chatRoom = chatReader.readRoom(chatRoomId); + final Member newMember = memberReader.readByMemberId(chatMessageCreateRequest.getSenderId()); + final ChatMessage entranceMessage = chatWriter.enterRoom(newMember, chatRoom); - return ChatMessageResponse.from(enteringMessage); - } - - @Transactional - public ChatMessage enterRoomAndSaveEnteringMessages(final ChatRoom chatRoom, final Member member) { - chatValidator.validateIsNotExistedRoomMember(chatRoom, member); - - final String content = MessageType.makeEnterMessage(member.getNickname()); - final ChatMessage chatMessage = buildChatMessage(ENTER, content, chatRoom, member); - chatRoom.enterRoom(chatMessage); - - return chatMessageRepository.save(chatMessage); + return ChatResponseMapper.mapToChatMessageResponseDto(entranceMessage); } + /** + * 채팅 메시지 전송 + */ @Transactional public ChatMessageResponse sendMessage( - final Long roomId, + final Long chatRoomId, final ChatMessageCreateRequest chatMessageCreateRequest ) { - final Member sender = findMemberById(chatMessageCreateRequest.getSenderId()); - final ChatRoom chatRoom = findRoomById(roomId); - - chatValidator.validateIsExistedRoomMember(sender, chatRoom); - - final String content = chatMessageCreateRequest.getContent(); - final ChatMessage chatMessage = buildChatMessage(TALK, content, chatRoom, sender); - chatRoom.sendMessage(chatMessage); - final ChatMessage sendingMessage = chatMessageRepository.save(chatMessage); - - return ChatMessageResponse.from(sendingMessage); + final ChatRoom chatRoom = chatReader.readRoom(chatRoomId); + final Member sender = memberReader.readByMemberId(chatMessageCreateRequest.getSenderId()); + final ChatMessage chatMessage = chatWriter.sendMessage( + TALK, + chatMessageCreateRequest.getContent(), + sender, + chatRoom + ); + + return ChatResponseMapper.mapToChatMessageResponseDto(chatMessage); } + /** + * 채팅방 퇴장 + */ @Transactional public ChatMessageResponse leaveChatRoom( - final Long roomId, + final Long chatRoomId, final ChatMessageCreateRequest chatMessageCreateRequest ) { - final Member member = findMemberById(chatMessageCreateRequest.getSenderId()); - final ChatRoom chatRoom = findRoomById(roomId); - - chatValidator.validateIsExistedRoomMember(member, chatRoom); - chatValidator.validateChatRoomLeavingConditions(member, chatRoom); + final ChatRoom chatRoom = chatReader.readRoom(chatRoomId); + final Member member = memberReader.readByMemberId(chatMessageCreateRequest.getSenderId()); + final ChatMessage leaveMessage = chatWriter.leaveRoom(member, chatRoom); - final String content = MessageType.makeLeaveMessage(member.getNickname()); - final ChatMessage chatMessage = buildChatMessage(LEAVE, content, chatRoom, member); - chatRoom.leaveRoom(chatMessage); - final ChatMessage leavingMessage = chatMessageRepository.save(chatMessage); - - if (chatRoom.isEmptyRoom()) { - chatRoomRepository.delete(chatRoom); - } - - return ChatMessageResponse.from(leavingMessage); + return ChatResponseMapper.mapToChatMessageResponseDto(leaveMessage); } - private ChatMessage buildChatMessage( - final MessageType type, - final String content, - final ChatRoom chatRoom, - final Member member - ) { - return ChatMessage.builder() - .type(type) - .content(content) - .chatRoom(chatRoom) - .sender(member) - .build(); - } - - public List findAllMessagesInRoom(final Long loggedInMemberId, final Long roomId) { - final ChatRoom chatRoom = findRoomById(roomId); - final Member loggedInMember = findMemberById(loggedInMemberId); - - chatValidator.validateIsExistedRoomMember(loggedInMember, chatRoom); - - final ChatMessage lastEnteringMessage = chatRoom.getLastEnteringChatMessageByMember(loggedInMember); - - return chatRoom.getChatMessages() + /** + * 특정 채팅방의 모든 메시지 목록 조회 + */ + public List findAllMessagesInRoom(final Long loggedInMemberId, final Long chatRoomId) { + return chatReader.readMessagesAfterEntrance(loggedInMemberId, chatRoomId) .stream() - .filter(thisChatMessage -> isEqualOrAfterThanLastEnteringMessage(lastEnteringMessage, thisChatMessage)) - .map(ChatMessageResponse::from) + .map(ChatResponseMapper::mapToChatMessageResponseDto) .toList(); } - - private Boolean isEqualOrAfterThanLastEnteringMessage( - final ChatMessage lastEnteringMessage, - final ChatMessage thisChatMessage - ) { - return DateTimeUtil.isEqualOrAfter(lastEnteringMessage.getCreatedAt(), thisChatMessage.getCreatedAt()); - } - - private ChatRoom findRoomById(final Long roomId) { - return chatRoomRepository.findById(roomId) - .orElseThrow(() -> new ChatException(CHAT_ROOM_NOT_FOUND, roomId)); - } - - private Member findMemberById(final Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); - } } diff --git a/src/main/java/kr/pickple/back/chat/service/ChatRoomFindService.java b/src/main/java/kr/pickple/back/chat/service/ChatRoomFindService.java index 7715988d..fc1cf393 100644 --- a/src/main/java/kr/pickple/back/chat/service/ChatRoomFindService.java +++ b/src/main/java/kr/pickple/back/chat/service/ChatRoomFindService.java @@ -1,29 +1,25 @@ package kr.pickple.back.chat.service; -import static kr.pickple.back.chat.exception.ChatExceptionCode.*; import static kr.pickple.back.member.exception.MemberExceptionCode.*; -import java.time.LocalDateTime; import java.util.List; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.domain.ChatRoomMember; import kr.pickple.back.chat.domain.RoomType; +import kr.pickple.back.chat.dto.mapper.ChatResponseMapper; import kr.pickple.back.chat.dto.response.ChatRoomDetailResponse; import kr.pickple.back.chat.dto.response.ChatRoomResponse; -import kr.pickple.back.chat.exception.ChatException; -import kr.pickple.back.chat.repository.ChatRoomMemberRepository; -import kr.pickple.back.chat.repository.ChatRoomRepository; +import kr.pickple.back.chat.implement.ChatReader; import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.implement.CrewReader; import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.repository.GameRepository; +import kr.pickple.back.game.implement.GameReader; import kr.pickple.back.member.domain.Member; import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.implement.MemberReader; import lombok.RequiredArgsConstructor; @Service @@ -31,76 +27,65 @@ @Transactional(readOnly = true) public class ChatRoomFindService { - private final ChatRoomRepository chatRoomRepository; - private final ChatRoomMemberRepository chatRoomMemberRepository; - private final MemberRepository memberRepository; - private final CrewRepository crewRepository; - private final GameRepository gameRepository; + private final MemberReader memberReader; + private final CrewReader crewReader; + private final GameReader gameReader; + private final ChatReader chatReader; - public List findAllActiveChatRoomsByType(final Long loggedInMemberId, final RoomType type) { - final Member loggedInMember = findMemberById(loggedInMemberId); + /** + * 채팅방 타입에 따른 참여중인 모든 채팅방 목록 조회 + */ + public List findAllEnteringChatRoomsByType(final Long loggedInMemberId, final RoomType type) { + if (!memberReader.existsByMemberId(loggedInMemberId)) { + throw new MemberException(MEMBER_NOT_FOUND, loggedInMemberId); + } - return chatRoomMemberRepository.findAllByActiveTrueAndMember(loggedInMember) + return chatReader.readEnteringRoomsByType(loggedInMemberId, type) .stream() - .map(ChatRoomMember::getChatRoom) - .filter(chatRoom -> chatRoom.isMatchedRoomType(type)) - .map(chatRoom -> getChatRoomResponse(loggedInMemberId, chatRoom)) - .toList(); + .map(chatRoom -> ChatResponseMapper.mapToChatRoomResponseDto( + chatReader.readLastMessage(chatRoom), + getChatRoomDetailResponse(loggedInMemberId, chatRoom)) + ).toList(); } - private ChatRoomResponse getChatRoomResponse(final Long loggedInMemberId, final ChatRoom chatRoom) { - final String lastMessageContent = chatRoom.getLastChatMessage().getContent(); - final LocalDateTime lastMessageCreatedAt = chatRoom.getLastChatMessage().getCreatedAt(); - final ChatRoomDetailResponse chatRoomDetail = getChatRoomDetailResponse(loggedInMemberId, chatRoom); + /** + * 단일 채팅방 정보 상세 조회 + */ + public ChatRoomDetailResponse findChatRoomById(final Long loggedInMemberId, final Long chatRoomId) { + final ChatRoom chatRoom = chatReader.readRoom(chatRoomId); - return ChatRoomResponse.of(chatRoomDetail, lastMessageContent, lastMessageCreatedAt); + return getChatRoomDetailResponse(loggedInMemberId, chatRoom); } - public ChatRoomDetailResponse findChatRoomById(final Long loggedInMemberId, final Long roomId) { - return getChatRoomDetailResponse(loggedInMemberId, findRoomById(roomId)); - } - - private ChatRoom findRoomById(final Long roomId) { - return chatRoomRepository.findById(roomId) - .orElseThrow(() -> new ChatException(CHAT_ROOM_NOT_FOUND, roomId)); - } - - private ChatRoomDetailResponse getChatRoomDetailResponse(final Long loggedInMemberId, final ChatRoom chatRoom) { + private ChatRoomDetailResponse getChatRoomDetailResponse(final Long memberId, final ChatRoom chatRoom) { return switch (chatRoom.getType()) { - case PERSONAL -> getPersonalChatRoomDetailResponse(loggedInMemberId, chatRoom); + case PERSONAL -> getPersonalChatRoomDetailResponse(memberId, chatRoom); case CREW -> getCrewChatRoomDetailResponse(chatRoom); case GAME -> getGameChatRoomDetailResponse(chatRoom); }; } - private ChatRoomDetailResponse getPersonalChatRoomDetailResponse(final Long memberId, final ChatRoom chatRoom) { - final Member sender = findMemberById(memberId); - - final Member receiver = chatRoom.getAllMembersInRoom() - .stream() - .filter(roomMember -> !roomMember.equals(sender)) - .findFirst() - .orElseThrow(() -> new ChatException(CHAT_RECEIVER_NOT_FOUND)); - - return ChatRoomDetailResponse.of(chatRoom, receiver); - } + private ChatRoomDetailResponse getPersonalChatRoomDetailResponse( + final Long senderId, + final ChatRoom chatRoom + ) { + final Member sender = memberReader.readByMemberId(senderId); + final Member receiver = chatReader.readReceiver(senderId, chatRoom.getChatRoomId()); - private Member findMemberById(final Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); + return ChatResponseMapper.mapToPersonalChatRoomDetailResponseDto(sender, receiver, chatRoom); } private ChatRoomDetailResponse getCrewChatRoomDetailResponse(final ChatRoom chatRoom) { - final Crew crew = crewRepository.findByChatRoom(chatRoom) - .orElseThrow(() -> new ChatException(CHAT_CREW_NOT_FOUND, chatRoom.getId())); + final Crew crew = crewReader.readByChatRoomId(chatRoom.getChatRoomId()); + final List members = chatReader.readRoomMembers(chatRoom.getChatRoomId()); - return ChatRoomDetailResponse.of(chatRoom, crew); + return ChatResponseMapper.mapToCrewChatRoomDetailResponseDto(crew, chatRoom, members); } private ChatRoomDetailResponse getGameChatRoomDetailResponse(final ChatRoom chatRoom) { - final Game game = gameRepository.findByChatRoom(chatRoom) - .orElseThrow(() -> new ChatException(CHAT_GAME_NOT_FOUND, chatRoom.getId())); + final Game game = gameReader.readByChatRoomId(chatRoom.getChatRoomId()); + final List members = chatReader.readRoomMembers(chatRoom.getChatRoomId()); - return ChatRoomDetailResponse.of(chatRoom, game); + return ChatResponseMapper.mapToGameChatRoomDetailResponseDto(game, chatRoom, members); } } diff --git a/src/main/java/kr/pickple/back/chat/service/ChatRoomService.java b/src/main/java/kr/pickple/back/chat/service/ChatRoomService.java index 79051c68..73e43974 100644 --- a/src/main/java/kr/pickple/back/chat/service/ChatRoomService.java +++ b/src/main/java/kr/pickple/back/chat/service/ChatRoomService.java @@ -1,98 +1,69 @@ package kr.pickple.back.chat.service; +import static kr.pickple.back.chat.exception.ChatExceptionCode.*; + +import java.text.MessageFormat; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.domain.ChatRoomMember; -import kr.pickple.back.chat.domain.RoomType; -import kr.pickple.back.chat.dto.request.PersonalChatRoomCreateRequest; +import kr.pickple.back.chat.domain.PersonalChatRoomStatus; +import kr.pickple.back.chat.dto.mapper.ChatResponseMapper; import kr.pickple.back.chat.dto.response.ChatRoomDetailResponse; -import kr.pickple.back.chat.dto.response.PersonalChatRoomExistedResponse; +import kr.pickple.back.chat.dto.response.PersonalChatRoomStatusResponse; import kr.pickple.back.chat.exception.ChatException; -import kr.pickple.back.chat.repository.ChatRoomMemberRepository; -import kr.pickple.back.chat.repository.ChatRoomRepository; +import kr.pickple.back.chat.implement.ChatReader; +import kr.pickple.back.chat.implement.ChatWriter; import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.implement.MemberReader; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import static java.text.MessageFormat.format; -import static kr.pickple.back.chat.domain.RoomType.PERSONAL; -import static kr.pickple.back.chat.exception.ChatExceptionCode.CHAT_ROOM_NOT_FOUND; -import static kr.pickple.back.member.exception.MemberExceptionCode.MEMBER_NOT_FOUND; @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class ChatRoomService { - private final ChatMessageService chatMessageService; - private final ChatValidator chatValidator; - - private final ChatRoomRepository chatRoomRepository; - private final ChatRoomMemberRepository chatRoomMemberRepository; - private final MemberRepository memberRepository; + private final MemberReader memberReader; + private final ChatReader chatReader; + private final ChatWriter chatWriter; + /** + * 새 1:1 채팅방 생성 + */ @Transactional - public ChatRoomDetailResponse createPersonalRoom( - final Long senderId, - final PersonalChatRoomCreateRequest personalChatRoomCreateRequest - ) { - final Long receiverId = personalChatRoomCreateRequest.getReceiverId(); - final Member receiver = findMemberById(receiverId); - final Member sender = findMemberById(senderId); - - chatValidator.validateIsSelfChat(receiver, sender); + public ChatRoomDetailResponse createPersonalRoom(final Long senderId, final Long receiverId) { + validateSelfChat(senderId, receiverId); - final String personalRoomName = format("{0},{1}", sender.getNickname(), receiver.getNickname()); - final ChatRoom savedChatRoom = saveNewChatRoom(sender, personalRoomName, PERSONAL); - chatMessageService.enterRoomAndSaveEnteringMessages(savedChatRoom, receiver); + final Member sender = memberReader.readByMemberId(senderId); + final Member receiver = memberReader.readByMemberId(receiverId); - return ChatRoomDetailResponse.of(savedChatRoom, receiver); - } - - @Transactional - public ChatRoom saveNewChatRoom(final Member member, final String roomName, final RoomType type) { - final ChatRoom newChatRoom = ChatRoom.builder() - .type(type) - .name(roomName) - .build(); + final String roomName = MessageFormat.format("{0},{1}", sender.getNickname(), receiver.getNickname()); + final ChatRoom chatRoom = chatWriter.createNewPersonalRoom(roomName); - final ChatRoom savedChatRoom = chatRoomRepository.save(newChatRoom); - chatMessageService.enterRoomAndSaveEnteringMessages(savedChatRoom, member); + chatWriter.enterRoom(sender, chatRoom); + chatWriter.enterRoom(receiver, chatRoom); - return savedChatRoom; + return ChatResponseMapper.mapToPersonalChatRoomDetailResponseDto(sender, receiver, chatRoom); } - public PersonalChatRoomExistedResponse findActivePersonalChatRoomWithReceiver( + /** + * 특정 사용자와의 1:1 채팅방 존재 여부 조회 + */ + public PersonalChatRoomStatusResponse findPersonalChatRoomStatus( final Long senderId, final Long receiverId ) { - final Member sender = findMemberById(senderId); - final Member receiver = findMemberById(receiverId); + validateSelfChat(senderId, receiverId); - chatValidator.validateIsSelfChat(receiver, sender); + final PersonalChatRoomStatus personalChatRoomStatus = chatReader.readPersonalRoomStatus(senderId, receiverId); - final ChatRoomMember foundChatRoomMember = chatRoomMemberRepository.findAllByMember(sender) - .stream() - .filter(chatRoomMember -> isPersonalChatRoomWithReceiver(chatRoomMember, receiver)) - .findFirst() - .orElseThrow(() -> new ChatException(CHAT_ROOM_NOT_FOUND)); - - final Long personalChatRoomId = foundChatRoomMember.getChatRoom().getId(); - final Boolean isSenderActive = foundChatRoomMember.isActive(); - - return PersonalChatRoomExistedResponse.of(personalChatRoomId, isSenderActive); + return ChatResponseMapper.mapToPersonalChatRoomStatusResponseDto(personalChatRoomStatus); } - private Member findMemberById(final Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); - } - - private Boolean isPersonalChatRoomWithReceiver(final ChatRoomMember chatRoomMember, final Member receiver) { - final ChatRoom chatRoom = chatRoomMember.getChatRoom(); - - return chatRoom.isMatchedRoomType(PERSONAL) && chatRoom.isEntered(receiver); + private void validateSelfChat(final Long senderId, final Long receiverId) { + if (senderId.equals(receiverId)) { + throw new ChatException(CHAT_MEMBER_CANNOT_CHAT_SELF, senderId); + } } } diff --git a/src/main/java/kr/pickple/back/chat/service/ChatValidator.java b/src/main/java/kr/pickple/back/chat/service/ChatValidator.java deleted file mode 100644 index 3403b81f..00000000 --- a/src/main/java/kr/pickple/back/chat/service/ChatValidator.java +++ /dev/null @@ -1,88 +0,0 @@ -package kr.pickple.back.chat.service; - -import static kr.pickple.back.chat.exception.ChatExceptionCode.*; - -import java.util.Optional; - -import org.springframework.stereotype.Component; - -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.exception.ChatException; -import kr.pickple.back.chat.repository.ChatRoomMemberRepository; -import kr.pickple.back.common.util.DateTimeUtil; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.crew.repository.CrewRepository; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.repository.GameRepository; -import kr.pickple.back.member.domain.Member; -import lombok.RequiredArgsConstructor; - -@Component -@RequiredArgsConstructor -public class ChatValidator { - - private final ChatRoomMemberRepository chatRoomMemberRepository; - private final CrewRepository crewRepository; - private final GameRepository gameRepository; - - public void validateIsSelfChat(Member receiver, Member sender) { - if (sender.equals(receiver)) { - throw new ChatException(CHAT_MEMBER_CANNOT_CHAT_SELF, sender.getId()); - } - } - - public void validateIsNotExistedRoomMember(final ChatRoom chatRoom, final Member member) { - if (isExistedRoomMember(chatRoom, member)) { - throw new ChatException(CHAT_MEMBER_IS_ALREADY_IN_ROOM, member.getId(), chatRoom.getId()); - } - } - - public void validateIsExistedRoomMember(final Member member, final ChatRoom chatRoom) { - if (!isExistedRoomMember(chatRoom, member)) { - throw new ChatException(CHAT_MEMBER_IS_NOT_IN_ROOM, member.getId(), chatRoom.getId()); - } - } - - private Boolean isExistedRoomMember(final ChatRoom chatRoom, final Member member) { - return chatRoomMemberRepository.existsByActiveTrueAndChatRoomAndMember(chatRoom, member); - } - - public void validateChatRoomLeavingConditions(final Member member, final ChatRoom chatRoom) { - switch (chatRoom.getType()) { - case CREW -> validateCrewChatRoomLeavingConditions(member, chatRoom); - case GAME -> validateGameChatRoomLeavingConditions(member, chatRoom); - } - } - - private void validateCrewChatRoomLeavingConditions(final Member member, final ChatRoom chatRoom) { - final Optional optionalCrew = crewRepository.findByChatRoom(chatRoom); - - if (optionalCrew.isEmpty()) { - return; - } - - final Crew crew = optionalCrew.get(); - - if (crew.isConfirmedCrewMember(member)) { - throw new ChatException(CHAT_CREW_CHATROOM_NOT_ALLOWED_TO_LEAVE, member.getId(), chatRoom.getId()); - } - } - - private void validateGameChatRoomLeavingConditions(final Member member, final ChatRoom chatRoom) { - final Optional optionalGame = gameRepository.findByChatRoom(chatRoom); - - if (optionalGame.isEmpty()) { - return; - } - - final Game game = optionalGame.get(); - - if (isGameNotOver(game)) { - throw new ChatException(CHAT_GAME_CHATROOM_NOT_ALLOWED_TO_LEAVE, member.getId(), game.getId()); - } - } - - private Boolean isGameNotOver(final Game game) { - return DateTimeUtil.isAfterThanNow(game.getPlayEndDatetime()); - } -} diff --git a/src/main/java/kr/pickple/back/common/config/property/AsyncProperties.java b/src/main/java/kr/pickple/back/common/config/property/AsyncProperties.java index d458d78f..7fae39c3 100644 --- a/src/main/java/kr/pickple/back/common/config/property/AsyncProperties.java +++ b/src/main/java/kr/pickple/back/common/config/property/AsyncProperties.java @@ -1,11 +1,12 @@ package kr.pickple.back.common.config.property; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; @Getter @Setter diff --git a/src/main/java/kr/pickple/back/common/config/property/S3Properties.java b/src/main/java/kr/pickple/back/common/config/property/S3Properties.java index 1cee2921..74c4d96f 100644 --- a/src/main/java/kr/pickple/back/common/config/property/S3Properties.java +++ b/src/main/java/kr/pickple/back/common/config/property/S3Properties.java @@ -1,8 +1,9 @@ package kr.pickple.back.common.config.property; +import org.springframework.boot.context.properties.ConfigurationProperties; + import lombok.Getter; import lombok.RequiredArgsConstructor; -import org.springframework.boot.context.properties.ConfigurationProperties; @Getter @RequiredArgsConstructor diff --git a/src/main/java/kr/pickple/back/common/domain/BaseEntity.java b/src/main/java/kr/pickple/back/common/domain/BaseEntity.java index 7c6236ac..e676c6ad 100644 --- a/src/main/java/kr/pickple/back/common/domain/BaseEntity.java +++ b/src/main/java/kr/pickple/back/common/domain/BaseEntity.java @@ -1,14 +1,15 @@ package kr.pickple.back.common.domain; -import jakarta.persistence.Column; -import jakarta.persistence.EntityListeners; -import jakarta.persistence.MappedSuperclass; -import lombok.Getter; +import java.time.LocalDateTime; + import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; -import java.time.LocalDateTime; +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; @Getter @MappedSuperclass diff --git a/src/main/java/kr/pickple/back/common/domain/RegistrationStatus.java b/src/main/java/kr/pickple/back/common/domain/RegistrationStatus.java index 14a5616e..a5e0c513 100644 --- a/src/main/java/kr/pickple/back/common/domain/RegistrationStatus.java +++ b/src/main/java/kr/pickple/back/common/domain/RegistrationStatus.java @@ -19,7 +19,6 @@ @RequiredArgsConstructor public enum RegistrationStatus { - NONE("없음"), WAITING("대기"), CONFIRMED("확정"), ; diff --git a/src/main/java/kr/pickple/back/common/util/RandomUtil.java b/src/main/java/kr/pickple/back/common/util/RandomUtil.java index ec55e52a..fad74bdf 100644 --- a/src/main/java/kr/pickple/back/common/util/RandomUtil.java +++ b/src/main/java/kr/pickple/back/common/util/RandomUtil.java @@ -4,7 +4,7 @@ public final class RandomUtil { - public static int getRandomNumber(final int start, final int end) { + public static Integer getRandomNumber(final int start, final int end) { final Random random = new Random(); return random.nextInt(end) + start; diff --git a/src/main/java/kr/pickple/back/crew/controller/CrewController.java b/src/main/java/kr/pickple/back/crew/controller/CrewController.java index ea9f6990..0f629f3e 100644 --- a/src/main/java/kr/pickple/back/crew/controller/CrewController.java +++ b/src/main/java/kr/pickple/back/crew/controller/CrewController.java @@ -41,7 +41,7 @@ public ResponseEntity createCrew( @Valid @RequestBody final CrewCreateRequest crewCreateRequest ) { return ResponseEntity.status(CREATED) - .body(crewService.createCrew(crewCreateRequest, loggedInMemberId)); + .body(crewService.createCrew(loggedInMemberId, crewCreateRequest)); } @GetMapping("/{crewId}") @@ -53,24 +53,24 @@ public ResponseEntity findCrewById( } @PostMapping("/{crewId}/members") - public ResponseEntity applyForCrewMemberShip( + public ResponseEntity registerCrewMember( @Login final Long loggedInMemberId, @PathVariable final Long crewId ) { - crewMemberService.applyForCrewMemberShip(crewId, loggedInMemberId); + crewMemberService.registerCrewMember(crewId, loggedInMemberId); return ResponseEntity.status(NO_CONTENT) .build(); } @GetMapping("/{crewId}/members") - public ResponseEntity findAllCrewMembers( + public ResponseEntity findAllCrewMembersByStatus( @Login final Long loggedInMemberId, @PathVariable final Long crewId, @RequestParam final RegistrationStatus status ) { return ResponseEntity.status(OK) - .body(crewMemberService.findAllCrewMembers(loggedInMemberId, crewId, status)); + .body(crewMemberService.findAllCrewMembersByStatus(loggedInMemberId, crewId, status)); } @PatchMapping("/{crewId}/members/{memberId}") @@ -80,7 +80,12 @@ public ResponseEntity updateCrewMemberRegistrationStatus( @PathVariable final Long memberId, @Valid @RequestBody final CrewMemberUpdateStatusRequest crewMemberStatusUpdateRequest ) { - crewMemberService.crewMemberStatusUpdate(loggedInMemberId, crewId, memberId, crewMemberStatusUpdateRequest); + crewMemberService.updateCrewMemberRegistrationStatus( + loggedInMemberId, + crewId, + memberId, + crewMemberStatusUpdateRequest.getStatus() + ); return ResponseEntity.status(NO_CONTENT) .build(); @@ -99,12 +104,12 @@ public ResponseEntity deleteCrewMember( } @GetMapping - public ResponseEntity> findCrewsByAddress( + public ResponseEntity> findNearCrewsByAddress( @RequestParam final String addressDepth1, @RequestParam final String addressDepth2, final Pageable pageable ) { return ResponseEntity.status(OK) - .body(crewService.findCrewByAddress(addressDepth1, addressDepth2, pageable)); + .body(crewService.findNearCrewsByAddress(addressDepth1, addressDepth2, pageable)); } } diff --git a/src/main/java/kr/pickple/back/crew/domain/Crew.java b/src/main/java/kr/pickple/back/crew/domain/Crew.java index 067a5c9e..8b4d9842 100644 --- a/src/main/java/kr/pickple/back/crew/domain/Crew.java +++ b/src/main/java/kr/pickple/back/crew/domain/Crew.java @@ -1,190 +1,56 @@ package kr.pickple.back.crew.domain; -import static kr.pickple.back.crew.domain.CrewStatus.*; -import static kr.pickple.back.crew.exception.CrewExceptionCode.*; +import static kr.pickple.back.crew.domain.CrewStatus.CLOSED; +import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_CAPACITY_LIMIT_REACHED; +import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_STATUS_IS_CLOSED; -import java.util.List; - -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; -import jakarta.validation.constraints.NotNull; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.common.domain.BaseEntity; -import kr.pickple.back.common.domain.RegistrationStatus; import kr.pickple.back.crew.exception.CrewException; -import kr.pickple.back.crew.util.CrewStatusConverter; import kr.pickple.back.member.domain.Member; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Getter -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Crew extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Crew { - @NotNull - @Column(unique = true, length = 20) + private Long crewId; private String name; - - @Column(length = 1000) private String content; - - @NotNull - private Integer memberCount = 1; - - @NotNull - @Column(length = 300) + private Integer memberCount; + private Integer maxMemberCount; + private CrewStatus status; + private Member leader; + private String addressDepth1Name; + private String addressDepth2Name; private String profileImageUrl; - - @NotNull - @Column(length = 300) private String backgroundImageUrl; - - @NotNull - @Column(length = 10) - @Convert(converter = CrewStatusConverter.class) - private CrewStatus status = OPEN; - - @NotNull - private Integer likeCount = 0; - - @NotNull - private Integer maxMemberCount = 1; - - @NotNull - private Integer competitionPoint = 0; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "leader_id") - private Member leader; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "address_depth1_id") - private AddressDepth1 addressDepth1; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "address_depth2_id") - private AddressDepth2 addressDepth2; - - @Embedded - private CrewMembers crewMembers = new CrewMembers(); - - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "chat_room_id") + private Integer likeCount; + private Integer competitionPoint; private ChatRoom chatRoom; - @Builder - private Crew( - final String name, - final String content, - final String profileImageUrl, - final String backgroundImageUrl, - final Integer maxMemberCount, - final Member leader, - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2 - ) { - this.name = name; - this.content = content; - this.profileImageUrl = profileImageUrl; - this.backgroundImageUrl = backgroundImageUrl; - this.maxMemberCount = maxMemberCount; - this.leader = leader; - this.addressDepth1 = addressDepth1; - this.addressDepth2 = addressDepth2; - - updateStatusIfCrewMemberFull(); - } - - public List getMembersByStatus(final RegistrationStatus status) { - return crewMembers.getCrewMembers(status); - } - - public List getCrewMembers() { - return crewMembers.getCrewMembers(); - } - - public void addCrewMember(final Member member) { - crewMembers.addCrewMember(this, member); - } - public void increaseMemberCount() { - validateCrewIsClosedOrFull(); - - this.memberCount++; - - updateStatusIfCrewMemberFull(); - } - - private void updateStatusIfCrewMemberFull() { - if (isFullCrew()) { - this.status = CLOSED; - } - } - - private void validateCrewIsClosedOrFull() { - validateCrewClosed(); - validateCrewFull(); - } - - private void validateCrewClosed() { - if (isClosedCrew()) { + if (status == CLOSED) { throw new CrewException(CREW_STATUS_IS_CLOSED, status); } - } - private Boolean isClosedCrew() { - return status == CLOSED; - } - - private void validateCrewFull() { - if (isFullCrew()) { + if (memberCount.equals(maxMemberCount)) { throw new CrewException(CREW_CAPACITY_LIMIT_REACHED, memberCount); } - } - private Boolean isFullCrew() { - return memberCount.equals(maxMemberCount); - } + memberCount += 1; - public Boolean isLeader(final Member member) { - return member.equals(leader); + if (memberCount.equals(maxMemberCount)) { + this.status = CLOSED; + } } public Boolean isLeader(final Long memberId) { - return memberId.equals(leader.getId()); - } - - public void makeNewCrewChatRoom(final ChatRoom chatRoom) { - chatRoom.updateMaxMemberCount(maxMemberCount); - this.chatRoom = chatRoom; - } - - public Boolean isConfirmedCrewMember(final Member member) { - return crewMembers.isAlreadyConfirmed(member); - } - - public List getCrewMembers(RegistrationStatus status) { - return crewMembers.getCrewMembers(status); + return leader.isIdMatched(memberId); } } diff --git a/src/main/java/kr/pickple/back/crew/domain/CrewMember.java b/src/main/java/kr/pickple/back/crew/domain/CrewMember.java index adc5844d..e0e8087f 100644 --- a/src/main/java/kr/pickple/back/crew/domain/CrewMember.java +++ b/src/main/java/kr/pickple/back/crew/domain/CrewMember.java @@ -1,78 +1,36 @@ package kr.pickple.back.crew.domain; -import static kr.pickple.back.common.domain.RegistrationStatus.*; +import static kr.pickple.back.crew.exception.CrewExceptionCode.*; -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.validation.constraints.NotNull; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.common.domain.BaseEntity; import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.common.util.RegistrationStatusAttributeConverter; +import kr.pickple.back.crew.exception.CrewException; import kr.pickple.back.member.domain.Member; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @Getter -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class CrewMember extends BaseEntity { +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class CrewMember { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - @Convert(converter = RegistrationStatusAttributeConverter.class) - @Column(length = 10) - private RegistrationStatus status = WAITING; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") + private Long crewMemberId; + private RegistrationStatus status; private Member member; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "crew_id") private Crew crew; - @Builder - private CrewMember(final Member member, final Crew crew) { - this.member = member; - this.crew = crew; + public void updateCrewMemberId(final Long crewMemberId) { + this.crewMemberId = crewMemberId; } - public Boolean equalsStatus(final RegistrationStatus status) { - return this.status == status; - } - - public Boolean equalsCrew(final Crew crew) { - return this.crew.equals(crew); - } - - public void confirmRegistration() { - this.status = CONFIRMED; - } - - public void updateStatus(final RegistrationStatus status) { - if (this.status == WAITING && status == CONFIRMED) { - crew.increaseMemberCount(); + public void updateRegistrationStatus(final RegistrationStatus status) { + if (this.status == status) { + throw new CrewException(CREW_MEMBER_ALREADY_IN_THAT_REGISTRATION_STATUS, status); } this.status = status; } - - public ChatRoom getCrewChatRoom() { - return crew.getChatRoom(); - } } diff --git a/src/main/java/kr/pickple/back/crew/domain/CrewMembers.java b/src/main/java/kr/pickple/back/crew/domain/CrewMembers.java deleted file mode 100644 index 313b44f9..00000000 --- a/src/main/java/kr/pickple/back/crew/domain/CrewMembers.java +++ /dev/null @@ -1,64 +0,0 @@ -package kr.pickple.back.crew.domain; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.OneToMany; -import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.crew.exception.CrewException; -import kr.pickple.back.member.domain.Member; -import lombok.Getter; - -import java.util.ArrayList; -import java.util.List; - -import static kr.pickple.back.common.domain.RegistrationStatus.*; -import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_MEMBER_ALREADY_EXISTED; - -@Embeddable -public class CrewMembers { - - @Getter - @OneToMany(mappedBy = "crew", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) - private List crewMembers = new ArrayList<>(); - - public List getCrewMembers(final RegistrationStatus status) { - return crewMembers.stream() - .filter(crewMember -> crewMember.equalsStatus(status)) - .map(CrewMember::getMember) - .toList(); - } - - public void addCrewMember(final Crew crew, final Member member) { - validateIsAlreadyRegisteredCrewMember(member); - - final CrewMember crewMember = buildCrewMember(crew, member); - if (member.equals(crew.getLeader())) { - crewMember.confirmRegistration(); - } - - crewMembers.add(crewMember); - } - - private void validateIsAlreadyRegisteredCrewMember(final Member member) { - if (isAlreadyRegistered(member)) { - throw new CrewException(CREW_MEMBER_ALREADY_EXISTED, member.getId()); - } - } - - private Boolean isAlreadyRegistered(final Member member) { - return crewMembers.stream() - .anyMatch(crewMember -> member.equals(crewMember.getMember())); - } - - private CrewMember buildCrewMember(final Crew crew, final Member member) { - return CrewMember.builder() - .member(member) - .crew(crew) - .build(); - } - - public Boolean isAlreadyConfirmed(final Member member) { - return crewMembers.stream() - .anyMatch(crewMember -> member.equals(crewMember.getMember()) && crewMember.equalsStatus(CONFIRMED)); - } -} diff --git a/src/main/java/kr/pickple/back/crew/domain/CrewProfile.java b/src/main/java/kr/pickple/back/crew/domain/CrewProfile.java new file mode 100644 index 00000000..cf21d9ba --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/domain/CrewProfile.java @@ -0,0 +1,30 @@ +package kr.pickple.back.crew.domain; + +import java.util.List; + +import kr.pickple.back.member.domain.Member; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class CrewProfile { + + private Long crewId; + private String name; + private String content; + private Integer memberCount; + private Integer maxMemberCount; + private String profileImageUrl; + private String backgroundImageUrl; + private CrewStatus status; + private Integer likeCount; + private Integer competitionPoint; + private Member leader; + private String addressDepth1; + private String addressDepth2; + private List members; +} diff --git a/src/main/java/kr/pickple/back/crew/domain/CrewStatus.java b/src/main/java/kr/pickple/back/crew/domain/CrewStatus.java index 87b2ccea..18175f70 100644 --- a/src/main/java/kr/pickple/back/crew/domain/CrewStatus.java +++ b/src/main/java/kr/pickple/back/crew/domain/CrewStatus.java @@ -1,10 +1,6 @@ package kr.pickple.back.crew.domain; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; -import kr.pickple.back.crew.exception.CrewException; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import static kr.pickple.back.crew.exception.CrewExceptionCode.*; import java.util.Collections; import java.util.Map; @@ -12,7 +8,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_STATUS_NOT_FOUND; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import kr.pickple.back.crew.exception.CrewException; +import lombok.Getter; +import lombok.RequiredArgsConstructor; @Getter @RequiredArgsConstructor @@ -33,6 +34,7 @@ public static CrewStatus from(final String description) { if (crewStatusMap.containsKey(description)) { return crewStatusMap.get(description); } + throw new CrewException(CREW_STATUS_NOT_FOUND, description); } } diff --git a/src/main/java/kr/pickple/back/crew/domain/NewCrew.java b/src/main/java/kr/pickple/back/crew/domain/NewCrew.java new file mode 100644 index 00000000..d24573c0 --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/domain/NewCrew.java @@ -0,0 +1,51 @@ +package kr.pickple.back.crew.domain; + +import kr.pickple.back.chat.domain.ChatRoom; +import kr.pickple.back.member.domain.Member; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class NewCrew { + + private String name; + private String content; + private Integer maxMemberCount; + private String addressDepth1Name; + private String addressDepth2Name; + private String profileImageUrl; + private String backgroundImageUrl; + private Member leader; + private ChatRoom chatRoom; + + @Builder + private NewCrew( + final String name, + final String content, + final Integer maxMemberCount, + final String addressDepth1Name, + final String addressDepth2Name + ) { + this.name = name; + this.content = content; + this.maxMemberCount = maxMemberCount; + this.addressDepth1Name = addressDepth1Name; + this.addressDepth2Name = addressDepth2Name; + } + + public void assignImageUrls(final String profileImageUrl, final String backgroundImageUrl) { + this.profileImageUrl = profileImageUrl; + this.backgroundImageUrl = backgroundImageUrl; + } + + public void assignLeader(final Member leader) { + this.leader = leader; + } + + public void assignChatRoom(final ChatRoom chatRoom) { + this.chatRoom = chatRoom; + } +} diff --git a/src/main/java/kr/pickple/back/crew/dto/mapper/CrewRequestMapper.java b/src/main/java/kr/pickple/back/crew/dto/mapper/CrewRequestMapper.java new file mode 100644 index 00000000..21569daa --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/dto/mapper/CrewRequestMapper.java @@ -0,0 +1,20 @@ +package kr.pickple.back.crew.dto.mapper; + +import kr.pickple.back.crew.domain.NewCrew; +import kr.pickple.back.crew.dto.request.CrewCreateRequest; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class CrewRequestMapper { + + public static NewCrew mapToNewCrewDomain(final CrewCreateRequest crewCreateRequest) { + return NewCrew.builder() + .name(crewCreateRequest.getName()) + .content(crewCreateRequest.getContent()) + .maxMemberCount(crewCreateRequest.getMaxMemberCount()) + .addressDepth1Name(crewCreateRequest.getAddressDepth1()) + .addressDepth2Name(crewCreateRequest.getAddressDepth2()) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/crew/dto/mapper/CrewResponseMapper.java b/src/main/java/kr/pickple/back/crew/dto/mapper/CrewResponseMapper.java new file mode 100644 index 00000000..83eac04b --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/dto/mapper/CrewResponseMapper.java @@ -0,0 +1,96 @@ +package kr.pickple.back.crew.dto.mapper; + +import java.util.List; + +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.crew.domain.CrewProfile; +import kr.pickple.back.crew.dto.response.CrewIdResponse; +import kr.pickple.back.crew.dto.response.CrewMemberRegistrationStatusResponse; +import kr.pickple.back.crew.dto.response.CrewProfileResponse; +import kr.pickple.back.crew.dto.response.CrewResponse; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.dto.mapper.MemberResponseMapper; +import kr.pickple.back.member.dto.response.MemberResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class CrewResponseMapper { + + public static CrewIdResponse mapToCrewIdResponseDto(final Long crewId) { + return CrewIdResponse.from(crewId); + } + + public static CrewProfileResponse mapToCrewProfileResponseDto(final Crew crew, final List members) { + final List memberResponses = members.stream() + .map(MemberResponseMapper::mapToMemberResponseDto) + .toList(); + + return CrewProfileResponse.builder() + .id(crew.getCrewId()) + .name(crew.getName()) + .content(crew.getContent()) + .memberCount(crew.getMemberCount()) + .maxMemberCount(crew.getMaxMemberCount()) + .profileImageUrl(crew.getProfileImageUrl()) + .backgroundImageUrl(crew.getBackgroundImageUrl()) + .status(crew.getStatus()) + .likeCount(crew.getLikeCount()) + .competitionPoint(crew.getCompetitionPoint()) + .leader(MemberResponseMapper.mapToMemberResponseDto(crew.getLeader())) + .addressDepth1(crew.getAddressDepth1Name()) + .addressDepth2(crew.getAddressDepth2Name()) + .members(memberResponses) + .build(); + } + + public static CrewResponse mapToCrewResponseDto(final Crew crew) { + return CrewResponse.builder() + .id(crew.getCrewId()) + .name(crew.getName()) + .content(crew.getContent()) + .memberCount(crew.getMemberCount()) + .maxMemberCount(crew.getMaxMemberCount()) + .profileImageUrl(crew.getProfileImageUrl()) + .backgroundImageUrl(crew.getBackgroundImageUrl()) + .status(crew.getStatus()) + .likeCount(crew.getLikeCount()) + .competitionPoint(crew.getCompetitionPoint()) + .leader(MemberResponseMapper.mapToMemberResponseDto(crew.getLeader())) + .addressDepth1(crew.getAddressDepth1Name()) + .addressDepth2(crew.getAddressDepth2Name()) + .build(); + } + + public static List mapToCrewProfilesResponseDto( + final List crewProfiles + ) { + return crewProfiles.stream() + .map(crewProfile -> CrewProfileResponse.builder() + .id(crewProfile.getCrewId()) + .name(crewProfile.getName()) + .content(crewProfile.getContent()) + .memberCount(crewProfile.getMemberCount()) + .maxMemberCount(crewProfile.getMaxMemberCount()) + .profileImageUrl(crewProfile.getProfileImageUrl()) + .backgroundImageUrl(crewProfile.getBackgroundImageUrl()) + .status(crewProfile.getStatus()) + .likeCount(crewProfile.getLikeCount()) + .competitionPoint(crewProfile.getCompetitionPoint()) + .leader(MemberResponseMapper.mapToMemberResponseDto(crewProfile.getLeader())) + .addressDepth1(crewProfile.getAddressDepth1()) + .addressDepth2(crewProfile.getAddressDepth2()) + .members(MemberResponseMapper.mapToMemberResponseDtos(crewProfile.getMembers())) + .build() + ) + .toList(); + + } + + public static CrewMemberRegistrationStatusResponse mapToCrewMemberRegistrationStatusResponse( + final RegistrationStatus status + ) { + return CrewMemberRegistrationStatusResponse.from(status); + } +} diff --git a/src/main/java/kr/pickple/back/crew/dto/request/CrewCreateRequest.java b/src/main/java/kr/pickple/back/crew/dto/request/CrewCreateRequest.java index c5893fe8..1245f773 100644 --- a/src/main/java/kr/pickple/back/crew/dto/request/CrewCreateRequest.java +++ b/src/main/java/kr/pickple/back/crew/dto/request/CrewCreateRequest.java @@ -5,9 +5,6 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; -import kr.pickple.back.address.dto.response.MainAddressResponse; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.member.domain.Member; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; @@ -37,23 +34,4 @@ public class CrewCreateRequest { @NotBlank(message = "해당 크루의 활동 장소(구) 정보는 필수입니다.") private String addressDepth2; - - public Crew toEntity( - final Member leader, - final MainAddressResponse mainAddressResponse, - final String profile, - final String background - ) { - - return Crew.builder() - .name(name) - .content(content) - .maxMemberCount(maxMemberCount) - .leader(leader) - .profileImageUrl(profile) - .backgroundImageUrl(background) - .addressDepth1(mainAddressResponse.getAddressDepth1()) - .addressDepth2(mainAddressResponse.getAddressDepth2()) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/crew/dto/request/CrewMemberUpdateStatusRequest.java b/src/main/java/kr/pickple/back/crew/dto/request/CrewMemberUpdateStatusRequest.java index b7073739..5df65e8d 100644 --- a/src/main/java/kr/pickple/back/crew/dto/request/CrewMemberUpdateStatusRequest.java +++ b/src/main/java/kr/pickple/back/crew/dto/request/CrewMemberUpdateStatusRequest.java @@ -2,7 +2,11 @@ import jakarta.validation.constraints.NotNull; import kr.pickple.back.common.domain.RegistrationStatus; -import lombok.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; @Getter @Builder diff --git a/src/main/java/kr/pickple/back/member/dto/response/CrewMemberRegistrationStatusResponse.java b/src/main/java/kr/pickple/back/crew/dto/response/CrewMemberRegistrationStatusResponse.java similarity index 86% rename from src/main/java/kr/pickple/back/member/dto/response/CrewMemberRegistrationStatusResponse.java rename to src/main/java/kr/pickple/back/crew/dto/response/CrewMemberRegistrationStatusResponse.java index 5c81301b..30748702 100644 --- a/src/main/java/kr/pickple/back/member/dto/response/CrewMemberRegistrationStatusResponse.java +++ b/src/main/java/kr/pickple/back/crew/dto/response/CrewMemberRegistrationStatusResponse.java @@ -1,4 +1,4 @@ -package kr.pickple.back.member.dto.response; +package kr.pickple.back.crew.dto.response; import kr.pickple.back.common.domain.RegistrationStatus; import lombok.AllArgsConstructor; diff --git a/src/main/java/kr/pickple/back/crew/dto/response/CrewProfileResponse.java b/src/main/java/kr/pickple/back/crew/dto/response/CrewProfileResponse.java index 643587c1..bf446688 100644 --- a/src/main/java/kr/pickple/back/crew/dto/response/CrewProfileResponse.java +++ b/src/main/java/kr/pickple/back/crew/dto/response/CrewProfileResponse.java @@ -2,14 +2,16 @@ import java.util.List; -import kr.pickple.back.crew.domain.Crew; import kr.pickple.back.crew.domain.CrewStatus; import kr.pickple.back.member.dto.response.MemberResponse; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @Getter @Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class CrewProfileResponse { private Long id; @@ -26,23 +28,4 @@ public class CrewProfileResponse { private String addressDepth1; private String addressDepth2; private List members; - - public static CrewProfileResponse of(final Crew crew, final List crewMemberList) { - return CrewProfileResponse.builder() - .id(crew.getId()) - .name(crew.getName()) - .content(crew.getContent()) - .memberCount(crew.getMemberCount()) - .maxMemberCount(crew.getMaxMemberCount()) - .profileImageUrl(crew.getProfileImageUrl()) - .backgroundImageUrl(crew.getBackgroundImageUrl()) - .status(crew.getStatus()) - .likeCount(crew.getLikeCount()) - .competitionPoint(crew.getCompetitionPoint()) - .leader(MemberResponse.from(crew.getLeader())) - .addressDepth1(crew.getAddressDepth1().getName()) - .addressDepth2(crew.getAddressDepth2().getName()) - .members(crewMemberList) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/crew/dto/response/CrewResponse.java b/src/main/java/kr/pickple/back/crew/dto/response/CrewResponse.java index 2d01ee75..c58603d9 100644 --- a/src/main/java/kr/pickple/back/crew/dto/response/CrewResponse.java +++ b/src/main/java/kr/pickple/back/crew/dto/response/CrewResponse.java @@ -1,6 +1,5 @@ package kr.pickple.back.crew.dto.response; -import kr.pickple.back.crew.domain.Crew; import kr.pickple.back.crew.domain.CrewStatus; import kr.pickple.back.member.dto.response.MemberResponse; import lombok.AccessLevel; @@ -26,22 +25,4 @@ public class CrewResponse { private MemberResponse leader; private String addressDepth1; private String addressDepth2; - - public static CrewResponse from(final Crew crew) { - return CrewResponse.builder() - .id(crew.getId()) - .name(crew.getName()) - .content(crew.getContent()) - .memberCount(crew.getMemberCount()) - .maxMemberCount(crew.getMaxMemberCount()) - .profileImageUrl(crew.getProfileImageUrl()) - .backgroundImageUrl(crew.getBackgroundImageUrl()) - .status(crew.getStatus()) - .likeCount(crew.getLikeCount()) - .competitionPoint(crew.getCompetitionPoint()) - .leader(MemberResponse.from(crew.getLeader())) - .addressDepth1(crew.getAddressDepth1().getName()) - .addressDepth2(crew.getAddressDepth2().getName()) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/crew/exception/CrewExceptionCode.java b/src/main/java/kr/pickple/back/crew/exception/CrewExceptionCode.java index 8166425d..0607f835 100644 --- a/src/main/java/kr/pickple/back/crew/exception/CrewExceptionCode.java +++ b/src/main/java/kr/pickple/back/crew/exception/CrewExceptionCode.java @@ -22,6 +22,7 @@ public enum CrewExceptionCode implements ExceptionCode { CREW_MEMBER_STATUS_IS_NOT_WAITING(HttpStatus.BAD_REQUEST, "CRE-010", "해당 크루에 가입 신청 대기 상태가 아니라면, 가입 신청을 취소할 수 없음"), CREW_LEADER_CANNOT_BE_DELETED(HttpStatus.BAD_REQUEST, "CRE-011", "크루장은 자신의 크루에서 삭제될 수 없음"), CREW_CREATE_MAX_COUNT_EXCEEDED(HttpStatus.BAD_REQUEST, "CRE-012", "사용자는 크루를 특정 MAX 값을 초과하여 생성할 수 없음"), + CREW_MEMBER_ALREADY_IN_THAT_REGISTRATION_STATUS(HttpStatus.BAD_REQUEST, "CRE-013", "크루원은 이미 해당 등록 상태를 가지고 있음"), ; private final HttpStatus status; diff --git a/src/main/java/kr/pickple/back/crew/implement/CrewMapper.java b/src/main/java/kr/pickple/back/crew/implement/CrewMapper.java new file mode 100644 index 00000000..9775ddb9 --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/implement/CrewMapper.java @@ -0,0 +1,99 @@ +package kr.pickple.back.crew.implement; + +import java.util.List; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.crew.domain.CrewMember; +import kr.pickple.back.crew.domain.CrewProfile; +import kr.pickple.back.crew.domain.NewCrew; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.crew.repository.entity.CrewMemberEntity; +import kr.pickple.back.member.domain.Member; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class CrewMapper { + + public static CrewEntity mapNewCrewDomainToEntity(final NewCrew newCrew, final MainAddress mainAddress) { + return CrewEntity.builder() + .name(newCrew.getName()) + .content(newCrew.getContent()) + .maxMemberCount(newCrew.getMaxMemberCount()) + .leaderId(newCrew.getLeader().getMemberId()) + .profileImageUrl(newCrew.getProfileImageUrl()) + .backgroundImageUrl(newCrew.getBackgroundImageUrl()) + .addressDepth1Id(mainAddress.getAddressDepth1Id()) + .addressDepth2Id(mainAddress.getAddressDepth2Id()) + .chatRoomId(newCrew.getChatRoom().getChatRoomId()) + .build(); + } + + public static Crew mapCrewEntityToDomain( + final CrewEntity crewEntity, + final MainAddress mainAddress, + final Member leader + ) { + return Crew.builder() + .crewId(crewEntity.getId()) + .name(crewEntity.getName()) + .content(crewEntity.getContent()) + .memberCount(crewEntity.getMemberCount()) + .maxMemberCount(crewEntity.getMaxMemberCount()) + .status(crewEntity.getStatus()) + .leader(leader) + .addressDepth1Name(mainAddress.getAddressDepth1Name()) + .addressDepth2Name(mainAddress.getAddressDepth2Name()) + .profileImageUrl(crewEntity.getProfileImageUrl()) + .backgroundImageUrl(crewEntity.getBackgroundImageUrl()) + .likeCount(crewEntity.getLikeCount()) + .competitionPoint(crewEntity.getCompetitionPoint()) + .build(); + } + + public static CrewMemberEntity mapCrewMemberDomainToEntity(final CrewMember crewMember) { + return CrewMemberEntity.builder() + .status(crewMember.getStatus()) + .memberId(crewMember.getMember().getMemberId()) + .crewId(crewMember.getCrew().getCrewId()) + .build(); + } + + public static CrewMember mapCrewMemberEntityToDomain( + final CrewMemberEntity crewMemberEntity, + final Member member, + final Crew crew + ) { + return CrewMember.builder() + .crewMemberId(crewMemberEntity.getId()) + .status(crewMemberEntity.getStatus()) + .member(member) + .crew(crew) + .build(); + } + + public static CrewProfile mapCrewEntityToCrewProfile( + final CrewEntity crewEntity, + final MainAddress mainAddress, + final Member leader, + final List members + ) { + return CrewProfile.builder() + .crewId(crewEntity.getId()) + .name(crewEntity.getName()) + .content(crewEntity.getContent()) + .memberCount(crewEntity.getMemberCount()) + .maxMemberCount(crewEntity.getMaxMemberCount()) + .profileImageUrl(crewEntity.getProfileImageUrl()) + .backgroundImageUrl(crewEntity.getBackgroundImageUrl()) + .status(crewEntity.getStatus()) + .likeCount(crewEntity.getLikeCount()) + .competitionPoint(crewEntity.getCompetitionPoint()) + .leader(leader) + .addressDepth1(mainAddress.getAddressDepth1Name()) + .addressDepth2(mainAddress.getAddressDepth2Name()) + .members(members) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/crew/implement/CrewReader.java b/src/main/java/kr/pickple/back/crew/implement/CrewReader.java new file mode 100644 index 00000000..16fc76bb --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/implement/CrewReader.java @@ -0,0 +1,145 @@ +package kr.pickple.back.crew.implement; + +import static kr.pickple.back.chat.exception.ChatExceptionCode.*; +import static kr.pickple.back.common.domain.RegistrationStatus.*; +import static kr.pickple.back.crew.exception.CrewExceptionCode.*; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.implement.AddressReader; +import kr.pickple.back.chat.exception.ChatException; +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.crew.domain.CrewMember; +import kr.pickple.back.crew.domain.CrewProfile; +import kr.pickple.back.crew.exception.CrewException; +import kr.pickple.back.crew.repository.CrewMemberRepository; +import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.crew.repository.entity.CrewMemberEntity; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.implement.MemberReader; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class CrewReader { + + private final AddressReader addressReader; + private final MemberReader memberReader; + private final CrewRepository crewRepository; + private final CrewMemberRepository crewMemberRepository; + + public Integer countByLeaderId(final Long leaderId) { + return crewRepository.countByLeaderId(leaderId); + } + + public Crew read(final Long crewId) { + final CrewEntity crewEntity = getCrewById(crewId); + + return mapCrewEntityToDomain(crewEntity); + } + + private CrewEntity getCrewById(final Long crewId) { + return crewRepository.findById(crewId) + .orElseThrow(() -> new CrewException(CREW_NOT_FOUND, crewId)); + } + + public Crew readByChatRoomId(final Long chatRoomId) { + final CrewEntity crewEntity = crewRepository.findByChatRoomId(chatRoomId) + .orElseThrow(() -> new ChatException(CHAT_CREW_NOT_FOUND, chatRoomId)); + + return mapCrewEntityToDomain(crewEntity); + } + + private Crew mapCrewEntityToDomain(final CrewEntity crewEntity) { + final MainAddress mainAddress = addressReader.readMainAddressByIds( + crewEntity.getAddressDepth1Id(), + crewEntity.getAddressDepth2Id() + ); + + final Member leader = memberReader.readByMemberId(crewEntity.getLeaderId()); + + return CrewMapper.mapCrewEntityToDomain(crewEntity, mainAddress, leader); + } + + public List readNearCrewsByAddress( + final String addressDepth1Name, + final String addressDepth2Name, + final Pageable pageable + ) { + final MainAddress mainAddress = addressReader.readMainAddressByNames(addressDepth1Name, addressDepth2Name); + final Page crewEntities = crewRepository.findByAddressDepth1IdAndAddressDepth2Id( + mainAddress.getAddressDepth1Id(), + mainAddress.getAddressDepth2Id(), + pageable + ); + + return crewEntities.stream() + .map(crewEntity -> CrewMapper.mapCrewEntityToDomain( + crewEntity, + mainAddress, + memberReader.readByMemberId(crewEntity.getLeaderId()) + ) + ).toList(); + } + + public CrewMember readCrewMember(final Long memberId, final Long crewId) { + final CrewMemberEntity crewMemberEntity = crewMemberRepository.findByMemberIdAndCrewId(memberId, crewId) + .orElseThrow(() -> new CrewException(CREW_MEMBER_NOT_FOUND, memberId, crewId)); + final Member member = memberReader.readByMemberId(memberId); + final Crew crew = read(crewId); + + return CrewMapper.mapCrewMemberEntityToDomain(crewMemberEntity, member, crew); + } + + public List readAllMembersInStatus(final Long crewId, final RegistrationStatus status) { + return crewMemberRepository.findAllByCrewIdAndStatus(crewId, status) + .stream() + .map(crewMemberEntity -> memberReader.readByMemberId(crewMemberEntity.getMemberId())) + .toList(); + } + + public List readAllCrewProfilesByMemberIdAndStatus( + final Long memberId, + final RegistrationStatus memberStatus + ) { + final List crewEntities = crewMemberRepository.findAllByMemberIdAndStatus(memberId, memberStatus) + .stream() + .map(crewMember -> getCrewById(crewMember.getCrewId())) + .toList(); + + return mapCrewEntitiesToCrewProfiles(crewEntities, memberStatus); + } + + public List readAllCrewProfilesByLeaderId(final Long memberId) { + final List crewEntities = crewRepository.findAllByLeaderId(memberId); + + return mapCrewEntitiesToCrewProfiles(crewEntities, CONFIRMED); + } + + private List mapCrewEntitiesToCrewProfiles( + final List crewEntities, + final RegistrationStatus registrationStatus + ) { + return crewEntities.stream() + .map(crew -> { + final List members = readAllMembersInStatus(crew.getId(), registrationStatus); + final MainAddress mainAddress = addressReader.readMainAddressByIds( + crew.getAddressDepth1Id(), + crew.getAddressDepth2Id() + ); + final Member leader = memberReader.readByMemberId(crew.getLeaderId()); + + return CrewMapper.mapCrewEntityToCrewProfile(crew, mainAddress, leader, members); + }) + .toList(); + } +} diff --git a/src/main/java/kr/pickple/back/crew/implement/CrewWriter.java b/src/main/java/kr/pickple/back/crew/implement/CrewWriter.java new file mode 100644 index 00000000..0e54eac4 --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/implement/CrewWriter.java @@ -0,0 +1,96 @@ +package kr.pickple.back.crew.implement; + +import static kr.pickple.back.common.domain.RegistrationStatus.WAITING; +import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_IS_EXISTED; +import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_MEMBER_ALREADY_EXISTED; +import static kr.pickple.back.crew.exception.CrewExceptionCode.CREW_MEMBER_STATUS_IS_NOT_WAITING; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.implement.AddressReader; +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.crew.domain.CrewMember; +import kr.pickple.back.crew.domain.NewCrew; +import kr.pickple.back.crew.exception.CrewException; +import kr.pickple.back.crew.repository.CrewMemberRepository; +import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.crew.repository.entity.CrewMemberEntity; +import kr.pickple.back.member.domain.Member; +import lombok.RequiredArgsConstructor; + +@Component +@Transactional +@RequiredArgsConstructor +public class CrewWriter { + + private final AddressReader addressReader; + private final CrewRepository crewRepository; + private final CrewMemberRepository crewMemberRepository; + + public Crew create(final NewCrew newCrew) { + if (crewRepository.existsByName(newCrew.getName())) { + throw new CrewException(CREW_IS_EXISTED, newCrew.getName()); + } + + final MainAddress mainAddress = addressReader.readMainAddressByNames( + newCrew.getAddressDepth1Name(), + newCrew.getAddressDepth2Name() + ); + + final CrewEntity crewEntity = CrewMapper.mapNewCrewDomainToEntity(newCrew, mainAddress); + final CrewEntity savedCrewEntity = crewRepository.save(crewEntity); + + return CrewMapper.mapCrewEntityToDomain( + savedCrewEntity, + mainAddress, + newCrew.getLeader() + ); + } + + public CrewMember register(final Member member, final Crew crew) { + final Long memberId = member.getMemberId(); + final Long crewId = crew.getCrewId(); + + if (crewMemberRepository.existsByCrewIdAndMemberId(crewId, memberId)) { + throw new CrewException(CREW_MEMBER_ALREADY_EXISTED, crewId, memberId); + } + + final CrewMember crewMember = CrewMember.builder() + .status(WAITING) + .member(member) + .crew(crew) + .build(); + + final CrewMemberEntity crewMemberEntity = CrewMapper.mapCrewMemberDomainToEntity(crewMember); + final CrewMemberEntity savedCrewMemberEntity = crewMemberRepository.save(crewMemberEntity); + crewMember.updateCrewMemberId(savedCrewMemberEntity.getId()); + + return crewMember; + } + + public void updateMemberRegistrationStatus(final CrewMember crewMember, final RegistrationStatus status) { + crewMember.updateRegistrationStatus(status); + crewMemberRepository.updateRegistrationStatus(crewMember.getCrewMemberId(), status); + } + + public void increaseMemberCount(final Crew crew) { + crew.increaseMemberCount(); + crewRepository.updateMemberCountAndStatus(crew.getCrewId(), crew.getMemberCount(), crew.getStatus()); + } + + public void cancel(final CrewMember crewMember) { + if (crewMember.getStatus() != WAITING) { + throw new CrewException(CREW_MEMBER_STATUS_IS_NOT_WAITING, crewMember.getCrewMemberId()); + } + + delete(crewMember); + } + + public void delete(final CrewMember crewMember) { + crewMemberRepository.deleteById(crewMember.getCrewMemberId()); + } +} diff --git a/src/main/java/kr/pickple/back/crew/repository/CrewMemberRepository.java b/src/main/java/kr/pickple/back/crew/repository/CrewMemberRepository.java index 94b2f2e9..b9ae0f35 100644 --- a/src/main/java/kr/pickple/back/crew/repository/CrewMemberRepository.java +++ b/src/main/java/kr/pickple/back/crew/repository/CrewMemberRepository.java @@ -1,12 +1,28 @@ package kr.pickple.back.crew.repository; -import kr.pickple.back.crew.domain.CrewMember; +import java.util.List; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; -import java.util.Optional; +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.crew.repository.entity.CrewMemberEntity; + +public interface CrewMemberRepository extends JpaRepository { + + Optional findByMemberIdAndCrewId(final Long memberId, final Long crewId); + + List findAllByCrewIdAndStatus(final Long crewId, final RegistrationStatus status); + + List findAllByMemberIdAndStatus(final Long memberId, final RegistrationStatus status); -public interface CrewMemberRepository extends JpaRepository { + Boolean existsByCrewIdAndMemberId(final Long crewId, final Long memberId); - Optional findByMemberIdAndCrewId(final Long memberId, final Long crewId); + Boolean existsByCrewIdAndMemberIdAndStatus(final Long crewId, final Long memberId, final RegistrationStatus status); + @Modifying(clearAutomatically = true) + @Query("update CrewMemberEntity cm set cm.status = :status where cm.id = :crewMemberId") + void updateRegistrationStatus(final Long crewMemberId, final RegistrationStatus status); } diff --git a/src/main/java/kr/pickple/back/crew/repository/CrewRepository.java b/src/main/java/kr/pickple/back/crew/repository/CrewRepository.java index 7fe81e3a..77d87e76 100644 --- a/src/main/java/kr/pickple/back/crew/repository/CrewRepository.java +++ b/src/main/java/kr/pickple/back/crew/repository/CrewRepository.java @@ -1,20 +1,37 @@ package kr.pickple.back.crew.repository; +import java.util.List; import java.util.Optional; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.crew.domain.Crew; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; -public interface CrewRepository extends JpaRepository { +import kr.pickple.back.crew.domain.CrewStatus; +import kr.pickple.back.crew.repository.entity.CrewEntity; - boolean existsByName(final String name); +public interface CrewRepository extends JpaRepository { - Page findByAddressDepth1AndAddressDepth2(final AddressDepth1 addressDepth1, final AddressDepth2 addressDepth2, final Pageable pageable); + Boolean existsByName(final String name); - Optional findByChatRoom(final ChatRoom chatRoom); + Page findByAddressDepth1IdAndAddressDepth2Id( + final Long addressDepth1Id, + final Long addressDepth2Id, + final Pageable pageable + ); + + Optional findByChatRoomId(final Long chatRoomId); + + List findAllByLeaderId(final Long leaderId); + + Integer countByLeaderId(final Long leaderId); + + @Modifying(clearAutomatically = true) + @Query("update CrewEntity c set c.memberCount = :memberCount, c.status = :status where c.id = :crewId") + void updateMemberCountAndStatus(final Long crewId, final Integer memberCount, final CrewStatus status); + + @Query("select c.chatRoomId from CrewEntity c where c.id = :crewId") + Long findChatRoomId(final Long crewId); } diff --git a/src/main/java/kr/pickple/back/crew/repository/entity/CrewEntity.java b/src/main/java/kr/pickple/back/crew/repository/entity/CrewEntity.java new file mode 100644 index 00000000..de0375e3 --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/repository/entity/CrewEntity.java @@ -0,0 +1,102 @@ +package kr.pickple.back.crew.repository.entity; + +import static kr.pickple.back.crew.domain.CrewStatus.*; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import kr.pickple.back.common.domain.BaseEntity; +import kr.pickple.back.crew.domain.CrewStatus; +import kr.pickple.back.crew.util.CrewStatusConverter; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@Table(name = "crew") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CrewEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Column(unique = true, length = 20) + private String name; + + @Column(length = 1000) + private String content; + + @NotNull + private Integer memberCount = 1; + + @NotNull + @Column(length = 300) + private String profileImageUrl; + + @NotNull + @Column(length = 300) + private String backgroundImageUrl; + + @NotNull + @Column(length = 10) + @Convert(converter = CrewStatusConverter.class) + private CrewStatus status = OPEN; + + @NotNull + private Integer likeCount = 0; + + @NotNull + private Integer maxMemberCount = 1; + + @NotNull + private Integer competitionPoint = 0; + + @NotNull + private Long leaderId; + + @NotNull + @Column(name = "address_depth1_id") + private Long addressDepth1Id; + + @NotNull + @Column(name = "address_depth2_id") + private Long addressDepth2Id; + + private Long chatRoomId; + + @Builder + private CrewEntity( + final String name, + final String content, + final String profileImageUrl, + final String backgroundImageUrl, + final Integer maxMemberCount, + final Long leaderId, + final Long addressDepth1Id, + final Long addressDepth2Id, + final Long chatRoomId + ) { + this.name = name; + this.content = content; + this.profileImageUrl = profileImageUrl; + this.backgroundImageUrl = backgroundImageUrl; + this.maxMemberCount = maxMemberCount; + this.leaderId = leaderId; + this.addressDepth1Id = addressDepth1Id; + this.addressDepth2Id = addressDepth2Id; + this.chatRoomId = chatRoomId; + } + + public Boolean isLeader(final Long memberId) { + return memberId.equals(leaderId); + } +} diff --git a/src/main/java/kr/pickple/back/crew/repository/entity/CrewMemberEntity.java b/src/main/java/kr/pickple/back/crew/repository/entity/CrewMemberEntity.java new file mode 100644 index 00000000..92b7ea1c --- /dev/null +++ b/src/main/java/kr/pickple/back/crew/repository/entity/CrewMemberEntity.java @@ -0,0 +1,48 @@ +package kr.pickple.back.crew.repository.entity; + +import static kr.pickple.back.common.domain.RegistrationStatus.*; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import kr.pickple.back.common.domain.BaseEntity; +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.common.util.RegistrationStatusAttributeConverter; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Entity +@Table(name = "crew_member") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CrewMemberEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Convert(converter = RegistrationStatusAttributeConverter.class) + @Column(length = 10) + private RegistrationStatus status = WAITING; + + @NotNull + private Long memberId; + + @NotNull + private Long crewId; + + @Builder + private CrewMemberEntity(final RegistrationStatus status, final Long memberId, final Long crewId) { + this.status = status; + this.memberId = memberId; + this.crewId = crewId; + } +} diff --git a/src/main/java/kr/pickple/back/crew/service/CrewMemberService.java b/src/main/java/kr/pickple/back/crew/service/CrewMemberService.java index 3cc911e3..d60be4bc 100644 --- a/src/main/java/kr/pickple/back/crew/service/CrewMemberService.java +++ b/src/main/java/kr/pickple/back/crew/service/CrewMemberService.java @@ -1,101 +1,99 @@ package kr.pickple.back.crew.service; +import static kr.pickple.back.crew.exception.CrewExceptionCode.*; + +import java.util.List; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + import kr.pickple.back.alarm.event.crew.CrewJoinRequestNotificationEvent; import kr.pickple.back.alarm.event.crew.CrewMemberJoinedEvent; import kr.pickple.back.alarm.event.crew.CrewMemberRejectedEvent; -import kr.pickple.back.chat.service.ChatMessageService; +import kr.pickple.back.chat.implement.ChatReader; +import kr.pickple.back.chat.implement.ChatWriter; import kr.pickple.back.common.domain.RegistrationStatus; import kr.pickple.back.crew.domain.Crew; import kr.pickple.back.crew.domain.CrewMember; -import kr.pickple.back.crew.dto.request.CrewMemberUpdateStatusRequest; +import kr.pickple.back.crew.domain.CrewProfile; +import kr.pickple.back.crew.dto.mapper.CrewResponseMapper; +import kr.pickple.back.crew.dto.response.CrewMemberRegistrationStatusResponse; import kr.pickple.back.crew.dto.response.CrewProfileResponse; import kr.pickple.back.crew.exception.CrewException; -import kr.pickple.back.crew.repository.CrewMemberRepository; -import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.implement.CrewReader; +import kr.pickple.back.crew.implement.CrewWriter; import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.dto.response.MemberResponse; -import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.implement.MemberReader; import lombok.RequiredArgsConstructor; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; -import static kr.pickple.back.common.domain.RegistrationStatus.WAITING; -import static kr.pickple.back.crew.exception.CrewExceptionCode.*; -import static kr.pickple.back.member.exception.MemberExceptionCode.MEMBER_NOT_FOUND; @Service -@Transactional(readOnly = true) @RequiredArgsConstructor +@Transactional(readOnly = true) public class CrewMemberService { - private final CrewRepository crewRepository; - private final MemberRepository memberRepository; - private final CrewMemberRepository crewMemberRepository; - private final ChatMessageService chatMessageService; + private final MemberReader memberReader; + private final CrewReader crewReader; + private final CrewWriter crewWriter; + private final ChatReader chatReader; + private final ChatWriter chatWriter; private final ApplicationEventPublisher eventPublisher; + /** + * 크루 가입 신청 + */ @Transactional - public void applyForCrewMemberShip(final Long crewId, final Long loggedInMemberId) { - final Crew crew = findCrewById(crewId); - final Member member = findMemberById(loggedInMemberId); + public void registerCrewMember(final Long crewId, final Long loggedInMemberId) { + final Crew crew = crewReader.read(crewId); + final Member member = memberReader.readByMemberId(loggedInMemberId); - crew.addCrewMember(member); + crewWriter.register(member, crew); eventPublisher.publishEvent(CrewJoinRequestNotificationEvent.builder() .crewId(crewId) - .memberId(crew.getLeader().getId()) + .memberId(crew.getLeader().getMemberId()) .build()); } - public CrewProfileResponse findAllCrewMembers( + /** + * 크루에 가입 신청된 혹은 확정된 사용자 정보 목록 조회 + */ + public CrewProfileResponse findAllCrewMembersByStatus( final Long loggedInMemberId, final Long crewId, final RegistrationStatus status ) { - final Crew crew = findCrewById(crewId); - - validateIsLeader(loggedInMemberId, crew); - - final List members = crew.getMembersByStatus(status); - final List crewMemberResponses = members.stream() - .map(MemberResponse::from) - .toList(); + final Crew crew = crewReader.read(crewId); - return CrewProfileResponse.of(crew, crewMemberResponses); - } + if (!crew.isLeader(loggedInMemberId)) { + throw new CrewException(CREW_IS_NOT_LEADER, loggedInMemberId); + } - private Crew findCrewById(final Long crewId) { - return crewRepository.findById(crewId) - .orElseThrow(() -> new CrewException(CREW_NOT_FOUND, crewId)); - } + final List members = crewReader.readAllMembersInStatus(crewId, status); - private Member findMemberById(final Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); + return CrewResponseMapper.mapToCrewProfileResponseDto(crew, members); } + /** + * 크루 가입 신청 수락 + */ @Transactional - public void crewMemberStatusUpdate( + public void updateCrewMemberRegistrationStatus( final Long loggedInMemberId, final Long crewId, final Long memberId, - final CrewMemberUpdateStatusRequest crewMemberUpdateStatusRequest + final RegistrationStatus newRegistrationStatus ) { - final CrewMember crewMember = findCrewMemberByCrewIdAndMemberId(crewId, memberId); + final CrewMember crewMember = crewReader.readCrewMember(memberId, crewId); final Crew crew = crewMember.getCrew(); - validateIsLeader(loggedInMemberId, crew); - - final RegistrationStatus updateStatus = crewMemberUpdateStatusRequest.getStatus(); - enterCrewChatRoom(updateStatus, crewMember); + if (!crew.isLeader(loggedInMemberId)) { + throw new CrewException(CREW_IS_NOT_LEADER, loggedInMemberId); + } - crewMember.updateStatus(updateStatus); - crewMember.updateStatus(crewMemberUpdateStatusRequest.getStatus()); + crewWriter.updateMemberRegistrationStatus(crewMember, newRegistrationStatus); + crewWriter.increaseMemberCount(crew); + chatWriter.enterRoom(crewMember.getMember(), chatReader.readRoomByCrewId(crewId)); eventPublisher.publishEvent(CrewMemberJoinedEvent.builder() .crewId(crewId) @@ -103,66 +101,74 @@ public void crewMemberStatusUpdate( .build()); } - private void validateIsLeader(final Long loggedInMemberId, final Crew crew) { - if (!crew.isLeader(loggedInMemberId)) { - throw new CrewException(CREW_IS_NOT_LEADER, loggedInMemberId); - } - } - - private void enterCrewChatRoom(final RegistrationStatus updateStatus, final CrewMember crewMember) { - final RegistrationStatus nowStatus = crewMember.getStatus(); - - if (nowStatus == WAITING && updateStatus == CONFIRMED) { - chatMessageService.enterRoomAndSaveEnteringMessages(crewMember.getCrewChatRoom(), crewMember.getMember()); - } - } - + /** + * 크루원 가입 신청 거절/취소 + */ @Transactional public void deleteCrewMember(final Long loggedInMemberId, final Long crewId, final Long memberId) { - final CrewMember crewMember = findCrewMemberByCrewIdAndMemberId(crewId, memberId); + final CrewMember crewMember = crewReader.readCrewMember(memberId, crewId); final Crew crew = crewMember.getCrew(); if (crew.isLeader(loggedInMemberId)) { - validateIsLeaderSelfDeleted(loggedInMemberId, memberId); + validateLeaderDeleteSelf(loggedInMemberId, memberId); + crewWriter.delete(crewMember); eventPublisher.publishEvent(CrewMemberRejectedEvent.builder() .crewId(crewId) .memberId(memberId) .build()); - deleteCrewMember(crewMember); - return; } if (loggedInMemberId.equals(memberId)) { - cancelCrewMember(crewMember); + crewWriter.cancel(crewMember); + return; } throw new CrewException(CREW_MEMBER_NOT_ALLOWED, loggedInMemberId); } - private CrewMember findCrewMemberByCrewIdAndMemberId(final Long crewId, final Long memberId) { - return crewMemberRepository.findByMemberIdAndCrewId(memberId, crewId) - .orElseThrow(() -> new CrewException(CREW_MEMBER_NOT_FOUND, memberId, crewId)); + private void validateLeaderDeleteSelf(final Long leaderId, final Long deletingMemberId) { + if (leaderId.equals(deletingMemberId)) { + throw new CrewException(CREW_LEADER_CANNOT_BE_DELETED, deletingMemberId); + } } - private void validateIsLeaderSelfDeleted(Long loggedInMemberId, Long memberId) { - if (loggedInMemberId.equals(memberId)) { - throw new CrewException(CREW_LEADER_CANNOT_BE_DELETED, loggedInMemberId); - } + /** + * 사용자가 가입한 크루 목록 조회 + */ + public List findAllJoinedCrews( + final Long memberId, + final RegistrationStatus memberStatus + ) { + final List crewProfiles = crewReader.readAllCrewProfilesByMemberIdAndStatus( + memberId, + memberStatus + ); + + return CrewResponseMapper.mapToCrewProfilesResponseDto(crewProfiles); } - private void cancelCrewMember(final CrewMember crewMember) { - if (crewMember.getStatus() != WAITING) { - throw new CrewException(CREW_MEMBER_STATUS_IS_NOT_WAITING); - } + /** + * 사용자가 만든 크루 목록 조회 + */ + public List findCreatedCrews(final Long memberId) { + final List crewProfiles = crewReader.readAllCrewProfilesByLeaderId(memberId); - deleteCrewMember(crewMember); + return CrewResponseMapper.mapToCrewProfilesResponseDto(crewProfiles); } - private void deleteCrewMember(final CrewMember crewMember) { - crewMemberRepository.delete(crewMember); + /** + * 사용자의 크루 가입 신청 여부 조회 + */ + public CrewMemberRegistrationStatusResponse findRegistrationStatusForCrew( + final Long memberId, + final Long crewId + ) { + final CrewMember crewMember = crewReader.readCrewMember(memberId, crewId); + + return CrewResponseMapper.mapToCrewMemberRegistrationStatusResponse(crewMember.getStatus()); } } diff --git a/src/main/java/kr/pickple/back/crew/service/CrewService.java b/src/main/java/kr/pickple/back/crew/service/CrewService.java index e52d4dca..8a0e109e 100644 --- a/src/main/java/kr/pickple/back/crew/service/CrewService.java +++ b/src/main/java/kr/pickple/back/crew/service/CrewService.java @@ -3,127 +3,113 @@ import static kr.pickple.back.chat.domain.RoomType.*; import static kr.pickple.back.common.domain.RegistrationStatus.*; import static kr.pickple.back.crew.exception.CrewExceptionCode.*; -import static kr.pickple.back.member.exception.MemberExceptionCode.*; import java.text.MessageFormat; import java.util.List; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import kr.pickple.back.address.dto.response.MainAddressResponse; -import kr.pickple.back.address.service.AddressService; import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.service.ChatRoomService; +import kr.pickple.back.chat.implement.ChatWriter; import kr.pickple.back.common.config.property.S3Properties; import kr.pickple.back.common.util.RandomUtil; import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.crew.domain.CrewMember; +import kr.pickple.back.crew.domain.NewCrew; +import kr.pickple.back.crew.dto.mapper.CrewRequestMapper; +import kr.pickple.back.crew.dto.mapper.CrewResponseMapper; import kr.pickple.back.crew.dto.request.CrewCreateRequest; import kr.pickple.back.crew.dto.response.CrewIdResponse; import kr.pickple.back.crew.dto.response.CrewProfileResponse; import kr.pickple.back.crew.exception.CrewException; -import kr.pickple.back.crew.exception.CrewExceptionCode; -import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.implement.CrewReader; +import kr.pickple.back.crew.implement.CrewWriter; import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.dto.response.MemberResponse; -import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.implement.MemberReader; import lombok.RequiredArgsConstructor; @Service -@Transactional(readOnly = true) @RequiredArgsConstructor +@Transactional(readOnly = true) public class CrewService { private static final Integer CREW_IMAGE_START_NUMBER = 1; private static final Integer CREW_IMAGE_END_NUMBER = 20; private static final Integer CREW_CREATE_MAX_SIZE = 3; + private final MemberReader memberReader; + private final CrewReader crewReader; + private final CrewWriter crewWriter; + private final ChatWriter chatWriter; private final S3Properties s3Properties; - private final CrewRepository crewRepository; - private final MemberRepository memberRepository; - private final AddressService addressService; - private final ChatRoomService chatRoomService; + /** + * 크루 생성 + */ @Transactional - public CrewIdResponse createCrew(final CrewCreateRequest crewCreateRequest, final Long loggedInMemberId) { - validateIsDuplicatedCrewInfo(crewCreateRequest.getName()); - - final Member leader = memberRepository.findById(loggedInMemberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND)); - - validateMemberCreatedCrewsCount(leader); + public CrewIdResponse createCrew(final Long loggedInMemberId, final CrewCreateRequest crewCreateRequest) { + final NewCrew newCrew = CrewRequestMapper.mapToNewCrewDomain(crewCreateRequest); - final MainAddressResponse mainAddressResponse = addressService.findMainAddressByNames( - crewCreateRequest.getAddressDepth1(), - crewCreateRequest.getAddressDepth2() - ); + final Member leader = memberReader.readByMemberId(loggedInMemberId); + validateCreateCrewMoreThanMaxCount(loggedInMemberId); - final Integer crewImageRandomNumber = RandomUtil.getRandomNumber(CREW_IMAGE_START_NUMBER, - CREW_IMAGE_END_NUMBER); + final ChatRoom chatRoom = chatWriter.createNewGroupRoom(CREW, newCrew.getName(), newCrew.getMaxMemberCount()); - final Crew crew = crewCreateRequest.toEntity( - leader, - mainAddressResponse, - MessageFormat.format(s3Properties.getCrewProfile(), crewImageRandomNumber), - MessageFormat.format(s3Properties.getCrewBackground(), crewImageRandomNumber) - ); - crew.addCrewMember(leader); + newCrew.assignLeader(leader); + newCrew.assignChatRoom(chatRoom); + assignImageUrls(newCrew); - final ChatRoom chatRoom = chatRoomService.saveNewChatRoom(leader, crew.getName(), CREW); - crew.makeNewCrewChatRoom(chatRoom); + final Crew crew = crewWriter.create(newCrew); + final CrewMember crewLeader = crewWriter.register(leader, crew); + crewWriter.updateMemberRegistrationStatus(crewLeader, CONFIRMED); + chatWriter.enterRoom(leader, chatRoom); - final Long crewId = crewRepository.save(crew).getId(); - - return CrewIdResponse.from(crewId); + return CrewResponseMapper.mapToCrewIdResponseDto(crew.getCrewId()); } - private void validateMemberCreatedCrewsCount(final Member leader) { - final Long createdCrewsCount = leader.getCreatedCrewsCount(); + private void validateCreateCrewMoreThanMaxCount(final Long leaderId) { + final Integer createdCrewsCount = crewReader.countByLeaderId(leaderId); if (createdCrewsCount >= CREW_CREATE_MAX_SIZE) { throw new CrewException(CREW_CREATE_MAX_COUNT_EXCEEDED, createdCrewsCount); } } - public CrewProfileResponse findCrewById(final Long crewId) { - final Crew crew = crewRepository.findById(crewId) - .orElseThrow(() -> new CrewException(CrewExceptionCode.CREW_NOT_FOUND)); + private void assignImageUrls(final NewCrew newCrew) { + final Integer randomImageNumber = RandomUtil.getRandomNumber(CREW_IMAGE_START_NUMBER, CREW_IMAGE_END_NUMBER); + final String profileImageUrl = MessageFormat.format(s3Properties.getCrewProfile(), randomImageNumber); + final String backgroundImageUrl = MessageFormat.format(s3Properties.getCrewBackground(), randomImageNumber); - final List confirmedCrewMembers = crew.getMembersByStatus(CONFIRMED); - final List crewMembers = confirmedCrewMembers.stream() - .map(MemberResponse::from) - .toList(); + newCrew.assignImageUrls(profileImageUrl, backgroundImageUrl); + } + + /** + * 크루 상세 조회 + */ + public CrewProfileResponse findCrewById(final Long crewId) { + final Crew crew = crewReader.read(crewId); + final List members = crewReader.readAllMembersInStatus(crewId, CONFIRMED); - return CrewProfileResponse.of(crew, crewMembers); + return CrewResponseMapper.mapToCrewProfileResponseDto(crew, members); } - public List findCrewByAddress( + /** + * 사용자 근처 크루 목록 조회 + */ + public List findNearCrewsByAddress( final String addressDepth1, final String addressDepth2, final Pageable pageable ) { - final MainAddressResponse mainAddressResponse = addressService.findMainAddressByNames(addressDepth1, - addressDepth2); - - final Page crews = crewRepository.findByAddressDepth1AndAddressDepth2( - mainAddressResponse.getAddressDepth1(), - mainAddressResponse.getAddressDepth2(), - pageable - ); - - return crews.stream() - .map(crew -> CrewProfileResponse.of(crew, crew.getMembersByStatus(CONFIRMED).stream() - .map(MemberResponse::from) - .toList())) - .toList(); - } + return crewReader.readNearCrewsByAddress(addressDepth1, addressDepth2, pageable) + .stream() + .map(crew -> { + final List members = crewReader.readAllMembersInStatus(crew.getCrewId(), CONFIRMED); - private void validateIsDuplicatedCrewInfo(final String name) { - if (crewRepository.existsByName(name)) { - throw new CrewException(CREW_IS_EXISTED, name); - } + return CrewResponseMapper.mapToCrewProfileResponseDto(crew, members); + }) + .toList(); } } diff --git a/src/main/java/kr/pickple/back/game/controller/GameController.java b/src/main/java/kr/pickple/back/game/controller/GameController.java index 88789386..2bc81928 100644 --- a/src/main/java/kr/pickple/back/game/controller/GameController.java +++ b/src/main/java/kr/pickple/back/game/controller/GameController.java @@ -28,6 +28,8 @@ import kr.pickple.back.game.dto.response.GameResponse; import kr.pickple.back.game.dto.response.GamesAndLocationResponse; import kr.pickple.back.game.service.GameFacadeService; +import kr.pickple.back.game.service.GameMemberService; +import kr.pickple.back.game.service.GameReviewMannerScoresService; import kr.pickple.back.game.service.GameService; import lombok.RequiredArgsConstructor; @@ -37,6 +39,8 @@ public class GameController { private final GameService gameService; + private final GameMemberService gameMemberService; + private final GameReviewMannerScoresService gameReviewMannerScoresService; private final GameFacadeService gameFacadeService; @PostMapping @@ -53,7 +57,7 @@ public ResponseEntity findGameDetailsById( @PathVariable final Long gameId ) { return ResponseEntity.status(OK) - .body(gameService.findGameDetailsById(gameId)); + .body(gameService.findGameById(gameId)); } @GetMapping @@ -71,20 +75,20 @@ public ResponseEntity registerGameMember( @Login final Long loggedInMemberId, @PathVariable final Long gameId ) { - gameService.registerGameMember(gameId, loggedInMemberId); + gameMemberService.registerGameMember(gameId, loggedInMemberId); return ResponseEntity.status(NO_CONTENT) .build(); } @GetMapping("/{gameId}/members") - public ResponseEntity findAllGameMembers( + public ResponseEntity findAllGameMembersByStatus( @Login final Long loggedInMemberId, @PathVariable final Long gameId, @RequestParam final RegistrationStatus status ) { return ResponseEntity.status(OK) - .body(gameService.findAllGameMembers(loggedInMemberId, gameId, status)); + .body(gameMemberService.findAllGameMembersByStatus(gameId, status)); } @PatchMapping("/{gameId}/members/{memberId}") @@ -94,11 +98,11 @@ public ResponseEntity updateGameMemberRegistrationStatus( @PathVariable final Long memberId, @Valid @RequestBody final GameMemberRegistrationStatusUpdateRequest gameMemberRegistrationStatusUpdateRequest ) { - gameService.updateGameMemberRegistrationStatus( + gameMemberService.updateGameMemberRegistrationStatus( loggedInMemberId, gameId, memberId, - gameMemberRegistrationStatusUpdateRequest + gameMemberRegistrationStatusUpdateRequest.getStatus() ); return ResponseEntity.status(NO_CONTENT) @@ -111,7 +115,7 @@ public ResponseEntity deleteGameMember( @PathVariable final Long gameId, @PathVariable final Long memberId ) { - gameService.deleteGameMember(loggedInMemberId, gameId, memberId); + gameMemberService.deleteGameMember(loggedInMemberId, gameId, memberId); return ResponseEntity.status(NO_CONTENT) .build(); @@ -124,7 +128,7 @@ public ResponseEntity reviewMannerScores( @Valid @RequestBody final MannerScoreReviewsRequest mannerScoreReviewsRequest ) { final List mannerScoreReviews = mannerScoreReviewsRequest.getMannerScoreReviews(); - gameService.reviewMannerScores(loggedInMemberId, gameId, mannerScoreReviews); + gameReviewMannerScoresService.reviewMannerScores(loggedInMemberId, gameId, mannerScoreReviews); return ResponseEntity.status(NO_CONTENT) .build(); diff --git a/src/main/java/kr/pickple/back/game/domain/Game.java b/src/main/java/kr/pickple/back/game/domain/Game.java index 2e8429b6..6211e2b2 100644 --- a/src/main/java/kr/pickple/back/game/domain/Game.java +++ b/src/main/java/kr/pickple/back/game/domain/Game.java @@ -8,216 +8,69 @@ import java.time.LocalTime; import java.util.List; -import org.locationtech.jts.geom.Point; - -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToOne; -import jakarta.validation.constraints.NotNull; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.common.domain.BaseEntity; -import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.crew.exception.CrewException; import kr.pickple.back.game.exception.GameException; -import kr.pickple.back.game.util.GameStatusConverter; import kr.pickple.back.member.domain.Member; import kr.pickple.back.position.domain.Position; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; -import lombok.NoArgsConstructor; -@Entity @Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Game extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class Game { - @Column(length = 1000) + private Long gameId; private String content; - - @NotNull private LocalDate playDate; - - @NotNull private LocalTime playStartTime; - - @NotNull private LocalTime playEndTime; - - @NotNull private Integer playTimeMinutes; - - @NotNull - @Column(length = 50) private String mainAddress; - - @NotNull - @Column(length = 50) private String detailAddress; - - @NotNull - private Point point; - - @NotNull - @Convert(converter = GameStatusConverter.class) - @Column(length = 10) - private GameStatus status = OPEN; - - //todo 현호: 게시글 상세 조회 기능 구현시 viewCount 올리는 기능 구현 - @NotNull - private Integer viewCount = 0; - - @NotNull - private Integer cost = 0; - - @NotNull - private Integer memberCount = 1; - - @NotNull - private Integer maxMemberCount = 2; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "host_id") + private Double latitude; + private Double longitude; + private GameStatus status; + private Integer viewCount; + private Integer cost; + private Integer memberCount; + private Integer maxMemberCount; private Member host; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "address_depth1_id") - private AddressDepth1 addressDepth1; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "address_depth2_id") - private AddressDepth2 addressDepth2; - - @Embedded - private GamePositions gamePositions = new GamePositions(); - - @Embedded - private GameMembers gameMembers = new GameMembers(); - - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "chat_room_id") - private ChatRoom chatRoom; - - @Builder - private Game( - final String content, - final LocalDate playDate, - final LocalTime playStartTime, - final LocalTime playEndTime, - final Integer playTimeMinutes, - final String mainAddress, - final String detailAddress, - final Integer cost, - final Integer maxMemberCount, - final Member host, - final Point point, - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2, - final List positions - ) { - this.content = content; - this.playDate = playDate; - this.playStartTime = playStartTime; - this.playEndTime = playEndTime; - this.playTimeMinutes = playTimeMinutes; - this.mainAddress = mainAddress; - this.detailAddress = detailAddress; - this.cost = cost; - this.maxMemberCount = maxMemberCount; - this.host = host; - this.point = point; - this.addressDepth1 = addressDepth1; - this.addressDepth2 = addressDepth2; - - updateGamePositions(positions); - } - - public void updateGameStatus(final GameStatus gameStatus) { - status = gameStatus; - } - - private void updateGamePositions(final List positions) { - gamePositions.updateGamePositions(this, positions); - } - - public List getPositions() { - return gamePositions.getPositions(); - } - - public List getMembersByStatus(final RegistrationStatus status) { - return gameMembers.getMembersByStatus(status); - } - - public List getGameMembers() { - return gameMembers.getGameMembers(); - } - - public LocalDateTime getPlayEndDatetime() { - return LocalDateTime.of(playDate, playEndTime); - } - - public void addGameMember(final Member member) { - gameMembers.addGameMember(this, member); - } + private String addressDepth1Name; + private String addressDepth2Name; + private List positions; public void increaseMemberCount() { - if (isClosedGame()) { + if (status != OPEN) { throw new GameException(GAME_STATUS_IS_CLOSED, status); } - if (isFullGame()) { - throw new GameException(GAME_CAPACITY_LIMIT_REACHED, memberCount); + if (memberCount.equals(maxMemberCount)) { + throw new CrewException(GAME_CAPACITY_LIMIT_REACHED, memberCount); } memberCount += 1; - if (isFullGame()) { - status = CLOSED; + if (memberCount.equals(maxMemberCount)) { + this.status = CLOSED; } } - private Boolean isClosedGame() { - return status == CLOSED; + public LocalDateTime getPlayStartDatetime() { + return LocalDateTime.of(playDate, playStartTime); } - public Boolean isNotEndedGame() { - return status != ENDED; - } - - private Boolean isFullGame() { - return memberCount.equals(maxMemberCount); - } - - public void increaseViewCount() { - viewCount++; + public LocalDateTime getPlayEndDatetime() { + return LocalDateTime.of(playDate, playEndTime); } - public Boolean isHost(final Member member) { - return member.equals(host); + public Boolean isNotEndedGame() { + return status != ENDED; } public Boolean isHost(final Long hostId) { - return hostId.equals(host.getId()); - } - - public void makeNewCrewChatRoom(final ChatRoom chatRoom) { - chatRoom.updateMaxMemberCount(maxMemberCount); - this.chatRoom = chatRoom; + return hostId.equals(this.host.getMemberId()); } } diff --git a/src/main/java/kr/pickple/back/game/domain/GameMember.java b/src/main/java/kr/pickple/back/game/domain/GameMember.java index b68e6e4c..6167070c 100644 --- a/src/main/java/kr/pickple/back/game/domain/GameMember.java +++ b/src/main/java/kr/pickple/back/game/domain/GameMember.java @@ -1,91 +1,41 @@ package kr.pickple.back.game.domain; -import jakarta.persistence.*; -import jakarta.validation.constraints.NotNull; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.common.domain.BaseEntity; +import static java.lang.Boolean.*; +import static kr.pickple.back.common.domain.RegistrationStatus.*; + import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.common.util.RegistrationStatusAttributeConverter; import kr.pickple.back.member.domain.Member; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; -import static java.lang.Boolean.FALSE; -import static java.lang.Boolean.TRUE; -import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; -import static kr.pickple.back.common.domain.RegistrationStatus.WAITING; - @Getter -@Entity -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class GameMember extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - @Convert(converter = RegistrationStatusAttributeConverter.class) - @Column(length = 10) - private RegistrationStatus status = WAITING; - - @NotNull - private Boolean isReview = FALSE; +@Builder +@NoArgsConstructor(access = AccessLevel.PRIVATE) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class GameMember { - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") + private Long gameMemberId; + private RegistrationStatus status; private Member member; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "game_id") private Game game; + private Boolean isReview = FALSE; - @Builder - private GameMember(final Member member, final Game game) { - this.status = getRegistrationStatus(member, game); - this.member = member; - this.game = game; + public void updateGameMemberId(final Long gameMemberId) { + this.gameMemberId = gameMemberId; } - public void updateStatus(final RegistrationStatus status) { - if (this.status == WAITING && status == CONFIRMED) { - game.increaseMemberCount(); - } - + public void updateRegistrationStatus(final RegistrationStatus status) { this.status = status; } - public Boolean equalsStatus(final RegistrationStatus status) { - return this.status == status; + public Boolean isReviewDone() { + return this.isReview; } - public Boolean equalsGame(final Game game) { - return this.game.equals(game); - } - - public ChatRoom getCrewChatRoom() { - return game.getChatRoom(); - } - - public Boolean isAlreadyReviewDone() { - return isReview; - } - - public void updateReviewDone() { - this.isReview = TRUE; - } - - private RegistrationStatus getRegistrationStatus(final Member member, final Game game) { - final Member host = game.getHost(); - - if (member.equals(host)) { - return CONFIRMED; - } - - return status; + public Boolean isStatusChangedFromWaitingToConfirmed(final RegistrationStatus newRegistrationStatus) { + return this.status == WAITING && newRegistrationStatus == CONFIRMED; } } diff --git a/src/main/java/kr/pickple/back/game/domain/GameMembers.java b/src/main/java/kr/pickple/back/game/domain/GameMembers.java deleted file mode 100644 index 2b37743c..00000000 --- a/src/main/java/kr/pickple/back/game/domain/GameMembers.java +++ /dev/null @@ -1,57 +0,0 @@ -package kr.pickple.back.game.domain; - -import static kr.pickple.back.game.exception.GameExceptionCode.*; - -import java.util.ArrayList; -import java.util.List; - -import org.hibernate.annotations.BatchSize; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.OneToMany; -import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.game.exception.GameException; -import kr.pickple.back.member.domain.Member; -import lombok.Getter; - -@Embeddable -public class GameMembers { - - @Getter - @BatchSize(size = 1000) - @OneToMany(mappedBy = "game", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) - private List gameMembers = new ArrayList<>(); - - public List getMembersByStatus(final RegistrationStatus status) { - return gameMembers.stream() - .filter(gameMember -> gameMember.equalsStatus(status)) - .map(GameMember::getMember) - .toList(); - } - - public void addGameMember(final Game game, final Member member) { - validateIsAlreadyRegisteredGameMember(member); - - final GameMember gameMember = buildGameMember(game, member); - gameMembers.add(gameMember); - } - - private void validateIsAlreadyRegisteredGameMember(final Member member) { - if (isAlreadyRegistered(member)) { - throw new GameException(GAME_MEMBER_IS_EXISTED, member.getId()); - } - } - - private boolean isAlreadyRegistered(final Member member) { - return gameMembers.stream() - .anyMatch(gameMember -> member.equals(gameMember.getMember())); - } - - private GameMember buildGameMember(final Game game, final Member member) { - return GameMember.builder() - .member(member) - .game(game) - .build(); - } -} diff --git a/src/main/java/kr/pickple/back/game/domain/GamePositions.java b/src/main/java/kr/pickple/back/game/domain/GamePositions.java deleted file mode 100644 index cf56ada0..00000000 --- a/src/main/java/kr/pickple/back/game/domain/GamePositions.java +++ /dev/null @@ -1,53 +0,0 @@ -package kr.pickple.back.game.domain; - -import static kr.pickple.back.game.exception.GameExceptionCode.*; - -import java.util.ArrayList; -import java.util.List; - -import org.hibernate.annotations.BatchSize; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.OneToMany; -import kr.pickple.back.game.exception.GameException; -import kr.pickple.back.position.domain.Position; - -@Embeddable -public class GamePositions { - - @BatchSize(size = 1000) - @OneToMany(mappedBy = "game", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) - private List gamePositions = new ArrayList<>(); - - public List getPositions() { - return gamePositions.stream() - .map(GamePosition::getPosition) - .toList(); - } - - public void updateGamePositions(final Game game, final List positions) { - validateIsDuplicatedPositions(positions); - - positions.stream() - .map(position -> buildGamePosition(game, position)) - .forEach(gamePositions::add); - } - - private void validateIsDuplicatedPositions(final List positions) { - long distinctPositionsSize = positions.stream() - .distinct() - .count(); - - if (distinctPositionsSize < positions.size()) { - throw new GameException(GAME_POSITIONS_IS_DUPLICATED, positions); - } - } - - private GamePosition buildGamePosition(final Game game, final Position position) { - return GamePosition.builder() - .position(position) - .game(game) - .build(); - } -} diff --git a/src/main/java/kr/pickple/back/game/domain/NewGame.java b/src/main/java/kr/pickple/back/game/domain/NewGame.java new file mode 100644 index 00000000..7334686d --- /dev/null +++ b/src/main/java/kr/pickple/back/game/domain/NewGame.java @@ -0,0 +1,70 @@ +package kr.pickple.back.game.domain; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +import kr.pickple.back.chat.domain.ChatRoom; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.position.domain.Position; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class NewGame { + + private String content; + private LocalDate playDate; + private LocalTime playStartTime; + private LocalTime playEndTime; + private Integer playTimeMinutes; + private String mainAddress; + private String detailAddress; + private Integer cost; + private Integer maxMemberCount; + private List positions; + private String addressDepth1Name; + private String addressDepth2Name; + private Member host; + private ChatRoom chatRoom; + + @Builder + private NewGame( + final String content, + final LocalDate playDate, + final LocalTime playStartTime, + final LocalTime playEndTime, + final Integer playTimeMinutes, + final String mainAddress, + final String detailAddress, + final Integer cost, + final Integer maxMemberCount, + final List positions, + final String addressDepth1Name, + final String addressDepth2Name + ) { + this.content = content; + this.playDate = playDate; + this.playStartTime = playStartTime; + this.playEndTime = playEndTime; + this.playTimeMinutes = playTimeMinutes; + this.mainAddress = mainAddress; + this.detailAddress = detailAddress; + this.cost = cost; + this.maxMemberCount = maxMemberCount; + this.positions = positions; + this.addressDepth1Name = addressDepth1Name; + this.addressDepth2Name = addressDepth2Name; + } + + public void assignHost(final Member host) { + this.host = host; + } + + public void assignChatRoom(final ChatRoom chatRoom) { + this.chatRoom = chatRoom; + } +} diff --git a/src/main/java/kr/pickple/back/game/dto/mapper/GameRequestMapper.java b/src/main/java/kr/pickple/back/game/dto/mapper/GameRequestMapper.java new file mode 100644 index 00000000..fbac5d50 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/dto/mapper/GameRequestMapper.java @@ -0,0 +1,30 @@ +package kr.pickple.back.game.dto.mapper; + +import java.time.LocalTime; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.game.domain.NewGame; +import kr.pickple.back.game.dto.request.GameCreateRequest; + +public class GameRequestMapper { + + public static NewGame mapToNewGameDomain(final GameCreateRequest gameCreateRequest, final MainAddress mainAddress) { + final LocalTime playEndTime = gameCreateRequest.getPlayStartTime() + .plusMinutes(gameCreateRequest.getPlayTimeMinutes()); + + return NewGame.builder() + .content(gameCreateRequest.getContent()) + .playDate(gameCreateRequest.getPlayDate()) + .playStartTime(gameCreateRequest.getPlayStartTime()) + .playEndTime(playEndTime) + .playTimeMinutes(gameCreateRequest.getPlayTimeMinutes()) + .mainAddress(gameCreateRequest.getMainAddress()) + .detailAddress(gameCreateRequest.getDetailAddress()) + .cost(gameCreateRequest.getCost()) + .maxMemberCount(gameCreateRequest.getMaxMemberCount()) + .positions(gameCreateRequest.getPositions()) + .addressDepth1Name(mainAddress.getAddressDepth1Name()) + .addressDepth2Name(mainAddress.getAddressDepth2Name()) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/game/dto/mapper/GameResponseMapper.java b/src/main/java/kr/pickple/back/game/dto/mapper/GameResponseMapper.java new file mode 100644 index 00000000..a11719b5 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/dto/mapper/GameResponseMapper.java @@ -0,0 +1,93 @@ +package kr.pickple.back.game.dto.mapper; + +import java.util.List; + +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.dto.response.GameMemberRegistrationStatusResponse; +import kr.pickple.back.game.dto.response.GameResponse; +import kr.pickple.back.game.dto.response.MemberGameResponse; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.dto.mapper.MemberResponseMapper; +import kr.pickple.back.member.dto.response.MemberResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class GameResponseMapper { + + public static GameResponse mapToGameResponseDto(final Game gameDomain, final List members) { + final List memberResponses = members + .stream() + .map(MemberResponseMapper::mapToMemberResponseDto) + .toList(); + + return GameResponse.builder() + .id(gameDomain.getGameId()) + .content(gameDomain.getContent()) + .playDate(gameDomain.getPlayDate()) + .playStartTime(gameDomain.getPlayStartTime()) + .playEndTime(gameDomain.getPlayEndTime()) + .playTimeMinutes(gameDomain.getPlayTimeMinutes()) + .mainAddress(gameDomain.getMainAddress()) + .detailAddress(gameDomain.getDetailAddress()) + .latitude(gameDomain.getLatitude()) + .longitude(gameDomain.getLongitude()) + .status(gameDomain.getStatus()) + .viewCount(gameDomain.getViewCount()) + .cost(gameDomain.getCost()) + .memberCount(gameDomain.getMemberCount()) + .maxMemberCount(gameDomain.getMaxMemberCount()) + .host(MemberResponseMapper.mapToMemberResponseDto(gameDomain.getHost())) + .addressDepth1(gameDomain.getAddressDepth1Name()) + .addressDepth2(gameDomain.getAddressDepth2Name()) + .positions(gameDomain.getPositions()) + .members(memberResponses) + .build(); + } + + public static MemberGameResponse mapToMemberGameResponseDto( + final Game game, + final List members, + final Boolean isReviewDone + ) { + final List memberResponses = members.stream() + .map(MemberResponseMapper::mapToMemberResponseDto) + .toList(); + + return MemberGameResponse.builder() + .id(game.getGameId()) + .content(game.getContent()) + .playDate(game.getPlayDate()) + .playStartTime(game.getPlayStartTime()) + .playEndTime(game.getPlayEndTime()) + .playTimeMinutes(game.getPlayTimeMinutes()) + .mainAddress(game.getMainAddress()) + .detailAddress(game.getDetailAddress()) + .latitude(game.getLatitude()) + .longitude(game.getLongitude()) + .status(game.getStatus()) + .isReviewDone(isReviewDone) + .viewCount(game.getViewCount()) + .cost(game.getCost()) + .memberCount(game.getMemberCount()) + .maxMemberCount(game.getMaxMemberCount()) + .host(MemberResponseMapper.mapToMemberResponseDto(game.getHost())) + .addressDepth1(game.getAddressDepth1Name()) + .addressDepth2(game.getAddressDepth2Name()) + .positions(game.getPositions()) + .members(memberResponses) + .build(); + } + + public static GameMemberRegistrationStatusResponse mapToGameMemberRegistrationStatusResponseDto( + final RegistrationStatus status, + final Boolean isReviewDone + ) { + return GameMemberRegistrationStatusResponse + .builder() + .memberRegistrationStatus(status) + .isReviewDone(isReviewDone) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/game/dto/request/GameCreateRequest.java b/src/main/java/kr/pickple/back/game/dto/request/GameCreateRequest.java index 4a253b6e..0ec1c929 100644 --- a/src/main/java/kr/pickple/back/game/dto/request/GameCreateRequest.java +++ b/src/main/java/kr/pickple/back/game/dto/request/GameCreateRequest.java @@ -4,8 +4,6 @@ import java.time.LocalTime; import java.util.List; -import org.locationtech.jts.geom.Point; - import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.validation.constraints.Max; @@ -15,9 +13,6 @@ import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; import jakarta.validation.constraints.Size; -import kr.pickple.back.address.dto.response.MainAddressResponse; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.member.domain.Member; import kr.pickple.back.position.domain.Position; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -65,27 +60,4 @@ public class GameCreateRequest { @NotNull(message = "포지션 목록은 null일 수 없음") private List positions; - - public Game toEntity( - final Member host, - final MainAddressResponse mainAddressResponse, - final Point point - ) { - return Game.builder() - .content(content) - .playDate(playDate) - .playStartTime(playStartTime) - .playEndTime(playStartTime.plusMinutes(playTimeMinutes)) - .playTimeMinutes(playTimeMinutes) - .mainAddress(mainAddress) - .detailAddress(detailAddress) - .cost(cost) - .maxMemberCount(maxMemberCount) - .host(host) - .point(point) - .addressDepth1(mainAddressResponse.getAddressDepth1()) - .addressDepth2(mainAddressResponse.getAddressDepth2()) - .positions(positions) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/member/dto/response/GameMemberRegistrationStatusResponse.java b/src/main/java/kr/pickple/back/game/dto/response/GameMemberRegistrationStatusResponse.java similarity index 64% rename from src/main/java/kr/pickple/back/member/dto/response/GameMemberRegistrationStatusResponse.java rename to src/main/java/kr/pickple/back/game/dto/response/GameMemberRegistrationStatusResponse.java index 15732042..95920841 100644 --- a/src/main/java/kr/pickple/back/member/dto/response/GameMemberRegistrationStatusResponse.java +++ b/src/main/java/kr/pickple/back/game/dto/response/GameMemberRegistrationStatusResponse.java @@ -1,11 +1,14 @@ -package kr.pickple.back.member.dto.response; +package kr.pickple.back.game.dto.response; import kr.pickple.back.common.domain.RegistrationStatus; +import lombok.AccessLevel; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; @Getter -@AllArgsConstructor(staticName = "of") +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) public class GameMemberRegistrationStatusResponse { private final RegistrationStatus memberRegistrationStatus; diff --git a/src/main/java/kr/pickple/back/game/dto/response/GameResponse.java b/src/main/java/kr/pickple/back/game/dto/response/GameResponse.java index dcf37463..fb7f4e14 100644 --- a/src/main/java/kr/pickple/back/game/dto/response/GameResponse.java +++ b/src/main/java/kr/pickple/back/game/dto/response/GameResponse.java @@ -4,7 +4,6 @@ import java.time.LocalTime; import java.util.List; -import kr.pickple.back.game.domain.Game; import kr.pickple.back.game.domain.GameStatus; import kr.pickple.back.member.dto.response.MemberResponse; import kr.pickple.back.position.domain.Position; @@ -38,29 +37,4 @@ public class GameResponse { private String addressDepth2; private List positions; private List members; - - public static GameResponse of(final Game game, final List memberResponses) { - return GameResponse.builder() - .id(game.getId()) - .content(game.getContent()) - .playDate(game.getPlayDate()) - .playStartTime(game.getPlayStartTime()) - .playEndTime(game.getPlayEndTime()) - .playTimeMinutes(game.getPlayTimeMinutes()) - .mainAddress(game.getMainAddress()) - .detailAddress(game.getDetailAddress()) - .latitude(game.getPoint().getY()) - .longitude(game.getPoint().getX()) - .status(game.getStatus()) - .viewCount(game.getViewCount()) - .cost(game.getCost()) - .memberCount(game.getMemberCount()) - .maxMemberCount(game.getMaxMemberCount()) - .host(MemberResponse.from(game.getHost())) - .addressDepth1(game.getAddressDepth1().getName()) - .addressDepth2(game.getAddressDepth2().getName()) - .positions(game.getPositions()) - .members(memberResponses) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/game/dto/response/MemberGameResponse.java b/src/main/java/kr/pickple/back/game/dto/response/MemberGameResponse.java new file mode 100644 index 00000000..092921f2 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/dto/response/MemberGameResponse.java @@ -0,0 +1,41 @@ +package kr.pickple.back.game.dto.response; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +import kr.pickple.back.game.domain.GameStatus; +import kr.pickple.back.member.dto.response.MemberResponse; +import kr.pickple.back.position.domain.Position; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class MemberGameResponse { + + private Long id; + private String content; + private LocalDate playDate; + private LocalTime playStartTime; + private LocalTime playEndTime; + private Integer playTimeMinutes; + private String mainAddress; + private String detailAddress; + private Double latitude; + private Double longitude; + private GameStatus status; + private Boolean isReviewDone; + private Integer viewCount; + private Integer cost; + private Integer memberCount; + private Integer maxMemberCount; + private MemberResponse host; + private String addressDepth1; + private String addressDepth2; + private List positions; + private List members; +} diff --git a/src/main/java/kr/pickple/back/game/exception/GameExceptionCode.java b/src/main/java/kr/pickple/back/game/exception/GameExceptionCode.java index c0baaa65..68b41326 100644 --- a/src/main/java/kr/pickple/back/game/exception/GameExceptionCode.java +++ b/src/main/java/kr/pickple/back/game/exception/GameExceptionCode.java @@ -1,9 +1,10 @@ package kr.pickple.back.game.exception; +import org.springframework.http.HttpStatus; + import kr.pickple.back.common.exception.ExceptionCode; import lombok.Getter; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; @Getter @RequiredArgsConstructor @@ -17,7 +18,8 @@ public enum GameExceptionCode implements ExceptionCode { GAME_SEARCH_CATEGORY_IS_INVALID(HttpStatus.BAD_REQUEST, "GAM-006", "게스트 모집글의 검색 카테고리 키워드가 유효하지 않음"), GAME_MEMBER_IS_NOT_HOST(HttpStatus.FORBIDDEN, "GAM-007", "해당 게스트 모집글의 호스트 권한이 필요함"), GAME_HOST_CANNOT_BE_DELETED(HttpStatus.BAD_REQUEST, "GAM-008", "호스트는 자신의 게스트 모집글에서 삭제될 수 없음"), - GAME_MEMBER_STATUS_IS_NOT_WAITING(HttpStatus.BAD_REQUEST, "GAM-009", "해당 게스트 모집글에 참여 신청 대기 상태가 아니라면, 참여 신청을 취소할 수 없음"), + GAME_MEMBER_STATUS_IS_NOT_WAITING(HttpStatus.BAD_REQUEST, "GAM-009", + "해당 게스트 모집글에 참여 신청 대기 상태가 아니라면, 참여 신청을 취소할 수 없음"), GAME_NOT_ALLOWED_TO_DELETE_GAME_MEMBER(HttpStatus.FORBIDDEN, "GAM-010", "해당 사용자의 게스트 모집글 참여 신청을 거절 혹은 취소할 권한이 필요함"), GAME_MEMBERS_CAN_REVIEW_DURING_POSSIBLE_PERIOD(HttpStatus.BAD_REQUEST, "GAM-011", "리뷰 가능 기간이 아님 (경기 종료 후부터 7일)"), GAME_MEMBER_CANNOT_REVIEW_SELF(HttpStatus.BAD_REQUEST, "GAM-012", "자기 자신에게 리뷰를 남길 수 없음"), diff --git a/src/main/java/kr/pickple/back/game/implement/GameMapper.java b/src/main/java/kr/pickple/back/game/implement/GameMapper.java new file mode 100644 index 00000000..135ff9bc --- /dev/null +++ b/src/main/java/kr/pickple/back/game/implement/GameMapper.java @@ -0,0 +1,70 @@ +package kr.pickple.back.game.implement; + +import java.util.List; + +import org.locationtech.jts.geom.Point; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.domain.NewGame; +import kr.pickple.back.game.repository.entity.GameEntity; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.position.domain.Position; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class GameMapper { + + public static Game mapGameEntityToDomain( + final GameEntity gameEntity, + final MainAddress mainAddress, + final Member host, + final List positions + ) { + return Game.builder() + .gameId(gameEntity.getId()) + .content(gameEntity.getContent()) + .playDate(gameEntity.getPlayDate()) + .playStartTime(gameEntity.getPlayStartTime()) + .playEndTime(gameEntity.getPlayEndTime()) + .playTimeMinutes(gameEntity.getPlayTimeMinutes()) + .mainAddress(gameEntity.getMainAddress()) + .detailAddress(gameEntity.getDetailAddress()) + .latitude(gameEntity.getPoint().getY()) + .longitude(gameEntity.getPoint().getX()) + .status(gameEntity.getStatus()) + .viewCount(gameEntity.getViewCount()) + .cost(gameEntity.getCost()) + .memberCount(gameEntity.getMemberCount()) + .maxMemberCount(gameEntity.getMaxMemberCount()) + .host(host) + .addressDepth1Name(mainAddress.getAddressDepth1Name()) + .addressDepth2Name(mainAddress.getAddressDepth2Name()) + .positions(positions) + .build(); + } + + public static GameEntity mapNewGameDomainToEntity( + final NewGame newGame, + final Point point, + final MainAddress mainAddress + ) { + return GameEntity.builder() + .content(newGame.getContent()) + .playDate(newGame.getPlayDate()) + .playStartTime(newGame.getPlayStartTime()) + .playEndTime(newGame.getPlayEndTime()) + .playTimeMinutes(newGame.getPlayTimeMinutes()) + .mainAddress(newGame.getMainAddress()) + .detailAddress(newGame.getDetailAddress()) + .cost(newGame.getCost()) + .maxMemberCount(newGame.getMaxMemberCount()) + .hostId(newGame.getHost().getMemberId()) + .point(point) + .addressDepth1Id(mainAddress.getAddressDepth1Id()) + .addressDepth2Id(mainAddress.getAddressDepth2Id()) + .chatRoomId(newGame.getChatRoom().getChatRoomId()) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/game/implement/GameMemberMapper.java b/src/main/java/kr/pickple/back/game/implement/GameMemberMapper.java new file mode 100644 index 00000000..61d2e48f --- /dev/null +++ b/src/main/java/kr/pickple/back/game/implement/GameMemberMapper.java @@ -0,0 +1,34 @@ +package kr.pickple.back.game.implement; + +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.domain.GameMember; +import kr.pickple.back.game.repository.entity.GameMemberEntity; +import kr.pickple.back.member.domain.Member; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class GameMemberMapper { + + public static GameMemberEntity mapGameMemberDomainToEntity(final GameMember gameMember) { + return GameMemberEntity.builder() + .status(gameMember.getStatus()) + .memberId(gameMember.getMember().getMemberId()) + .gameId(gameMember.getGame().getGameId()) + .build(); + } + + public static GameMember mapGameMemberEntityToDomain( + final GameMemberEntity gameMemberEntity, + final Member member, + final Game game + ) { + return GameMember.builder() + .gameMemberId(gameMemberEntity.getId()) + .status(gameMemberEntity.getStatus()) + .member(member) + .game(game) + .isReview(gameMemberEntity.isReviewDone()) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/game/implement/GameMemberReader.java b/src/main/java/kr/pickple/back/game/implement/GameMemberReader.java new file mode 100644 index 00000000..296bbfe2 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/implement/GameMemberReader.java @@ -0,0 +1,82 @@ +package kr.pickple.back.game.implement; + +import static kr.pickple.back.common.domain.RegistrationStatus.*; +import static kr.pickple.back.game.exception.GameExceptionCode.*; + +import java.util.List; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.domain.GameMember; +import kr.pickple.back.game.exception.GameException; +import kr.pickple.back.game.repository.GameMemberRepository; +import kr.pickple.back.game.repository.entity.GameMemberEntity; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.implement.MemberReader; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GameMemberReader { + + private final MemberReader memberReader; + private final GameReader gameReader; + private final GameMemberRepository gameMemberRepository; + + public GameMember readConfirmedStatusByMemberIdAndGameId(final Long memberId, final Long gameId) { + final GameMemberEntity gameMemberEntity = gameMemberRepository.findByMemberIdAndGameIdAndStatus( + memberId, + gameId, + CONFIRMED + ) + .orElseThrow(() -> new GameException(GAME_MEMBER_NOT_FOUND, gameId, memberId)); + + return getGameMember(gameMemberEntity); + } + + public GameMember readByMemberIdAndGameId(final Long memberId, final Long gameId) { + final GameMemberEntity gameMemberEntity = gameMemberRepository.findByMemberIdAndGameId(memberId, gameId) + .orElseThrow(() -> new GameException(GAME_MEMBER_NOT_FOUND, gameId, memberId)); + + return getGameMember(gameMemberEntity); + } + + private GameMember getGameMember(final GameMemberEntity gameMemberEntity) { + final Member member = memberReader.readByMemberId(gameMemberEntity.getMemberId()); + final Game game = gameReader.read(gameMemberEntity.getGameId()); + + return GameMemberMapper.mapGameMemberEntityToDomain(gameMemberEntity, member, game); + } + + public List readAllByMemberIdAndStatus( + final Long memberId, + final RegistrationStatus status + ) { + return gameMemberRepository.findAllByMemberIdAndStatus(memberId, status) + .stream() + .map(gameMemberEntity -> GameMemberMapper.mapGameMemberEntityToDomain( + gameMemberEntity, + memberReader.readByMemberId(gameMemberEntity.getMemberId()), + gameReader.read(gameMemberEntity.getGameId()) + ) + ).toList(); + } + + public List readMembersByGameIdAndStatus(final Long gameId, final RegistrationStatus status) { + return gameMemberRepository.findAllByGameIdAndStatus(gameId, status) + .stream() + .map(gameMemberEntity -> memberReader.readByMemberId(gameMemberEntity.getMemberId())) + .toList(); + } + + public Boolean isReviewDoneByGameIdAndMemberId(final Long gameId, final Long memberId) { + final GameMemberEntity gameMemberEntity = gameMemberRepository.findByMemberIdAndGameId(memberId, gameId) + .orElseThrow(() -> new GameException(GAME_MEMBER_NOT_FOUND, gameId, memberId)); + + return gameMemberEntity.isReviewDone(); + } +} diff --git a/src/main/java/kr/pickple/back/game/implement/GameMemberWriter.java b/src/main/java/kr/pickple/back/game/implement/GameMemberWriter.java new file mode 100644 index 00000000..a7dada7a --- /dev/null +++ b/src/main/java/kr/pickple/back/game/implement/GameMemberWriter.java @@ -0,0 +1,69 @@ +package kr.pickple.back.game.implement; + +import static kr.pickple.back.common.domain.RegistrationStatus.*; +import static kr.pickple.back.game.exception.GameExceptionCode.*; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.domain.GameMember; +import kr.pickple.back.game.exception.GameException; +import kr.pickple.back.game.repository.GameMemberRepository; +import kr.pickple.back.game.repository.GameRepository; +import kr.pickple.back.game.repository.entity.GameMemberEntity; +import kr.pickple.back.member.domain.Member; +import lombok.RequiredArgsConstructor; + +@Component +@Transactional +@RequiredArgsConstructor +public class GameMemberWriter { + + private final GameMemberReader gameMemberReader; + private final GameRepository gameRepository; + private final GameMemberRepository gameMemberRepository; + + public GameMember register(final Member member, final Game game) { + final Long memberId = member.getMemberId(); + final Long gameId = game.getGameId(); + + if (gameMemberRepository.existsByGameIdAndMemberId(gameId, memberId)) { + throw new GameException(GAME_MEMBER_IS_EXISTED, gameId, memberId); + } + + final GameMember gameMember = GameMember.builder() + .status(WAITING) + .member(member) + .game(game) + .build(); + + final GameMemberEntity gameMemberEntity = GameMemberMapper.mapGameMemberDomainToEntity(gameMember); + final GameMemberEntity savedGameMemberEntity = gameMemberRepository.save(gameMemberEntity); + gameMember.updateGameMemberId(savedGameMemberEntity.getId()); + + return gameMember; + } + + public void updateMemberRegistrationStatus(final GameMember gameMember, final RegistrationStatus status) { + gameMember.updateRegistrationStatus(status); + gameMemberRepository.updateRegistrationStatus(gameMember.getGameMemberId(), status); + + if (gameMember.isStatusChangedFromWaitingToConfirmed(status)) { + final Game game = gameMember.getGame(); + game.increaseMemberCount(); + gameRepository.updateMemberCountAndStatus(game.getGameId(), game.getMemberCount(), game.getStatus()); + } + } + + public void updateReviewDone(final Long memberId, final Long gameId) { + final GameMember gameMember = gameMemberReader.readConfirmedStatusByMemberIdAndGameId(memberId, gameId); + + gameMemberRepository.updateReviewDone(gameMember.getGameMemberId(), true); + } + + public void deleteGameMember(final GameMember gameMember) { + gameMemberRepository.deleteById(gameMember.getGameMemberId()); + } +} diff --git a/src/main/java/kr/pickple/back/game/implement/GameReader.java b/src/main/java/kr/pickple/back/game/implement/GameReader.java new file mode 100644 index 00000000..7b5f431b --- /dev/null +++ b/src/main/java/kr/pickple/back/game/implement/GameReader.java @@ -0,0 +1,162 @@ +package kr.pickple.back.game.implement; + +import static kr.pickple.back.address.exception.AddressExceptionCode.*; +import static kr.pickple.back.chat.exception.ChatExceptionCode.*; +import static kr.pickple.back.game.exception.GameExceptionCode.*; + +import java.util.List; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.exception.AddressException; +import kr.pickple.back.address.implement.AddressReader; +import kr.pickple.back.address.repository.AddressDepth1Repository; +import kr.pickple.back.address.repository.AddressDepth2Repository; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; +import kr.pickple.back.chat.exception.ChatException; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.domain.GameStatus; +import kr.pickple.back.game.exception.GameException; +import kr.pickple.back.game.repository.GamePositionRepository; +import kr.pickple.back.game.repository.GameRepository; +import kr.pickple.back.game.repository.entity.GameEntity; +import kr.pickple.back.game.repository.entity.GamePositionEntity; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.implement.MemberReader; +import kr.pickple.back.position.domain.Position; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GameReader { + + private final AddressReader addressReader; + private final AddressDepth1Repository addressDepth1Repository; + private final AddressDepth2Repository addressDepth2Repository; + private final MemberReader memberReader; + private final GameRepository gameRepository; + private final GamePositionRepository gamePositionRepository; + + public Game read(final Long gameId) { + final GameEntity gameEntity = gameRepository.findById(gameId) + .orElseThrow(() -> new GameException(GAME_NOT_FOUND, gameId)); + + gameEntity.increaseViewCount(); + + return mapGameEntityToDomain(gameEntity); + } + + public Game readByChatRoomId(final Long chatRoomId) { + final GameEntity gameEntity = gameRepository.findByChatRoomId(chatRoomId) + .orElseThrow(() -> new ChatException(CHAT_GAME_NOT_FOUND, chatRoomId)); + + return mapGameEntityToDomain(gameEntity); + } + + public List readAllByHostId(final Long hostId) { + return gameRepository.findAllByHostId(hostId) + .stream() + .map(this::mapGameEntityToDomain) + .toList(); + } + + private Game mapGameEntityToDomain(final GameEntity gameEntity) { + final Member host = memberReader.readByMemberId(gameEntity.getHostId()); + + final List positions = readPositionsByGameId(gameEntity.getId()); + + final MainAddress mainAddress = addressReader.readMainAddressByIds( + gameEntity.getAddressDepth1Id(), + gameEntity.getAddressDepth2Id() + ); + + return GameMapper.mapGameEntityToDomain(gameEntity, mainAddress, host, positions); + } + + public List readAllByAddress(final String address, final Pageable pageable) { + final PageRequest pageRequest = PageRequest.of( + pageable.getPageNumber(), + pageable.getPageSize(), + Sort.by( + Sort.Order.asc("playDate"), + Sort.Order.asc("playStartTime"), + Sort.Order.asc("id") + ) + ); + + final MainAddress mainAddress = addressReader.readMainAddressFromFullAddress(address); + + final Page gameEntities = gameRepository.findByAddressDepth1IdAndAddressDepth2IdAndStatusNot( + mainAddress.getAddressDepth1Id(), + mainAddress.getAddressDepth2Id(), + GameStatus.ENDED, + pageRequest + ); + + return gameEntities.stream() + .map(gameEntity -> GameMapper.mapGameEntityToDomain( + gameEntity, + mainAddress, + memberReader.readByMemberId(gameEntity.getHostId()), + readPositionsByGameId(gameEntity.getId()))) + .toList(); + } + + public List readAllWithInAddress(final MainAddress mainAddress) { + final String addressDepth1Name = mainAddress.getAddressDepth1Name(); + final String addressDepth2Name = mainAddress.getAddressDepth2Name(); + + final AddressDepth1Entity addressDepth1Entity = addressDepth1Repository.findByName(addressDepth1Name) + .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, addressDepth1Name)); + final AddressDepth2Entity addressDepth2Entity = addressDepth2Repository.findByNameAndAddressDepth1Id( + addressDepth2Name, addressDepth1Entity.getId()) + .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, addressDepth1Name, addressDepth2Name)); + + final List gameEntities = gameRepository.findGamesWithInAddress( + addressDepth1Entity, + addressDepth2Entity + ); + + return gameEntities.stream() + .map(gameEntity -> GameMapper.mapGameEntityToDomain( + gameEntity, + mainAddress, + memberReader.readByMemberId(gameEntity.getHostId()), + readPositionsByGameId(gameEntity.getId()))) + .toList(); + } + + public List readAllWithInDistance( + final Double latitude, + final Double longitude, + final Double distance + ) { + final List gameEntities = gameRepository.findGamesWithInDistance(latitude, longitude, distance); + + return gameEntities.stream() + .map(gameEntity -> GameMapper.mapGameEntityToDomain( + gameEntity, + addressReader.readMainAddressByIds( + gameEntity.getAddressDepth1Id(), + gameEntity.getAddressDepth2Id() + ), + memberReader.readByMemberId(gameEntity.getHostId()), + readPositionsByGameId(gameEntity.getId()))) + .toList(); + } + + private List readPositionsByGameId(final Long gameId) { + return gamePositionRepository.findAllByGameId(gameId) + .stream() + .map(GamePositionEntity::getPosition) + .toList(); + } +} diff --git a/src/main/java/kr/pickple/back/game/implement/GameWriter.java b/src/main/java/kr/pickple/back/game/implement/GameWriter.java new file mode 100644 index 00000000..28d3e922 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/implement/GameWriter.java @@ -0,0 +1,124 @@ +package kr.pickple.back.game.implement; + +import static kr.pickple.back.common.domain.RegistrationStatus.*; +import static kr.pickple.back.game.exception.GameExceptionCode.*; + +import java.util.List; + +import org.locationtech.jts.geom.Point; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.implement.AddressReader; +import kr.pickple.back.address.service.kakao.KakaoAddressSearchClient; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.domain.GameStatus; +import kr.pickple.back.game.domain.NewGame; +import kr.pickple.back.game.dto.request.MannerScoreReview; +import kr.pickple.back.game.exception.GameException; +import kr.pickple.back.game.repository.GameMemberRepository; +import kr.pickple.back.game.repository.GamePositionJdbcRepository; +import kr.pickple.back.game.repository.GamePositionRepository; +import kr.pickple.back.game.repository.GameRepository; +import kr.pickple.back.game.repository.entity.GameEntity; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.implement.MemberReader; +import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.position.domain.Position; +import lombok.RequiredArgsConstructor; + +@Component +@Transactional +@RequiredArgsConstructor +public class GameWriter { + + private final AddressReader addressReader; + private final MemberReader memberReader; + private final MemberRepository memberRepository; + private final GameRepository gameRepository; + private final GameMemberRepository gameMemberRepository; + private final GamePositionRepository gamePositionRepository; + private final KakaoAddressSearchClient kakaoAddressSearchClient; + private final GamePositionJdbcRepository gamePositionJdbcRepository; + + public Game create(final NewGame newGame) { + final Point point = kakaoAddressSearchClient.fetchAddress(newGame.getMainAddress()); + + final MainAddress mainAddress = addressReader.readMainAddressByNames( + newGame.getAddressDepth1Name(), + newGame.getAddressDepth2Name() + ); + + final GameEntity gameEntity = GameMapper.mapNewGameDomainToEntity(newGame, point, mainAddress); + final GameEntity savedGameEntity = gameRepository.save(gameEntity); + + setPositionsToGame(newGame.getPositions(), savedGameEntity.getId()); + + return GameMapper.mapGameEntityToDomain( + savedGameEntity, + mainAddress, + newGame.getHost(), + newGame.getPositions() + ); + } + + private void setPositionsToGame(final List positions, final Long gameId) { + validateIsDuplicatedPositions(positions); + + gamePositionJdbcRepository.creatGamePositions(positions, gameId); + } + + private void validateIsDuplicatedPositions(final List positions) { + final long distinctPositionsSize = positions.stream() + .distinct() + .count(); + + if (distinctPositionsSize != positions.size()) { + throw new GameException(GAME_POSITIONS_IS_DUPLICATED, positions); + } + } + + public void updateMemberRegistrationStatus(final GameStatus status, final Long gameId) { + gameRepository.updateRegistrationStatus(status, gameId); + } + + public void reviewMannerScores( + final Long memberId, + final Long gameId, + final List mannerScoreReviews + ) { + final Member member = memberReader.readByMemberId(memberId); + + mannerScoreReviews.forEach(review -> { + final Member reviewedMember = getReviewedMember(gameId, review.getMemberId()); + + if (member.equals(reviewedMember)) { + throw new GameException(GAME_MEMBER_CANNOT_REVIEW_SELF, memberId, reviewedMember.getMemberId()); + } + + reviewedMember.updateMannerScore(review.getMannerScore()); + memberRepository.updateMannerScore( + reviewedMember.getMemberId(), + reviewedMember.getMannerScore(), + reviewedMember.getMannerScoreCount() + ); + }); + + } + + private Member getReviewedMember(final Long gameId, final Long reviewedMemberId) { + return getConfirmedMembers(gameId) + .stream() + .filter(confirmedMember -> confirmedMember.isIdMatched(reviewedMemberId)) + .findFirst() + .orElseThrow(() -> new GameException(GAME_MEMBER_NOT_FOUND, reviewedMemberId)); + } + + private List getConfirmedMembers(final Long gameId) { + return gameMemberRepository.findAllByGameIdAndStatus(gameId, CONFIRMED) + .stream() + .map(gameMember -> memberReader.readByMemberId(gameMember.getMemberId())) + .toList(); + } +} diff --git a/src/main/java/kr/pickple/back/game/repository/GameMemberRepository.java b/src/main/java/kr/pickple/back/game/repository/GameMemberRepository.java index 42fd5c65..caf7a1b4 100644 --- a/src/main/java/kr/pickple/back/game/repository/GameMemberRepository.java +++ b/src/main/java/kr/pickple/back/game/repository/GameMemberRepository.java @@ -1,19 +1,36 @@ package kr.pickple.back.game.repository; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.game.domain.GameMember; +import kr.pickple.back.game.repository.entity.GameMemberEntity; -public interface GameMemberRepository extends JpaRepository { +public interface GameMemberRepository extends JpaRepository { - Optional findByMemberIdAndGameId(final Long memberId, final Long gameId); + Optional findByMemberIdAndGameId(final Long memberId, final Long gameId); - Optional findByMemberIdAndGameIdAndStatus( + Optional findByMemberIdAndGameIdAndStatus( final Long memberId, final Long gameId, final RegistrationStatus status ); + + List findAllByMemberIdAndStatus(final Long memberId, final RegistrationStatus memberStatus); + + List findAllByGameIdAndStatus(final Long gameId, final RegistrationStatus status); + + Boolean existsByGameIdAndMemberId(final Long gameId, final Long memberId); + + @Modifying(clearAutomatically = true) + @Query("update GameMemberEntity gm set gm.status = :status where gm.id = :gameMemberId") + void updateRegistrationStatus(final Long gameMemberId, final RegistrationStatus status); + + @Modifying(clearAutomatically = true) + @Query("update GameMemberEntity gm set gm.isReview = :isReview where gm.id = :gameMemberId") + void updateReviewDone(Long gameMemberId, Boolean isReview); } diff --git a/src/main/java/kr/pickple/back/game/repository/GamePositionJdbcRepository.java b/src/main/java/kr/pickple/back/game/repository/GamePositionJdbcRepository.java new file mode 100644 index 00000000..0befbbb5 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/repository/GamePositionJdbcRepository.java @@ -0,0 +1,43 @@ +package kr.pickple.back.game.repository; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import kr.pickple.back.position.domain.Position; +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class GamePositionJdbcRepository { + + private static final String GAME_POSITION_INSERT_SQL = "INSERT INTO game_position (position, game_id) VALUES(?, ?)"; + + private final JdbcTemplate jdbcTemplate; + + public void creatGamePositions(final List positions, final Long gameId) { + jdbcTemplate.batchUpdate( + GAME_POSITION_INSERT_SQL, + new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + Position position = positions.get(i); + ps.setString(1, position.getAcronym()); + ps.setLong(2, gameId); + } + + @Override + public int getBatchSize() { + return positions.size(); + } + } + ); + } +} + + + diff --git a/src/main/java/kr/pickple/back/game/repository/GamePositionRepository.java b/src/main/java/kr/pickple/back/game/repository/GamePositionRepository.java new file mode 100644 index 00000000..0d949559 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/repository/GamePositionRepository.java @@ -0,0 +1,13 @@ +package kr.pickple.back.game.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import kr.pickple.back.game.repository.entity.GamePositionEntity; + +public interface GamePositionRepository extends JpaRepository { + + List findAllByGameId(final Long gameId); +} + diff --git a/src/main/java/kr/pickple/back/game/repository/GameRepository.java b/src/main/java/kr/pickple/back/game/repository/GameRepository.java index 5d4eed5f..056ba405 100644 --- a/src/main/java/kr/pickple/back/game/repository/GameRepository.java +++ b/src/main/java/kr/pickple/back/game/repository/GameRepository.java @@ -1,25 +1,38 @@ package kr.pickple.back.game.repository; +import java.util.List; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.game.domain.Game; import kr.pickple.back.game.domain.GameStatus; +import kr.pickple.back.game.repository.entity.GameEntity; -public interface GameRepository extends JpaRepository, GameSearchRepository { +public interface GameRepository extends JpaRepository, GameSearchRepository { - Page findByAddressDepth1AndAddressDepth2AndStatusNot( - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2, + Page findByAddressDepth1IdAndAddressDepth2IdAndStatusNot( + final Long addressDepth1Id, + final Long addressDepth2Id, final GameStatus status, final Pageable pageable ); - Optional findByChatRoom(final ChatRoom chatRoom); + Optional findByChatRoomId(final Long chatRoomId); + + List findAllByHostId(final Long hostId); + + @Modifying(clearAutomatically = true) + @Query("update GameEntity g set g.memberCount = :memberCount, g.status = :status where g.id = :gameId") + void updateMemberCountAndStatus(final Long gameId, final Integer memberCount, final GameStatus status); + + @Modifying(clearAutomatically = true) + @Query("update GameEntity g set g.status = :status where g.id = :gameId") + void updateRegistrationStatus(final GameStatus status, final Long gameId); + + @Query("select g.chatRoomId from GameEntity g where g.id = :gameId") + Long findChatRoomId(final Long gameId); } diff --git a/src/main/java/kr/pickple/back/game/repository/GameSearchRepository.java b/src/main/java/kr/pickple/back/game/repository/GameSearchRepository.java index 1d555792..80cc8173 100644 --- a/src/main/java/kr/pickple/back/game/repository/GameSearchRepository.java +++ b/src/main/java/kr/pickple/back/game/repository/GameSearchRepository.java @@ -2,13 +2,16 @@ import java.util.List; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.game.domain.Game; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; +import kr.pickple.back.game.repository.entity.GameEntity; public interface GameSearchRepository { - List findGamesWithInDistance(final Double latitude, final Double longitude, final Double distance); + List findGamesWithInDistance(final Double latitude, final Double longitude, final Double distance); - List findGamesWithInAddress(final AddressDepth1 addressDepth1, final AddressDepth2 addressDepth2); + List findGamesWithInAddress( + final AddressDepth1Entity addressDepth1Entity, + final AddressDepth2Entity addressDepth2Entity + ); } diff --git a/src/main/java/kr/pickple/back/game/repository/GameSearchRepositoryImpl.java b/src/main/java/kr/pickple/back/game/repository/GameSearchRepositoryImpl.java index 0d3ac5bd..97f41eb7 100644 --- a/src/main/java/kr/pickple/back/game/repository/GameSearchRepositoryImpl.java +++ b/src/main/java/kr/pickple/back/game/repository/GameSearchRepositoryImpl.java @@ -1,6 +1,6 @@ package kr.pickple.back.game.repository; -import static kr.pickple.back.game.domain.QGame.*; +import static kr.pickple.back.game.repository.entity.QGameEntity.*; import static kr.pickple.back.map.domain.QMapPolygon.*; import java.util.List; @@ -10,9 +10,9 @@ import com.querydsl.core.types.dsl.Expressions; import com.querydsl.jpa.impl.JPAQueryFactory; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.game.domain.Game; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; +import kr.pickple.back.game.repository.entity.GameEntity; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -21,7 +21,7 @@ public class GameSearchRepositoryImpl implements GameSearchRepository { private final JPAQueryFactory jpaQueryFactory; @Override - public List findGamesWithInDistance( + public List findGamesWithInDistance( final Double latitude, final Double longitude, final Double distance @@ -29,11 +29,7 @@ public List findGamesWithInDistance( final String pointWKT = String.format("POINT(%s %s)", latitude, longitude); return jpaQueryFactory - .selectFrom(game) - .join(game.host).fetchJoin() - .join(game.addressDepth1).fetchJoin() - .join(game.addressDepth2).fetchJoin() - .leftJoin(game.gameMembers.gameMembers).fetchJoin() + .selectFrom(gameEntity) .where(isWithInDistance(pointWKT, distance)) .orderBy(getOrderByDistance(pointWKT)) .fetch(); @@ -49,23 +45,22 @@ private BooleanExpression isWithInDistance(final String pointWKT, final Double d private OrderSpecifier getOrderByDistance(final String pointWKT) { return Expressions.numberTemplate( - Double.class, - "ST_Distance_Sphere(point, ST_GeomFromText({0}, 4326))", - pointWKT - ) - .asc(); + Double.class, + "ST_Distance_Sphere(point, ST_GeomFromText({0}, 4326))", + pointWKT + ).asc(); } @Override - public List findGamesWithInAddress( - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2 + public List findGamesWithInAddress( + final AddressDepth1Entity addressDepth1Entity, + final AddressDepth2Entity addressDepth2Entity ) { return jpaQueryFactory - .select(game) - .from(game) + .select(gameEntity) + .from(gameEntity) .join(mapPolygon).on(isWithInAddress()) - .where(isAddress(addressDepth1, addressDepth2)) + .where(isAddress(addressDepth1Entity, addressDepth2Entity)) .orderBy(getOrderByAddress()) .fetch(); } @@ -74,28 +69,30 @@ private BooleanExpression isWithInAddress() { return Expressions.booleanTemplate( "ST_Contains({0}, {1})", mapPolygon.polygon, - game.point + gameEntity.point ); } - private BooleanExpression isAddress(final AddressDepth1 addressDepth1, final AddressDepth2 addressDepth2) { - return isAddressDepth1(addressDepth1).and(isAddressDepth2(addressDepth2)); + private BooleanExpression isAddress( + final AddressDepth1Entity addressDepth1Entity, + final AddressDepth2Entity addressDepth2Entity + ) { + return isAddressDepth1(addressDepth1Entity).and(isAddressDepth2(addressDepth2Entity)); } - private BooleanExpression isAddressDepth1(final AddressDepth1 addressDepth1) { - return mapPolygon.addressDepth1.eq(addressDepth1); + private BooleanExpression isAddressDepth1(final AddressDepth1Entity addressDepth1Entity) { + return mapPolygon.addressDepth1.eq(addressDepth1Entity); } - private BooleanExpression isAddressDepth2(final AddressDepth2 addressDepth2) { - return mapPolygon.addressDepth2.eq(addressDepth2); + private BooleanExpression isAddressDepth2(final AddressDepth2Entity addressDepth2Entity) { + return mapPolygon.addressDepth2.eq(addressDepth2Entity); } private OrderSpecifier getOrderByAddress() { return Expressions.numberTemplate( - Double.class, - "ST_Distance_Sphere({0}, ST_GeomFromText('POINT(' || {1} || ' ' || {2} || ')', 4326))", - game.point, mapPolygon.latitude, mapPolygon.longitude - ) - .asc(); + Double.class, + "ST_Distance_Sphere({0}, ST_GeomFromText('POINT(' || {1} || ' ' || {2} || ')', 4326))", + gameEntity.point, mapPolygon.latitude, mapPolygon.longitude + ).asc(); } } diff --git a/src/main/java/kr/pickple/back/game/repository/entity/GameEntity.java b/src/main/java/kr/pickple/back/game/repository/entity/GameEntity.java new file mode 100644 index 00000000..aacd86c6 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/repository/entity/GameEntity.java @@ -0,0 +1,138 @@ +package kr.pickple.back.game.repository.entity; + +import static kr.pickple.back.game.domain.GameStatus.*; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + +import org.locationtech.jts.geom.Point; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import kr.pickple.back.common.domain.BaseEntity; +import kr.pickple.back.game.domain.GameStatus; +import kr.pickple.back.game.util.GameStatusConverter; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "game") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GameEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(length = 1000) + private String content; + + @NotNull + private LocalDate playDate; + + @NotNull + private LocalTime playStartTime; + + @NotNull + private LocalTime playEndTime; + + @NotNull + private Integer playTimeMinutes; + + @NotNull + @Column(length = 50) + private String mainAddress; + + @NotNull + @Column(length = 50) + private String detailAddress; + + @NotNull + private Point point; + + @NotNull + @Convert(converter = GameStatusConverter.class) + @Column(length = 10) + private final GameStatus status = OPEN; + + //todo 현호: 게시글 상세 조회 기능 구현시 viewCount 올리는 기능 구현 + @NotNull + private Integer viewCount = 0; + + @NotNull + private Integer cost = 0; + + @NotNull + private final Integer memberCount = 1; + + @NotNull + private Integer maxMemberCount = 2; + + @NotNull + private Long hostId; + + @NotNull + @Column(name = "address_depth1_id") + private Long addressDepth1Id; + + @NotNull + @Column(name = "address_depth2_id") + private Long addressDepth2Id; + + private Long chatRoomId; + + @Builder + private GameEntity( + final String content, + final LocalDate playDate, + final LocalTime playStartTime, + final LocalTime playEndTime, + final Integer playTimeMinutes, + final String mainAddress, + final String detailAddress, + final Integer cost, + final Integer maxMemberCount, + final Long hostId, + final Point point, + final Long addressDepth1Id, + final Long addressDepth2Id, + final Long chatRoomId + ) { + this.content = content; + this.playDate = playDate; + this.playStartTime = playStartTime; + this.playEndTime = playEndTime; + this.playTimeMinutes = playTimeMinutes; + this.mainAddress = mainAddress; + this.detailAddress = detailAddress; + this.cost = cost; + this.maxMemberCount = maxMemberCount; + this.hostId = hostId; + this.point = point; + this.addressDepth1Id = addressDepth1Id; + this.addressDepth2Id = addressDepth2Id; + this.chatRoomId = chatRoomId; + } + + public LocalDateTime getPlayEndDatetime() { + return LocalDateTime.of(playDate, playEndTime); + } + + public void increaseViewCount() { + viewCount++; + } + + public Boolean isHost(final Long hostId) { + return hostId.equals(this.hostId); + } +} diff --git a/src/main/java/kr/pickple/back/game/repository/entity/GameMemberEntity.java b/src/main/java/kr/pickple/back/game/repository/entity/GameMemberEntity.java new file mode 100644 index 00000000..f6df8b80 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/repository/entity/GameMemberEntity.java @@ -0,0 +1,59 @@ +package kr.pickple.back.game.repository.entity; + +import static java.lang.Boolean.*; +import static kr.pickple.back.common.domain.RegistrationStatus.*; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import kr.pickple.back.common.domain.BaseEntity; +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.common.util.RegistrationStatusAttributeConverter; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Table(name = "game_member") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class GameMemberEntity extends BaseEntity { + + @Id + @Getter + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Getter + @NotNull + @Convert(converter = RegistrationStatusAttributeConverter.class) + @Column(length = 10) + private RegistrationStatus status = WAITING; + + @NotNull + private Boolean isReview = FALSE; + + @Getter + @NotNull + private Long memberId; + + @Getter + @NotNull + private Long gameId; + + @Builder + private GameMemberEntity(final Long memberId, final Long gameId, final RegistrationStatus status) { + this.status = status; + this.memberId = memberId; + this.gameId = gameId; + } + + public Boolean isReviewDone() { + return this.isReview; + } +} diff --git a/src/main/java/kr/pickple/back/game/domain/GamePosition.java b/src/main/java/kr/pickple/back/game/repository/entity/GamePositionEntity.java similarity index 68% rename from src/main/java/kr/pickple/back/game/domain/GamePosition.java rename to src/main/java/kr/pickple/back/game/repository/entity/GamePositionEntity.java index 159ce78b..e556ebc5 100644 --- a/src/main/java/kr/pickple/back/game/domain/GamePosition.java +++ b/src/main/java/kr/pickple/back/game/repository/entity/GamePositionEntity.java @@ -1,14 +1,12 @@ -package kr.pickple.back.game.domain; +package kr.pickple.back.game.repository.entity; import jakarta.persistence.Column; import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import kr.pickple.back.common.domain.BaseEntity; import kr.pickple.back.position.domain.Position; @@ -19,8 +17,9 @@ import lombok.NoArgsConstructor; @Entity +@Table(name = "game_position") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class GamePosition extends BaseEntity { +public class GamePositionEntity extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -32,13 +31,12 @@ public class GamePosition extends BaseEntity { @Column(length = 2) private Position position; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "game_id") - private Game game; + @NotNull + private Long gameId; @Builder - private GamePosition(final Position position, final Game game) { + private GamePositionEntity(final Position position, final Long gameId) { this.position = position; - this.game = game; + this.gameId = gameId; } } diff --git a/src/main/java/kr/pickple/back/game/service/GameFacadeService.java b/src/main/java/kr/pickple/back/game/service/GameFacadeService.java index abb27630..17b79cb0 100644 --- a/src/main/java/kr/pickple/back/game/service/GameFacadeService.java +++ b/src/main/java/kr/pickple/back/game/service/GameFacadeService.java @@ -1,13 +1,19 @@ package kr.pickple.back.game.service; +import static kr.pickple.back.common.domain.RegistrationStatus.*; + import java.util.List; import org.springframework.stereotype.Component; -import kr.pickple.back.address.dto.response.MainAddressResponse; -import kr.pickple.back.address.service.AddressService; +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.implement.AddressReader; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.dto.mapper.GameResponseMapper; import kr.pickple.back.game.dto.response.GameResponse; import kr.pickple.back.game.dto.response.GamesAndLocationResponse; +import kr.pickple.back.game.implement.GameMemberReader; +import kr.pickple.back.game.implement.GameReader; import kr.pickple.back.map.domain.MapPolygon; import kr.pickple.back.map.service.MapService; import lombok.RequiredArgsConstructor; @@ -16,19 +22,31 @@ @RequiredArgsConstructor public class GameFacadeService { - private final GameService gameService; + private final AddressReader addressReader; private final MapService mapService; - private final AddressService addressService; + private final GameReader gameReader; + private final GameMemberReader gameMemberReader; + /** + * 특정 지역의 게스트 모집글 조회 + */ public GamesAndLocationResponse findGamesWithInAddress( - final String addressDepth1, - final String addressDepth2 + final String addressDepth1Name, + final String addressDepth2Name ) { - final MainAddressResponse mainAddressResponse = addressService.findMainAddressByNames(addressDepth1, - addressDepth2); + final MainAddress mainAddress = addressReader.readMainAddressByNames(addressDepth1Name, addressDepth2Name); + + final List games = gameReader.readAllWithInAddress(mainAddress); + + final List gameResponses = games.stream() + .map(gameDomain -> GameResponseMapper.mapToGameResponseDto( + gameDomain, + gameMemberReader.readMembersByGameIdAndStatus(gameDomain.getGameId(), CONFIRMED) + ) + ) + .toList(); - final List gameResponses = gameService.findGamesWithInAddress(mainAddressResponse); - final MapPolygon mapPolygon = mapService.findMapPolygonByMainAddress(mainAddressResponse); + final MapPolygon mapPolygon = mapService.findMapPolygonByMainAddress(mainAddress); return GamesAndLocationResponse.of(gameResponses, mapPolygon); } diff --git a/src/main/java/kr/pickple/back/game/service/GameMemberService.java b/src/main/java/kr/pickple/back/game/service/GameMemberService.java new file mode 100644 index 00000000..e2e18d31 --- /dev/null +++ b/src/main/java/kr/pickple/back/game/service/GameMemberService.java @@ -0,0 +1,190 @@ +package kr.pickple.back.game.service; + +import static kr.pickple.back.common.domain.RegistrationStatus.*; +import static kr.pickple.back.game.exception.GameExceptionCode.*; + +import java.util.List; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.alarm.event.game.GameJoinRequestNotificationEvent; +import kr.pickple.back.alarm.event.game.GameMemberJoinedEvent; +import kr.pickple.back.alarm.event.game.GameMemberRejectedEvent; +import kr.pickple.back.chat.implement.ChatReader; +import kr.pickple.back.chat.implement.ChatWriter; +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.domain.GameMember; +import kr.pickple.back.game.dto.mapper.GameResponseMapper; +import kr.pickple.back.game.dto.response.GameMemberRegistrationStatusResponse; +import kr.pickple.back.game.dto.response.GameResponse; +import kr.pickple.back.game.dto.response.MemberGameResponse; +import kr.pickple.back.game.exception.GameException; +import kr.pickple.back.game.implement.GameMemberReader; +import kr.pickple.back.game.implement.GameMemberWriter; +import kr.pickple.back.game.implement.GameReader; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.implement.MemberReader; +import lombok.RequiredArgsConstructor; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class GameMemberService { + + private final MemberReader memberReader; + private final GameReader gameReader; + private final GameMemberReader gameMemberReader; + private final GameMemberWriter gameMemberWriter; + private final ChatReader chatReader; + private final ChatWriter chatWriter; + private final ApplicationEventPublisher eventPublisher; + + /** + * 게스트 모집 참여 신청 + */ + @Transactional + public void registerGameMember(final Long gameId, final Long loggedInMemberId) { + final Game gameDomain = gameReader.read(gameId); + final Member memberDomain = memberReader.readByMemberId(loggedInMemberId); + + gameMemberWriter.register(memberDomain, gameDomain); + + eventPublisher.publishEvent(GameJoinRequestNotificationEvent.builder() + .gameId(gameId) + .memberId(loggedInMemberId) + .build()); + } + + /** + * 게스트 모집에 참여 신청된 혹은 확정된 사용자 정보 목록 조회 + */ + public GameResponse findAllGameMembersByStatus( + final Long gameId, + final RegistrationStatus status + ) { + final Game game = gameReader.read(gameId); + + final List members = gameMemberReader.readMembersByGameIdAndStatus(gameId, status); + + return GameResponseMapper.mapToGameResponseDto(game, members); + } + + /** + * 게스트 모집 참여 신청 수락 + */ + @Transactional + public void updateGameMemberRegistrationStatus( + final Long loggedInMemberId, + final Long gameId, + final Long memberId, + final RegistrationStatus newRegistrationStatus + ) { + final GameMember gameMember = gameMemberReader.readByMemberIdAndGameId(memberId, gameId); + final Game game = gameMember.getGame(); + + if (!game.isHost(loggedInMemberId)) { + throw new GameException(GAME_MEMBER_IS_NOT_HOST, loggedInMemberId); + } + + gameMemberWriter.updateMemberRegistrationStatus(gameMember, newRegistrationStatus); + chatWriter.enterRoom(gameMember.getMember(), chatReader.readRoomByGameId(gameId)); + + eventPublisher.publishEvent(GameMemberJoinedEvent.builder() + .gameId(gameId) + .memberId(memberId) + .build()); + } + + /** + * 게스트 모집 참여 신청 거절/취소 + */ + @Transactional + public void deleteGameMember(final Long loggedInMemberId, final Long gameId, final Long memberId) { + final GameMember gameMember = gameMemberReader.readByMemberIdAndGameId(memberId, gameId); + final Game game = gameMember.getGame(); + + if (game.isHost(loggedInMemberId)) { + validateIsHostSelfDeleted(loggedInMemberId, memberId); + + gameMemberWriter.deleteGameMember(gameMember); + + eventPublisher.publishEvent(GameMemberRejectedEvent.builder() + .gameId(gameId) + .memberId(memberId) + .build()); + + return; + } + + if (loggedInMemberId.equals(memberId)) { + cancelGameMember(gameMember); + + return; + } + + throw new GameException(GAME_NOT_ALLOWED_TO_DELETE_GAME_MEMBER, loggedInMemberId); + } + + private void validateIsHostSelfDeleted(final Long loggedInMemberId, final Long memberId) { + if (loggedInMemberId.equals(memberId)) { + throw new GameException(GAME_HOST_CANNOT_BE_DELETED, loggedInMemberId); + } + } + + private void cancelGameMember(final GameMember gameMember) { + final RegistrationStatus status = gameMember.getStatus(); + + if (status != WAITING) { + throw new GameException(GAME_MEMBER_STATUS_IS_NOT_WAITING, status); + } + + gameMemberWriter.deleteGameMember(gameMember); + } + + /** + * 사용자의 참여 확정 게스트 모집글 목록 조회 + */ + public List findAllJoinedGames(final Long memberId, final RegistrationStatus status) { + return gameMemberReader.readAllByMemberIdAndStatus(memberId, status) + .stream() + .map(memberGame -> GameResponseMapper.mapToMemberGameResponseDto( + memberGame.getGame(), + gameMemberReader.readMembersByGameIdAndStatus(memberGame.getGame().getGameId(), CONFIRMED), + memberGame.isReviewDone() + ) + ).toList(); + } + + /** + * 사용자가 만든 게스트 모집글 목록 조회 + */ + public List findAllCreatedGames(final Long hostId) { + final List createdGames = gameReader.readAllByHostId(hostId); + + return createdGames.stream() + .map(game -> GameResponseMapper.mapToMemberGameResponseDto( + game, + gameMemberReader.readMembersByGameIdAndStatus(game.getGameId(), CONFIRMED), + gameMemberReader.isReviewDoneByGameIdAndMemberId(game.getGameId(), hostId) + ) + ).toList(); + } + + /** + * 사용자의 게스트 모집 신청 여부 조회 + */ + public GameMemberRegistrationStatusResponse findRegistrationStatusForGame( + final Long memberId, + final Long gameId + ) { + final GameMember gameMember = gameMemberReader.readConfirmedStatusByMemberIdAndGameId(memberId, gameId); + + return GameResponseMapper.mapToGameMemberRegistrationStatusResponseDto( + gameMember.getStatus(), + gameMember.isReviewDone() + ); + } +} diff --git a/src/main/java/kr/pickple/back/game/service/GameReviewMannerScoresService.java b/src/main/java/kr/pickple/back/game/service/GameReviewMannerScoresService.java new file mode 100644 index 00000000..a08a9b9c --- /dev/null +++ b/src/main/java/kr/pickple/back/game/service/GameReviewMannerScoresService.java @@ -0,0 +1,72 @@ +package kr.pickple.back.game.service; + +import static kr.pickple.back.game.exception.GameExceptionCode.*; + +import java.time.LocalDateTime; +import java.util.List; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.common.util.DateTimeUtil; +import kr.pickple.back.game.domain.Game; +import kr.pickple.back.game.domain.GameMember; +import kr.pickple.back.game.dto.request.MannerScoreReview; +import kr.pickple.back.game.exception.GameException; +import kr.pickple.back.game.implement.GameMemberReader; +import kr.pickple.back.game.implement.GameMemberWriter; +import kr.pickple.back.game.implement.GameWriter; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class GameReviewMannerScoresService { + + private static final int REVIEW_POSSIBLE_DAYS = 7; + + private final GameWriter gameWriter; + private final GameMemberReader gameMemberReader; + private final GameMemberWriter gameMemberWriter; + + @Transactional + public void reviewMannerScores( + final Long loggedInMemberId, + final Long gameId, + final List mannerScoreReviews + ) { + final GameMember gameMember = gameMemberReader.readConfirmedStatusByMemberIdAndGameId(loggedInMemberId, gameId); + + if (gameMember.isReviewDone()) { + throw new GameException(GAME_MEMBER_NOT_ALLOWED_TO_REVIEW_AGAIN, loggedInMemberId); + } + + final Game game = gameMember.getGame(); + final LocalDateTime gamePlayEndDateTime = game.getPlayEndDatetime(); + + if (isNotReviewPeriod(gamePlayEndDateTime)) { + throw new GameException( + GAME_MEMBERS_CAN_REVIEW_DURING_POSSIBLE_PERIOD, + game.getPlayDate(), + game.getPlayEndTime() + ); + } + + gameWriter.reviewMannerScores(loggedInMemberId, gameId, mannerScoreReviews); + gameMemberWriter.updateReviewDone(loggedInMemberId, gameId); + } + + private Boolean isNotReviewPeriod(final LocalDateTime gamePlayEndDateTime) { + return isBeforeThanPlayEndTime(gamePlayEndDateTime) || isAfterReviewPossibleTime(gamePlayEndDateTime); + } + + private Boolean isBeforeThanPlayEndTime(final LocalDateTime gamePlayEndDateTime) { + return DateTimeUtil.isAfterThanNow(gamePlayEndDateTime); + } + + private Boolean isAfterReviewPossibleTime(final LocalDateTime gamePlayEndDateTime) { + final LocalDateTime reviewDeadlineDatetime = gamePlayEndDateTime.plusDays(REVIEW_POSSIBLE_DAYS); + + return DateTimeUtil.isEqualOrAfter(reviewDeadlineDatetime, LocalDateTime.now()); + } +} diff --git a/src/main/java/kr/pickple/back/game/service/GameService.java b/src/main/java/kr/pickple/back/game/service/GameService.java index 66eca51b..ff0c296f 100644 --- a/src/main/java/kr/pickple/back/game/service/GameService.java +++ b/src/main/java/kr/pickple/back/game/service/GameService.java @@ -4,7 +4,6 @@ import static kr.pickple.back.common.domain.RegistrationStatus.*; import static kr.pickple.back.game.domain.GameStatus.*; import static kr.pickple.back.game.exception.GameExceptionCode.*; -import static kr.pickple.back.member.exception.MemberExceptionCode.*; import java.text.MessageFormat; import java.time.Duration; @@ -12,43 +11,31 @@ import java.time.format.DateTimeFormatter; import java.util.List; -import org.locationtech.jts.geom.Point; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import kr.pickple.back.address.dto.response.MainAddressResponse; -import kr.pickple.back.address.service.AddressService; -import kr.pickple.back.address.service.kakao.KakaoAddressSearchClient; -import kr.pickple.back.alarm.event.game.GameJoinRequestNotificationEvent; -import kr.pickple.back.alarm.event.game.GameMemberJoinedEvent; -import kr.pickple.back.alarm.event.game.GameMemberRejectedEvent; +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.implement.AddressReader; import kr.pickple.back.auth.repository.RedisRepository; import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.service.ChatMessageService; -import kr.pickple.back.chat.service.ChatRoomService; -import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.common.util.DateTimeUtil; +import kr.pickple.back.chat.implement.ChatWriter; import kr.pickple.back.game.domain.Category; import kr.pickple.back.game.domain.Game; import kr.pickple.back.game.domain.GameMember; -import kr.pickple.back.game.domain.GameStatus; +import kr.pickple.back.game.domain.NewGame; +import kr.pickple.back.game.dto.mapper.GameRequestMapper; +import kr.pickple.back.game.dto.mapper.GameResponseMapper; import kr.pickple.back.game.dto.request.GameCreateRequest; -import kr.pickple.back.game.dto.request.GameMemberRegistrationStatusUpdateRequest; -import kr.pickple.back.game.dto.request.MannerScoreReview; import kr.pickple.back.game.dto.response.GameIdResponse; import kr.pickple.back.game.dto.response.GameResponse; import kr.pickple.back.game.exception.GameException; -import kr.pickple.back.game.repository.GameMemberRepository; -import kr.pickple.back.game.repository.GameRepository; +import kr.pickple.back.game.implement.GameMemberReader; +import kr.pickple.back.game.implement.GameMemberWriter; +import kr.pickple.back.game.implement.GameReader; +import kr.pickple.back.game.implement.GameWriter; import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.dto.response.MemberResponse; -import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.implement.MemberReader; import lombok.RequiredArgsConstructor; @Service @@ -56,138 +43,84 @@ @Transactional(readOnly = true) public class GameService { - private static final int REVIEW_POSSIBLE_DAYS = 7; - - private final GameRepository gameRepository; - private final GameMemberRepository gameMemberRepository; - private final MemberRepository memberRepository; - private final KakaoAddressSearchClient kakaoAddressSearchClient; - private final AddressService addressService; - private final ChatRoomService chatRoomService; - private final ChatMessageService chatMessageService; - private final ApplicationEventPublisher eventPublisher; + private final AddressReader addressReader; + private final MemberReader memberReader; + private final GameReader gameReader; + private final GameMemberReader gameMemberReader; + private final GameWriter gameWriter; + private final GameMemberWriter gameMemberWriter; + private final ChatWriter chatWriter; private final RedisRepository redisRepository; + /** + * 게임 생성 + */ @Transactional public GameIdResponse createGame(final GameCreateRequest gameCreateRequest, final Long loggedInMemberId) { - final Member host = findMemberById(loggedInMemberId); - final Point point = kakaoAddressSearchClient.fetchAddress( - gameCreateRequest.getMainAddress()); - final MainAddressResponse mainAddressResponse = addressService.findMainAddressByAddressStrings( + final MainAddress mainAddress = addressReader.readMainAddressFromFullAddress( gameCreateRequest.getMainAddress()); + final NewGame newGame = GameRequestMapper.mapToNewGameDomain(gameCreateRequest, mainAddress); + final Member host = memberReader.readByMemberId(loggedInMemberId); + final ChatRoom chatRoom = chatWriter.createNewGroupRoom( + GAME, + makeGameChatRoomName(newGame), + newGame.getMaxMemberCount() + ); - final Game game = gameCreateRequest.toEntity(host, mainAddressResponse, point); - game.addGameMember(host); - - final ChatRoom chatRoom = chatRoomService.saveNewChatRoom(host, makeGameRoomName(game), GAME); - game.makeNewCrewChatRoom(chatRoom); - - final Long savedGameId = gameRepository.save(game).getId(); - saveGameStatusUpdateEventToRedis(game, savedGameId); - - return GameIdResponse.from(savedGameId); - } - - private void saveGameStatusUpdateEventToRedis(final Game game, final Long savedGameId) { - final LocalDateTime gameCreatedDateTime = LocalDateTime.now(); - - // 경기를 생성한 시각과 경기 시작 시간의 차 - final Long secondsOfBetweenCreatedAndPlay = getSecondsBetweenCreatedAndPlay(gameCreatedDateTime, game); - - // 경기를 생성한 시각과 경기 종료 시간의 차 - final Long secondsOfBetweenCreatedAndEnd = getSecondsBetweenCreatedAndEnd(gameCreatedDateTime, game); - - final String closedGameStatusUpdateKey = makeGameStatusUpdateKey(CLOSED, savedGameId); - final String endedGameStatusUpdateKey = makeGameStatusUpdateKey(ENDED, savedGameId); - - redisRepository.saveHash(closedGameStatusUpdateKey, "", "", secondsOfBetweenCreatedAndPlay); - redisRepository.saveHash(endedGameStatusUpdateKey, "", "", secondsOfBetweenCreatedAndEnd); - } - - private Long getSecondsBetweenCreatedAndPlay(final LocalDateTime gameCreatedDateTime, final Game game) { - final LocalDateTime gamePlayDateTime = LocalDateTime.of(game.getPlayDate(), game.getPlayStartTime()); - - return getSecondsBetween(gameCreatedDateTime, gamePlayDateTime); - } - - private Long getSecondsBetweenCreatedAndEnd(final LocalDateTime gameCreatedDateTime, final Game game) { - final LocalDateTime gameEndDateTime = game.getPlayEndDatetime(); - - return getSecondsBetween(gameCreatedDateTime, gameEndDateTime); - } + newGame.assignHost(host); + newGame.assignChatRoom(chatRoom); - private static long getSecondsBetween( - final LocalDateTime gameCreatedDateTime, - final LocalDateTime gamePlayDateTime - ) { - return Duration.between(gameCreatedDateTime, gamePlayDateTime) - .getSeconds(); - } + final Game game = gameWriter.create(newGame); + final GameMember gameHost = gameMemberWriter.register(host, game); + gameMemberWriter.updateMemberRegistrationStatus(gameHost, CONFIRMED); + chatWriter.enterRoom(host, chatRoom); - private String makeGameStatusUpdateKey(final GameStatus gameStatus, final Long id) { - return String.format("game:%s:%d", gameStatus.toString(), id); - } + saveGameStatusUpdateEventToRedis(game); - @Transactional - public void updateGameStatus(final GameStatus gameStatus, final Long gameId) { - final Game game = findGameById(gameId); - game.updateGameStatus(gameStatus); + return GameIdResponse.from(game.getGameId()); } - private String makeGameRoomName(final Game game) { - final String playDateFormat = game.getPlayDate().format(DateTimeFormatter.ofPattern("MM.dd")); - final String addressDepth2Name = game.getAddressDepth2().getName(); + private String makeGameChatRoomName(final NewGame newGame) { + final String playDateFormat = newGame.getPlayDate().format(DateTimeFormatter.ofPattern("MM.dd")); + final String addressDepth2Name = newGame.getAddressDepth2Name(); return MessageFormat.format("{0} {1}", playDateFormat, addressDepth2Name); } - @Transactional - public GameResponse findGameDetailsById(final Long gameId) { - final Game game = findGameById(gameId); - final List memberResponses = game.getMembersByStatus(CONFIRMED) - .stream() - .map(MemberResponse::from) - .toList(); - - game.increaseViewCount(); + private void saveGameStatusUpdateEventToRedis(final Game game) { + final LocalDateTime gameCreatedDateTime = LocalDateTime.now(); - return GameResponse.of(game, memberResponses); - } + // 경기를 생성한 시각과 경기 시작 시간의 차 + final Long secondsBetweenCreatedAndPlay = getSecondsBetween(gameCreatedDateTime, game.getPlayStartDatetime()); - @Transactional - public void registerGameMember(final Long gameId, final Long loggedInMemberId) { - final Game game = findGameById(gameId); - final Member member = findMemberById(loggedInMemberId); + // 경기를 생성한 시각과 경기 종료 시간의 차 + final Long secondsBetweenCreatedAndEnd = getSecondsBetween(gameCreatedDateTime, game.getPlayEndDatetime()); - game.addGameMember(member); + final String closedGameStatusUpdateKey = MessageFormat.format("game:{0}:{1}", CLOSED, game.getGameId()); + final String endedGameStatusUpdateKey = MessageFormat.format("game:{0}:{1}", ENDED, game.getGameId()); - eventPublisher.publishEvent(GameJoinRequestNotificationEvent.builder() - .gameId(gameId) - .memberId(game.getHost().getId()) - .build()); + redisRepository.saveHash(closedGameStatusUpdateKey, "", "", secondsBetweenCreatedAndPlay); + redisRepository.saveHash(endedGameStatusUpdateKey, "", "", secondsBetweenCreatedAndEnd); } - private Game findGameById(final Long gameId) { - return gameRepository.findById(gameId) - .orElseThrow(() -> new GameException(GAME_NOT_FOUND, gameId)); + private Long getSecondsBetween(final LocalDateTime start, final LocalDateTime end) { + return Duration.between(start, end).getSeconds(); } - public GameResponse findAllGameMembers( - final Long loggedInMemberId, - final Long gameId, - final RegistrationStatus status - ) { - final GameMember gameMember = findGameMemberByGameIdAndMemberId(gameId, loggedInMemberId); - final Game game = gameMember.getGame(); - final Member loggedInMember = gameMember.getMember(); - - if (!game.isHost(loggedInMember) && status == WAITING) { - throw new GameException(GAME_MEMBER_IS_NOT_HOST, loggedInMemberId); - } + /** + * 게임 상세 조회 + */ + @Transactional + public GameResponse findGameById(final Long gameId) { + final Game game = gameReader.read(gameId); + final List members = gameMemberReader.readMembersByGameIdAndStatus(gameId, CONFIRMED); - return GameResponse.of(game, getMemberResponses(game, status)); + return GameResponseMapper.mapToGameResponseDto(game, members); } + /** + * 게임 카테고리별 조회 + */ public List findGamesByCategory( final Category category, final String value, @@ -200,220 +133,36 @@ public List findGamesByCategory( }; } + /** + * 주소별 게스트 모집글 조회 + */ private List findGamesByAddress(final String address, final Pageable pageable) { - final MainAddressResponse mainAddressResponse = addressService.findMainAddressByAddressStrings(address); - - final PageRequest pageRequest = PageRequest.of( - pageable.getPageNumber(), - pageable.getPageSize(), - Sort.by( - Sort.Order.asc("playDate"), - Sort.Order.asc("playStartTime"), - Sort.Order.asc("id") - ) - ); - - final Page games = gameRepository.findByAddressDepth1AndAddressDepth2AndStatusNot( - mainAddressResponse.getAddressDepth1(), - mainAddressResponse.getAddressDepth2(), - GameStatus.ENDED, - pageRequest - ); + final List games = gameReader.readAllByAddress(address, pageable); return games.stream() - .map(game -> GameResponse.of(game, getMemberResponses(game, CONFIRMED))) - .toList(); - } - - private List getMemberResponses(final Game game, final RegistrationStatus status) { - return game.getMembersByStatus(status) - .stream() - .map(MemberResponse::from) - .toList(); - } - - @Transactional - public void updateGameMemberRegistrationStatus( - final Long loggedInMemberId, - final Long gameId, - final Long memberId, - final GameMemberRegistrationStatusUpdateRequest gameMemberRegistrationStatusUpdateRequest - ) { - final GameMember gameMember = findGameMemberByGameIdAndMemberId(gameId, memberId); - final Game game = gameMember.getGame(); - - validateIsHost(loggedInMemberId, game); - - final RegistrationStatus updateStatus = gameMemberRegistrationStatusUpdateRequest.getStatus(); - enterGameChatRoom(updateStatus, gameMember); - - gameMember.updateStatus(updateStatus); - - eventPublisher.publishEvent(GameMemberJoinedEvent.builder() - .gameId(gameId) - .memberId(memberId) - .build()); - } - - private void validateIsHost(final Long loggedInMemberId, final Game game) { - final Member loggedInMember = findMemberById(loggedInMemberId); - - if (!game.isHost(loggedInMember)) { - throw new GameException(GAME_MEMBER_IS_NOT_HOST, loggedInMemberId); - } - } - - private void enterGameChatRoom(final RegistrationStatus updateStatus, final GameMember gameMember) { - final RegistrationStatus nowStatus = gameMember.getStatus(); - - if (nowStatus == WAITING && updateStatus == CONFIRMED) { - chatMessageService.enterRoomAndSaveEnteringMessages(gameMember.getCrewChatRoom(), gameMember.getMember()); - } - } - - @Transactional - public void deleteGameMember(final Long loggedInMemberId, final Long gameId, final Long memberId) { - final GameMember gameMember = findGameMemberByGameIdAndMemberId(gameId, memberId); - final Game game = gameMember.getGame(); - final Member member = gameMember.getMember(); - final Member loggedInMember = findMemberById(loggedInMemberId); - - if (game.isHost(loggedInMember)) { - validateIsHostSelfDeleted(loggedInMember, member); - - eventPublisher.publishEvent(GameMemberRejectedEvent.builder() - .gameId(gameId) - .memberId(memberId) - .build()); - - deleteGameMember(gameMember); - - return; - } - - if (loggedInMember.equals(member)) { - cancelGameMember(gameMember); - - return; - } - - throw new GameException(GAME_NOT_ALLOWED_TO_DELETE_GAME_MEMBER, loggedInMemberId); - } - - private Member findMemberById(final Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); - } - - private void validateIsHostSelfDeleted(final Member loggedInMember, final Member member) { - if (loggedInMember.equals(member)) { - throw new GameException(GAME_HOST_CANNOT_BE_DELETED, loggedInMember.getId()); - } - } - - private void cancelGameMember(final GameMember gameMember) { - RegistrationStatus status = gameMember.getStatus(); - - if (status != WAITING) { - throw new GameException(GAME_MEMBER_STATUS_IS_NOT_WAITING, status); - } - - deleteGameMember(gameMember); - } - - private void deleteGameMember(final GameMember gameMember) { - gameMemberRepository.delete(gameMember); - } - - @Transactional - public void reviewMannerScores( - final Long loggedInMemberId, - final Long gameId, - final List mannerScoreReviews - ) { - final GameMember gameMember = gameMemberRepository.findByMemberIdAndGameIdAndStatus( - loggedInMemberId, - gameId, - CONFIRMED - ) - .orElseThrow(() -> new GameException(GAME_MEMBER_NOT_FOUND, gameId, loggedInMemberId)); - - if (gameMember.isAlreadyReviewDone()) { - throw new GameException(GAME_MEMBER_NOT_ALLOWED_TO_REVIEW_AGAIN, loggedInMemberId); - } - - final Game game = gameMember.getGame(); - final Member loggedInMember = gameMember.getMember(); - - if (isNotReviewPeriod(game)) { - throw new GameException(GAME_MEMBERS_CAN_REVIEW_DURING_POSSIBLE_PERIOD, game.getPlayDate(), - game.getPlayEndTime()); - } - - mannerScoreReviews.forEach(review -> { - final Member reviewedMember = getReviewedMember(game, review.getMemberId()); - validateIsSelfReview(loggedInMember, reviewedMember); - reviewedMember.updateMannerScore(review.getMannerScore()); - }); - - gameMember.updateReviewDone(); - } - - private void validateIsSelfReview(final Member loggedInMember, final Member reviewedMember) { - if (loggedInMember.equals(reviewedMember)) { - throw new GameException(GAME_MEMBER_CANNOT_REVIEW_SELF, loggedInMember.getId(), reviewedMember.getId()); - } - } - - private GameMember findGameMemberByGameIdAndMemberId(final Long gameId, final Long memberId) { - return gameMemberRepository.findByMemberIdAndGameId(memberId, gameId) - .orElseThrow(() -> new GameException(GAME_MEMBER_NOT_FOUND, gameId, memberId)); - } - - private Boolean isNotReviewPeriod(final Game game) { - return isBeforeThanPlayEndTime(game) || isAfterReviewPossibleTime(game); - } - - private Boolean isBeforeThanPlayEndTime(final Game game) { - return DateTimeUtil.isAfterThanNow(game.getPlayEndDatetime()); - } - - private Boolean isAfterReviewPossibleTime(final Game game) { - final LocalDateTime reviewDeadlineDatetime = game.getPlayEndDatetime().plusDays(REVIEW_POSSIBLE_DAYS); - - return DateTimeUtil.isEqualOrAfter(reviewDeadlineDatetime, LocalDateTime.now()); - } - - private Member getReviewedMember(final Game game, final Long reviewedMemberId) { - return game.getMembersByStatus(CONFIRMED) - .stream() - .filter(confirmedMember -> confirmedMember.getId() == reviewedMemberId) - .findFirst() - .orElseThrow(() -> new GameException(GAME_MEMBER_NOT_FOUND, reviewedMemberId)); + .map(game -> GameResponseMapper.mapToGameResponseDto( + game, + gameMemberReader.readMembersByGameIdAndStatus(game.getGameId(), CONFIRMED) + ) + ).toList(); } + /** + * 중심 좌표(위도, 경도)로 부터 특정 거리(M) 까지의 게스트 모집글 조회 + */ public List findGamesWithInDistance( final Double latitude, final Double longitude, final Double distance ) { - final List games = gameRepository.findGamesWithInDistance(latitude, longitude, distance); - - return games.stream() - .filter(Game::isNotEndedGame) - .map(game -> GameResponse.of(game, getMemberResponses(game, CONFIRMED))) - .toList(); - } - - public List findGamesWithInAddress(final MainAddressResponse mainAddressResponse) { - final List games = gameRepository.findGamesWithInAddress( - mainAddressResponse.getAddressDepth1(), - mainAddressResponse.getAddressDepth2() - ); + final List games = gameReader.readAllWithInDistance(latitude, longitude, distance); return games.stream() .filter(Game::isNotEndedGame) - .map(game -> GameResponse.of(game, getMemberResponses(game, CONFIRMED))) - .toList(); + .map(game -> GameResponseMapper.mapToGameResponseDto( + game, + gameMemberReader.readMembersByGameIdAndStatus(game.getGameId(), CONFIRMED) + ) + ).toList(); } } diff --git a/src/main/java/kr/pickple/back/game/service/RedisExpirationListener.java b/src/main/java/kr/pickple/back/game/service/RedisExpirationListener.java index 69ae611b..d7e74561 100644 --- a/src/main/java/kr/pickple/back/game/service/RedisExpirationListener.java +++ b/src/main/java/kr/pickple/back/game/service/RedisExpirationListener.java @@ -7,6 +7,7 @@ import org.springframework.stereotype.Component; import kr.pickple.back.game.domain.GameStatus; +import kr.pickple.back.game.implement.GameWriter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,8 +15,8 @@ @Component @RequiredArgsConstructor public class RedisExpirationListener implements MessageListener { - - private final GameService gameService; + + private final GameWriter gameWriter; @Override public void onMessage(final Message message, final byte[] pattern) { @@ -26,7 +27,7 @@ public void onMessage(final Message message, final byte[] pattern) { final GameStatus gameStatus = GameStatus.valueOf(stringTokenizer.nextToken()); final Long gameId = Long.parseLong(stringTokenizer.nextToken()); - gameService.updateGameStatus(gameStatus, gameId); + gameWriter.updateMemberRegistrationStatus(gameStatus, gameId); } } } diff --git a/src/main/java/kr/pickple/back/map/domain/MapPolygon.java b/src/main/java/kr/pickple/back/map/domain/MapPolygon.java index caa66230..0cf58acc 100644 --- a/src/main/java/kr/pickple/back/map/domain/MapPolygon.java +++ b/src/main/java/kr/pickple/back/map/domain/MapPolygon.java @@ -12,8 +12,8 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.NotNull; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -30,12 +30,12 @@ public class MapPolygon { @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "address_depth1_id") - private AddressDepth1 addressDepth1; + private AddressDepth1Entity addressDepth1; @NotNull @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "address_depth2_id") - private AddressDepth2 addressDepth2; + private AddressDepth2Entity addressDepth2; @NotNull private BigDecimal latitude; diff --git a/src/main/java/kr/pickple/back/map/repository/MapPolygonRepository.java b/src/main/java/kr/pickple/back/map/repository/MapPolygonRepository.java index bdbfe6d8..2320ada1 100644 --- a/src/main/java/kr/pickple/back/map/repository/MapPolygonRepository.java +++ b/src/main/java/kr/pickple/back/map/repository/MapPolygonRepository.java @@ -2,12 +2,12 @@ import org.springframework.data.jpa.repository.JpaRepository; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; import kr.pickple.back.map.domain.MapPolygon; public interface MapPolygonRepository extends JpaRepository { - MapPolygon findByAddressDepth1AndAddressDepth2(final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2); + MapPolygon findByAddressDepth1IdAndAddressDepth2Id( + final Long addressDepth1Id, + final Long addressDepth2Id + ); } diff --git a/src/main/java/kr/pickple/back/map/service/MapService.java b/src/main/java/kr/pickple/back/map/service/MapService.java index 73d483c4..2ca804b8 100644 --- a/src/main/java/kr/pickple/back/map/service/MapService.java +++ b/src/main/java/kr/pickple/back/map/service/MapService.java @@ -4,7 +4,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import kr.pickple.back.address.dto.response.MainAddressResponse; +import kr.pickple.back.address.domain.MainAddress; import kr.pickple.back.map.domain.MapPolygon; import kr.pickple.back.map.repository.MapPolygonRepository; import lombok.RequiredArgsConstructor; @@ -16,11 +16,12 @@ public class MapService { private final MapPolygonRepository mapPolygonRepository; @Transactional(readOnly = true) - @Cacheable(cacheManager = "caffeineCacheManager", cacheNames = "polygon", key = "#mainAddressResponse.addressDepth2.name") - public MapPolygon findMapPolygonByMainAddress(final MainAddressResponse mainAddressResponse) { - return mapPolygonRepository.findByAddressDepth1AndAddressDepth2( - mainAddressResponse.getAddressDepth1(), - mainAddressResponse.getAddressDepth2() + @Cacheable(cacheManager = "caffeineCacheManager", cacheNames = "polygon", key = "#mainAddress.addressDepth2Id") + public MapPolygon findMapPolygonByMainAddress(final MainAddress mainAddress) { + + return mapPolygonRepository.findByAddressDepth1IdAndAddressDepth2Id( + mainAddress.getAddressDepth1Id(), + mainAddress.getAddressDepth2Id() ); } } diff --git a/src/main/java/kr/pickple/back/member/controller/Identification.java b/src/main/java/kr/pickple/back/member/controller/Identification.java new file mode 100644 index 00000000..f01af6c6 --- /dev/null +++ b/src/main/java/kr/pickple/back/member/controller/Identification.java @@ -0,0 +1,13 @@ +package kr.pickple.back.member.controller; + +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +@Retention(RUNTIME) +@Target({ElementType.METHOD}) +public @interface Identification { + +} diff --git a/src/main/java/kr/pickple/back/member/controller/IdentificationAspect.java b/src/main/java/kr/pickple/back/member/controller/IdentificationAspect.java new file mode 100644 index 00000000..8a14a486 --- /dev/null +++ b/src/main/java/kr/pickple/back/member/controller/IdentificationAspect.java @@ -0,0 +1,24 @@ +package kr.pickple.back.member.controller; + +import static kr.pickple.back.member.exception.MemberExceptionCode.*; + +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; + +import kr.pickple.back.member.exception.MemberException; + +@Aspect +@Component +public class IdentificationAspect { + + @Before(value = "@annotation(Identification) && args(loggedInMemberId, memberId, ..)", argNames = "loggedInMemberId, memberId") + public void checkIdentification( + final Long loggedInMemberId, + final Long memberId + ) { + if (!loggedInMemberId.equals(memberId)) { + throw new MemberException(MEMBER_MISMATCH, loggedInMemberId, memberId); + } + } +} diff --git a/src/main/java/kr/pickple/back/member/controller/MemberController.java b/src/main/java/kr/pickple/back/member/controller/MemberController.java index ae0ebc8f..51147e64 100644 --- a/src/main/java/kr/pickple/back/member/controller/MemberController.java +++ b/src/main/java/kr/pickple/back/member/controller/MemberController.java @@ -23,11 +23,15 @@ import kr.pickple.back.auth.config.resolver.SignUp; import kr.pickple.back.common.domain.RegistrationStatus; import kr.pickple.back.crew.dto.response.CrewProfileResponse; +import kr.pickple.back.crew.service.CrewMemberService; +import kr.pickple.back.game.service.GameMemberService; +import kr.pickple.back.member.domain.NewMember; +import kr.pickple.back.member.dto.mapper.MemberRequestMapper; import kr.pickple.back.member.dto.request.MemberCreateRequest; import kr.pickple.back.member.dto.response.AuthenticatedMemberResponse; -import kr.pickple.back.member.dto.response.CrewMemberRegistrationStatusResponse; -import kr.pickple.back.member.dto.response.GameMemberRegistrationStatusResponse; -import kr.pickple.back.member.dto.response.MemberGameResponse; +import kr.pickple.back.crew.dto.response.CrewMemberRegistrationStatusResponse; +import kr.pickple.back.game.dto.response.GameMemberRegistrationStatusResponse; +import kr.pickple.back.game.dto.response.MemberGameResponse; import kr.pickple.back.member.dto.response.MemberProfileResponse; import kr.pickple.back.member.exception.MemberException; import kr.pickple.back.member.service.MemberService; @@ -39,6 +43,8 @@ public class MemberController { private final MemberService memberService; + private final CrewMemberService crewMemberService; + private final GameMemberService gameMemberService; private final JwtProperties jwtProperties; @PostMapping @@ -54,7 +60,9 @@ public ResponseEntity createMember( throw new MemberException(MEMBER_SIGNUP_OAUTH_SUBJECT_INVALID, requestOauthSubject); } - final AuthenticatedMemberResponse authenticatedMemberResponse = memberService.createMember(memberCreateRequest); + final NewMember newMember = MemberRequestMapper.mapToNewMemberDomain(memberCreateRequest); + final AuthenticatedMemberResponse authenticatedMemberResponse = memberService.createMember(newMember); + final String refreshToken = authenticatedMemberResponse.getRefreshToken(); if (refreshToken != null) { @@ -79,62 +87,67 @@ public ResponseEntity findMemberProfileById(@PathVariable .body(memberService.findMemberProfileById(memberId)); } + @Identification @GetMapping("/{memberId}/crews") - public ResponseEntity> findAllCrewsByMemberId( + public ResponseEntity> findAllJoinedCrews( @Login final Long loggedInMemberId, @PathVariable final Long memberId, @RequestParam final RegistrationStatus status ) { return ResponseEntity.status(OK) - .body(memberService.findAllCrewsByMemberId(loggedInMemberId, memberId, status)); + .body(crewMemberService.findAllJoinedCrews(memberId, status)); } + @Identification @GetMapping("/{memberId}/created-crews") - public ResponseEntity> findCreatedCrewsByMemberId( + public ResponseEntity> findCreatedCrews( @Login final Long loggedInMemberId, @PathVariable final Long memberId ) { return ResponseEntity.status(OK) - .body(memberService.findCreatedCrewsByMemberId(loggedInMemberId, memberId)); + .body(crewMemberService.findCreatedCrews(memberId)); + } + + @Identification + @GetMapping("/{memberId}/crews/{crewId}/registration-status") + public ResponseEntity findRegistrationStatusForCrew( + @Login final Long loggedInMemberId, + @PathVariable final Long memberId, + @PathVariable final Long crewId + ) { + return ResponseEntity.status(OK) + .body(crewMemberService.findRegistrationStatusForCrew(memberId, crewId)); } + @Identification @GetMapping("/{memberId}/games") - public ResponseEntity> findAllMemberGames( + public ResponseEntity> findAllJoinedGames( @Login final Long loggedInMemberId, @PathVariable final Long memberId, @RequestParam final RegistrationStatus status ) { return ResponseEntity.status(OK) - .body(memberService.findAllMemberGames(loggedInMemberId, memberId, status)); + .body(gameMemberService.findAllJoinedGames(memberId, status)); } + @Identification @GetMapping("/{memberId}/created-games") public ResponseEntity> findAllCreatedGames( @Login final Long loggedInMemberId, @PathVariable final Long memberId ) { return ResponseEntity.status(OK) - .body(memberService.findAllCreatedGames(loggedInMemberId, memberId)); + .body(gameMemberService.findAllCreatedGames(memberId)); } + @Identification @GetMapping("/{memberId}/games/{gameId}/registration-status") - public ResponseEntity findMemberRegistrationStatusForGame( + public ResponseEntity findRegistrationStatusForGame( @Login final Long loggedInMemberId, @PathVariable final Long memberId, @PathVariable final Long gameId ) { return ResponseEntity.status(OK) - .body(memberService.findMemberRegistrationStatusForGame(loggedInMemberId, memberId, gameId)); + .body(gameMemberService.findRegistrationStatusForGame(memberId, gameId)); } - - @GetMapping("/{memberId}/crews/{crewId}/registration-status") - public ResponseEntity findMemberRegistrationStatusForCrew( - @Login final Long loggedInMemberId, - @PathVariable final Long memberId, - @PathVariable final Long crewId - ) { - return ResponseEntity.status(OK) - .body(memberService.findMemberRegistrationStatusForCrew(loggedInMemberId, memberId, crewId)); - } - } diff --git a/src/main/java/kr/pickple/back/member/domain/Member.java b/src/main/java/kr/pickple/back/member/domain/Member.java index a14e15e8..b5684aa9 100644 --- a/src/main/java/kr/pickple/back/member/domain/Member.java +++ b/src/main/java/kr/pickple/back/member/domain/Member.java @@ -2,172 +2,37 @@ import static kr.pickple.back.member.exception.MemberExceptionCode.*; -import java.text.MessageFormat; import java.util.List; -import jakarta.persistence.Column; -import jakarta.persistence.Convert; -import jakarta.persistence.Embedded; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.validation.constraints.NotNull; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.auth.domain.oauth.OauthProvider; -import kr.pickple.back.common.domain.BaseEntity; -import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.crew.domain.CrewMember; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.domain.GameMember; import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.util.MemberStatusConverter; import kr.pickple.back.position.domain.Position; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.NoArgsConstructor; -@Entity @Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@EqualsAndHashCode(of = "id", callSuper = false) -public class Member extends BaseEntity { +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@EqualsAndHashCode(of = "memberId") +public class Member { public static final List MANNER_SCORE_POINT_RANGE = List.of(-1, 0, 1); - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @NotNull - @Column(unique = true, length = 100) + private Long memberId; private String email; - - @NotNull - @Column(unique = true, length = 20) private String nickname; - - @Column(length = 1000) private String introduction; - - @NotNull - @Column(length = 300) private String profileImageUrl; + private Integer mannerScore; + private Integer mannerScoreCount; + private String addressDepth1Name; + private String addressDepth2Name; + private List positions; - @NotNull - @Convert(converter = MemberStatusConverter.class) - @Column(length = 10) - private MemberStatus status; - - @NotNull - private Integer mannerScore = 0; - - @NotNull - private Integer mannerScoreCount = 0; - - @NotNull - @Column(unique = true) - private Long oauthId; - - @NotNull - @Enumerated(value = EnumType.STRING) - private OauthProvider oauthProvider; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "address_depth1_id") - private AddressDepth1 addressDepth1; - - @NotNull - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "address_depth2_id") - private AddressDepth2 addressDepth2; - - @Embedded - private MemberPositions memberPositions = new MemberPositions(); - - @Embedded - private MemberCrews memberCrews = new MemberCrews(); - - @Embedded - private MemberGames memberGames = new MemberGames(); - - @Builder - private Member( - final String email, - final String nickname, - final String profileImageUrl, - final MemberStatus status, - final Long oauthId, - final OauthProvider oauthProvider, - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2, - final List positions - ) { - this.email = email; - this.nickname = nickname; - this.profileImageUrl = profileImageUrl; - this.status = status; - this.oauthId = oauthId; - this.oauthProvider = oauthProvider; - this.addressDepth1 = addressDepth1; - this.addressDepth2 = addressDepth2; - - setDefaultIntroduction(nickname); - updateMemberPositions(positions); - } - - private void updateMemberPositions(final List positions) { - memberPositions.updateMemberPositions(this, positions); - } - - private void setDefaultIntroduction(final String nickname) { - this.introduction = MessageFormat.format("안녕하세요. {0}입니다.", nickname); - } - - public RegistrationStatus findCrewRegistrationStatus(final Crew crew) { - return memberCrews.findCrewRegistrationStatus(crew); - } - - public RegistrationStatus findGameRegistrationStatus(final Game game) { - return memberGames.findGameRegistrationStatus(game); - } - - public Boolean isAlreadyReviewDoneInGame(final Game game) { - return memberGames.isAlreadyReviewDoneInGame(game); - } - - public List getCrewsByStatus(RegistrationStatus status) { - return memberCrews.getCrewsByStatus(status); - } - - public Long getCreatedCrewsCount() { - return memberCrews.getCreatedCrewsCountByMember(this); - } - - public List getMemberGamesByStatus(final RegistrationStatus status) { - return memberGames.getMemberGamesByStatus(status); - } - - public List getCreatedCrews() { - return memberCrews.getCreatedCrewsByMember(this); - } - - public List getCreatedMemberGames() { - return memberGames.getCreatedMemberGames(this); - } - - public List getPositions() { - return memberPositions.getPositions(); + public Boolean isIdMatched(final Long memberId) { + return this.memberId.equals(memberId); } public void updateMannerScore(final Integer mannerScorePoint) { @@ -180,12 +45,4 @@ public void updateMannerScore(final Integer mannerScorePoint) { throw new MemberException(MEMBER_UPDATING_MANNER_SCORE_POINT_OUT_OF_RANGE, mannerScorePoint); } - - public void addMemberCrew(final CrewMember memberCrew) { - memberCrews.addMemberCrew(memberCrew); - } - - public void addMemberGame(final GameMember memberGame) { - memberGames.addMemberGame(memberGame); - } } diff --git a/src/main/java/kr/pickple/back/member/domain/MemberCrews.java b/src/main/java/kr/pickple/back/member/domain/MemberCrews.java deleted file mode 100644 index 39bcc4df..00000000 --- a/src/main/java/kr/pickple/back/member/domain/MemberCrews.java +++ /dev/null @@ -1,51 +0,0 @@ -package kr.pickple.back.member.domain; - -import java.util.ArrayList; -import java.util.List; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.OneToMany; -import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.crew.domain.CrewMember; - -@Embeddable -public class MemberCrews { - - @OneToMany(mappedBy = "member", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) - private List memberCrews = new ArrayList<>(); - - public RegistrationStatus findCrewRegistrationStatus(final Crew crew) { - return memberCrews.stream() - .filter(memberCrew -> memberCrew.equalsCrew(crew)) - .findFirst() - .map(CrewMember::getStatus) - .orElse(RegistrationStatus.NONE); - } - - public List getCrewsByStatus(final RegistrationStatus status) { - return memberCrews.stream() - .filter(memberCrew -> memberCrew.equalsStatus(status)) - .map(CrewMember::getCrew) - .toList(); - } - - public List getCreatedCrewsByMember(final Member member) { - return memberCrews.stream() - .map(CrewMember::getCrew) - .filter(crew -> crew.isLeader(member)) - .toList(); - } - - public void addMemberCrew(final CrewMember memberCrew) { - memberCrews.add(memberCrew); - } - - public Long getCreatedCrewsCountByMember(final Member member) { - return memberCrews.stream() - .map(CrewMember::getCrew) - .filter(crew -> crew.isLeader(member)) - .count(); - } -} diff --git a/src/main/java/kr/pickple/back/member/domain/MemberGames.java b/src/main/java/kr/pickple/back/member/domain/MemberGames.java deleted file mode 100644 index 0ab5385c..00000000 --- a/src/main/java/kr/pickple/back/member/domain/MemberGames.java +++ /dev/null @@ -1,56 +0,0 @@ -package kr.pickple.back.member.domain; - -import static java.lang.Boolean.*; - -import java.util.ArrayList; -import java.util.List; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.OneToMany; -import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.domain.GameMember; - -@Embeddable -public class MemberGames { - - @OneToMany(mappedBy = "member", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) - private List memberGames = new ArrayList<>(); - - public RegistrationStatus findGameRegistrationStatus(final Game game) { - return memberGames.stream() - .filter(memberGame -> memberGame.equalsGame(game)) - .findFirst() - .map(GameMember::getStatus) - .orElse(RegistrationStatus.NONE); - } - - public Boolean isAlreadyReviewDoneInGame(final Game game) { - return memberGames.stream() - .filter(memberGame -> memberGame.equalsGame(game)) - .findFirst() - .map(GameMember::isAlreadyReviewDone) - .orElse(FALSE); - } - - public List getMemberGamesByStatus(final RegistrationStatus status) { - return memberGames.stream() - .filter(memberGame -> memberGame.equalsStatus(status)) - .toList(); - } - - public List getCreatedMemberGames(final Member member) { - return memberGames.stream() - .filter(gameMember -> { - final Game game = gameMember.getGame(); - - return game.isHost(member); - }) - .toList(); - } - - public void addMemberGame(final GameMember memberGame) { - memberGames.add(memberGame); - } -} diff --git a/src/main/java/kr/pickple/back/member/domain/MemberPositions.java b/src/main/java/kr/pickple/back/member/domain/MemberPositions.java deleted file mode 100644 index eeac9935..00000000 --- a/src/main/java/kr/pickple/back/member/domain/MemberPositions.java +++ /dev/null @@ -1,52 +0,0 @@ -package kr.pickple.back.member.domain; - -import static kr.pickple.back.member.exception.MemberExceptionCode.*; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -import org.hibernate.annotations.BatchSize; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Embeddable; -import jakarta.persistence.OneToMany; -import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.position.domain.Position; - -@Embeddable -public class MemberPositions { - - @BatchSize(size = 1000) - @OneToMany(mappedBy = "member", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) - private List memberPositions = new ArrayList<>(); - - public List getPositions() { - return memberPositions.stream() - .map(MemberPosition::getPosition) - .toList(); - } - - public void updateMemberPositions(final Member member, final List positions) { - validateIsDuplicatedPositions(positions); - - positions.stream() - .map(position -> buildMemberPosition(member, position)) - .forEach(memberPositions::add); - } - - private void validateIsDuplicatedPositions(final List positions) { - long distinctPositionsSize = new HashSet<>(positions).size(); - - if (distinctPositionsSize < positions.size()) { - throw new MemberException(MEMBER_POSITIONS_IS_DUPLICATED, positions); - } - } - - private MemberPosition buildMemberPosition(final Member member, final Position position) { - return MemberPosition.builder() - .position(position) - .member(member) - .build(); - } -} diff --git a/src/main/java/kr/pickple/back/member/domain/MemberProfile.java b/src/main/java/kr/pickple/back/member/domain/MemberProfile.java new file mode 100644 index 00000000..50b66928 --- /dev/null +++ b/src/main/java/kr/pickple/back/member/domain/MemberProfile.java @@ -0,0 +1,32 @@ +package kr.pickple.back.member.domain; + +import java.util.List; + +import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.position.domain.Position; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class MemberProfile { + + private Long memberId; + private String email; + private String nickname; + private String introduction; + private String profileImageUrl; + private Integer mannerScore; + private Integer mannerScoreCount; + private String addressDepth1Name; + private String addressDepth2Name; + private List positions; + private List joinedCrews; + + public void updateJoinedCrews(final List joinedCrews) { + this.joinedCrews = joinedCrews; + } +} diff --git a/src/main/java/kr/pickple/back/member/domain/NewMember.java b/src/main/java/kr/pickple/back/member/domain/NewMember.java new file mode 100644 index 00000000..35a81b69 --- /dev/null +++ b/src/main/java/kr/pickple/back/member/domain/NewMember.java @@ -0,0 +1,36 @@ +package kr.pickple.back.member.domain; + +import java.util.List; + +import kr.pickple.back.auth.domain.oauth.OauthProvider; +import kr.pickple.back.auth.domain.token.AuthTokens; +import kr.pickple.back.position.domain.Position; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class NewMember { + + private Long memberId; + private AuthTokens authTokens; + private String nickname; + private String profileImageUrl; + private String email; + private List positions; + private Long oauthId; + private OauthProvider oauthProvider; + private String addressDepth1Name; + private String addressDepth2Name; + + public void updateMemberId(final Long memberId) { + this.memberId = memberId; + } + + public void updateAuthTokens(final AuthTokens authTokens) { + this.authTokens = authTokens; + } +} diff --git a/src/main/java/kr/pickple/back/member/dto/mapper/MemberRequestMapper.java b/src/main/java/kr/pickple/back/member/dto/mapper/MemberRequestMapper.java new file mode 100644 index 00000000..c6d74669 --- /dev/null +++ b/src/main/java/kr/pickple/back/member/dto/mapper/MemberRequestMapper.java @@ -0,0 +1,23 @@ +package kr.pickple.back.member.dto.mapper; + +import kr.pickple.back.member.domain.NewMember; +import kr.pickple.back.member.dto.request.MemberCreateRequest; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class MemberRequestMapper { + + public static NewMember mapToNewMemberDomain(final MemberCreateRequest memberCreateRequest) { + return NewMember.builder() + .email(memberCreateRequest.getEmail()) + .nickname(memberCreateRequest.getNickname()) + .profileImageUrl(memberCreateRequest.getProfileImageUrl()) + .positions(memberCreateRequest.getPositions()) + .oauthId(memberCreateRequest.getOauthId()) + .oauthProvider(memberCreateRequest.getOauthProvider()) + .addressDepth1Name(memberCreateRequest.getAddressDepth1()) + .addressDepth2Name(memberCreateRequest.getAddressDepth2()) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/member/dto/mapper/MemberResponseMapper.java b/src/main/java/kr/pickple/back/member/dto/mapper/MemberResponseMapper.java new file mode 100644 index 00000000..df5a44ce --- /dev/null +++ b/src/main/java/kr/pickple/back/member/dto/mapper/MemberResponseMapper.java @@ -0,0 +1,75 @@ +package kr.pickple.back.member.dto.mapper; + +import java.util.List; + +import kr.pickple.back.crew.dto.mapper.CrewResponseMapper; +import kr.pickple.back.crew.dto.response.CrewResponse; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.domain.MemberProfile; +import kr.pickple.back.member.domain.NewMember; +import kr.pickple.back.member.dto.response.AuthenticatedMemberResponse; +import kr.pickple.back.member.dto.response.MemberProfileResponse; +import kr.pickple.back.member.dto.response.MemberResponse; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class MemberResponseMapper { + + public static AuthenticatedMemberResponse mapToAuthenticatedMemberResponseDto(final NewMember newMember) { + return AuthenticatedMemberResponse.builder() + .accessToken(newMember.getAuthTokens().getAccessToken()) + .refreshToken(newMember.getAuthTokens().getRefreshToken()) + .id(newMember.getMemberId()) + .nickname(newMember.getNickname()) + .profileImageUrl(newMember.getProfileImageUrl()) + .email(newMember.getEmail()) + .oauthId(newMember.getOauthId()) + .oauthProvider(newMember.getOauthProvider()) + .addressDepth1(newMember.getAddressDepth1Name()) + .addressDepth2(newMember.getAddressDepth2Name()) + .build(); + } + + public static MemberProfileResponse mapToMemberProfileResponseDto(final MemberProfile memberProfile) { + final List joinedCrewResponses = memberProfile.getJoinedCrews() + .stream() + .map(CrewResponseMapper::mapToCrewResponseDto) + .toList(); + + return MemberProfileResponse.builder() + .id(memberProfile.getMemberId()) + .email(memberProfile.getEmail()) + .nickname(memberProfile.getNickname()) + .introduction(memberProfile.getIntroduction()) + .profileImageUrl(memberProfile.getProfileImageUrl()) + .mannerScore(memberProfile.getMannerScore()) + .mannerScoreCount(memberProfile.getMannerScoreCount()) + .addressDepth1(memberProfile.getAddressDepth1Name()) + .addressDepth2(memberProfile.getAddressDepth2Name()) + .positions(memberProfile.getPositions()) + .crews(joinedCrewResponses) + .build(); + } + + public static MemberResponse mapToMemberResponseDto(final Member member) { + return MemberResponse.builder() + .id(member.getMemberId()) + .email(member.getEmail()) + .nickname(member.getNickname()) + .introduction(member.getIntroduction()) + .profileImageUrl(member.getProfileImageUrl()) + .mannerScore(member.getMannerScore()) + .mannerScoreCount(member.getMannerScoreCount()) + .addressDepth1(member.getAddressDepth1Name()) + .addressDepth2(member.getAddressDepth2Name()) + .positions(member.getPositions()) + .build(); + } + + public static List mapToMemberResponseDtos(final List members) { + return members.stream() + .map(MemberResponseMapper::mapToMemberResponseDto) + .toList(); + } +} diff --git a/src/main/java/kr/pickple/back/member/dto/request/MemberCreateRequest.java b/src/main/java/kr/pickple/back/member/dto/request/MemberCreateRequest.java index 114e928d..3f21b4d1 100644 --- a/src/main/java/kr/pickple/back/member/dto/request/MemberCreateRequest.java +++ b/src/main/java/kr/pickple/back/member/dto/request/MemberCreateRequest.java @@ -5,10 +5,7 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; -import kr.pickple.back.address.dto.response.MainAddressResponse; import kr.pickple.back.auth.domain.oauth.OauthProvider; -import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.domain.MemberStatus; import kr.pickple.back.position.domain.Position; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -46,18 +43,4 @@ public class MemberCreateRequest { @NotBlank(message = "주소2는 null이거나 빈 문자열이거나 공백일 수 없음") private String addressDepth2; - - public Member toEntity(final MainAddressResponse mainAddressResponse) { - return Member.builder() - .email(email) - .nickname(nickname) - .profileImageUrl(profileImageUrl) - .status(MemberStatus.ACTIVE) - .oauthId(oauthId) - .oauthProvider(oauthProvider) - .addressDepth1(mainAddressResponse.getAddressDepth1()) - .addressDepth2(mainAddressResponse.getAddressDepth2()) - .positions(positions) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/member/dto/response/AuthenticatedMemberResponse.java b/src/main/java/kr/pickple/back/member/dto/response/AuthenticatedMemberResponse.java index 9e86ba5e..3f5f89a8 100644 --- a/src/main/java/kr/pickple/back/member/dto/response/AuthenticatedMemberResponse.java +++ b/src/main/java/kr/pickple/back/member/dto/response/AuthenticatedMemberResponse.java @@ -1,9 +1,10 @@ package kr.pickple.back.member.dto.response; +import kr.pickple.back.address.domain.MainAddress; import kr.pickple.back.auth.domain.oauth.OauthMember; import kr.pickple.back.auth.domain.oauth.OauthProvider; import kr.pickple.back.auth.domain.token.AuthTokens; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.repository.entity.MemberEntity; import lombok.Builder; import lombok.Getter; @@ -46,7 +47,11 @@ private AuthenticatedMemberResponse( this.addressDepth2 = addressDepth2; } - public static AuthenticatedMemberResponse of(final Member member, final AuthTokens authTokens) { + public static AuthenticatedMemberResponse of( + final MemberEntity member, + final AuthTokens authTokens, + final MainAddress mainAddress + ) { return AuthenticatedMemberResponse.builder() .accessToken(authTokens.getAccessToken()) .refreshToken(authTokens.getRefreshToken()) @@ -56,8 +61,8 @@ public static AuthenticatedMemberResponse of(final Member member, final AuthToke .email(member.getEmail()) .oauthId(member.getOauthId()) .oauthProvider(member.getOauthProvider()) - .addressDepth1(member.getAddressDepth1().getName()) - .addressDepth2(member.getAddressDepth2().getName()) + .addressDepth1(mainAddress.getAddressDepth1Name()) + .addressDepth2(mainAddress.getAddressDepth2Name()) .build(); } diff --git a/src/main/java/kr/pickple/back/member/dto/response/MemberGameResponse.java b/src/main/java/kr/pickple/back/member/dto/response/MemberGameResponse.java deleted file mode 100644 index f2eafd70..00000000 --- a/src/main/java/kr/pickple/back/member/dto/response/MemberGameResponse.java +++ /dev/null @@ -1,70 +0,0 @@ -package kr.pickple.back.member.dto.response; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; - -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.domain.GameMember; -import kr.pickple.back.game.domain.GameStatus; -import kr.pickple.back.position.domain.Position; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -@AllArgsConstructor(access = AccessLevel.PRIVATE) -public class MemberGameResponse { - - private Long id; - private String content; - private LocalDate playDate; - private LocalTime playStartTime; - private LocalTime playEndTime; - private Integer playTimeMinutes; - private String mainAddress; - private String detailAddress; - private Double latitude; - private Double longitude; - private GameStatus status; - private Boolean isReviewDone; - private Integer viewCount; - private Integer cost; - private Integer memberCount; - private Integer maxMemberCount; - private MemberResponse host; - private String addressDepth1; - private String addressDepth2; - private List positions; - private List members; - - public static MemberGameResponse of(final GameMember gameMember, final List memberResponses) { - final Game game = gameMember.getGame(); - - return MemberGameResponse.builder() - .id(game.getId()) - .content(game.getContent()) - .playDate(game.getPlayDate()) - .playStartTime(game.getPlayStartTime()) - .playEndTime(game.getPlayEndTime()) - .playTimeMinutes(game.getPlayTimeMinutes()) - .mainAddress(game.getMainAddress()) - .detailAddress(game.getDetailAddress()) - .latitude(game.getPoint().getY()) - .longitude(game.getPoint().getX()) - .status(game.getStatus()) - .isReviewDone(gameMember.isAlreadyReviewDone()) - .viewCount(game.getViewCount()) - .cost(game.getCost()) - .memberCount(game.getMemberCount()) - .maxMemberCount(game.getMaxMemberCount()) - .host(MemberResponse.from(game.getHost())) - .addressDepth1(game.getAddressDepth1().getName()) - .addressDepth2(game.getAddressDepth2().getName()) - .positions(game.getPositions()) - .members(memberResponses) - .build(); - } -} diff --git a/src/main/java/kr/pickple/back/member/dto/response/MemberProfileResponse.java b/src/main/java/kr/pickple/back/member/dto/response/MemberProfileResponse.java index 038f721d..e859dc2a 100644 --- a/src/main/java/kr/pickple/back/member/dto/response/MemberProfileResponse.java +++ b/src/main/java/kr/pickple/back/member/dto/response/MemberProfileResponse.java @@ -3,7 +3,6 @@ import java.util.List; import kr.pickple.back.crew.dto.response.CrewResponse; -import kr.pickple.back.member.domain.Member; import kr.pickple.back.position.domain.Position; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -26,20 +25,4 @@ public class MemberProfileResponse { private String addressDepth2; private List positions; private List crews; - - public static MemberProfileResponse of(final Member member, final List crewResponses) { - return MemberProfileResponse.builder() - .id(member.getId()) - .email(member.getEmail()) - .nickname(member.getNickname()) - .introduction(member.getIntroduction()) - .profileImageUrl(member.getProfileImageUrl()) - .mannerScore(member.getMannerScore()) - .mannerScoreCount(member.getMannerScoreCount()) - .addressDepth1(member.getAddressDepth1().getName()) - .addressDepth2(member.getAddressDepth2().getName()) - .positions(member.getPositions()) - .crews(crewResponses) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/member/dto/response/MemberResponse.java b/src/main/java/kr/pickple/back/member/dto/response/MemberResponse.java index 841e8c77..95a487f1 100644 --- a/src/main/java/kr/pickple/back/member/dto/response/MemberResponse.java +++ b/src/main/java/kr/pickple/back/member/dto/response/MemberResponse.java @@ -2,7 +2,6 @@ import java.util.List; -import kr.pickple.back.member.domain.Member; import kr.pickple.back.position.domain.Position; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -24,19 +23,4 @@ public class MemberResponse { private String addressDepth1; private String addressDepth2; private List positions; - - public static MemberResponse from(final Member member) { - return MemberResponse.builder() - .id(member.getId()) - .email(member.getEmail()) - .nickname(member.getNickname()) - .introduction(member.getIntroduction()) - .profileImageUrl(member.getProfileImageUrl()) - .mannerScore(member.getMannerScore()) - .mannerScoreCount(member.getMannerScoreCount()) - .addressDepth1(member.getAddressDepth1().getName()) - .addressDepth2(member.getAddressDepth2().getName()) - .positions(member.getPositions()) - .build(); - } } diff --git a/src/main/java/kr/pickple/back/member/implement/MemberMapper.java b/src/main/java/kr/pickple/back/member/implement/MemberMapper.java new file mode 100644 index 00000000..918f1fcc --- /dev/null +++ b/src/main/java/kr/pickple/back/member/implement/MemberMapper.java @@ -0,0 +1,71 @@ +package kr.pickple.back.member.implement; + +import java.util.List; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.domain.MemberProfile; +import kr.pickple.back.member.domain.MemberStatus; +import kr.pickple.back.member.domain.NewMember; +import kr.pickple.back.member.repository.entity.MemberEntity; +import kr.pickple.back.position.domain.Position; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class MemberMapper { + + public static MemberEntity mapToMemberEntity(final NewMember newMember, final MainAddress mainAddress) { + return MemberEntity.builder() + .email(newMember.getEmail()) + .nickname(newMember.getNickname()) + .profileImageUrl(newMember.getProfileImageUrl()) + .status(MemberStatus.ACTIVE) + .oauthId(newMember.getOauthId()) + .oauthProvider(newMember.getOauthProvider()) + .addressDepth1Id(mainAddress.getAddressDepth1Id()) + .addressDepth2Id(mainAddress.getAddressDepth2Id()) + .build(); + } + + public static MemberProfile mapToMemberProfileDomain( + final MemberEntity member, + final MainAddress mainAddress, + final List positions, + final List crews + ) { + + return MemberProfile.builder() + .memberId(member.getId()) + .email(member.getEmail()) + .nickname(member.getNickname()) + .profileImageUrl(member.getProfileImageUrl()) + .mannerScore(member.getMannerScore()) + .mannerScoreCount(member.getMannerScoreCount()) + .addressDepth1Name(mainAddress.getAddressDepth1Name()) + .addressDepth2Name(mainAddress.getAddressDepth2Name()) + .positions(positions) + .joinedCrews(crews) + .build(); + } + + public static Member mapToMemberDomain( + final MemberEntity memberEntity, + final MainAddress mainAddress, + final List positions + ) { + return Member.builder() + .memberId(memberEntity.getId()) + .email(memberEntity.getEmail()) + .nickname(memberEntity.getNickname()) + .introduction(memberEntity.getIntroduction()) + .profileImageUrl(memberEntity.getProfileImageUrl()) + .mannerScore(memberEntity.getMannerScore()) + .mannerScoreCount(memberEntity.getMannerScoreCount()) + .addressDepth1Name(mainAddress.getAddressDepth1Name()) + .addressDepth2Name(mainAddress.getAddressDepth2Name()) + .positions(positions) + .build(); + } +} diff --git a/src/main/java/kr/pickple/back/member/implement/MemberReader.java b/src/main/java/kr/pickple/back/member/implement/MemberReader.java new file mode 100644 index 00000000..9e8d1658 --- /dev/null +++ b/src/main/java/kr/pickple/back/member/implement/MemberReader.java @@ -0,0 +1,112 @@ +package kr.pickple.back.member.implement; + +import static kr.pickple.back.common.domain.RegistrationStatus.*; +import static kr.pickple.back.crew.exception.CrewExceptionCode.*; +import static kr.pickple.back.member.exception.MemberExceptionCode.*; + +import java.util.List; +import java.util.Optional; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.implement.AddressReader; +import kr.pickple.back.crew.domain.Crew; +import kr.pickple.back.crew.exception.CrewException; +import kr.pickple.back.crew.implement.CrewMapper; +import kr.pickple.back.crew.repository.CrewMemberRepository; +import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.crew.repository.entity.CrewMemberEntity; +import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.domain.MemberProfile; +import kr.pickple.back.member.exception.MemberException; +import kr.pickple.back.member.repository.MemberPositionRepository; +import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.repository.entity.MemberEntity; +import kr.pickple.back.member.repository.entity.MemberPositionEntity; +import kr.pickple.back.position.domain.Position; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MemberReader { + + private final AddressReader addressReader; + private final MemberRepository memberRepository; + private final MemberPositionRepository memberPositionRepository; + private final CrewRepository crewRepository; + private final CrewMemberRepository crewMemberRepository; + + public Boolean existsByMemberId(final Long memberId) { + return memberRepository.existsById(memberId); + } + + public Optional readByOauthId(final Long oauthId) { + return memberRepository.findByOauthId(oauthId); + } + + public Member readByMemberId(final Long memberId) { + final MemberEntity memberEntity = readEntityByMemberId(memberId); + + final MainAddress mainAddress = addressReader.readMainAddressByIds( + memberEntity.getAddressDepth1Id(), + memberEntity.getAddressDepth2Id() + ); + + final List positions = memberPositionRepository.findAllByMemberId(memberId) + .stream() + .map(MemberPositionEntity::getPosition) + .toList(); + + return MemberMapper.mapToMemberDomain(memberEntity, mainAddress, positions); + } + + public MemberProfile readProfileByMemberId(final Long memberId) { + final MemberEntity member = readEntityByMemberId(memberId); + final MainAddress mainAddress = addressReader.readMainAddressByIds( + member.getAddressDepth1Id(), + member.getAddressDepth2Id() + ); + final List positions = readPositionsByMemberId(memberId); + + final List crews = readCrewsByMemberId(member.getId()); + + return MemberMapper.mapToMemberProfileDomain(member, mainAddress, positions, crews); + } + + private MemberEntity readEntityByMemberId(final Long memberId) { + return memberRepository.findById(memberId) + .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); + } + + private List readPositionsByMemberId(final Long memberId) { + return memberPositionRepository.findAllByMemberId(memberId) + .stream() + .map(MemberPositionEntity::getPosition) + .toList(); + } + + private List readCrewsByMemberId(final Long memberId) { + return crewMemberRepository.findAllByMemberIdAndStatus(memberId, CONFIRMED) + .stream() + .map(this::readCrewEntityByCrewId) + .map(crewEntity -> { + final MainAddress mainAddress = addressReader.readMainAddressByIds( + crewEntity.getAddressDepth1Id(), + crewEntity.getAddressDepth2Id() + ); + final Member leader = readByMemberId(crewEntity.getLeaderId()); + + return CrewMapper.mapCrewEntityToDomain(crewEntity, mainAddress, leader); + }) + .toList(); + } + + private CrewEntity readCrewEntityByCrewId(final CrewMemberEntity crewMember) { + return crewRepository.findById(crewMember.getCrewId()) + .orElseThrow(() -> new CrewException(CREW_NOT_FOUND, crewMember.getCrewId())); + } +} diff --git a/src/main/java/kr/pickple/back/member/implement/MemberWriter.java b/src/main/java/kr/pickple/back/member/implement/MemberWriter.java new file mode 100644 index 00000000..fe50918f --- /dev/null +++ b/src/main/java/kr/pickple/back/member/implement/MemberWriter.java @@ -0,0 +1,75 @@ +package kr.pickple.back.member.implement; + +import static kr.pickple.back.member.exception.MemberExceptionCode.*; + +import java.util.List; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.implement.AddressReader; +import kr.pickple.back.auth.implement.TokenManager; +import kr.pickple.back.member.domain.NewMember; +import kr.pickple.back.member.exception.MemberException; +import kr.pickple.back.member.repository.MemberPositionJdbcRepository; +import kr.pickple.back.member.repository.MemberPositionRepository; +import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.repository.entity.MemberEntity; +import kr.pickple.back.position.domain.Position; +import lombok.RequiredArgsConstructor; + +@Component +@Transactional +@RequiredArgsConstructor +public class MemberWriter { + + private final TokenManager tokenManager; + private final AddressReader addressReader; + private final MemberRepository memberRepository; + private final MemberPositionRepository memberPositionRepository; + private final MemberPositionJdbcRepository memberPositionJdbcRepository; + + public NewMember create(final NewMember newMember) { + validateIsDuplicatedMemberInfo(newMember); + + final MainAddress mainAddress = addressReader.readMainAddressByNames( + newMember.getAddressDepth1Name(), + newMember.getAddressDepth2Name() + ); + + final MemberEntity memberEntity = MemberMapper.mapToMemberEntity(newMember, mainAddress); + final MemberEntity savedMemberEntity = memberRepository.save(memberEntity); + + newMember.updateMemberId(savedMemberEntity.getId()); + setPositionsToMember(newMember.getPositions(), newMember.getMemberId()); + + return newMember; + } + + private void validateIsDuplicatedMemberInfo(final NewMember newMember) { + final String email = newMember.getEmail(); + final String nickname = newMember.getNickname(); + final Long oauthId = newMember.getOauthId(); + + if (memberRepository.existsByEmailOrNicknameOrOauthId(email, nickname, oauthId)) { + throw new MemberException(MEMBER_IS_EXISTED, email, nickname, oauthId); + } + } + + private void setPositionsToMember(final List positions, final Long memberId) { + validateIsDuplicatedPositions(positions); + + memberPositionJdbcRepository.creatMemberPositions(positions, memberId); + } + + private void validateIsDuplicatedPositions(final List positions) { + final Long distinctPositionsSize = positions.stream() + .distinct() + .count(); + + if (distinctPositionsSize != positions.size()) { + throw new MemberException(MEMBER_POSITIONS_IS_DUPLICATED, positions); + } + } +} diff --git a/src/main/java/kr/pickple/back/member/repository/MemberPositionJdbcRepository.java b/src/main/java/kr/pickple/back/member/repository/MemberPositionJdbcRepository.java new file mode 100644 index 00000000..d6511d69 --- /dev/null +++ b/src/main/java/kr/pickple/back/member/repository/MemberPositionJdbcRepository.java @@ -0,0 +1,40 @@ +package kr.pickple.back.member.repository; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +import org.springframework.jdbc.core.BatchPreparedStatementSetter; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; + +import kr.pickple.back.position.domain.Position; +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class MemberPositionJdbcRepository { + + private static final String MEMBER_POSITION_INSERT_SQL = "INSERT INTO member_position (position, member_id) VALUES(?, ?)"; + + private final JdbcTemplate jdbcTemplate; + + public void creatMemberPositions(final List positions, final Long memberId) { + jdbcTemplate.batchUpdate( + MEMBER_POSITION_INSERT_SQL, + new BatchPreparedStatementSetter() { + @Override + public void setValues(PreparedStatement ps, int i) throws SQLException { + Position position = positions.get(i); + ps.setString(1, position.getAcronym()); + ps.setLong(2, memberId); + } + + @Override + public int getBatchSize() { + return positions.size(); + } + } + ); + } +} diff --git a/src/main/java/kr/pickple/back/member/repository/MemberPositionRepository.java b/src/main/java/kr/pickple/back/member/repository/MemberPositionRepository.java new file mode 100644 index 00000000..14eaf8cc --- /dev/null +++ b/src/main/java/kr/pickple/back/member/repository/MemberPositionRepository.java @@ -0,0 +1,12 @@ +package kr.pickple.back.member.repository; + +import java.util.List; + +import org.springframework.data.jpa.repository.JpaRepository; + +import kr.pickple.back.member.repository.entity.MemberPositionEntity; + +public interface MemberPositionRepository extends JpaRepository { + + List findAllByMemberId(final Long memberId); +} diff --git a/src/main/java/kr/pickple/back/member/repository/MemberRepository.java b/src/main/java/kr/pickple/back/member/repository/MemberRepository.java index 2e332ab6..9c7dd040 100644 --- a/src/main/java/kr/pickple/back/member/repository/MemberRepository.java +++ b/src/main/java/kr/pickple/back/member/repository/MemberRepository.java @@ -1,13 +1,23 @@ package kr.pickple.back.member.repository; -import kr.pickple.back.member.domain.Member; +import java.util.Optional; + import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; -import java.util.Optional; +import kr.pickple.back.member.repository.entity.MemberEntity; -public interface MemberRepository extends JpaRepository { +public interface MemberRepository extends JpaRepository { Boolean existsByEmailOrNicknameOrOauthId(final String email, final String nickname, final Long oauthId); - Optional findByOauthId(final Long oauthId); + Optional findByOauthId(final Long oauthId); + + @Modifying(clearAutomatically = true) + @Query(""" + update MemberEntity m + set m.mannerScore = :mannerScore, m.mannerScoreCount = :mannerScoreCount + where m.id = :memberId""") + void updateMannerScore(final Long memberId, final Integer mannerScore, final Integer mannerScoreCount); } diff --git a/src/main/java/kr/pickple/back/member/repository/entity/MemberEntity.java b/src/main/java/kr/pickple/back/member/repository/entity/MemberEntity.java new file mode 100644 index 00000000..98283b41 --- /dev/null +++ b/src/main/java/kr/pickple/back/member/repository/entity/MemberEntity.java @@ -0,0 +1,121 @@ +package kr.pickple.back.member.repository.entity; + +import static kr.pickple.back.member.exception.MemberExceptionCode.*; + +import java.text.MessageFormat; +import java.util.List; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import kr.pickple.back.auth.domain.oauth.OauthProvider; +import kr.pickple.back.common.domain.BaseEntity; +import kr.pickple.back.member.domain.MemberStatus; +import kr.pickple.back.member.exception.MemberException; +import kr.pickple.back.member.util.MemberStatusConverter; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "member") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EqualsAndHashCode(of = "id", callSuper = false) +public class MemberEntity extends BaseEntity { + + public static final List MANNER_SCORE_POINT_RANGE = List.of(-1, 0, 1); + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @NotNull + @Column(unique = true, length = 100) + private String email; + + @NotNull + @Column(unique = true, length = 20) + private String nickname; + + @Column(length = 1000) + private String introduction; + + @NotNull + @Column(length = 300) + private String profileImageUrl; + + @NotNull + @Convert(converter = MemberStatusConverter.class) + @Column(length = 10) + private MemberStatus status; + + @NotNull + private Integer mannerScore = 0; + + @NotNull + private Integer mannerScoreCount = 0; + + @NotNull + @Column(unique = true) + private Long oauthId; + + @NotNull + @Enumerated(value = EnumType.STRING) + private OauthProvider oauthProvider; + + @NotNull + @Column(name = "address_depth1_id") + private Long addressDepth1Id; + + @NotNull + @Column(name = "address_depth2_id") + private Long addressDepth2Id; + + @Builder + private MemberEntity( + final String email, + final String nickname, + final String profileImageUrl, + final MemberStatus status, + final Long oauthId, + final OauthProvider oauthProvider, + final Long addressDepth1Id, + final Long addressDepth2Id + ) { + this.email = email; + this.nickname = nickname; + this.profileImageUrl = profileImageUrl; + this.status = status; + this.oauthId = oauthId; + this.oauthProvider = oauthProvider; + this.addressDepth1Id = addressDepth1Id; + this.addressDepth2Id = addressDepth2Id; + + setDefaultIntroduction(nickname); + } + + private void setDefaultIntroduction(final String nickname) { + this.introduction = MessageFormat.format("안녕하세요. {0}입니다.", nickname); + } + + public void updateMannerScore(final Integer mannerScorePoint) { + if (MANNER_SCORE_POINT_RANGE.contains(mannerScorePoint)) { + this.mannerScore += mannerScorePoint; + this.mannerScoreCount += 1; + + return; + } + + throw new MemberException(MEMBER_UPDATING_MANNER_SCORE_POINT_OUT_OF_RANGE, mannerScorePoint); + } +} diff --git a/src/main/java/kr/pickple/back/member/domain/MemberPosition.java b/src/main/java/kr/pickple/back/member/repository/entity/MemberPositionEntity.java similarity index 66% rename from src/main/java/kr/pickple/back/member/domain/MemberPosition.java rename to src/main/java/kr/pickple/back/member/repository/entity/MemberPositionEntity.java index 7a126dd8..6d18f1c8 100644 --- a/src/main/java/kr/pickple/back/member/domain/MemberPosition.java +++ b/src/main/java/kr/pickple/back/member/repository/entity/MemberPositionEntity.java @@ -1,14 +1,12 @@ -package kr.pickple.back.member.domain; +package kr.pickple.back.member.repository.entity; import jakarta.persistence.Column; import jakarta.persistence.Convert; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; import kr.pickple.back.common.domain.BaseEntity; import kr.pickple.back.position.domain.Position; @@ -19,26 +17,26 @@ import lombok.NoArgsConstructor; @Entity +@Getter +@Table(name = "member_position") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class MemberPosition extends BaseEntity { +public class MemberPositionEntity extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Getter @NotNull @Convert(converter = PositionConverter.class) @Column(length = 2) private Position position; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "member_id") - private Member member; + @NotNull + private Long memberId; @Builder - private MemberPosition(final Position position, final Member member) { + private MemberPositionEntity(final Position position, final Long memberId) { this.position = position; - this.member = member; + this.memberId = memberId; } } diff --git a/src/main/java/kr/pickple/back/member/service/MemberService.java b/src/main/java/kr/pickple/back/member/service/MemberService.java index c8c5d224..416ee48f 100644 --- a/src/main/java/kr/pickple/back/member/service/MemberService.java +++ b/src/main/java/kr/pickple/back/member/service/MemberService.java @@ -1,43 +1,17 @@ package kr.pickple.back.member.service; -import static kr.pickple.back.common.domain.RegistrationStatus.*; -import static kr.pickple.back.crew.exception.CrewExceptionCode.*; -import static kr.pickple.back.game.exception.GameExceptionCode.*; -import static kr.pickple.back.member.exception.MemberExceptionCode.*; - -import java.time.LocalDateTime; -import java.util.List; - import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import kr.pickple.back.address.dto.response.MainAddressResponse; -import kr.pickple.back.address.service.AddressService; -import kr.pickple.back.auth.config.property.JwtProperties; import kr.pickple.back.auth.domain.token.AuthTokens; -import kr.pickple.back.auth.domain.token.JwtProvider; -import kr.pickple.back.auth.domain.token.RefreshToken; -import kr.pickple.back.auth.repository.RedisRepository; -import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.crew.dto.response.CrewProfileResponse; -import kr.pickple.back.crew.dto.response.CrewResponse; -import kr.pickple.back.crew.exception.CrewException; -import kr.pickple.back.crew.repository.CrewRepository; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.domain.GameMember; -import kr.pickple.back.game.exception.GameException; -import kr.pickple.back.game.repository.GameRepository; -import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.dto.request.MemberCreateRequest; +import kr.pickple.back.auth.implement.TokenManager; +import kr.pickple.back.member.domain.MemberProfile; +import kr.pickple.back.member.domain.NewMember; +import kr.pickple.back.member.dto.mapper.MemberResponseMapper; import kr.pickple.back.member.dto.response.AuthenticatedMemberResponse; -import kr.pickple.back.member.dto.response.CrewMemberRegistrationStatusResponse; -import kr.pickple.back.member.dto.response.GameMemberRegistrationStatusResponse; -import kr.pickple.back.member.dto.response.MemberGameResponse; import kr.pickple.back.member.dto.response.MemberProfileResponse; -import kr.pickple.back.member.dto.response.MemberResponse; -import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.implement.MemberReader; +import kr.pickple.back.member.implement.MemberWriter; import lombok.RequiredArgsConstructor; @Service @@ -45,192 +19,28 @@ @Transactional(readOnly = true) public class MemberService { - private static final String REFRESH_TOKEN_KEY = "refresh_token"; - - private final AddressService addressService; - private final MemberRepository memberRepository; - private final CrewRepository crewRepository; - private final GameRepository gameRepository; - private final RedisRepository redisRepository; - private final JwtProvider jwtProvider; - private final JwtProperties jwtProperties; + private final TokenManager tokenManager; + private final MemberWriter memberWriter; + private final MemberReader memberReader; + /** + * 사용자 회원가입 (카카오) + */ @Transactional - public AuthenticatedMemberResponse createMember(final MemberCreateRequest memberCreateRequest) { - validateIsDuplicatedMemberInfo(memberCreateRequest); - - final MainAddressResponse mainAddressResponse = addressService.findMainAddressByNames( - memberCreateRequest.getAddressDepth1(), - memberCreateRequest.getAddressDepth2() - ); - - final Member member = memberCreateRequest.toEntity(mainAddressResponse); - final Member savedMember = memberRepository.save(member); - - final AuthTokens loginTokens = jwtProvider.createLoginToken(String.valueOf(savedMember.getId())); - - final RefreshToken refreshToken = RefreshToken.builder() - .token(loginTokens.getRefreshToken()) - .memberId(savedMember.getId()) - .createdAt(LocalDateTime.now()) - .build(); + public AuthenticatedMemberResponse createMember(final NewMember newMember) { + final NewMember savedNewMember = memberWriter.create(newMember); + final AuthTokens authTokens = tokenManager.create(savedNewMember.getMemberId()); + savedNewMember.updateAuthTokens(authTokens); - redisRepository.saveHash( - REFRESH_TOKEN_KEY, - refreshToken.getToken(), - refreshToken, - jwtProperties.getRefreshTokenExpirationTime() - ); - - return AuthenticatedMemberResponse.of(savedMember, loginTokens); - } - - private void validateIsDuplicatedMemberInfo(final MemberCreateRequest memberCreateRequest) { - final String email = memberCreateRequest.getEmail(); - final String nickname = memberCreateRequest.getNickname(); - final Long oauthId = memberCreateRequest.getOauthId(); - - if (memberRepository.existsByEmailOrNicknameOrOauthId(email, nickname, oauthId)) { - throw new MemberException(MEMBER_IS_EXISTED, email, nickname, oauthId); - } + return MemberResponseMapper.mapToAuthenticatedMemberResponseDto(savedNewMember); } + /** + * 사용자 프로필 조회 + */ public MemberProfileResponse findMemberProfileById(final Long memberId) { - final Member member = findMemberById(memberId); - final List crewResponses = member.getCrewsByStatus(CONFIRMED) - .stream() - .map(CrewResponse::from) - .toList(); - - return MemberProfileResponse.of(member, crewResponses); - } - - public List findAllCrewsByMemberId( - final Long loggedInMemberId, - final Long memberId, - final RegistrationStatus memberStatus - ) { - validateSelfMemberAccess(loggedInMemberId, memberId); - - final Member member = findMemberById(memberId); - final List crews = member.getCrewsByStatus(memberStatus); - - return convertToCrewProfileResponses(crews, memberStatus); - } - - public List findCreatedCrewsByMemberId(final Long loggedInMemberId, final Long memberId) { - validateSelfMemberAccess(loggedInMemberId, memberId); - - final Member member = findMemberById(memberId); - final List crews = member.getCreatedCrews(); - - return convertToCrewProfileResponses(crews, CONFIRMED); - } - - private List convertToCrewProfileResponses( - final List crews, - final RegistrationStatus memberStatus - ) { - return crews.stream() - .map(crew -> CrewProfileResponse.of(crew, getMemberResponsesByCrew(crew, memberStatus))) - .toList(); - } - - private List getMemberResponsesByCrew(final Crew crew, final RegistrationStatus memberStatus) { - return crew.getMembersByStatus(memberStatus) - .stream() - .map(MemberResponse::from) - .toList(); - } - - public List findAllMemberGames( - final Long loggedInMemberId, - final Long memberId, - final RegistrationStatus memberStatus - ) { - validateSelfMemberAccess(loggedInMemberId, memberId); - - final Member member = findMemberById(memberId); - final List memberGames = member.getMemberGamesByStatus(memberStatus); - - return convertToMemberGameResponses(memberGames, memberStatus); - } - - public List findAllCreatedGames(final Long loggedInMemberId, final Long memberId) { - validateSelfMemberAccess(loggedInMemberId, memberId); - - final Member member = findMemberById(memberId); - final List memberGames = member.getCreatedMemberGames(); - - return convertToMemberGameResponses(memberGames, CONFIRMED); - } - - private void validateSelfMemberAccess(Long loggedInMemberId, Long memberId) { - if (!loggedInMemberId.equals(memberId)) { - throw new MemberException(MEMBER_MISMATCH, loggedInMemberId, memberId); - } - } - - private Member findMemberById(final Long memberId) { - return memberRepository.findById(memberId) - .orElseThrow(() -> new MemberException(MEMBER_NOT_FOUND, memberId)); - } - - private Crew findCrewById(final Long crewId) { - return crewRepository.findById(crewId) - .orElseThrow(() -> new CrewException(CREW_NOT_FOUND, crewId)); - } - - private Game findGameById(final Long gameId) { - return gameRepository.findById(gameId) - .orElseThrow(() -> new GameException(GAME_NOT_FOUND, gameId)); - } - - private List convertToMemberGameResponses( - final List memberGames, - final RegistrationStatus memberStatus - ) { - return memberGames.stream() - .map(memberGame -> MemberGameResponse.of( - memberGame, - getMemberResponsesByGame(memberGame.getGame(), memberStatus) - )) - .toList(); - } - - private List getMemberResponsesByGame(final Game game, final RegistrationStatus memberStatus) { - return game.getMembersByStatus(memberStatus) - .stream() - .map(MemberResponse::from) - .toList(); - } - - public GameMemberRegistrationStatusResponse findMemberRegistrationStatusForGame( - final Long loggedInMemberId, - final Long memberId, - final Long gameId - ) { - validateSelfMemberAccess(loggedInMemberId, memberId); - - final Member member = findMemberById(memberId); - final Game game = findGameById(gameId); - - final RegistrationStatus memberRegistrationStatus = member.findGameRegistrationStatus(game); - final Boolean isReviewDone = member.isAlreadyReviewDoneInGame(game); - - return GameMemberRegistrationStatusResponse.of(memberRegistrationStatus, isReviewDone); - } - - public CrewMemberRegistrationStatusResponse findMemberRegistrationStatusForCrew( - final Long loggedInMemberId, - final Long memberId, - final Long crewId - ) { - validateSelfMemberAccess(loggedInMemberId, memberId); - - final Member member = findMemberById(memberId); - final Crew crew = findCrewById(crewId); + final MemberProfile memberProfile = memberReader.readProfileByMemberId(memberId); - return CrewMemberRegistrationStatusResponse.from(member.findCrewRegistrationStatus(crew)); + return MemberResponseMapper.mapToMemberProfileResponseDto(memberProfile); } } diff --git a/src/main/java/kr/pickple/back/position/domain/Position.java b/src/main/java/kr/pickple/back/position/domain/Position.java index c9375cf9..40a0c6d9 100644 --- a/src/main/java/kr/pickple/back/position/domain/Position.java +++ b/src/main/java/kr/pickple/back/position/domain/Position.java @@ -38,7 +38,7 @@ public enum Position { private final String description; @JsonCreator - public static Position from(final String positionAcronym) { + public static Position fromGamePositions(final String positionAcronym) { if (positionMap.containsKey(positionAcronym)) { return positionMap.get(positionAcronym); } diff --git a/src/main/java/kr/pickple/back/position/util/PositionConverter.java b/src/main/java/kr/pickple/back/position/util/PositionConverter.java index 78ace935..ecfa4d04 100644 --- a/src/main/java/kr/pickple/back/position/util/PositionConverter.java +++ b/src/main/java/kr/pickple/back/position/util/PositionConverter.java @@ -14,6 +14,6 @@ public String convertToDatabaseColumn(final Position position) { @Override public Position convertToEntityAttribute(final String acronym) { - return Position.from(acronym); + return Position.fromGamePositions(acronym); } } diff --git a/src/main/java/kr/pickple/back/ranking/repository/RankingJdbcRepository.java b/src/main/java/kr/pickple/back/ranking/repository/RankingJdbcRepository.java index 52a1aba5..a00e9189 100644 --- a/src/main/java/kr/pickple/back/ranking/repository/RankingJdbcRepository.java +++ b/src/main/java/kr/pickple/back/ranking/repository/RankingJdbcRepository.java @@ -74,10 +74,6 @@ DISTINCT CASE WHEN game_member.created_at > DATE_SUB(NOW(), INTERVAL 1 MONTH) private final JdbcTemplate jdbcTemplate; - public List getCrewRankings() { - return jdbcTemplate.query(CREW_RANKING_SQL, crewRankingRowMapper()); - } - private static RowMapper crewRankingRowMapper() { return (rs, rowNum) -> CrewRankingResponse.builder() .id(rs.getLong("id")) @@ -92,4 +88,8 @@ private static RowMapper crewRankingRowMapper() { .rank(rs.getInt("ranking")) .build(); } + + public List getCrewRankings() { + return jdbcTemplate.query(CREW_RANKING_SQL, crewRankingRowMapper()); + } } diff --git a/src/main/resources/db/migration/V1.0__Init.sql b/src/main/resources/db/migration/V1.0__Init.sql index d47b0179..afc4f3f1 100644 --- a/src/main/resources/db/migration/V1.0__Init.sql +++ b/src/main/resources/db/migration/V1.0__Init.sql @@ -92,7 +92,7 @@ CREATE TABLE IF NOT EXISTS `pickpledev`.`crew_member` REFERENCES `pickpledev`.`member` (`id`) ); -CREATE TABLE IF NOT EXISTS `pickpledev`.`game` +CREATE TABLE IF NOT EXISTS `pickpledev`.`gameEntity` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `content` VARCHAR(1000) NOT NULL, @@ -137,7 +137,7 @@ CREATE TABLE IF NOT EXISTS `pickpledev`.`game_member` PRIMARY KEY (`id`), CONSTRAINT `FK_game_TO_game_member_1` FOREIGN KEY (`game_id`) - REFERENCES `pickpledev`.`game` (`id`), + REFERENCES `pickpledev`.`gameEntity` (`id`), CONSTRAINT `FK_member_TO_game_member_1` FOREIGN KEY (`member_id`) REFERENCES `pickpledev`.`member` (`id`) @@ -153,7 +153,7 @@ CREATE TABLE IF NOT EXISTS `pickpledev`.`game_position` PRIMARY KEY (`id`), CONSTRAINT `FK_game_TO_game_position_1` FOREIGN KEY (`game_id`) - REFERENCES `pickpledev`.`game` (`id`) + REFERENCES `pickpledev`.`gameEntity` (`id`) ); CREATE TABLE IF NOT EXISTS `pickpledev`.`member_position` diff --git a/src/main/resources/static/docs/pickple.json b/src/main/resources/static/docs/pickple.json index ca499dd5..e01da9f8 100644 --- a/src/main/resources/static/docs/pickple.json +++ b/src/main/resources/static/docs/pickple.json @@ -57,7 +57,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMCIsImlhdCI6MTcwMTU2Njg1NSwiZXhwIjoxNzAxNTY4MDg1fQ.dky4FMADbBUY2M8dcmUrTxw9koP-dbauIrX6mtmxTRh0h0Mwci2inbMU-XQrgxMX_Jq8tzRen4FINU0AYqhYfg" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIzMiIsImlhdCI6MTcwNjYyNDkyMCwiZXhwIjoxNzA2NjI2MTUwfQ.8oI1Kjk68djjJ5ORIyWsvRejnXROyEGE-NlfNgOq8r2tbCyLhZr2m7OQs4JXqXEmsLTQWppLwKMr6AalTSXEMA" } ], "responses" : { "200" : { @@ -93,7 +93,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI5IiwiaWF0IjoxNzAxNTY2ODU1LCJleHAiOjE3MDE1NjgwODV9.P4XUGWSRDKqGUJMu73bgn7KfMLR56eO0UH898FVVy8aNa89LMLeY1uZeXVtPCqYz0O41A48qAX2acSZGzDzViA" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIzMSIsImlhdCI6MTcwNjYyNDkyMCwiZXhwIjoxNzA2NjI2MTUwfQ.H2nKWoEMpzhUVLQ6F9IYy5QQfasm420xStZ57fD3fGci5sn2G_zdQis1DjBQynn6gAOX_bp4yRyDgFRSsFnpIw" } ], "responses" : { "204" : { @@ -119,7 +119,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI4IiwiaWF0IjoxNzAxNTY2ODU1LCJleHAiOjE3MDE1NjgwODV9.QNPjx4UHYNeiVj0MSIEWarNt-aTFe_Lr9seUYNrd1x3K66PwRQqTU4Fo0aKIZtXqimFhkGwdaxx220fg7naLOA" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIzMCIsImlhdCI6MTcwNjYyNDkyMCwiZXhwIjoxNzA2NjI2MTUwfQ.jSy7Gs1psIghSM7vRLK5GUAHdQFKueHCk1v8Mz5M-3HXVchWI8p82P-3vqNU3Ns7zWezPG7CSLyK16_dkCnZrg" } ], "responses" : { "200" : { @@ -157,7 +157,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzAxNTY2ODYwLCJleHAiOjE3MDE1NjgwOTB9.8LeuXgGZ77WiiElviih92QztQyPbA-VVW0wA3_5FFxxS1JRAK-cRJkOSdex6jRd1_iFSdlXaMgf8oXl2J3BC7g" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzEzODQ2Mjg0LCJleHAiOjE3MTM4NDc1MTR9.a4DqR2TAPkO82YHlEWP-q0YMUc5xjJrVJXvuUZ4X3VqW_XxNDgJbqW_JMzvq_ufh65NEOXWRjBCyo9b8hOq9ow" } ], "responses" : { "204" : { @@ -183,7 +183,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzAxNTY2ODYwLCJleHAiOjE3MDE1NjgwOTB9.8LeuXgGZ77WiiElviih92QztQyPbA-VVW0wA3_5FFxxS1JRAK-cRJkOSdex6jRd1_iFSdlXaMgf8oXl2J3BC7g" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzEzODQ2Mjg0LCJleHAiOjE3MTM4NDc1MTR9.a4DqR2TAPkO82YHlEWP-q0YMUc5xjJrVJXvuUZ4X3VqW_XxNDgJbqW_JMzvq_ufh65NEOXWRjBCyo9b8hOq9ow" } ], "responses" : { "201" : { @@ -195,7 +195,7 @@ }, "examples" : { "oauth-access-token-refresh" : { - "value" : "{\n \"accessToken\" : \"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzAxNTY2ODYwLCJleHAiOjE3MDE1NjgwOTB9.8LeuXgGZ77WiiElviih92QztQyPbA-VVW0wA3_5FFxxS1JRAK-cRJkOSdex6jRd1_iFSdlXaMgf8oXl2J3BC7g\"\n}" + "value" : "{\n \"accessToken\" : \"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzEzODQ2Mjg0LCJleHAiOjE3MTM4NDc1MTR9.a4DqR2TAPkO82YHlEWP-q0YMUc5xjJrVJXvuUZ4X3VqW_XxNDgJbqW_JMzvq_ufh65NEOXWRjBCyo9b8hOq9ow\"\n}" } } } @@ -294,54 +294,6 @@ } } }, - "/crew-alarms/{crewAlarmId}" : { - "patch" : { - "tags" : [ "crew Alarm" ], - "summary" : "사용자의 크루 알람에 대하여 읽음 여부 수정", - "description" : "사용자가 보낸 크루 알람의 읽음 상태를 변경한다.", - "operationId" : "update-crew-alarm-status", - "parameters" : [ { - "name" : "crewAlarmId", - "in" : "path", - "description" : "크루 알람 ID", - "required" : true, - "schema" : { - "type" : "string" - } - }, { - "name" : "Authorization", - "in" : "header", - "description" : "Bearer 토큰", - "required" : true, - "schema" : { - "type" : "string" - }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMSIsImlhdCI6MTcwMTU2Njg1NSwiZXhwIjoxNzAxNTY4MDg1fQ.V-mkDb9AL6qlT_olBpL3B8LT1wR605i8Lv7hS59mEs140vv7p77ie5Ln-z3ATsGU2w7WdYkm-1p_s1GgkbgALQ" - } ], - "requestBody" : { - "content" : { - "application/json;charset=UTF-8" : { - "schema" : { - "$ref" : "#/components/schemas/game-alarms-gameAlarmId-480660043" - }, - "examples" : { - "update-crew-alarm-status" : { - "value" : "{\n \"isRead\" : true\n}" - } - } - } - } - }, - "responses" : { - "204" : { - "description" : "204" - } - }, - "security" : [ { - "bearerAuthJWT" : [ ] - } ] - } - }, "/crews" : { "get" : { "tags" : [ "Crew" ], @@ -358,7 +310,7 @@ }, "examples" : { "findCrewsByAddress" : { - "value" : "[ {\n \"id\" : 11,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 2,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 33,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"members\" : [ {\n \"id\" : 33,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 34,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n} ]" + "value" : "[ {\n \"id\" : 11,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 2,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 20,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon1.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"members\" : [ {\n \"id\" : 20,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon1.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n }, {\n \"id\" : 21,\n \"email\" : \"pickple2@pickple.kr\",\n \"nickname\" : \"pickple2\",\n \"introduction\" : \"안녕하세요. pickple2입니다.\",\n \"profileImageUrl\" : \"https://amazon2.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n } ]\n} ]" } } } @@ -392,7 +344,7 @@ }, "examples" : { "find-crew" : { - "value" : "{\n \"id\" : 10,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 1,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 30,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"members\" : [ {\n \"id\" : 30,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n}" + "value" : "{\n \"id\" : 10,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 1,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 19,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"members\" : [ {\n \"id\" : 19,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n } ]\n}" } } } @@ -423,7 +375,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyNyIsImlhdCI6MTcwMTU2Njg2MSwiZXhwIjoxNzAxNTY4MDkxfQ.doO2_l-MBIXnqge5paiBc5Y8EpyW9V8wEQ1R6e4ShSDfv8NJyfR0_TeVyJxQhMKFLeYpLY4vm2k91TzLO1qclA" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxNiIsImlhdCI6MTcxMzg0NjI4MCwiZXhwIjoxNzEzODQ3NTEwfQ.B77ocJpbEv1C6HCQEDJLwKgobSOM77rBJUBnb6TWkuFo0OvjqSNJfe9IMFBJxNpTm1LS6dwsbmJgMvkSfVWkmA" } ], "responses" : { "204" : { @@ -465,7 +417,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIzNSIsImlhdCI6MTcwMTU2Njg2MiwiZXhwIjoxNzAxNTY4MDkyfQ.dHVqlIJ9YIy0hjvL6uyZkbQQu6wD2Phz6bvab25q2XZwjm5e9JdFKJLyE38Xnuf5xmHHs7fmjOZ3tJ7nTiSGkg" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMiIsImlhdCI6MTcxMzg0NjI4MCwiZXhwIjoxNzEzODQ3NTEwfQ.u_9lfw3NNlmUdIxWJyU7R9ulK59aEh7WdhIOfyMYLCp7G_2uTtFk28a2ebdgEb8T1PcQNvqZ6sHuZ3gAlWgZ_A" } ], "responses" : { "204" : { @@ -505,7 +457,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyNCIsImlhdCI6MTcwMTU2Njg2MSwiZXhwIjoxNzAxNTY4MDkxfQ.Dtk6jefQhKWV0QRSsIpVXbZdgCI8X1tipN0JgA_nR35JGFYKu7n5jksTR2yfio5Mldk470GnvYRptfwgvwdJ6g" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMyIsImlhdCI6MTcxMzg0NjI3OSwiZXhwIjoxNzEzODQ3NTA5fQ.dg63FdiK6kd7--4xuvLPbVewtR1JshOwcyUSCZWpf8OBOnLLT-eJyC84faxI3vVOrqzmpd-3tLojwr9xTUSsGQ" } ], "requestBody" : { "content" : { @@ -553,7 +505,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMyIsImlhdCI6MTcwMTU2Njg1NSwiZXhwIjoxNzAxNTY4MDg1fQ.WPlkLiA6GaxKVNYZFG9DsUMJWdpPfn9VeHKVwTNlA9RpyBGbWvvkQqmYlZo7WwZna7EvsDfuQJk_Vl6DQY4hOQ" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzA2NjI0OTE1LCJleHAiOjE3MDY2MjYxNDV9.dC4zkKKpRMumx9RADVFPvgkHd6Kl5cpfi5WhoUdSjZqwaVcSdJ5jMRg9ipnPR5e_Nh8Fc7P_pBGF7UjBQ_GKEg" } ], "requestBody" : { "content" : { @@ -628,7 +580,7 @@ }, "examples" : { "find-games-by-category" : { - "value" : "[ {\n \"id\" : 14,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2023-11-10\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"viewCount\" : 0,\n \"cost\" : 100,\n \"memberCount\" : 3,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 66,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"members\" : [ {\n \"id\" : 66,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 67,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 68,\n \"email\" : \"pickple2@pickple.kr\",\n \"nickname\" : \"pickple2\",\n \"introduction\" : \"안녕하세요. pickple2입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n} ]" + "value" : "[ {\n \"id\" : 6,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2024-01-29\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"viewCount\" : 0,\n \"cost\" : 100,\n \"memberCount\" : 3,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 26,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"members\" : [ {\n \"id\" : 26,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 27,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 28,\n \"email\" : \"pickple2@pickple.kr\",\n \"nickname\" : \"pickple2\",\n \"introduction\" : \"안녕하세요. pickple2입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n} ]" } } } @@ -662,7 +614,7 @@ }, "examples" : { "find-game" : { - "value" : "{\n \"id\" : 13,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2023-11-10\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"viewCount\" : 1,\n \"cost\" : 100,\n \"memberCount\" : 3,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 63,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"members\" : [ {\n \"id\" : 63,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 64,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 65,\n \"email\" : \"pickple2@pickple.kr\",\n \"nickname\" : \"pickple2\",\n \"introduction\" : \"안녕하세요. pickple2입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n}" + "value" : "{\n \"id\" : 5,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2024-01-29\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"viewCount\" : 1,\n \"cost\" : 100,\n \"memberCount\" : 3,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 23,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"members\" : [ {\n \"id\" : 23,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 24,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 25,\n \"email\" : \"pickple2@pickple.kr\",\n \"nickname\" : \"pickple2\",\n \"introduction\" : \"안녕하세요. pickple2입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n}" } } } @@ -701,7 +653,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI1NCIsImlhdCI6MTcwMTU2Njg2MiwiZXhwIjoxNzAxNTY4MDkyfQ.PKyEjeMo9nYEF3oWApLMazfKHaj8ELrQLiJx5h6fkbciOcUBb7MWnUZzh83uQC31dqTkqXtUM6V4gtqNVkFuTQ" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxNCIsImlhdCI6MTcwNjYyNDkxOCwiZXhwIjoxNzA2NjI2MTQ4fQ.iElm-JprLtsMW7SGNpoTM-LY6lqcUwonVW7xneXYOSKmqXjc8TtQiKRQmtvVNgzHmjPwufPv3PmnPLEKY85jmw" } ], "responses" : { "200" : { @@ -713,7 +665,7 @@ }, "examples" : { "find-all-waiting-or-confirmed-gameMembers" : { - "value" : "{\n \"id\" : 9,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2023-11-10\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"viewCount\" : 0,\n \"cost\" : 100,\n \"memberCount\" : 1,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 54,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"members\" : [ {\n \"id\" : 55,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 56,\n \"email\" : \"pickple2@pickple.kr\",\n \"nickname\" : \"pickple2\",\n \"introduction\" : \"안녕하세요. pickple2입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n}" + "value" : "{\n \"id\" : 1,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2024-01-29\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"viewCount\" : 0,\n \"cost\" : 100,\n \"memberCount\" : 1,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 14,\n \"email\" : \"pickple0@pickple.kr\",\n \"nickname\" : \"pickple0\",\n \"introduction\" : \"안녕하세요. pickple0입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"members\" : [ {\n \"id\" : 15,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n }, {\n \"id\" : 16,\n \"email\" : \"pickple2@pickple.kr\",\n \"nickname\" : \"pickple2\",\n \"introduction\" : \"안녕하세요. pickple2입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n}" } } } @@ -745,7 +697,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2MCIsImlhdCI6MTcwMTU2Njg2MiwiZXhwIjoxNzAxNTY4MDkyfQ.15coCclinhnhDVpT4kinpCkrjgknDgD-RI_YGw_NAY9Z6ikXrycFr4CC4yWVPvM4ZkNdbPKuCo8p5OmLP53X0Q" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMCIsImlhdCI6MTcwNjYyNDkxOCwiZXhwIjoxNzA2NjI2MTQ4fQ.HOhZSsEClNlPlQ7QAyqn6Fx_cZwf_lwvLeZdqZwIh4kNeV2h49XcauT0OVS3Pp00dG94d4IV1z6b4dMbNuzGzA" } ], "responses" : { "204" : { @@ -779,7 +731,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2OSIsImlhdCI6MTcwMTU2Njg2MiwiZXhwIjoxNzAxNTY4MDkyfQ.IuqFIajJ81XnSXtXkbLwAf4PogAKw96wVapOiU5CZJ5GYe6_F1vHOlv6Hc9NrrH8XtWeTubwUntreRbKIGsH7w" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyOSIsImlhdCI6MTcwNjYyNDkxOCwiZXhwIjoxNzA2NjI2MTQ4fQ.K9ZpCg9Livn_tM1oEVNTgLTN_RB73s9SUCglOG1tTbeAlcWhCmo9hfoH_dlJcgsQfmsuY1CD0flLNvS4wJ0r6Q" } ], "requestBody" : { "content" : { @@ -789,7 +741,7 @@ }, "examples" : { "review-mannerScores" : { - "value" : "{\n \"mannerScoreReviews\" : [ {\n \"memberId\" : 70,\n \"mannerScore\" : 1\n }, {\n \"memberId\" : 71,\n \"mannerScore\" : 1\n } ]\n}" + "value" : "{\n \"mannerScoreReviews\" : [ {\n \"memberId\" : 30,\n \"mannerScore\" : 1\n }, {\n \"memberId\" : 31,\n \"mannerScore\" : 1\n } ]\n}" } } } @@ -835,7 +787,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI2MSIsImlhdCI6MTcwMTU2Njg2MiwiZXhwIjoxNzAxNTY4MDkyfQ.9zmNT0ZdihZYqcoZj7efP0gnOTM6mEDwW_sYWMcfCWHV4vnikjd35bta-Mnz50Bc02dAmYp2FLhRg2CvjzN44w" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMSIsImlhdCI6MTcwNjYyNDkxOCwiZXhwIjoxNzA2NjI2MTQ4fQ.-mcs5fbkc2Io3_BaWpt6b7kDcK5vO3fbaQXLNxivURyRIn1kPHOOJHxY9RJHq6CVtPKclZCxBlRYLKFdAV9kgA" } ], "responses" : { "204" : { @@ -875,7 +827,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI1NyIsImlhdCI6MTcwMTU2Njg2MiwiZXhwIjoxNzAxNTY4MDkyfQ.0f0pN6FBGlf_XKmkQN72UbjtW44p3eQjJeq4EwHu5PQ3do5n_5jvaVkTLVjv2_8RvN4NNXEQfB_uwq5qxTNROw" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxNyIsImlhdCI6MTcwNjYyNDkxOCwiZXhwIjoxNzA2NjI2MTQ4fQ.6bdVIoKsyoRlIcGQKhExKwOCJY1YyO6EGq0E_dcRSD5ICvl-xBJBZQM7YRg6ZgcO1lEgWzLVMu5rPJnv3Zp13Q" } ], "requestBody" : { "content" : { @@ -915,7 +867,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJLQUtBTzk5OTk5OSIsImlhdCI6MTcwMTU2Njg2MywiZXhwIjoxNzAxNTY3MTYzfQ.vfPRkiqTG8-iqt4k3hCLLu1HoXooKmn_l2Ub4VBi_KvbwbVhP3GFVx_cwwpCphSWDvsFQlHK6DlIaMSe1_OKrg" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJLQUtBTzk5OTk5OSIsImlhdCI6MTcxMzg0NjI4NywiZXhwIjoxNzEzODQ2NTg3fQ.UmryklNnLm_mbdsvcTfioPwW8u-bYRc5ERfQzSZWPKiGO_o-fPosHdYufvVnOe6t7sfx7GgI0dhVb8RmS_FV2Q" } ], "requestBody" : { "content" : { @@ -949,7 +901,7 @@ }, "examples" : { "create-member" : { - "value" : "{\n \"accessToken\" : \"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI4MSIsImlhdCI6MTcwMTU2Njg2MywiZXhwIjoxNzAxNTY4MDkzfQ.VhlG0mwcOMGz4_uq_xAYRMmX0KJZwurVVuULckmnPTRFoRL0V6rZAQ-Vtu0sFN4sSD0cp4HLQzZwnG5g3xWK0g\",\n \"refreshToken\" : \"eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MDE1NjY4NjMsImV4cCI6MTcwMTU2ODI5Mn0.cohz57h1CJ2OnAl5mjYP3jEym4OcIs7WbVmZud0skmMEyWRUB8FQGLTWNYvGE-n3G42ymiOjFr_QAuAcGLct_g\",\n \"id\" : 81,\n \"nickname\" : \"백둥\",\n \"profileImageUrl\" : \"https://amazon.image/1\",\n \"email\" : \"pickple@pickple.kr\",\n \"oauthId\" : 999999,\n \"oauthProvider\" : \"KAKAO\",\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\"\n}" + "value" : "{\n \"accessToken\" : \"eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMSIsImlhdCI6MTcxMzg0NjI4NywiZXhwIjoxNzEzODQ3NTE3fQ.zEaZ4oOAeTHu2rnnwG1FAv0CueaEELnyqQJjcwGYMCn5jMU13FqAUOXLwCP-eOoe4tpH5eQJkvKB0LJE-G1SQA\",\n \"refreshToken\" : \"eyJhbGciOiJIUzUxMiJ9.eyJpYXQiOjE3MTM4NDYyODcsImV4cCI6MTcxMzg0NzcxN30.YXt8j5frKZA6Z8f69ffcSyo35T9ksqp64mA09z8HzgfevcTsKHDFm21oxuasNLy-eBPzTjvlMyAzrcEU13josA\",\n \"id\" : 21,\n \"nickname\" : \"백둥\",\n \"profileImageUrl\" : \"https://amazon.image/1\",\n \"email\" : \"pickple@pickple.kr\",\n \"oauthId\" : 999999,\n \"oauthProvider\" : \"KAKAO\",\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\"\n}" } } } @@ -986,7 +938,7 @@ }, "examples" : { "find-member" : { - "value" : "{\n \"id\" : 79,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"crews\" : [ {\n \"id\" : 17,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 1,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 79,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\"\n } ]\n}" + "value" : "{\n \"id\" : 19,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : null,\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ],\n \"crews\" : [ {\n \"id\" : 5,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 1,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 19,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\"\n } ]\n}" } } } @@ -1017,7 +969,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI3NyIsImlhdCI6MTcwMTU2Njg2MywiZXhwIjoxNzAxNTY4MDkzfQ.3SFrWLZL-VddXszlPAnE-Z9GgPROgy-IZmuBuU4VLwsLQPnCH4Ds48vpNYbMUIDd0_KeoN2bshMSPxL9GuV8HQ" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxNyIsImlhdCI6MTcxMzg0NjI4NywiZXhwIjoxNzEzODQ3NTE3fQ.E_NAteyKynKjd1whOtpmmho1RK4PGRDlW1c-SbykIz_XidE--6tbCBU5AZifbSx9bzl7pvR8wvgPcC-lM-rftQ" } ], "responses" : { "200" : { @@ -1029,7 +981,7 @@ }, "examples" : { "find-created-crews-by-member-id" : { - "value" : "[ {\n \"id\" : 16,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 1,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 77,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"members\" : [ {\n \"id\" : 77,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n} ]" + "value" : "[ {\n \"id\" : 4,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 1,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 17,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"members\" : [ {\n \"id\" : 17,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n } ]\n} ]" } } } @@ -1063,7 +1015,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI3NiIsImlhdCI6MTcwMTU2Njg2MywiZXhwIjoxNzAxNTY4MDkzfQ.cI-_oiW4IUE_l7AEt9uMaVbtfPWG-UNNTmPpMcoz8RwF622Lc_MgWtY5u8C3ElP4X0bjA0qfAaNp2O0bN5Ul1Q" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxNiIsImlhdCI6MTcxMzg0NjI4NywiZXhwIjoxNzEzODQ3NTE3fQ.Iqhjb0vowyrYR7gcvK3iWygZkZwBMIAe-nd34fNwOUWbxks-NUCkhogUFwzkulXnFSYK6MsmajfdnCEBY92tzg" } ], "responses" : { "200" : { @@ -1075,7 +1027,7 @@ }, "examples" : { "find-all-created-games" : { - "value" : "[ {\n \"id\" : 16,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2023-11-10\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"isReviewDone\" : false,\n \"viewCount\" : 0,\n \"cost\" : 100,\n \"memberCount\" : 1,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 76,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"members\" : [ {\n \"id\" : 76,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n} ]" + "value" : "[ {\n \"id\" : 1,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2024-04-22\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"isReviewDone\" : false,\n \"viewCount\" : 0,\n \"cost\" : 100,\n \"memberCount\" : 1,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 16,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ],\n \"members\" : [ {\n \"id\" : 16,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n } ]\n} ]" } } } @@ -1117,7 +1069,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI4MCIsImlhdCI6MTcwMTU2Njg2MywiZXhwIjoxNzAxNTY4MDkzfQ.hHV8xWsmhub5ADsCJiY8IT4hwTMMFmcNx4194d4FnXQCP8k-mza473N9cJvAtPSGUJEC6x8ROVLcFZHTJabWeg" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIyMCIsImlhdCI6MTcxMzg0NjI4NywiZXhwIjoxNzEzODQ3NTE3fQ.bxansnVnvEgNrZETHaIvWsSD8kIAPegPIbUhTj4kc77eJRo3X7UwM8zC0CiT5M6HpFdB4W4UCrnORFFWo0gM6w" } ], "responses" : { "200" : { @@ -1129,7 +1081,7 @@ }, "examples" : { "find-all-crews-by-member-id" : { - "value" : "[ {\n \"id\" : 18,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 1,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 80,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"members\" : [ {\n \"id\" : 80,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n} ]" + "value" : "[ {\n \"id\" : 6,\n \"name\" : \"백둥크루1\",\n \"content\" : \"안녕하세요 백둥크루1 입니다.\",\n \"memberCount\" : 1,\n \"maxMemberCount\" : 15,\n \"profileImageUrl\" : \"https://amazon.profileimage/1\",\n \"backgroundImageUrl\" : \"https://amazon.backgroundimage/1\",\n \"status\" : \"모집 중\",\n \"likeCount\" : 0,\n \"competitionPoint\" : 0,\n \"leader\" : {\n \"id\" : 20,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"members\" : [ {\n \"id\" : 20,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n } ]\n} ]" } } } @@ -1171,7 +1123,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI3OCIsImlhdCI6MTcwMTU2Njg2MywiZXhwIjoxNzAxNTY4MDkzfQ.0B-fbqzb5yk2oJTglqqw9nI7CHhFhEbSoQ2yL2YM2lGh2-blhmdi0f1Z0Omwgm_ZGjclyUD1wX-tivpw6xR9Rw" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxOCIsImlhdCI6MTcxMzg0NjI4NywiZXhwIjoxNzEzODQ3NTE3fQ.T_lNcp0F1NBwNKVX_TIrBE80wD61cvzpx3DQDIaxXELpiek4MTERgzs8qTUo0GeC-C7E49fDkO6JAege_TQTCA" } ], "responses" : { "200" : { @@ -1183,7 +1135,7 @@ }, "examples" : { "find-all-member-games" : { - "value" : "[ {\n \"id\" : 17,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2023-11-10\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"isReviewDone\" : false,\n \"viewCount\" : 0,\n \"cost\" : 100,\n \"memberCount\" : 1,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 78,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ],\n \"members\" : [ {\n \"id\" : 78,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"영등포구\",\n \"positions\" : [ \"C\", \"PG\" ]\n } ]\n} ]" + "value" : "[ {\n \"id\" : 2,\n \"content\" : \"하이하이 즐겜 한 판해요\",\n \"playDate\" : \"2024-04-22\",\n \"playStartTime\" : \"11:30:00\",\n \"playEndTime\" : \"13:00:00\",\n \"playTimeMinutes\" : 90,\n \"mainAddress\" : \"서울 영등포구 도림동 254\",\n \"detailAddress\" : \"영등포 다목적 체육관 2층 201호\",\n \"latitude\" : 126.75,\n \"longitude\" : 37.125,\n \"status\" : \"모집 중\",\n \"isReviewDone\" : false,\n \"viewCount\" : 1,\n \"cost\" : 100,\n \"memberCount\" : 1,\n \"maxMemberCount\" : 5,\n \"host\" : {\n \"id\" : 18,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n },\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ],\n \"members\" : [ {\n \"id\" : 18,\n \"email\" : \"pickple1@pickple.kr\",\n \"nickname\" : \"pickple1\",\n \"introduction\" : \"안녕하세요. pickple1입니다.\",\n \"profileImageUrl\" : \"https://amazon.image\",\n \"mannerScore\" : 0,\n \"mannerScoreCount\" : 0,\n \"addressDepth1\" : \"서울시\",\n \"addressDepth2\" : \"강남구\",\n \"positions\" : [ \"C\" ]\n } ]\n} ]" } } } @@ -1217,7 +1169,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzAxNTY2ODYxLCJleHAiOjE3MDE1NjgwOTF9.p0RzwmBmtLbPv1eS6bcUKNDZkEwdwHXU-As8RlkyYlcI_j_zDxrgP8cAayLFi8hN_kBdx9cURHXZ_ZFLwd4y5Q" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxIiwiaWF0IjoxNzEzODQ2Mjg0LCJleHAiOjE3MTM4NDc1MTR9.a4DqR2TAPkO82YHlEWP-q0YMUc5xjJrVJXvuUZ4X3VqW_XxNDgJbqW_JMzvq_ufh65NEOXWRjBCyo9b8hOq9ow" } ], "responses" : { "200" : { @@ -1229,7 +1181,7 @@ }, "examples" : { "find-all-messages-in-room" : { - "value" : "[ {\n \"type\" : \"입장\",\n \"content\" : \"pickple0님이 채팅방에 입장하셨습니다.\",\n \"sender\" : {\n \"id\" : 1,\n \"nickname\" : \"pickple0\",\n \"profileImageUrl\" : \"https://amazon.image\"\n },\n \"roomId\" : 1,\n \"createdAt\" : \"2023-12-03T10:27:41.232523\"\n}, {\n \"type\" : \"입장\",\n \"content\" : \"pickple1님이 채팅방에 입장하셨습니다.\",\n \"sender\" : {\n \"id\" : 2,\n \"nickname\" : \"pickple1\",\n \"profileImageUrl\" : \"https://amazon.image\"\n },\n \"roomId\" : 1,\n \"createdAt\" : \"2023-12-03T10:27:41.247651\"\n} ]" + "value" : "[ {\n \"type\" : \"입장\",\n \"content\" : \"pickple1님이 채팅방에 입장하셨습니다.\",\n \"sender\" : {\n \"id\" : 1,\n \"nickname\" : \"pickple1\",\n \"profileImageUrl\" : \"https://amazon1.image\"\n },\n \"roomId\" : 1,\n \"createdAt\" : \"2024-04-23T13:24:44.734488\"\n}, {\n \"type\" : \"입장\",\n \"content\" : \"pickple2님이 채팅방에 입장하셨습니다.\",\n \"sender\" : {\n \"id\" : 2,\n \"nickname\" : \"pickple2\",\n \"profileImageUrl\" : \"https://amazon2.image\"\n },\n \"roomId\" : 1,\n \"createdAt\" : \"2024-04-23T13:24:44.740654\"\n} ]" } } } @@ -1288,7 +1240,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI3IiwiaWF0IjoxNzAxNTY2ODYxLCJleHAiOjE3MDE1NjgwOTF9.kkaXNawaJGrpuCfSKVNx24_U_6nTIUIIhL055Ga5YkpszlOGGReNOer9lsE7eCjDg2d9ZNMV5qqOstW8tqffmg" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI3IiwiaWF0IjoxNzEzODQ2Mjg0LCJleHAiOjE3MTM4NDc1MTR9.684y0J0GZkxO6JWGPx17yVjRmcIi7287lIszOOb2iz4U7Td4y-bQBTD48qTkvV1kphkzhPgDnFzYChaqSUJvtw" } ], "responses" : { "200" : { @@ -1300,7 +1252,7 @@ }, "examples" : { "find-all-active-chatrooms-by-type" : { - "value" : "[ {\n \"id\" : 5,\n \"roomName\" : \"pickple1\",\n \"roomIconImageUrl\" : \"https://amazon.image\",\n \"type\" : \"개인\",\n \"memberCount\" : 2,\n \"maxMemberCount\" : 2,\n \"playStartTime\" : null,\n \"playTimeMinutes\" : null,\n \"lastMessageContent\" : \"pickple1님이 채팅방에 입장하셨습니다.\",\n \"lastMessageCreatedAt\" : \"2023-12-03T10:27:41.493498\",\n \"createdAt\" : \"2023-12-03T10:27:41.48333\"\n}, {\n \"id\" : 6,\n \"roomName\" : \"pickple2\",\n \"roomIconImageUrl\" : \"https://amazon.image\",\n \"type\" : \"개인\",\n \"memberCount\" : 2,\n \"maxMemberCount\" : 2,\n \"playStartTime\" : null,\n \"playTimeMinutes\" : null,\n \"lastMessageContent\" : \"pickple2님이 채팅방에 입장하셨습니다.\",\n \"lastMessageCreatedAt\" : \"2023-12-03T10:27:41.504592\",\n \"createdAt\" : \"2023-12-03T10:27:41.49518\"\n} ]" + "value" : "[ {\n \"id\" : 5,\n \"roomName\" : \"pickple2\",\n \"roomIconImageUrl\" : \"https://amazon2.image\",\n \"type\" : \"개인\",\n \"memberCount\" : 2,\n \"maxMemberCount\" : 2,\n \"playStartTime\" : null,\n \"playTimeMinutes\" : null,\n \"lastMessageContent\" : \"pickple2님이 채팅방에 입장하셨습니다.\",\n \"lastMessageCreatedAt\" : \"2024-04-23T13:24:44.977997\",\n \"createdAt\" : \"2024-04-23T13:24:44.966968\"\n}, {\n \"id\" : 6,\n \"roomName\" : \"pickple3\",\n \"roomIconImageUrl\" : \"https://amazon3.image\",\n \"type\" : \"개인\",\n \"memberCount\" : 2,\n \"maxMemberCount\" : 2,\n \"playStartTime\" : null,\n \"playTimeMinutes\" : null,\n \"lastMessageContent\" : \"pickple3님이 채팅방에 입장하셨습니다.\",\n \"lastMessageCreatedAt\" : \"2024-04-23T13:24:44.993376\",\n \"createdAt\" : \"2024-04-23T13:24:44.984141\"\n} ]" } } } @@ -1334,7 +1286,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI1IiwiaWF0IjoxNzAxNTY2ODYxLCJleHAiOjE3MDE1NjgwOTF9.2EQbscmlRrtHMrQfjBGd7IliOJnz9-LuTaNWv9ABGe30u-MMskLTl0SySE6HrnX45L7TX16uTv8yaJEakMrPqQ" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiI1IiwiaWF0IjoxNzEzODQ2Mjg0LCJleHAiOjE3MTM4NDc1MTR9.Tmlqj9n2N5NwXzv7WEmXvdZpBtWwPj_nDCzPFxQivMrXVRaQ_AgiI648odVo-4dBS52VbtpFRQScIrjjUCpoqA" } ], "responses" : { "200" : { @@ -1370,7 +1322,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIzIiwiaWF0IjoxNzAxNTY2ODYxLCJleHAiOjE3MDE1NjgwOTF9.7_hFBmZbk6Bl_31mwhtV46N1svfu7hMEfLU6XehASRiOG3fcY2TAWeGN1hzkcuvRFEGYtcl8viy1R7n9GKebfQ" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIzIiwiaWF0IjoxNzEzODQ2Mjg0LCJleHAiOjE3MTM4NDc1MTR9.hlT8ioUC69w-KMgQdyvzrGU2YYHhwsLXqyW9XD2P8xlpNZwyDKE5szKSUOxfgQf8bFTCcYO_VPiTTGEPm5q8dg" } ], "requestBody" : { "content" : { @@ -1396,7 +1348,7 @@ }, "examples" : { "create-personal-room" : { - "value" : "{\n \"id\" : 3,\n \"roomName\" : \"pickple1\",\n \"roomIconImageUrl\" : \"https://amazon.image\",\n \"type\" : \"개인\",\n \"domainId\" : 4,\n \"memberCount\" : 2,\n \"maxMemberCount\" : 2,\n \"playStartTime\" : null,\n \"playTimeMinutes\" : null,\n \"members\" : [ {\n \"id\" : 3,\n \"nickname\" : \"pickple0\",\n \"profileImageUrl\" : \"https://amazon.image\"\n }, {\n \"id\" : 4,\n \"nickname\" : \"pickple1\",\n \"profileImageUrl\" : \"https://amazon.image\"\n } ],\n \"createdAt\" : \"2023-12-03T10:27:41.347575\"\n}" + "value" : "{\n \"id\" : 3,\n \"roomName\" : \"pickple2\",\n \"roomIconImageUrl\" : \"https://amazon2.image\",\n \"type\" : \"개인\",\n \"domainId\" : 4,\n \"memberCount\" : 2,\n \"maxMemberCount\" : 2,\n \"playStartTime\" : null,\n \"playTimeMinutes\" : null,\n \"members\" : [ {\n \"id\" : 3,\n \"nickname\" : \"pickple1\",\n \"profileImageUrl\" : \"https://amazon1.image\"\n }, {\n \"id\" : 4,\n \"nickname\" : \"pickple2\",\n \"profileImageUrl\" : \"https://amazon2.image\"\n } ],\n \"createdAt\" : \"2024-04-23T13:24:44.85206\"\n}" } } } @@ -1430,7 +1382,7 @@ "schema" : { "type" : "string" }, - "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMCIsImlhdCI6MTcwMTU2Njg2MSwiZXhwIjoxNzAxNTY4MDkxfQ.af7bpBpMXewUOjbOgyMt5rlPsWCHBCpnugFg_tT6z0w7ADl1go0tyWKui2lr8ZwoRJSUf_IgXOxOrGft3-Vo7w" + "example" : "Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiIxMCIsImlhdCI6MTcxMzg0NjI4NSwiZXhwIjoxNzEzODQ3NTE1fQ.oYwN4RsrfdlW94XN2_JUy7DzoAr4Q_QSmeW0-1miiIxUqHFbY7S5lzRGZLCLJkmhdxnEs_fNDlviRtVzH5dg3w" } ], "responses" : { "200" : { @@ -1442,7 +1394,7 @@ }, "examples" : { "find-chatroom-by-id" : { - "value" : "{\n \"id\" : 7,\n \"roomName\" : \"pickple1\",\n \"roomIconImageUrl\" : \"https://amazon.image\",\n \"type\" : \"개인\",\n \"domainId\" : 11,\n \"memberCount\" : 2,\n \"maxMemberCount\" : 2,\n \"playStartTime\" : null,\n \"playTimeMinutes\" : null,\n \"members\" : [ {\n \"id\" : 10,\n \"nickname\" : \"pickple0\",\n \"profileImageUrl\" : \"https://amazon.image\"\n }, {\n \"id\" : 11,\n \"nickname\" : \"pickple1\",\n \"profileImageUrl\" : \"https://amazon.image\"\n } ],\n \"createdAt\" : \"2023-12-03T10:27:41.56635\"\n}" + "value" : "{\n \"id\" : 7,\n \"roomName\" : \"pickple2\",\n \"roomIconImageUrl\" : \"https://amazon2.image\",\n \"type\" : \"개인\",\n \"domainId\" : 11,\n \"memberCount\" : 2,\n \"maxMemberCount\" : 2,\n \"playStartTime\" : null,\n \"playTimeMinutes\" : null,\n \"members\" : [ {\n \"id\" : 10,\n \"nickname\" : \"pickple1\",\n \"profileImageUrl\" : \"https://amazon1.image\"\n }, {\n \"id\" : 11,\n \"nickname\" : \"pickple2\",\n \"profileImageUrl\" : \"https://amazon2.image\"\n } ],\n \"createdAt\" : \"2024-04-23T13:24:45.085962\"\n}" } } } @@ -1841,6 +1793,51 @@ } } }, + "ChatRoomResponse" : { + "title" : "ChatRoomResponse", + "type" : "array", + "items" : { + "type" : "object", + "properties" : { + "createdAt" : { + "type" : "string", + "description" : "채팅방 생성 시각" + }, + "lastMessageCreatedAt" : { + "type" : "string", + "description" : "마지막 메시지 전송 시각" + }, + "maxMemberCount" : { + "type" : "number", + "description" : "채팅방 인원제한" + }, + "lastMessageContent" : { + "type" : "string", + "description" : "마지막 메시지" + }, + "memberCount" : { + "type" : "number", + "description" : "현재 채팅방 인원수" + }, + "roomIconImageUrl" : { + "type" : "string", + "description" : "채팅방 아이콘 이미지 경로" + }, + "id" : { + "type" : "number", + "description" : "채팅방 ID" + }, + "type" : { + "type" : "string", + "description" : "채팅방 타입 (개인, 게스트, 크루)" + }, + "roomName" : { + "type" : "string", + "description" : "채팅방 이름" + } + } + } + }, "ChatRoomDetailResponse" : { "title" : "ChatRoomDetailResponse", "type" : "object", @@ -1900,51 +1897,6 @@ } } }, - "ChatRoomResponse" : { - "title" : "ChatRoomResponse", - "type" : "array", - "items" : { - "type" : "object", - "properties" : { - "createdAt" : { - "type" : "string", - "description" : "채팅방 생성 시각" - }, - "lastMessageCreatedAt" : { - "type" : "string", - "description" : "마지막 메시지 전송 시각" - }, - "maxMemberCount" : { - "type" : "number", - "description" : "채팅방 인원제한" - }, - "lastMessageContent" : { - "type" : "string", - "description" : "마지막 메시지" - }, - "memberCount" : { - "type" : "number", - "description" : "현재 채팅방 인원수" - }, - "roomIconImageUrl" : { - "type" : "string", - "description" : "채팅방 아이콘 이미지 경로" - }, - "id" : { - "type" : "number", - "description" : "채팅방 ID" - }, - "type" : { - "type" : "string", - "description" : "채팅방 타입 (개인, 게스트, 크루)" - }, - "roomName" : { - "type" : "string", - "description" : "채팅방 이름" - } - } - } - }, "MemberProfileResponse" : { "title" : "MemberProfileResponse", "type" : "object", diff --git a/src/test/java/kr/pickple/back/address/controller/AddressControllerTest.java b/src/test/java/kr/pickple/back/address/controller/AddressControllerTest.java new file mode 100644 index 00000000..e391e180 --- /dev/null +++ b/src/test/java/kr/pickple/back/address/controller/AddressControllerTest.java @@ -0,0 +1,45 @@ +package kr.pickple.back.address.controller; + +import static org.hamcrest.Matchers.is; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import kr.pickple.back.address.domain.AllAddress; +import kr.pickple.back.address.implement.AddressReader; + +@SpringBootTest +@AutoConfigureMockMvc +@AutoConfigureRestDocs +class AddressControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Autowired + private AddressReader addressReader; + + @Test + @DisplayName("모든 지역 목록을 조회한다.") + void findAllAddress_ReturnAllAddressResponse() throws Exception { + final AllAddress expectedAllAddress = addressReader.readAllAddress(); + + mockMvc.perform(get("/address")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.addressDepth1").value(expectedAllAddress.getAddressDepth1Name())) + .andExpect(jsonPath("$.addressDepth2List").value(is(expectedAllAddress.getAddressDepth2Names()))) + .andDo(print()); + } +} diff --git a/src/test/java/kr/pickple/back/address/docs/AddressDocumentTest.java b/src/test/java/kr/pickple/back/address/docs/AddressDocumentTest.java index eb23d747..66117aff 100644 --- a/src/test/java/kr/pickple/back/address/docs/AddressDocumentTest.java +++ b/src/test/java/kr/pickple/back/address/docs/AddressDocumentTest.java @@ -1,12 +1,14 @@ package kr.pickple.back.address.docs; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.*; -import static com.epages.restdocs.apispec.ResourceDocumentation.*; -import static com.epages.restdocs.apispec.Schema.*; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static com.epages.restdocs.apispec.Schema.schema; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -16,46 +18,42 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.restdocs.payload.JsonFieldType; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.transaction.annotation.Transactional; import com.epages.restdocs.apispec.ResourceSnippetParameters; -@Transactional @SpringBootTest @AutoConfigureMockMvc @AutoConfigureRestDocs class AddressDocumentTest { @Autowired - protected MockMvc mockMvc; + private MockMvc mockMvc; @Test @DisplayName("지역 목록 조회") void findAllAddress_ReturnAllAddressResponse() throws Exception { - // when - final ResultActions resultActions = mockMvc.perform(get("/address")) - .andExpect(status().isOk()); - - // then - resultActions.andDo(document("find-all-address", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - resource( - ResourceSnippetParameters.builder() - .tag("Address") - .summary("지역 목록 조회") - .description("전체 지역 목록을 조회한다.") - .responseSchema(schema("AllAddressResponse")) - .responseFields( - fieldWithPath("addressDepth1").type(JsonFieldType.STRING) - .description("주소1(도, 시)"), - fieldWithPath("addressDepth2List").type(JsonFieldType.ARRAY) - .description("주소1에 속한 주소2(구) 목록") - ) - .build() + mockMvc.perform(get("/address")) + .andExpect(status().isOk()) + .andDo(document("find-all-address", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + resource( + ResourceSnippetParameters.builder() + .tag("Address") + .summary("지역 목록 조회") + .description("전체 지역 목록을 조회한다.") + .responseSchema(schema("AllAddressResponse")) + .responseFields( + fieldWithPath("addressDepth1") + .type(JsonFieldType.STRING) + .description("주소1(도, 시)"), + fieldWithPath("addressDepth2List"). + type(JsonFieldType.ARRAY) + .description("주소1에 속한 주소2(구) 목록") + ) + .build() + ) ) - ) - ); + ); } } diff --git a/src/test/java/kr/pickple/back/address/implement/AddressReaderTest.java b/src/test/java/kr/pickple/back/address/implement/AddressReaderTest.java new file mode 100644 index 00000000..1bc5afb2 --- /dev/null +++ b/src/test/java/kr/pickple/back/address/implement/AddressReaderTest.java @@ -0,0 +1,167 @@ +package kr.pickple.back.address.implement; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; + +import java.util.List; +import java.util.Optional; + +import org.junit.jupiter.api.DisplayName; +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 kr.pickple.back.address.domain.Address; +import kr.pickple.back.address.domain.AllAddress; +import kr.pickple.back.address.domain.MainAddress; +import kr.pickple.back.address.exception.AddressException; +import kr.pickple.back.address.repository.AddressDepth1Repository; +import kr.pickple.back.address.repository.AddressDepth2Repository; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; + +@ExtendWith(MockitoExtension.class) +class AddressReaderTest { + + @Mock + private AddressDepth1Repository addressDepth1Repository; + + @Mock + private AddressDepth2Repository addressDepth2Repository; + + @InjectMocks + private AddressReader addressReader; + + @Test + @DisplayName("모든 지역 목록을 조회한다.") + void readAllAddress_ReturnAllAddress() { + given(addressDepth1Repository.findAll()) + .willReturn(List.of(addressDepth1Entity())); + given(addressDepth2Repository.findAllByAddressDepth1Id(anyLong())) + .willReturn(List.of(addressDepth2Entity())); + + final AllAddress expectedAllAddress = AllAddress.builder() + .addressDepth1Name("서울시") + .addressDepth2Names(List.of("강남구")) + .build(); + + assertThat(addressReader.readAllAddress()) + .isEqualTo(expectedAllAddress); + } + + @Test + @DisplayName("주소1과 주소2의 ID를 통해 주 활동 지역을 조회한다.") + void readMainAddressByIds_ReturnMainAddress() { + given(addressDepth1Repository.findById(anyLong())) + .willReturn(Optional.of(addressDepth1Entity())); + given(addressDepth2Repository.findById(anyLong())) + .willReturn(Optional.of(addressDepth2Entity())); + + assertThat(addressReader.readMainAddressByIds(1L, 1L)) + .isEqualTo(expectedMainAddress()); + } + + @Test + @DisplayName("주소1의 ID에 해당하는 주소가 없는 경우 예외를 던진다.") + void readMainAddressByIds_NotExistAddressDepth1_Exception() { + given(addressDepth1Repository.findById(anyLong())) + .willReturn(Optional.empty()); + + assertThatThrownBy(() -> addressReader.readMainAddressByIds(2L, 1L)) + .isInstanceOf(AddressException.class); + } + + @Test + @DisplayName("주소2의 ID에 해당하는 주소가 없는 경우 예외를 던진다.") + void readMainAddressByIds_NotExistAddressDepth2_Exception() { + given(addressDepth1Repository.findById(anyLong())) + .willReturn(Optional.of(addressDepth1Entity())); + given(addressDepth2Repository.findById(anyLong())) + .willReturn(Optional.empty()); + + assertThatThrownBy(() -> addressReader.readMainAddressByIds(1L, 2L)) + .isInstanceOf(AddressException.class); + } + + @Test + @DisplayName("주소1과 주소2의 이름을 통해 주 활동 지역을 조회한다.") + void readMainAddressByNames_ReturnMainAddress() { + given(addressDepth1Repository.findByName(anyString())) + .willReturn(Optional.of(addressDepth1Entity())); + given(addressDepth2Repository.findByNameAndAddressDepth1Id(anyString(), anyLong())) + .willReturn(Optional.of(addressDepth2Entity())); + + assertThat(addressReader.readMainAddressByNames("서울시", "강남구")) + .isEqualTo(expectedMainAddress()); + } + + @Test + @DisplayName("주소1의 이름에 해당하는 주소가 없는 경우 예외를 던진다.") + void readMainAddressByNames_NotExistAddressDepth1_Exception() { + given(addressDepth1Repository.findByName(anyString())) + .willReturn(Optional.empty()); + + assertThatThrownBy(() -> addressReader.readMainAddressByNames("뉴욕시", "강남구")) + .isInstanceOf(AddressException.class); + } + + @Test + @DisplayName("주소2의 이름에 해당하는 주소가 없는 경우 예외를 던진다.") + void readMainAddressByNames_NotExistAddressDepth2_Exception() { + given(addressDepth1Repository.findByName(anyString())) + .willReturn(Optional.of(addressDepth1Entity())); + given(addressDepth2Repository.findByNameAndAddressDepth1Id(anyString(), anyLong())) + .willReturn(Optional.empty()); + + assertThatThrownBy(() -> addressReader.readMainAddressByNames("서울시", "뉴욕구")) + .isInstanceOf(AddressException.class); + } + + @Test + @DisplayName("주소를 주소1과 주소2로 나누고 이를 통해 주 활동 지역을 조회한다.") + void readMainAddressFromFullAddress_ReturnMainAddress() { + given(addressDepth1Repository.findByName(anyString())) + .willReturn(Optional.of(addressDepth1Entity())); + given(addressDepth2Repository.findByNameAndAddressDepth1Id(anyString(), anyLong())) + .willReturn(Optional.of(addressDepth2Entity())); + + assertThat(addressReader.readMainAddressFromFullAddress("서울시 강남구 논현동 1")) + .isEqualTo(expectedMainAddress()); + } + + private AddressDepth1Entity addressDepth1Entity() { + return AddressDepth1Entity.builder() + .id(1L) + .name("서울시") + .build(); + } + + private AddressDepth2Entity addressDepth2Entity() { + return AddressDepth2Entity.builder() + .id(1L) + .name("강남구") + .addressDepth1Id(1L) + .build(); + } + + private MainAddress expectedMainAddress() { + final Address addressDepth1 = Address.builder() + .id(1L) + .name("서울시") + .build(); + final Address addressDepth2 = Address.builder() + .id(1L) + .name("강남구") + .build(); + + return MainAddress.builder() + .addressDepth1(addressDepth1) + .addressDepth2(addressDepth2) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/address/service/AddressServiceTest.java b/src/test/java/kr/pickple/back/address/service/AddressServiceTest.java deleted file mode 100644 index 52663b0b..00000000 --- a/src/test/java/kr/pickple/back/address/service/AddressServiceTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package kr.pickple.back.address.service; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import kr.pickple.back.address.dto.response.AllAddressResponse; -import kr.pickple.back.address.dto.response.MainAddressResponse; - -@SpringBootTest -class AddressServiceTest { - - @Autowired - private AddressService addressService; - - @Test - @DisplayName("지역 목록 조회 시, 전체 도, 시, 구 정보를 반환한다.") - void findAllAddress_ReturnAllAddressNames() { - //when - AllAddressResponse allAddressResponse = addressService.findAllAddress(); - - //then - assertThat(allAddressResponse.getAddressDepth1()).isNotNull(); - assertThat(allAddressResponse.getAddressDepth2List()).isNotEmpty(); - } - - @Test - @DisplayName("주 활동 지역 조회 시, 올바른 주소1, 주소2 이름을 넣었을 때, 각각의 주소 객체를 반환한다.") - void findMainAddressByNames_ValidAddressNames_ReturnMainAddressInstance() { - //given - String addressDepth1Name = "서울시"; - String addressDepth2Name = "강남구"; - - //when - MainAddressResponse mainAddressResponse = addressService.findMainAddressByNames(addressDepth1Name, - addressDepth2Name); - - //then - assertThat(mainAddressResponse.getAddressDepth1().getName()).isEqualTo(addressDepth1Name); - assertThat(mainAddressResponse.getAddressDepth2().getName()).isEqualTo(addressDepth2Name); - } -} diff --git a/src/test/java/kr/pickple/back/address/util/AddressParserTest.java b/src/test/java/kr/pickple/back/address/util/AddressParserTest.java new file mode 100644 index 00000000..135f60a1 --- /dev/null +++ b/src/test/java/kr/pickple/back/address/util/AddressParserTest.java @@ -0,0 +1,26 @@ +package kr.pickple.back.address.util; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class AddressParserTest { + + @ParameterizedTest + @CsvSource(value = { + "서울시 강남구 논현동 1,서울시,강남구", + "서울 송파구 석촌동 2,서울시,송파구", + "경기도 성남시 수정구 단대동 3,경기도,성남시" + }) + @DisplayName("전체 주소를 주소1과 주소2로 나눈다.") + void splitToAddressDepth1And2_ReturnAddressDepth1And2List( + final String mainAddressName, + final String addressDepth1Name, + final String addressDepth2Name + ) { + assertThat(AddressParser.splitToAddressDepth1And2(mainAddressName)) + .containsExactly(addressDepth1Name, addressDepth2Name); + } +} diff --git a/src/test/java/kr/pickple/back/alarm/IntegrationAlarmTest.java b/src/test/java/kr/pickple/back/alarm/IntegrationAlarmTest.java deleted file mode 100644 index e020f767..00000000 --- a/src/test/java/kr/pickple/back/alarm/IntegrationAlarmTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package kr.pickple.back.alarm; - -import com.fasterxml.jackson.databind.ObjectMapper; -import kr.pickple.back.auth.domain.token.JwtProvider; -import kr.pickple.back.fixture.setup.MemberSetup; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.web.servlet.MockMvc; - -@SpringBootTest -@AutoConfigureMockMvc -@AutoConfigureRestDocs -public abstract class IntegrationAlarmTest { - - @Autowired - protected MockMvc mockMvc; - - @Autowired - protected JwtProvider jwtProvider; - - @Autowired - protected MemberSetup memberSetup; - - @Autowired - protected ObjectMapper objectMapper; -} diff --git a/src/test/java/kr/pickple/back/alarm/controller/AlarmControllerTest.java b/src/test/java/kr/pickple/back/alarm/controller/AlarmControllerTest.java deleted file mode 100644 index 903f366b..00000000 --- a/src/test/java/kr/pickple/back/alarm/controller/AlarmControllerTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package kr.pickple.back.alarm.controller; - -import kr.pickple.back.alarm.IntegrationAlarmTest; -import kr.pickple.back.member.domain.Member; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; -import org.springframework.transaction.annotation.Transactional; - -import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -@Transactional -public class AlarmControllerTest extends IntegrationAlarmTest { - - private static final String BASE_URL = "/alarms"; - - @Test - @DisplayName("사용자는 재접속 시 읽지 않은 알람이 있는지 알 수 있다.") - void findUnreadAlarm_Success() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); - - //when & then - mockMvc.perform(get(BASE_URL + "/unread") - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)) - .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON)) - .andExpect(jsonPath("$.unread").exists()) - .andDo(print()); - } - - @Test - @DisplayName("사용자는 해당 사용자에게 온 모든 알람 목록을 조회할 수 있다.") - void findAllAlarms_Success() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); - - //when & then - mockMvc.perform(get(BASE_URL) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) - .param("size", "6")) - .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON)) - .andExpect(jsonPath("$.alarmResponse").exists()) - .andExpect(jsonPath("$.hasNext").exists()) - .andDo(print()); - } - - @Test - @DisplayName("사용자는 해당 사용자에게 온 모든 알람을 모두 삭제할 수 있다.") - void deleteAllAlarms_Success() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); - - //when & then - mockMvc.perform(delete(BASE_URL) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)) - .andExpect(status().isNoContent()) - .andDo(print()); - } -} diff --git a/src/test/java/kr/pickple/back/alarm/controller/CrewAlarmControllerTest.java b/src/test/java/kr/pickple/back/alarm/controller/CrewAlarmControllerTest.java deleted file mode 100644 index bd6d5f40..00000000 --- a/src/test/java/kr/pickple/back/alarm/controller/CrewAlarmControllerTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package kr.pickple.back.alarm.controller; - -import kr.pickple.back.alarm.IntegrationAlarmTest; -import kr.pickple.back.alarm.domain.CrewAlarm; -import kr.pickple.back.alarm.dto.request.CrewAlarmUpdateStatusRequest; -import kr.pickple.back.alarm.repository.CrewAlarmRepository; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.fixture.domain.CrewAlarmFixtures; -import kr.pickple.back.fixture.setup.CrewSetup; -import kr.pickple.back.member.domain.Member; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.transaction.annotation.Transactional; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@Transactional -public class CrewAlarmControllerTest extends IntegrationAlarmTest { - - private static final String BASE_URL = "/crew-alarms"; - - @Autowired - protected CrewSetup crewSetup; - - @Autowired - private CrewAlarmRepository crewAlarmRepository; - - @Test - @DisplayName("사용자는 보내진 크루 알람에 대하여 읽음 처리를 할 수 있다.") - void updateCrewAlarmStatus_Success() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final Crew crew = crewSetup.saveWithConfirmedMembers(1); - final CrewAlarm crewAlarm = crewAlarmRepository.save(CrewAlarmFixtures.crewAlarmBuild(member, crew)); - final CrewAlarmUpdateStatusRequest request = CrewAlarmUpdateStatusRequest.from(true); - - //when - final ResultActions resultActions = mockMvc.perform(patch(BASE_URL + "/" + crewAlarm.getId()) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))); - - //then - resultActions - .andExpect(status().isNoContent()); - final CrewAlarm updatedCrewAlarm = crewAlarmRepository.findById(crewAlarm.getId()).get(); - assertTrue(updatedCrewAlarm.getIsRead()); - } -} diff --git a/src/test/java/kr/pickple/back/alarm/controller/GameAlarmControllerTest.java b/src/test/java/kr/pickple/back/alarm/controller/GameAlarmControllerTest.java deleted file mode 100644 index 93dd7b75..00000000 --- a/src/test/java/kr/pickple/back/alarm/controller/GameAlarmControllerTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package kr.pickple.back.alarm.controller; - -import kr.pickple.back.alarm.IntegrationAlarmTest; -import kr.pickple.back.alarm.domain.GameAlarm; -import kr.pickple.back.alarm.dto.request.GameAlarmUpdateStatusRequest; -import kr.pickple.back.alarm.repository.GameAlarmRepository; -import kr.pickple.back.fixture.domain.GameAlarmFixtures; -import kr.pickple.back.fixture.setup.GameSetup; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.member.domain.Member; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.transaction.annotation.Transactional; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@Transactional -public class GameAlarmControllerTest extends IntegrationAlarmTest { - - private static final String BASE_URL = "/game-alarms"; - - @Autowired - protected GameSetup gameSetup; - - @Autowired - private GameAlarmRepository gameAlarmRepository; - - @Test - @DisplayName("사용자는 보내진 게임 알람에 대하여 읽음 처리를 할 수 있다.") - void updateGameAlarmStatus_Success() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final Game game = gameSetup.saveWithConfirmedMembers(1); - final GameAlarm gameAlarm = gameAlarmRepository.save(GameAlarmFixtures.gameAlarmBuild(member, game)); - final GameAlarmUpdateStatusRequest request = GameAlarmUpdateStatusRequest.from(true); - - //when - final ResultActions resultActions = mockMvc.perform(patch(BASE_URL + "/" + gameAlarm.getId()) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))); - - //then - resultActions - .andExpect(status().isNoContent()); - final GameAlarm updatedGameAlarm = gameAlarmRepository.findById(gameAlarm.getId()).get(); - assertTrue(updatedGameAlarm.getIsRead()); - } -} diff --git a/src/test/java/kr/pickple/back/alarm/docs/AlarmDocumentTest.java b/src/test/java/kr/pickple/back/alarm/docs/AlarmDocumentTest.java deleted file mode 100644 index 52c0ba0a..00000000 --- a/src/test/java/kr/pickple/back/alarm/docs/AlarmDocumentTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package kr.pickple.back.alarm.docs; - -import com.epages.restdocs.apispec.ResourceSnippetParameters; -import kr.pickple.back.alarm.IntegrationAlarmTest; -import kr.pickple.back.member.domain.Member; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.http.HttpHeaders; -import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.transaction.annotation.Transactional; - -import static com.epages.restdocs.apispec.ResourceDocumentation.*; -import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -@Transactional -public class AlarmDocumentTest extends IntegrationAlarmTest { - - private static final String BASE_URL = "/alarms"; - - @Test - @DisplayName("사용자의 재접속 시 읽지 않은 알람이 있는지 확인") - void findUnreadAlarm_ReturnAlarmExistStatusResponse() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); - - //when - final ResultActions resultActions = mockMvc.perform(get(BASE_URL + "/unread") - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)); - - //then - resultActions - .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON)) - .andExpect(jsonPath("$.unread").exists()) - .andDo(document("find-unread-alarm", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - resource( - ResourceSnippetParameters.builder() - .tag("Alarm") - .summary("사용자의 재접속 시 읽지 않은 알람이 있는지 확인") - .description("사용자의 재접속 시 읽지 않은 알람이 있는지 확인한다.") - .responseFields( - fieldWithPath("unread").type(JsonFieldType.BOOLEAN) - .description("읽지 않은 알람 존재 여부") - ) - .requestHeaders( - headerWithName(HttpHeaders.AUTHORIZATION).description("Bearer 토큰") - ) - .build() - ) - )); - } - - @Test - @DisplayName("해당 사용자에게 온 모든 알람 목록을 조회") - void findAllAlarms_ReturnAlarmResponse() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); - - //when - final ResultActions resultActions = mockMvc.perform(get(BASE_URL) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) - .param("size", "6")); - - //then - resultActions - .andExpect(status().isOk()) - .andExpect(content().contentType(APPLICATION_JSON)) - .andExpect(jsonPath("$.alarmResponse").exists()) - .andExpect(jsonPath("$.hasNext").exists()) - .andDo(document("find-all-alarms", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - resource( - ResourceSnippetParameters.builder() - .tag("Alarm") - .summary("해당 사용자에게 온 모든 알람 목록을 조회") - .description("해당 사용자에게 온 모든 알람 목록을 조회한다.") - .responseFields( - fieldWithPath("alarmResponse").type(JsonFieldType.ARRAY) - .description("알람 응답 목록"), - fieldWithPath("hasNext").type(JsonFieldType.BOOLEAN) - .description("다음 페이지 존재 여부"), - fieldWithPath("cursorId").type(JsonFieldType.NUMBER).optional() - .description("커서 ID. null일 경우 다음 페이지 없음") - ) - .queryParameters( - parameterWithName("size").description("페이지 크기").optional() - ) - .requestHeaders( - headerWithName(HttpHeaders.AUTHORIZATION).description("Bearer 토큰") - ) - .build() - ) - )); - } - - @Test - @DisplayName("해당 사용자에게 온 모든 알람을 모두 삭제") - void deleteAllAlarms_ReturnVoid() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final HttpHeaders headers = new HttpHeaders(); - headers.setBearerAuth(accessToken); - - //when - final ResultActions resultActions = mockMvc.perform(delete(BASE_URL) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken)); - - //then - resultActions - .andExpect(status().isNoContent()) - .andDo(document("delete-all-alarms", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - resource( - ResourceSnippetParameters.builder() - .tag("Alarm") - .summary("해당 사용자에게 온 모든 알람을 모두 삭제") - .description("해당 사용자에게 온 모든 알람을 모두 삭제한다.") - .requestHeaders( - headerWithName(HttpHeaders.AUTHORIZATION).description("Bearer 토큰") - ) - .build() - ) - )); - } -} diff --git a/src/test/java/kr/pickple/back/alarm/docs/CrewAlarmDocumentTest.java b/src/test/java/kr/pickple/back/alarm/docs/CrewAlarmDocumentTest.java deleted file mode 100644 index 0dde6cb6..00000000 --- a/src/test/java/kr/pickple/back/alarm/docs/CrewAlarmDocumentTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package kr.pickple.back.alarm.docs; - -import com.epages.restdocs.apispec.ResourceSnippetParameters; -import kr.pickple.back.alarm.IntegrationAlarmTest; -import kr.pickple.back.alarm.domain.CrewAlarm; -import kr.pickple.back.alarm.dto.request.CrewAlarmUpdateStatusRequest; -import kr.pickple.back.alarm.repository.CrewAlarmRepository; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.fixture.domain.CrewAlarmFixtures; -import kr.pickple.back.fixture.setup.CrewSetup; -import kr.pickple.back.member.domain.Member; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.web.servlet.ResultActions; -import org.springframework.transaction.annotation.Transactional; - -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; -import static com.epages.restdocs.apispec.ResourceDocumentation.resource; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -@Transactional -public class CrewAlarmDocumentTest extends IntegrationAlarmTest { - - private static final String BASE_URL = "/crew-alarms"; - - @Autowired - protected CrewSetup crewSetup; - - @Autowired - private CrewAlarmRepository crewAlarmRepository; - - @Test - @DisplayName("사용자의 크루 알람에 대하여 읽음 여부 수정") - void updateCrewAlarmStatus_ReturnVoid() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final Crew crew = crewSetup.saveWithConfirmedMembers(1); - final CrewAlarm crewAlarm = crewAlarmRepository.save(CrewAlarmFixtures.crewAlarmBuild(member, crew)); - final CrewAlarmUpdateStatusRequest request = CrewAlarmUpdateStatusRequest.from(true); - - //when - final ResultActions resultActions = mockMvc.perform(patch(BASE_URL + "/{crewAlarmId}", crewAlarm.getId()) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))); - - //then - resultActions - .andExpect(status().isNoContent()) - .andDo(document("update-crew-alarm-status", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - resource( - ResourceSnippetParameters.builder() - .tag("crew Alarm") - .summary("사용자의 크루 알람에 대하여 읽음 여부 수정") - .description("사용자가 보낸 크루 알람의 읽음 상태를 변경한다.") - .requestFields( - fieldWithPath("isRead").type(JsonFieldType.BOOLEAN) - .description("읽음 상태") - ) - .pathParameters( - parameterWithName("crewAlarmId").description("크루 알람 ID") - ) - .requestHeaders( - headerWithName(HttpHeaders.AUTHORIZATION).description("Bearer 토큰") - ) - .build() - ) - )); - } -} diff --git a/src/test/java/kr/pickple/back/alarm/docs/GameAlarmDocumentTest.java b/src/test/java/kr/pickple/back/alarm/docs/GameAlarmDocumentTest.java deleted file mode 100644 index cf48a9c8..00000000 --- a/src/test/java/kr/pickple/back/alarm/docs/GameAlarmDocumentTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package kr.pickple.back.alarm.docs; - -import com.epages.restdocs.apispec.ResourceSnippetParameters; -import kr.pickple.back.alarm.IntegrationAlarmTest; -import kr.pickple.back.alarm.domain.GameAlarm; -import kr.pickple.back.alarm.dto.request.GameAlarmUpdateStatusRequest; -import kr.pickple.back.alarm.repository.GameAlarmRepository; -import kr.pickple.back.fixture.domain.GameAlarmFixtures; -import kr.pickple.back.fixture.setup.GameSetup; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.member.domain.Member; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.web.servlet.ResultActions; - -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; -import static com.epages.restdocs.apispec.ResourceDocumentation.resource; -import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -public class GameAlarmDocumentTest extends IntegrationAlarmTest { - - private static final String BASE_URL = "/game-alarms"; - - @Autowired - protected GameSetup gameSetup; - - @Autowired - private GameAlarmRepository gameAlarmRepository; - - @Test - @DisplayName("사용자의 게임 알람에 대하여 읽음 여부 수정") - void updateGameAlarmStatus_ReturnVoid() throws Exception { - //given - final Member member = memberSetup.save(); - final String accessToken = jwtProvider.createLoginToken(member.getId().toString()).getAccessToken(); - final Game game = gameSetup.saveWithConfirmedMembers(1); - final GameAlarm gameAlarm = gameAlarmRepository.save(GameAlarmFixtures.gameAlarmBuild(member, game)); - final GameAlarmUpdateStatusRequest request = GameAlarmUpdateStatusRequest.from(true); - - //when - final ResultActions resultActions = mockMvc.perform(patch(BASE_URL + "/{gameAlarmId}", gameAlarm.getId()) - .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) - .contentType(MediaType.APPLICATION_JSON) - .content(objectMapper.writeValueAsString(request))); - - //then - resultActions - .andExpect(status().isNoContent()) - .andDo(document("update-game-alarm-status", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - resource( - ResourceSnippetParameters.builder() - .tag("Game Alarm") - .summary("사용자의 게임 알람에 대하여 읽음 여부 수정") - .description("사용자가 보낸 게임 알람의 읽음 상태를 변경한다.") - .requestFields( - fieldWithPath("isRead").type(JsonFieldType.BOOLEAN) - .description("읽음 상태") - ) - .pathParameters( - parameterWithName("gameAlarmId").description("게임 알람 ID") - ) - .requestHeaders( - headerWithName(HttpHeaders.AUTHORIZATION).description("Bearer 토큰") - ) - .build() - ) - )); - } -} diff --git a/src/test/java/kr/pickple/back/auth/service/AuthServiceTest.java b/src/test/java/kr/pickple/back/auth/service/AuthServiceTest.java index 8fafd11a..75e927bd 100644 --- a/src/test/java/kr/pickple/back/auth/service/AuthServiceTest.java +++ b/src/test/java/kr/pickple/back/auth/service/AuthServiceTest.java @@ -15,8 +15,8 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; import kr.pickple.back.auth.config.property.JwtProperties; import kr.pickple.back.auth.config.resolver.TokenExtractor; import kr.pickple.back.auth.domain.oauth.OauthMember; @@ -28,167 +28,165 @@ import kr.pickple.back.auth.repository.RedisRepository; import kr.pickple.back.auth.service.authcode.AuthCodeRequestUrlProviderComposite; import kr.pickple.back.auth.service.memberclient.OauthMemberClientComposite; -import kr.pickple.back.fixture.domain.AddressFixtures; -import kr.pickple.back.fixture.domain.AuthFixtures; import kr.pickple.back.fixture.domain.MemberFixtures; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.repository.entity.MemberEntity; import kr.pickple.back.member.dto.response.AuthenticatedMemberResponse; import kr.pickple.back.member.repository.MemberRepository; @ExtendWith(MockitoExtension.class) class AuthServiceTest { - private static final OauthProvider OAUTH_PROVIDER = OauthProvider.KAKAO; - private static final String REFRESH_TOKEN_KEY = "refresh_token"; - - @InjectMocks - private OauthService oauthService; - - @Mock - private AuthCodeRequestUrlProviderComposite authCodeRequestUrlProviderComposite; - - @Mock - private OauthMemberClientComposite oauthMemberClientComposite; - - @Mock - private MemberRepository memberRepository; - - @Mock - private JwtProvider jwtProvider; - - @Mock - private RedisRepository redisRepository; - - @Mock - private JwtProperties jwtProperties; - - @Mock - private TokenExtractor tokenExtractor; - - @Test - @DisplayName("oauth 제공자를 받으면 Oauth 로그인 페이지를 반환한다.") - void redirectOauthLoginPage() { - // given - final String redirectUrl = "http://test.url"; - - given(authCodeRequestUrlProviderComposite.provide(OAUTH_PROVIDER)).willReturn(redirectUrl); - - // when - final String oauthRedirectUrl = oauthService.getAuthCodeRequestUrl(OAUTH_PROVIDER); - - // then - assertThat(oauthRedirectUrl).isEqualTo(redirectUrl); - } - - @Test - @DisplayName("oauth를 이용하여 로그인 할 수 있다.") - void oauthLogin_ReturnAuthenticated() { - // given - final String authCode = "authCode"; - final OauthMember oauthMember = AuthFixtures.oauthMemberBuild(); - final AddressDepth1 addressDepth1 = AddressFixtures.addressDepth1Build(); - final AddressDepth2 addressDepth2 = AddressFixtures.addressDepth2Build(); - final Member member = MemberFixtures.memberBuild(addressDepth1, addressDepth2); - final AuthTokens loginTokens = AuthFixtures.authTokensBuild(); - final Long refreshTokenExpirationTime = 10000L; - - given(memberRepository.findByOauthId(anyLong())).willReturn(Optional.ofNullable(member)); - given(oauthMemberClientComposite.fetch(any(OauthProvider.class), anyString())).willReturn(oauthMember); - given(jwtProvider.createLoginToken(anyString())).willReturn(loginTokens); - given(jwtProperties.getRefreshTokenExpirationTime()).willReturn(refreshTokenExpirationTime); - - // when - AuthenticatedMemberResponse authenticatedMemberResponse = oauthService.processLoginOrRegistration( - OAUTH_PROVIDER, authCode); - - // then - assertThat(authenticatedMemberResponse.getOauthId()).isEqualTo(member.getOauthId()); - assertThat(authenticatedMemberResponse.getOauthProvider()).isEqualTo(member.getOauthProvider()); - assertThat(authenticatedMemberResponse.getEmail()).isEqualTo(member.getEmail()); - assertThat(authenticatedMemberResponse.getNickname()).isEqualTo(member.getNickname()); - assertThat(authenticatedMemberResponse.getProfileImageUrl()).isEqualTo(member.getProfileImageUrl()); - assertThat(authenticatedMemberResponse.getAccessToken()).isEqualTo(loginTokens.getAccessToken()); - assertThat(authenticatedMemberResponse.getRefreshToken()).isEqualTo(loginTokens.getRefreshToken()); - assertThat(authenticatedMemberResponse.getAddressDepth1()).isEqualTo(addressDepth1.getName()); - assertThat(authenticatedMemberResponse.getAddressDepth2()).isEqualTo(addressDepth2.getName()); - - verify(memberRepository).findByOauthId(anyLong()); - verify(oauthMemberClientComposite).fetch(any(OauthProvider.class), anyString()); - verify(jwtProvider).createLoginToken(anyString()); - verify(jwtProperties).getRefreshTokenExpirationTime(); - verify(redisRepository).saveHash(anyString(), anyString(), any(), anyLong()); - } - - @Test - @DisplayName("oauth를 이용하여 로그인시 회원 정보가 존재하지 않을 경우 회원가입 관련 정보를 응답한다.") - void oauthLogin_ReturnRegistration() { - // given - final String authCode = "authCode"; - final OauthMember oauthMember = AuthFixtures.oauthMemberBuild(); - final Member member = null; - final AuthTokens registerToken = AuthFixtures.authTokensBuild(); - - given(memberRepository.findByOauthId(anyLong())).willReturn(Optional.ofNullable(member)); - given(oauthMemberClientComposite.fetch(any(OauthProvider.class), anyString())).willReturn(oauthMember); - given(jwtProvider.createRegisterToken(anyString())).willReturn(registerToken); - - // when - final AuthenticatedMemberResponse authenticatedMemberResponse = oauthService.processLoginOrRegistration( - OAUTH_PROVIDER, authCode); - - // then - assertThat(authenticatedMemberResponse.getOauthId()).isEqualTo(oauthMember.getOauthId()); - assertThat(authenticatedMemberResponse.getOauthProvider()).isEqualTo(oauthMember.getOauthProvider()); - assertThat(authenticatedMemberResponse.getEmail()).isEqualTo(oauthMember.getEmail()); - assertThat(authenticatedMemberResponse.getNickname()).isEqualTo(oauthMember.getNickname()); - assertThat(authenticatedMemberResponse.getProfileImageUrl()).isEqualTo(oauthMember.getProfileImageUrl()); - assertThat(authenticatedMemberResponse.getAccessToken()).isEqualTo(registerToken.getAccessToken()); - assertThat(authenticatedMemberResponse.getId()).isNull(); - assertThat(authenticatedMemberResponse.getRefreshToken()).isNull(); - assertThat(authenticatedMemberResponse.getAddressDepth1()).isNull(); - assertThat(authenticatedMemberResponse.getAddressDepth2()).isNull(); - - verify(memberRepository).findByOauthId(anyLong()); - verify(oauthMemberClientComposite).fetch(any(OauthProvider.class), anyString()); - verify(jwtProvider).createRegisterToken(anyString()); - } - - @Test - @DisplayName("accessToken이 만료되었을 때 새로 갱신 할 수 있다.") - void regenerateAccessToken() { - // given - final AuthTokens authTokens = AuthFixtures.authTokensBuild(); - final String authorizationHeader = "Bearer " + authTokens.getAccessToken(); - final RefreshToken refreshToken = AuthFixtures.refreshTokenBuild(); - final String regeneratedAccessToken = AuthFixtures.authTokensBuild().getAccessToken(); - - given(tokenExtractor.extractAccessToken(authorizationHeader)).willReturn(authTokens.getAccessToken()); - given(jwtProvider.isValidRefreshAndInvalidAccess(authTokens.getRefreshToken(), - authTokens.getAccessToken())).willReturn(true); - given(redisRepository.findHash(REFRESH_TOKEN_KEY, authTokens.getRefreshToken())).willReturn(refreshToken); - given(jwtProvider.regenerateAccessToken(String.valueOf(1L))).willReturn(regeneratedAccessToken); - - // when - final AccessTokenResponse accessTokenResponse = oauthService.regenerateAccessToken(authTokens.getRefreshToken(), - authorizationHeader); - - // then - assertThat(accessTokenResponse.getAccessToken()).isEqualTo(regeneratedAccessToken); - - verify(tokenExtractor).extractAccessToken(anyString()); - verify(jwtProvider).isValidRefreshAndInvalidAccess(anyString(), anyString()); - verify(redisRepository).findHash(anyString(), anyString()); - verify(jwtProvider).regenerateAccessToken(anyString()); - } - - @Test - @DisplayName("로그인된 사용자는 로그아웃을 할 수 있다.") - void logout() { - // given && when - final AuthTokens authTokens = AuthFixtures.authTokensBuild(); - oauthService.deleteRefreshToken(authTokens.getRefreshToken()); - - // then - verify(redisRepository).deleteHash(REFRESH_TOKEN_KEY, authTokens.getRefreshToken()); - } + // private static final OauthProvider OAUTH_PROVIDER = OauthProvider.KAKAO; + // private static final String REFRESH_TOKEN_KEY = "refresh_token"; + // + // @InjectMocks + // private OauthService oauthService; + // + // @Mock + // private AuthCodeRequestUrlProviderComposite authCodeRequestUrlProviderComposite; + // + // @Mock + // private OauthMemberClientComposite oauthMemberClientComposite; + // + // @Mock + // private MemberRepository memberRepository; + // + // @Mock + // private JwtProvider jwtProvider; + // + // @Mock + // private RedisRepository redisRepository; + // + // @Mock + // private JwtProperties jwtProperties; + // + // @Mock + // private TokenExtractor tokenExtractor; + // + // @Test + // @DisplayName("oauth 제공자를 받으면 Oauth 로그인 페이지를 반환한다.") + // void redirectOauthLoginPage() { + // // given + // final String redirectUrl = "http://test.url"; + // + // given(authCodeRequestUrlProviderComposite.provide(OAUTH_PROVIDER)).willReturn(redirectUrl); + // + // // when + // final String oauthRedirectUrl = oauthService.getAuthCodeRequestUrl(OAUTH_PROVIDER); + // + // // then + // assertThat(oauthRedirectUrl).isEqualTo(redirectUrl); + // } + // + // @Test + // @DisplayName("oauth를 이용하여 로그인 할 수 있다.") + // void oauthLogin_ReturnAuthenticated() { + // // given + // final String authCode = "authCode"; + // final OauthMember oauthMember = AuthFixtures.oauthMemberBuild(); + // final AddressDepth1Entity addressDepth1 = AddressFixtures.addressDepth1Build(); + // final AddressDepth2Entity addressDepth2 = AddressFixtures.addressDepth2Build(); + // final MemberEntity member = MemberFixtures.memberBuild(addressDepth1, addressDepth2); + // final AuthTokens loginTokens = AuthFixtures.authTokensBuild(); + // final Long refreshTokenExpirationTime = 10000L; + // + // given(memberRepository.findByOauthId(anyLong())).willReturn(Optional.ofNullable(member)); + // given(oauthMemberClientComposite.fetch(any(OauthProvider.class), anyString())).willReturn(oauthMember); + // given(jwtProvider.createLoginToken(anyString())).willReturn(loginTokens); + // given(jwtProperties.getRefreshTokenExpirationTime()).willReturn(refreshTokenExpirationTime); + // + // // when + // AuthenticatedMemberResponse authenticatedMemberResponse = oauthService.processLoginOrRegistration( + // OAUTH_PROVIDER, authCode); + // + // // then + // assertThat(authenticatedMemberResponse.getOauthId()).isEqualTo(member.getOauthId()); + // assertThat(authenticatedMemberResponse.getOauthProvider()).isEqualTo(member.getOauthProvider()); + // assertThat(authenticatedMemberResponse.getEmail()).isEqualTo(member.getEmail()); + // assertThat(authenticatedMemberResponse.getNickname()).isEqualTo(member.getNickname()); + // assertThat(authenticatedMemberResponse.getProfileImageUrl()).isEqualTo(member.getProfileImageUrl()); + // assertThat(authenticatedMemberResponse.getAccessToken()).isEqualTo(loginTokens.getAccessToken()); + // assertThat(authenticatedMemberResponse.getRefreshToken()).isEqualTo(loginTokens.getRefreshToken()); + // assertThat(authenticatedMemberResponse.getAddressDepth1()).isEqualTo(addressDepth1.getName()); + // assertThat(authenticatedMemberResponse.getAddressDepth2()).isEqualTo(addressDepth2.getName()); + // + // verify(memberRepository).findByOauthId(anyLong()); + // verify(oauthMemberClientComposite).fetch(any(OauthProvider.class), anyString()); + // verify(jwtProvider).createLoginToken(anyString()); + // verify(jwtProperties).getRefreshTokenExpirationTime(); + // verify(redisRepository).saveHash(anyString(), anyString(), any(), anyLong()); + // } + // + // @Test + // @DisplayName("oauth를 이용하여 로그인시 회원 정보가 존재하지 않을 경우 회원가입 관련 정보를 응답한다.") + // void oauthLogin_ReturnRegistration() { + // // given + // final String authCode = "authCode"; + // final OauthMember oauthMember = AuthFixtures.oauthMemberBuild(); + // final MemberEntity member = null; + // final AuthTokens registerToken = AuthFixtures.authTokensBuild(); + // + // given(memberRepository.findByOauthId(anyLong())).willReturn(Optional.ofNullable(member)); + // given(oauthMemberClientComposite.fetch(any(OauthProvider.class), anyString())).willReturn(oauthMember); + // given(jwtProvider.createRegisterToken(anyString())).willReturn(registerToken); + // + // // when + // final AuthenticatedMemberResponse authenticatedMemberResponse = oauthService.processLoginOrRegistration( + // OAUTH_PROVIDER, authCode); + // + // // then + // assertThat(authenticatedMemberResponse.getOauthId()).isEqualTo(oauthMember.getOauthId()); + // assertThat(authenticatedMemberResponse.getOauthProvider()).isEqualTo(oauthMember.getOauthProvider()); + // assertThat(authenticatedMemberResponse.getEmail()).isEqualTo(oauthMember.getEmail()); + // assertThat(authenticatedMemberResponse.getNickname()).isEqualTo(oauthMember.getNickname()); + // assertThat(authenticatedMemberResponse.getProfileImageUrl()).isEqualTo(oauthMember.getProfileImageUrl()); + // assertThat(authenticatedMemberResponse.getAccessToken()).isEqualTo(registerToken.getAccessToken()); + // assertThat(authenticatedMemberResponse.getId()).isNull(); + // assertThat(authenticatedMemberResponse.getRefreshToken()).isNull(); + // assertThat(authenticatedMemberResponse.getAddressDepth1()).isNull(); + // assertThat(authenticatedMemberResponse.getAddressDepth2()).isNull(); + // + // verify(memberRepository).findByOauthId(anyLong()); + // verify(oauthMemberClientComposite).fetch(any(OauthProvider.class), anyString()); + // verify(jwtProvider).createRegisterToken(anyString()); + // } + // + // @Test + // @DisplayName("accessToken이 만료되었을 때 새로 갱신 할 수 있다.") + // void regenerateAccessToken() { + // // given + // final AuthTokens authTokens = AuthFixtures.authTokensBuild(); + // final String authorizationHeader = "Bearer " + authTokens.getAccessToken(); + // final RefreshToken refreshToken = AuthFixtures.refreshTokenBuild(); + // final String regeneratedAccessToken = AuthFixtures.authTokensBuild().getAccessToken(); + // + // given(tokenExtractor.extractAccessToken(authorizationHeader)).willReturn(authTokens.getAccessToken()); + // given(jwtProvider.isValidRefreshAndInvalidAccess(authTokens.getRefreshToken(), + // authTokens.getAccessToken())).willReturn(true); + // given(redisRepository.findHash(REFRESH_TOKEN_KEY, authTokens.getRefreshToken())).willReturn(refreshToken); + // given(jwtProvider.regenerateAccessToken(String.valueOf(1L))).willReturn(regeneratedAccessToken); + // + // // when + // final AccessTokenResponse accessTokenResponse = oauthService.regenerateAccessToken(authTokens.getRefreshToken(), + // authorizationHeader); + // + // // then + // assertThat(accessTokenResponse.getAccessToken()).isEqualTo(regeneratedAccessToken); + // + // verify(tokenExtractor).extractAccessToken(anyString()); + // verify(jwtProvider).isValidRefreshAndInvalidAccess(anyString(), anyString()); + // verify(redisRepository).findHash(anyString(), anyString()); + // verify(jwtProvider).regenerateAccessToken(anyString()); + // } + // + // @Test + // @DisplayName("로그인된 사용자는 로그아웃을 할 수 있다.") + // void logout() { + // // given && when + // final AuthTokens authTokens = AuthFixtures.authTokensBuild(); + // oauthService.deleteRefreshToken(authTokens.getRefreshToken()); + // + // // then + // verify(redisRepository).deleteHash(REFRESH_TOKEN_KEY, authTokens.getRefreshToken()); + // } } diff --git a/src/test/java/kr/pickple/back/chat/IntegrationChatTest.java b/src/test/java/kr/pickple/back/chat/IntegrationChatTest.java index 5688d0ce..621d7115 100644 --- a/src/test/java/kr/pickple/back/chat/IntegrationChatTest.java +++ b/src/test/java/kr/pickple/back/chat/IntegrationChatTest.java @@ -4,13 +4,11 @@ import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.web.servlet.MockMvc; import com.fasterxml.jackson.databind.ObjectMapper; import kr.pickple.back.auth.domain.token.JwtProvider; -import kr.pickple.back.auth.service.OauthService; import kr.pickple.back.chat.service.ChatRoomService; import kr.pickple.back.fixture.setup.MemberSetup; diff --git a/src/test/java/kr/pickple/back/chat/docs/ChatMessageDocumentTest.java b/src/test/java/kr/pickple/back/chat/docs/ChatMessageDocumentTest.java index 5be48f9f..19a92546 100644 --- a/src/test/java/kr/pickple/back/chat/docs/ChatMessageDocumentTest.java +++ b/src/test/java/kr/pickple/back/chat/docs/ChatMessageDocumentTest.java @@ -1,13 +1,17 @@ package kr.pickple.back.chat.docs; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.*; -import static com.epages.restdocs.apispec.ResourceDocumentation.*; -import static com.epages.restdocs.apispec.Schema.*; -import static org.springframework.http.HttpHeaders.*; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static com.epages.restdocs.apispec.ResourceDocumentation.headerWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static com.epages.restdocs.apispec.Schema.schema; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.List; @@ -22,10 +26,8 @@ import kr.pickple.back.auth.domain.token.AuthTokens; import kr.pickple.back.chat.IntegrationChatTest; -import kr.pickple.back.chat.dto.request.PersonalChatRoomCreateRequest; import kr.pickple.back.chat.dto.response.ChatRoomDetailResponse; -import kr.pickple.back.fixture.dto.ChatDtoFixtures; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.repository.entity.MemberEntity; @Transactional class ChatMessageDocumentTest extends IntegrationChatTest { @@ -36,15 +38,14 @@ class ChatMessageDocumentTest extends IntegrationChatTest { @DisplayName("특정 채팅방의 모든 메시지 목록 조회") void findAllMessagesInRoom_ReturnChatMessageResponses() throws Exception { // given - final List members = memberSetup.save(2); - final Member sender = members.get(0); - final Member receiver = members.get(1); - - final PersonalChatRoomCreateRequest personalChatRoomCreateRequest = ChatDtoFixtures.personalChatRoomCreateRequestBuild( - receiver.getId()); - final ChatRoomDetailResponse personalChatRoom = chatRoomService.createPersonalRoom(sender.getId(), - personalChatRoomCreateRequest); + final List members = memberSetup.save(2); + final MemberEntity sender = members.get(0); + final MemberEntity receiver = members.get(1); + final ChatRoomDetailResponse personalChatRoom = chatRoomService.createPersonalRoom( + sender.getId(), + receiver.getId() + ); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(sender.getId())); // when diff --git a/src/test/java/kr/pickple/back/chat/docs/ChatRoomDocumentTest.java b/src/test/java/kr/pickple/back/chat/docs/ChatRoomDocumentTest.java index 4168f22b..975ff5a3 100644 --- a/src/test/java/kr/pickple/back/chat/docs/ChatRoomDocumentTest.java +++ b/src/test/java/kr/pickple/back/chat/docs/ChatRoomDocumentTest.java @@ -1,14 +1,19 @@ package kr.pickple.back.chat.docs; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.*; -import static com.epages.restdocs.apispec.ResourceDocumentation.*; -import static com.epages.restdocs.apispec.Schema.*; -import static kr.pickple.back.chat.domain.RoomType.*; -import static org.springframework.http.HttpHeaders.*; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static com.epages.restdocs.apispec.ResourceDocumentation.headerWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static com.epages.restdocs.apispec.Schema.schema; +import static kr.pickple.back.chat.domain.RoomType.PERSONAL; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.List; @@ -27,7 +32,7 @@ import kr.pickple.back.chat.dto.request.PersonalChatRoomCreateRequest; import kr.pickple.back.chat.dto.response.ChatRoomDetailResponse; import kr.pickple.back.fixture.dto.ChatDtoFixtures; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.repository.entity.MemberEntity; @Transactional class ChatRoomDocumentTest extends IntegrationChatTest { @@ -38,14 +43,13 @@ class ChatRoomDocumentTest extends IntegrationChatTest { @DisplayName("새 1:1 채팅방 생성") void createPersonalRoom_ReturnChatRoomDetailResponse() throws Exception { // given - final List members = memberSetup.save(2); - final Member sender = members.get(0); - final Member receiver = members.get(1); + final List members = memberSetup.save(2); + final MemberEntity sender = members.get(0); + final MemberEntity receiver = members.get(1); + chatRoomService.createPersonalRoom(sender.getId(), receiver.getId()); final PersonalChatRoomCreateRequest personalChatRoomCreateRequest = ChatDtoFixtures.personalChatRoomCreateRequestBuild( receiver.getId()); - chatRoomService.createPersonalRoom(sender.getId(), personalChatRoomCreateRequest); - final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(sender.getId())); final String requestBody = objectMapper.writeValueAsString(personalChatRoomCreateRequest); @@ -117,13 +121,10 @@ void createPersonalRoom_ReturnChatRoomDetailResponse() throws Exception { @DisplayName("특정 사용자와의 1:1 채팅방 존재 여부 조회") void findActivePersonalChatRoomWithReceiver_ReturnPersonalChatRoomExistedResponse() throws Exception { // given - final List members = memberSetup.save(2); - final Member sender = members.get(0); - final Member receiver = members.get(1); - - final PersonalChatRoomCreateRequest personalChatRoomCreateRequest = ChatDtoFixtures.personalChatRoomCreateRequestBuild( - receiver.getId()); - chatRoomService.createPersonalRoom(sender.getId(), personalChatRoomCreateRequest); + final List members = memberSetup.save(2); + final MemberEntity sender = members.get(0); + final MemberEntity receiver = members.get(1); + chatRoomService.createPersonalRoom(sender.getId(), receiver.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(sender.getId())); @@ -168,18 +169,12 @@ void findActivePersonalChatRoomWithReceiver_ReturnPersonalChatRoomExistedRespons @DisplayName("채팅방 타입에 따른 참여중인 모든 채팅방 목록 조회") void findAllActiveChatRoomsByType_ReturnChatRoomResponses() throws Exception { // given - final List members = memberSetup.save(3); - final Member sender = members.get(0); - final Member receiver1 = members.get(1); - final Member receiver2 = members.get(2); - - final PersonalChatRoomCreateRequest personalChatRoomCreateRequest1 = ChatDtoFixtures.personalChatRoomCreateRequestBuild( - receiver1.getId()); - chatRoomService.createPersonalRoom(sender.getId(), personalChatRoomCreateRequest1); - - final PersonalChatRoomCreateRequest personalChatRoomCreateRequest2 = ChatDtoFixtures.personalChatRoomCreateRequestBuild( - receiver2.getId()); - chatRoomService.createPersonalRoom(sender.getId(), personalChatRoomCreateRequest2); + final List members = memberSetup.save(3); + final MemberEntity sender = members.get(0); + final MemberEntity receiver1 = members.get(1); + final MemberEntity receiver2 = members.get(2); + chatRoomService.createPersonalRoom(sender.getId(), receiver1.getId()); + chatRoomService.createPersonalRoom(sender.getId(), receiver2.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(sender.getId())); @@ -241,15 +236,14 @@ void findAllActiveChatRoomsByType_ReturnChatRoomResponses() throws Exception { @DisplayName("단일 채팅방 정보 상세 조회") void findChatRoomById_ReturnChatRoomDetailResponse() throws Exception { // given - final List members = memberSetup.save(2); - final Member sender = members.get(0); - final Member receiver = members.get(1); - - final PersonalChatRoomCreateRequest personalChatRoomCreateRequest = ChatDtoFixtures.personalChatRoomCreateRequestBuild( - receiver.getId()); - final ChatRoomDetailResponse personalChatRoom = chatRoomService.createPersonalRoom(sender.getId(), - personalChatRoomCreateRequest); + final List members = memberSetup.save(2); + final MemberEntity sender = members.get(0); + final MemberEntity receiver = members.get(1); + final ChatRoomDetailResponse personalChatRoom = chatRoomService.createPersonalRoom( + sender.getId(), + receiver.getId() + ); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(sender.getId())); // when diff --git a/src/test/java/kr/pickple/back/crew/controller/CrewControllerTest.java b/src/test/java/kr/pickple/back/crew/controller/CrewControllerTest.java index b9d4b53d..87dfcb58 100644 --- a/src/test/java/kr/pickple/back/crew/controller/CrewControllerTest.java +++ b/src/test/java/kr/pickple/back/crew/controller/CrewControllerTest.java @@ -1,14 +1,19 @@ package kr.pickple.back.crew.controller; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.auth.domain.token.AuthTokens; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.crew.dto.request.CrewMemberUpdateStatusRequest; -import kr.pickple.back.crew.IntegrationCrewTest; -import kr.pickple.back.fixture.dto.CrewDtoFixtures; -import kr.pickple.back.fixture.setup.AddressSetup; -import kr.pickple.back.member.domain.Member; +import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; +import static kr.pickple.back.common.domain.RegistrationStatus.WAITING; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.List; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -16,31 +21,54 @@ import org.springframework.test.web.servlet.ResultActions; import org.springframework.transaction.annotation.Transactional; -import java.util.List; - -import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; -import static org.springframework.http.HttpHeaders.AUTHORIZATION; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import kr.pickple.back.address.repository.AddressDepth1Repository; +import kr.pickple.back.address.repository.AddressDepth2Repository; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; +import kr.pickple.back.auth.domain.token.AuthTokens; +import kr.pickple.back.crew.IntegrationCrewTest; +import kr.pickple.back.crew.dto.request.CrewMemberUpdateStatusRequest; +import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.fixture.dto.CrewDtoFixtures; +import kr.pickple.back.member.repository.MemberPositionRepository; +import kr.pickple.back.member.repository.entity.MemberEntity; +import kr.pickple.back.member.repository.entity.MemberPositionEntity; @Transactional -public class CrewControllerTest extends IntegrationCrewTest { +class CrewControllerTest extends IntegrationCrewTest { private static final String BASE_URL = "/crews"; @Autowired - private AddressSetup addressSetup; + private AddressDepth1Repository addressDepth1Repository; + + @Autowired + private AddressDepth2Repository addressDepth2Repository; + + @Autowired + private MemberPositionRepository memberPositionRepository; + + @Autowired + private CrewRepository crewRepository; @Test @DisplayName("사용자는 해당 크루의 상세 정보를 조회할 수 있다.") void findCrewDetailsById_ReturnCrewResponse() throws Exception { //given - final Crew crew = crewSetup.saveWithConfirmedMembers(2); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); + + CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerConfirmed(member.getId(), crew.getId()); + crew = crewRepository.findById(crew.getId()).get(); + + final AddressDepth1Entity leaderAddressDepth1 = addressDepth1Repository.findById(leader.getAddressDepth1Id()) + .get(); + final AddressDepth2Entity leaderAddressDepth2 = addressDepth2Repository.findById(leader.getAddressDepth2Id()) + .get(); + final List leaderPositions = memberPositionRepository.findAllByMemberId(leader.getId()); //when final ResultActions resultActions = mockMvc.perform(get(BASE_URL + "/{crewId}", crew.getId())); @@ -58,19 +86,18 @@ void findCrewDetailsById_ReturnCrewResponse() throws Exception { .andExpect(jsonPath("status").value(crew.getStatus().getDescription())) .andExpect(jsonPath("likeCount").value(crew.getLikeCount())) .andExpect(jsonPath("competitionPoint").value(crew.getCompetitionPoint())) - .andExpect(jsonPath("leader.id").value(crewLeader.getId())) - .andExpect(jsonPath("leader.email").value(crewLeader.getEmail())) - .andExpect(jsonPath("leader.nickname").value(crewLeader.getNickname())) - .andExpect(jsonPath("leader.introduction").value(crewLeader.getIntroduction())) - .andExpect(jsonPath("leader.profileImageUrl").value(crewLeader.getProfileImageUrl())) - .andExpect(jsonPath("leader.mannerScore").value(crewLeader.getMannerScore())) - .andExpect(jsonPath("leader.mannerScoreCount").value(crewLeader.getMannerScoreCount())) - .andExpect(jsonPath("leader.addressDepth1").value(crewLeader.getAddressDepth1().getName())) - .andExpect(jsonPath("leader.addressDepth2").value(crewLeader.getAddressDepth2().getName())) - .andExpect(jsonPath("leader.positions[0]").value(crewLeader.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("leader.positions[1]").value(crewLeader.getPositions().get(1).getAcronym())) - .andExpect(jsonPath("members[0].id").value(crewLeader.getId())) - .andExpect(jsonPath("members[1].id").value(crewMember.getId())) + .andExpect(jsonPath("leader.id").value(leader.getId())) + .andExpect(jsonPath("leader.email").value(leader.getEmail())) + .andExpect(jsonPath("leader.nickname").value(leader.getNickname())) + .andExpect(jsonPath("leader.introduction").value(leader.getIntroduction())) + .andExpect(jsonPath("leader.profileImageUrl").value(leader.getProfileImageUrl())) + .andExpect(jsonPath("leader.mannerScore").value(leader.getMannerScore())) + .andExpect(jsonPath("leader.mannerScoreCount").value(leader.getMannerScoreCount())) + .andExpect(jsonPath("leader.addressDepth1").value(leaderAddressDepth1.getName())) + .andExpect(jsonPath("leader.addressDepth2").value(leaderAddressDepth2.getName())) + .andExpect(jsonPath("leader.positions[0]").value(leaderPositions.get(0).getPosition().getAcronym())) + .andExpect(jsonPath("members[0].id").value(leader.getId())) + .andExpect(jsonPath("members[1].id").value(member.getId())) .andDo(print()); } @@ -78,12 +105,13 @@ void findCrewDetailsById_ReturnCrewResponse() throws Exception { @DisplayName("사용자는 크루 모집글에 참여 신청을 할 수 있다.") void applyForCrewMemberShip_Success() throws Exception { //given - final List members = memberSetup.save(2); - final Member crewLeader = members.get(0); - final Member crewApplyMember = members.get(1); - final Crew crew = crewSetup.save(crewLeader); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); + + final CrewEntity crew = crewSetup.save(leader.getId()); - final String subject = String.valueOf(crewApplyMember.getId()); + final String subject = String.valueOf(member.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); //when @@ -100,19 +128,26 @@ void applyForCrewMemberShip_Success() throws Exception { @DisplayName("크루장은 크루 모집글에 참여 신청한 사용자 정보 목록을 조회할 수 있다.") void findAllCrewMembers_WaitingStatus_ReturnCrewResponse() throws Exception { //given - final Crew crew = crewSetup.saveWithConfirmedMembers(2); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); + + final CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerWaiting(member.getId(), crew.getId()); + + final AddressDepth1Entity leaderAddressDepth1 = addressDepth1Repository.findById(leader.getAddressDepth1Id()) + .get(); + final AddressDepth2Entity leaderAddressDepth2 = addressDepth2Repository.findById(leader.getAddressDepth2Id()) + .get(); + final List leaderPositions = memberPositionRepository.findAllByMemberId(leader.getId()); - final String subject = String.valueOf(crewLeader.getId()); + final String subject = String.valueOf(leader.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); //when final ResultActions resultActions = mockMvc.perform( get(BASE_URL + "/{crewId}/members", crew.getId()) - .param("status", CONFIRMED.getDescription()) + .param("status", WAITING.getDescription()) .header(AUTHORIZATION, "Bearer " + authTokens.getAccessToken()) ); @@ -129,19 +164,17 @@ void findAllCrewMembers_WaitingStatus_ReturnCrewResponse() throws Exception { .andExpect(jsonPath("status").value(crew.getStatus().getDescription())) .andExpect(jsonPath("likeCount").value(crew.getLikeCount())) .andExpect(jsonPath("competitionPoint").value(crew.getCompetitionPoint())) - .andExpect(jsonPath("leader.id").value(crewLeader.getId())) - .andExpect(jsonPath("leader.email").value(crewLeader.getEmail())) - .andExpect(jsonPath("leader.nickname").value(crewLeader.getNickname())) - .andExpect(jsonPath("leader.introduction").value(crewLeader.getIntroduction())) - .andExpect(jsonPath("leader.profileImageUrl").value(crewLeader.getProfileImageUrl())) - .andExpect(jsonPath("leader.mannerScore").value(crewLeader.getMannerScore())) - .andExpect(jsonPath("leader.mannerScoreCount").value(crewLeader.getMannerScoreCount())) - .andExpect(jsonPath("leader.addressDepth1").value(crewLeader.getAddressDepth1().getName())) - .andExpect(jsonPath("leader.addressDepth2").value(crewLeader.getAddressDepth2().getName())) - .andExpect(jsonPath("leader.positions[0]").value(crewLeader.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("leader.positions[1]").value(crewLeader.getPositions().get(1).getAcronym())) - .andExpect(jsonPath("members[0].id").value(crewLeader.getId())) - .andExpect(jsonPath("members[1].id").value(crewMember.getId())) + .andExpect(jsonPath("leader.id").value(leader.getId())) + .andExpect(jsonPath("leader.email").value(leader.getEmail())) + .andExpect(jsonPath("leader.nickname").value(leader.getNickname())) + .andExpect(jsonPath("leader.introduction").value(leader.getIntroduction())) + .andExpect(jsonPath("leader.profileImageUrl").value(leader.getProfileImageUrl())) + .andExpect(jsonPath("leader.mannerScore").value(leader.getMannerScore())) + .andExpect(jsonPath("leader.mannerScoreCount").value(leader.getMannerScoreCount())) + .andExpect(jsonPath("leader.addressDepth1").value(leaderAddressDepth1.getName())) + .andExpect(jsonPath("leader.addressDepth2").value(leaderAddressDepth2.getName())) + .andExpect(jsonPath("leader.positions[0]").value(leaderPositions.get(0).getPosition().getAcronym())) + .andExpect(jsonPath("members[0].id").value(member.getId())) .andDo(print()); } @@ -149,22 +182,23 @@ void findAllCrewMembers_WaitingStatus_ReturnCrewResponse() throws Exception { @DisplayName("크루장은 다른 사용자의 크루 모집글 참여 신청을 수락할 수 있다.") void updateCrewMemberRegistrationStatus_Success() throws Exception { //given - final Crew crew = crewSetup.saveWithWaitingMembers(2); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); - final String subject = String.valueOf(crewLeader.getId()); + final CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerWaiting(member.getId(), crew.getId()); + + final String subject = String.valueOf(leader.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); final CrewMemberUpdateStatusRequest crewMemberUpdateStatusRequest = CrewDtoFixtures - .crewMemberUpdateStatusRequest(CONFIRMED); + .crewMemberUpdateStatusRequestBuild(CONFIRMED); final String requestBody = objectMapper.writeValueAsString(crewMemberUpdateStatusRequest); //when final ResultActions resultActions = mockMvc.perform( - patch(BASE_URL + "/{crewId}/members/{memberId}", crew.getId(), crewMember.getId()) + patch(BASE_URL + "/{crewId}/members/{memberId}", crew.getId(), member.getId()) .contentType(MediaType.APPLICATION_JSON) .header(AUTHORIZATION, "Bearer " + authTokens.getAccessToken()) .content(requestBody) @@ -178,18 +212,19 @@ void updateCrewMemberRegistrationStatus_Success() throws Exception { @DisplayName("크루장은 다른 사용자의 크루 모집글 참여 신청을 거절할 수 있다.") void deleteCrewMember_CrewLeader_Success() throws Exception { //given - final Crew crew = crewSetup.saveWithWaitingMembers(2); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); + + final CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerWaiting(member.getId(), crew.getId()); - final String subject = String.valueOf(crewLeader.getId()); + final String subject = String.valueOf(leader.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); //when final ResultActions resultActions = mockMvc.perform( - delete(BASE_URL + "/{crewId}/members/{memberId}", crew.getId(), crewMember.getId()) + delete(BASE_URL + "/{crewId}/members/{memberId}", crew.getId(), member.getId()) .header(AUTHORIZATION, "Bearer " + authTokens.getAccessToken()) ); @@ -201,19 +236,25 @@ void deleteCrewMember_CrewLeader_Success() throws Exception { @DisplayName("사용자는 자신 위치 근처의 크루를 조회할 수 있다.") void findCrewsByAddress_Success() throws Exception { //given - final Crew crew = crewSetup.saveWithConfirmedMembers(2); - final AddressDepth1 addressDepth1 = addressSetup.findAddressDepth1("서울시"); - final AddressDepth2 addressDepth2 = addressSetup.findAddressDepth2("영등포구"); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); + + CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerConfirmed(member.getId(), crew.getId()); + crew = crewRepository.findById(crew.getId()).get(); + + final AddressDepth1Entity leaderAddressDepth1 = addressDepth1Repository.findById(leader.getAddressDepth1Id()) + .get(); + final AddressDepth2Entity leaderAddressDepth2 = addressDepth2Repository.findById(leader.getAddressDepth2Id()) + .get(); + final List leaderPositions = memberPositionRepository.findAllByMemberId(leader.getId()); //when final ResultActions resultActions = mockMvc.perform( get(BASE_URL) - .param("addressDepth1", addressDepth1.getName()) - .param("addressDepth2", addressDepth2.getName()) + .param("addressDepth1", "서울시") + .param("addressDepth2", "강남구") .param("page", "0") .param("size", "10") ); @@ -232,19 +273,19 @@ void findCrewsByAddress_Success() throws Exception { .andExpect(jsonPath("$[0].status").value(crew.getStatus().getDescription())) .andExpect(jsonPath("$[0].likeCount").value(crew.getLikeCount())) .andExpect(jsonPath("$[0].competitionPoint").value(crew.getCompetitionPoint())) - .andExpect(jsonPath("$[0].leader.id").value(crewLeader.getId())) - .andExpect(jsonPath("$[0].leader.email").value(crewLeader.getEmail())) - .andExpect(jsonPath("$[0].leader.nickname").value(crewLeader.getNickname())) - .andExpect(jsonPath("$[0].leader.introduction").value(crewLeader.getIntroduction())) - .andExpect(jsonPath("$[0].leader.profileImageUrl").value(crewLeader.getProfileImageUrl())) - .andExpect(jsonPath("$[0].leader.mannerScore").value(crewLeader.getMannerScore())) - .andExpect(jsonPath("$[0].leader.mannerScoreCount").value(crewLeader.getMannerScoreCount())) - .andExpect(jsonPath("$[0].leader.addressDepth1").value(crewLeader.getAddressDepth1().getName())) - .andExpect(jsonPath("$[0].leader.addressDepth2").value(crewLeader.getAddressDepth2().getName())) - .andExpect(jsonPath("$[0].leader.positions[0]").value(crewLeader.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("$[0].leader.positions[1]").value(crewLeader.getPositions().get(1).getAcronym())) - .andExpect(jsonPath("$[0].members[0].id").value(crewLeader.getId())) - .andExpect(jsonPath("$[0].members[1].id").value(crewMember.getId())) + .andExpect(jsonPath("$[0].leader.id").value(leader.getId())) + .andExpect(jsonPath("$[0].leader.email").value(leader.getEmail())) + .andExpect(jsonPath("$[0].leader.nickname").value(leader.getNickname())) + .andExpect(jsonPath("$[0].leader.introduction").value(leader.getIntroduction())) + .andExpect(jsonPath("$[0].leader.profileImageUrl").value(leader.getProfileImageUrl())) + .andExpect(jsonPath("$[0].leader.mannerScore").value(leader.getMannerScore())) + .andExpect(jsonPath("$[0].leader.mannerScoreCount").value(leader.getMannerScoreCount())) + .andExpect(jsonPath("$[0].leader.addressDepth1").value(leaderAddressDepth1.getName())) + .andExpect(jsonPath("$[0].leader.addressDepth2").value(leaderAddressDepth2.getName())) + .andExpect( + jsonPath("$[0].leader.positions[0]").value(leaderPositions.get(0).getPosition().getAcronym())) + .andExpect(jsonPath("$[0].members[0].id").value(leader.getId())) + .andExpect(jsonPath("$[0].members[1].id").value(member.getId())) .andDo(print()); } } diff --git a/src/test/java/kr/pickple/back/crew/docs/CrewDocumentTest.java b/src/test/java/kr/pickple/back/crew/docs/CrewDocumentTest.java index 649cfbce..7e87a2cb 100644 --- a/src/test/java/kr/pickple/back/crew/docs/CrewDocumentTest.java +++ b/src/test/java/kr/pickple/back/crew/docs/CrewDocumentTest.java @@ -1,16 +1,24 @@ package kr.pickple.back.crew.docs; -import com.epages.restdocs.apispec.ResourceSnippetParameters; -import com.epages.restdocs.apispec.SimpleType; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.auth.domain.token.AuthTokens; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.crew.dto.request.CrewMemberUpdateStatusRequest; -import kr.pickple.back.crew.IntegrationCrewTest; -import kr.pickple.back.fixture.dto.CrewDtoFixtures; -import kr.pickple.back.fixture.setup.AddressSetup; -import kr.pickple.back.member.domain.Member; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static com.epages.restdocs.apispec.ResourceDocumentation.headerWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static com.epages.restdocs.apispec.Schema.schema; +import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.util.List; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -19,31 +27,31 @@ import org.springframework.test.web.servlet.ResultActions; import org.springframework.transaction.annotation.Transactional; -import java.util.List; +import com.epages.restdocs.apispec.ResourceSnippetParameters; +import com.epages.restdocs.apispec.SimpleType; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; -import static com.epages.restdocs.apispec.ResourceDocumentation.*; -import static com.epages.restdocs.apispec.Schema.schema; -import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; -import static org.springframework.http.HttpHeaders.AUTHORIZATION; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import kr.pickple.back.auth.domain.token.AuthTokens; +import kr.pickple.back.crew.IntegrationCrewTest; +import kr.pickple.back.crew.dto.request.CrewMemberUpdateStatusRequest; +import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.fixture.dto.CrewDtoFixtures; +import kr.pickple.back.member.repository.entity.MemberEntity; @Transactional -public class CrewDocumentTest extends IntegrationCrewTest { +class CrewDocumentTest extends IntegrationCrewTest { private static final String BASE_URL = "/crews"; @Autowired - private AddressSetup addressSetup; + private CrewRepository crewRepository; @Test @DisplayName("크루원 모집글 상세 정보 조회") void findCrewById_ReturnCrewResponse() throws Exception { //given - final Crew crew = crewSetup.saveWithWaitingMembers(3); + final MemberEntity leader = memberSetup.save(); + final CrewEntity crew = crewSetup.save(leader.getId()); //when final ResultActions resultActions = mockMvc.perform(get(BASE_URL + "/{crewId}", crew.getId())) @@ -137,10 +145,11 @@ void findCrewById_ReturnCrewResponse() throws Exception { @DisplayName("크루 가입 신청") void applyForCrewMemberShip_ReturnVoid() throws Exception { //given - final List members = memberSetup.save(2); - final Member crewLeader = members.get(0); - final Member crewApplyMember = members.get(1); - final Crew crew = crewSetup.save(crewLeader); + final List members = memberSetup.save(2); + final MemberEntity crewLeader = members.get(0); + final MemberEntity crewApplyMember = members.get(1); + + final CrewEntity crew = crewSetup.save(crewLeader.getId()); final String subject = String.valueOf(crewApplyMember.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -178,13 +187,15 @@ void applyForCrewMemberShip_ReturnVoid() throws Exception { @DisplayName("크루원 모집에 참여 신청된 혹은 확정된 사용자 정보 목록 조회") void findAllCrewMembers_ReturnCrewResponseWithWaitingMembers() throws Exception { //given - final Crew crew = crewSetup.saveWithConfirmedMembers(2); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); - final String subject = String.valueOf(crewLeader.getId()); + CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerConfirmed(member.getId(), crew.getId()); + crew = crewRepository.findById(crew.getId()).get(); + + final String subject = String.valueOf(leader.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); //when @@ -283,22 +294,23 @@ void findAllCrewMembers_ReturnCrewResponseWithWaitingMembers() throws Exception @DisplayName("크루원 모집 참여 신청 수락") void updateCrewMemberRegistrationStatus_ReturnVoid() throws Exception { //given - final Crew crew = crewSetup.saveWithWaitingMembers(2); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); + + final CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerWaiting(member.getId(), crew.getId()); - final String subject = String.valueOf(crewLeader.getId()); + final String subject = String.valueOf(leader.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); final CrewMemberUpdateStatusRequest crewMemberUpdateStatusRequest = CrewDtoFixtures - .crewMemberUpdateStatusRequest(CONFIRMED); + .crewMemberUpdateStatusRequestBuild(CONFIRMED); final String requestBody = objectMapper.writeValueAsString(crewMemberUpdateStatusRequest); //when final ResultActions resultActions = mockMvc.perform( - patch(BASE_URL + "/{crewId}/members/{memberId}", crew.getId(), crewMember.getId()) + patch(BASE_URL + "/{crewId}/members/{memberId}", crew.getId(), member.getId()) .contentType(MediaType.APPLICATION_JSON) .header(AUTHORIZATION, "Bearer " + authTokens.getAccessToken()) .content(requestBody) @@ -337,18 +349,19 @@ void updateCrewMemberRegistrationStatus_ReturnVoid() throws Exception { @DisplayName("크루원 모집 참여 신청 거절/취소") void deleteCrewMember_ReturnVoid() throws Exception { //given - final Crew crew = crewSetup.saveWithWaitingMembers(2); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); - final String subject = String.valueOf(crewLeader.getId()); + final CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerWaiting(member.getId(), crew.getId()); + + final String subject = String.valueOf(leader.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); //when final ResultActions resultActions = mockMvc.perform( - delete(BASE_URL + "/{crewId}/members/{memberId}", crew.getId(), crewMember.getId()) + delete(BASE_URL + "/{crewId}/members/{memberId}", crew.getId(), member.getId()) .header(AUTHORIZATION, "Bearer " + authTokens.getAccessToken()) ) .andExpect(status().isNoContent()); @@ -380,17 +393,18 @@ void deleteCrewMember_ReturnVoid() throws Exception { @DisplayName("사용자 위치 근처 크루 조회") void findCrewsByAddress_ReturnCrews() throws Exception { //given - final Crew crew = crewSetup.saveWithConfirmedMembers(2); - final AddressDepth1 addressDepth1 = addressSetup.findAddressDepth1("서울시"); - final AddressDepth2 addressDepth2 = addressSetup.findAddressDepth2("영등포구"); - final Member crewLeader = crew.getLeader(); - final Member crewMember = crew.getCrewMembers().get(1).getMember(); + final List members = memberSetup.save(2); + final MemberEntity leader = members.get(0); + final MemberEntity member = members.get(1); + + final CrewEntity crew = crewSetup.save(leader.getId()); + crewSetup.registerConfirmed(member.getId(), crew.getId()); //when final ResultActions resultActions = mockMvc.perform( get(BASE_URL) - .param("addressDepth1", addressDepth1.getName()) - .param("addressDepth2", addressDepth2.getName()) + .param("addressDepth1", "서울시") + .param("addressDepth2", "강남구") .param("page", "0") .param("size", "10") ) diff --git a/src/test/java/kr/pickple/back/fixture/domain/AddressFixtures.java b/src/test/java/kr/pickple/back/fixture/domain/AddressFixtures.java deleted file mode 100644 index 06442312..00000000 --- a/src/test/java/kr/pickple/back/fixture/domain/AddressFixtures.java +++ /dev/null @@ -1,20 +0,0 @@ -package kr.pickple.back.fixture.domain; - -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; - -public class AddressFixtures { - - public static AddressDepth1 addressDepth1Build() { - return AddressDepth1.builder() - .name("서울시") - .build(); - } - - public static AddressDepth2 addressDepth2Build() { - return AddressDepth2.builder() - .name("영등포구") - .addressDepth1(addressDepth1Build()) - .build(); - } -} diff --git a/src/test/java/kr/pickple/back/fixture/domain/AuthFixtures.java b/src/test/java/kr/pickple/back/fixture/domain/AuthFixtures.java deleted file mode 100644 index f1c561eb..00000000 --- a/src/test/java/kr/pickple/back/fixture/domain/AuthFixtures.java +++ /dev/null @@ -1,36 +0,0 @@ -package kr.pickple.back.fixture.domain; - -import java.time.LocalDateTime; - -import kr.pickple.back.auth.domain.oauth.OauthMember; -import kr.pickple.back.auth.domain.oauth.OauthProvider; -import kr.pickple.back.auth.domain.token.AuthTokens; -import kr.pickple.back.auth.domain.token.RefreshToken; - -public class AuthFixtures { - - public static OauthMember oauthMemberBuild() { - return OauthMember.builder() - .oauthId(1L) - .oauthProvider(OauthProvider.KAKAO) - .email("pickple@pickple.kr") - .profileImageUrl("https://amazon.com/pickple/1") - .nickname("pickple") - .build(); - } - - public static AuthTokens authTokensBuild() { - return AuthTokens.builder() - .accessToken("accessToken") - .refreshToken("refreshToken") - .build(); - } - - public static RefreshToken refreshTokenBuild() { - return RefreshToken.builder() - .token("refreshToken") - .memberId(1L) - .createdAt(LocalDateTime.now()) - .build(); - } -} diff --git a/src/test/java/kr/pickple/back/fixture/domain/CrewAlarmFixtures.java b/src/test/java/kr/pickple/back/fixture/domain/CrewAlarmFixtures.java deleted file mode 100644 index 9dcd90a0..00000000 --- a/src/test/java/kr/pickple/back/fixture/domain/CrewAlarmFixtures.java +++ /dev/null @@ -1,18 +0,0 @@ -package kr.pickple.back.fixture.domain; - -import kr.pickple.back.alarm.domain.CrewAlarm; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.member.domain.Member; - -import static kr.pickple.back.alarm.domain.CrewAlarmType.CREW_LEADER_WAITING; - -public class CrewAlarmFixtures { - - public static CrewAlarm crewAlarmBuild(final Member member,final Crew crew) { - return CrewAlarm.builder() - .crew(crew) - .member(member) - .crewAlarmType(CREW_LEADER_WAITING) - .build(); - } -} diff --git a/src/test/java/kr/pickple/back/fixture/domain/CrewFixtures.java b/src/test/java/kr/pickple/back/fixture/domain/CrewFixtures.java deleted file mode 100644 index a9f448de..00000000 --- a/src/test/java/kr/pickple/back/fixture/domain/CrewFixtures.java +++ /dev/null @@ -1,66 +0,0 @@ -package kr.pickple.back.fixture.domain; - -import static kr.pickple.back.chat.domain.RoomType.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.IntStream; - -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.member.domain.Member; - -public class CrewFixtures { - - public static Crew crewBuild( - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2, - final Member leader - ) { - return Crew.builder() - .name("백둥크루1") - .content("안녕하세요 백둥크루1 입니다.") - .profileImageUrl("https://amazon.profileimage/1") - .backgroundImageUrl("https://amazon.backgroundimage/1") - .maxMemberCount(15) - .leader(leader) - .addressDepth1(addressDepth1) - .addressDepth2(addressDepth2) - .build(); - } - - public static ChatRoom crewChatRoomBuild() { - return ChatRoom.builder() - .name("백둥크루1") - .type(CREW) - .build(); - } - - public static List crewsBuild( - final Integer count, - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2, - final Member leader - ) { - final List crews = new ArrayList<>(); - - IntStream.range(0, count).forEach(i -> { - crews.add( - Crew.builder() - .name(String.format("백둥크루%d", i)) - .content(String.format("안녕하세요 백둥크루%d 입니다.", i)) - .profileImageUrl(String.format("https://amazon.profileimage/%d", i)) - .backgroundImageUrl(String.format("https://amazon.backgroundimage/%d", i)) - .maxMemberCount(15) - .leader(leader) - .addressDepth1(addressDepth1) - .addressDepth2(addressDepth2) - .build() - ); - }); - - return crews; - } -} diff --git a/src/test/java/kr/pickple/back/fixture/domain/GameAlarmFixtures.java b/src/test/java/kr/pickple/back/fixture/domain/GameAlarmFixtures.java deleted file mode 100644 index e301ed32..00000000 --- a/src/test/java/kr/pickple/back/fixture/domain/GameAlarmFixtures.java +++ /dev/null @@ -1,18 +0,0 @@ -package kr.pickple.back.fixture.domain; - -import kr.pickple.back.alarm.domain.GameAlarm; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.member.domain.Member; - -import static kr.pickple.back.alarm.domain.GameAlarmType.HOST_WAITING; - -public class GameAlarmFixtures { - - public static GameAlarm gameAlarmBuild(final Member member, final Game game) { - return GameAlarm.builder() - .game(game) - .member(member) - .gameAlarmType(HOST_WAITING) - .build(); - } -} diff --git a/src/test/java/kr/pickple/back/fixture/domain/GameFixtures.java b/src/test/java/kr/pickple/back/fixture/domain/GameFixtures.java deleted file mode 100644 index bdbed744..00000000 --- a/src/test/java/kr/pickple/back/fixture/domain/GameFixtures.java +++ /dev/null @@ -1,56 +0,0 @@ -package kr.pickple.back.fixture.domain; - -import static kr.pickple.back.chat.domain.RoomType.*; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.util.List; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.Point; -import org.locationtech.jts.geom.PrecisionModel; - -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.domain.RoomType; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.member.domain.Member; -import kr.pickple.back.position.domain.Position; - -public class GameFixtures { - - public static Game gameBuild( - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2, - final Member host - ) { - final GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326); - final Point point = geometryFactory.createPoint(new Coordinate(37.125, 126.75)); - - return Game.builder() - .content("하이하이 즐겜 한 판해요") - .playDate(LocalDate.now().minusDays(1)) - .playStartTime(LocalTime.of(11, 30)) - .playEndTime(LocalTime.of(13, 0)) - .playTimeMinutes(90) - .mainAddress("서울 영등포구 도림동 254") - .detailAddress("영등포 다목적 체육관 2층 201호") - .cost(100) - .maxMemberCount(5) - .point(point) - .host(host) - .addressDepth1(addressDepth1) - .addressDepth2(addressDepth2) - .positions(List.of(Position.CENTER, Position.POINT_GUARD)) - .build(); - } - - public static ChatRoom gameChatRoomBuild() { - return ChatRoom.builder() - .name("11.10 영등포구") - .type(GAME) - .build(); - } -} diff --git a/src/test/java/kr/pickple/back/fixture/domain/MemberFixtures.java b/src/test/java/kr/pickple/back/fixture/domain/MemberFixtures.java index a5ef8ce5..8510ce31 100644 --- a/src/test/java/kr/pickple/back/fixture/domain/MemberFixtures.java +++ b/src/test/java/kr/pickple/back/fixture/domain/MemberFixtures.java @@ -1,58 +1,49 @@ package kr.pickple.back.fixture.domain; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; +import java.util.List; + import kr.pickple.back.auth.domain.oauth.OauthProvider; +import kr.pickple.back.auth.domain.token.AuthTokens; import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.domain.MemberStatus; +import kr.pickple.back.member.domain.NewMember; import kr.pickple.back.position.domain.Position; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.IntStream; +public final class MemberFixtures { -public class MemberFixtures { + private MemberFixtures() { + } - public static Member memberBuild( - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2 - ) { + public static Member memberBuild() { return Member.builder() + .memberId(1L) .email("pickple1@pickple.kr") .nickname("pickple1") + .introduction("안녕하세요, 강백호입니다.") .profileImageUrl("https://amazon.image") - .status(MemberStatus.ACTIVE) - .oauthId(1L) - .oauthProvider(OauthProvider.KAKAO) - .addressDepth1(addressDepth1) - .addressDepth2(addressDepth2) - .positions(List.of(Position.CENTER, Position.POINT_GUARD)) + .mannerScore(0) + .mannerScoreCount(0) + .addressDepth1Name("서울시") + .addressDepth2Name("강남구") + .positions(List.of(Position.CENTER, Position.SHOOTING_GUARD)) .build(); } - public static List membersBuild( - final int count, - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2 - ) { - final List members = new ArrayList<>(); - - IntStream.range(0, count).forEach(i -> { - members.add( - Member.builder() - .email(String.format("pickple%d@pickple.kr", i)) - .nickname(String.format("pickple%d", i)) - .profileImageUrl("https://amazon.image") - .status(MemberStatus.ACTIVE) - .oauthId((long)i) - .oauthProvider(OauthProvider.KAKAO) - .addressDepth1(addressDepth1) - .addressDepth2(addressDepth2) - .positions(List.of(Position.CENTER, Position.POINT_GUARD)) - .build() - ); - }); + public static NewMember newMemberBuild() { + final AuthTokens authTokens = AuthTokens.builder() + .accessToken("accessToken") + .refreshToken("refreshToken") + .build(); - return members; + return NewMember.builder() + .authTokens(authTokens) + .nickname("pickple1") + .profileImageUrl("https://amazon.image") + .email("pickple1@pickple.kr") + .positions(List.of(Position.CENTER, Position.SHOOTING_GUARD)) + .oauthId(12345678L) + .oauthProvider(OauthProvider.KAKAO) + .addressDepth1Name("서울시") + .addressDepth2Name("강남구") + .build(); } } diff --git a/src/test/java/kr/pickple/back/fixture/dto/AddressDtoFixtures.java b/src/test/java/kr/pickple/back/fixture/dto/AddressDtoFixtures.java deleted file mode 100644 index 3cf6af97..00000000 --- a/src/test/java/kr/pickple/back/fixture/dto/AddressDtoFixtures.java +++ /dev/null @@ -1,31 +0,0 @@ -package kr.pickple.back.fixture.dto; - -import java.util.List; - -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.address.dto.response.AllAddressResponse; -import kr.pickple.back.address.dto.response.MainAddressResponse; - -public class AddressDtoFixtures { - - public static AllAddressResponse allAddressResponseBuild( - final AddressDepth1 addressDepth1, - final List addressDepth2s - ) { - return AllAddressResponse.builder() - .addressDepth1(addressDepth1.getName()) - .addressDepth2List(addressDepth2s.stream().map(AddressDepth2::getName).toList()) - .build(); - } - - public static MainAddressResponse mainAddressResponseBuild( - final AddressDepth1 addressDepth1, - final AddressDepth2 addressDepth2 - ) { - return MainAddressResponse.builder() - .addressDepth1(addressDepth1) - .addressDepth2(addressDepth2) - .build(); - } -} diff --git a/src/test/java/kr/pickple/back/fixture/dto/AuthDtoFixtures.java b/src/test/java/kr/pickple/back/fixture/dto/AuthDtoFixtures.java deleted file mode 100644 index 10d0fc8c..00000000 --- a/src/test/java/kr/pickple/back/fixture/dto/AuthDtoFixtures.java +++ /dev/null @@ -1,10 +0,0 @@ -package kr.pickple.back.fixture.dto; - -import kr.pickple.back.auth.dto.response.AccessTokenResponse; - -public class AuthDtoFixtures { - - public static AccessTokenResponse accessTokenResponseBuild() { - return AccessTokenResponse.of("accessToken"); - } -} diff --git a/src/test/java/kr/pickple/back/fixture/dto/ChatDtoFixtures.java b/src/test/java/kr/pickple/back/fixture/dto/ChatDtoFixtures.java index 3699e771..18b83e39 100644 --- a/src/test/java/kr/pickple/back/fixture/dto/ChatDtoFixtures.java +++ b/src/test/java/kr/pickple/back/fixture/dto/ChatDtoFixtures.java @@ -2,7 +2,10 @@ import kr.pickple.back.chat.dto.request.PersonalChatRoomCreateRequest; -public class ChatDtoFixtures { +public final class ChatDtoFixtures { + + private ChatDtoFixtures() { + } public static PersonalChatRoomCreateRequest personalChatRoomCreateRequestBuild(final Long receiverId) { return PersonalChatRoomCreateRequest.builder() diff --git a/src/test/java/kr/pickple/back/fixture/dto/CrewDtoFixtures.java b/src/test/java/kr/pickple/back/fixture/dto/CrewDtoFixtures.java index 554f024d..bceedc7f 100644 --- a/src/test/java/kr/pickple/back/fixture/dto/CrewDtoFixtures.java +++ b/src/test/java/kr/pickple/back/fixture/dto/CrewDtoFixtures.java @@ -1,24 +1,14 @@ package kr.pickple.back.fixture.dto; import kr.pickple.back.common.domain.RegistrationStatus; -import kr.pickple.back.crew.dto.request.CrewCreateRequest; import kr.pickple.back.crew.dto.request.CrewMemberUpdateStatusRequest; -public class CrewDtoFixtures { +public final class CrewDtoFixtures { - public static CrewCreateRequest crewCreateRequestBuild() { - return CrewCreateRequest.builder() - .name("백둥크루") - .content("안녕하세요. 백둥크루 입니다~!") - .maxMemberCount(15) - .addressDepth1("서울시") - .addressDepth2("영등포구") - .build(); + private CrewDtoFixtures() { } - public static CrewMemberUpdateStatusRequest crewMemberUpdateStatusRequest( - final RegistrationStatus status - ) { + public static CrewMemberUpdateStatusRequest crewMemberUpdateStatusRequestBuild(final RegistrationStatus status) { return CrewMemberUpdateStatusRequest.builder() .status(status) .build(); diff --git a/src/test/java/kr/pickple/back/fixture/dto/GameDtoFixtures.java b/src/test/java/kr/pickple/back/fixture/dto/GameDtoFixtures.java index b2fcab33..0ccff7e2 100644 --- a/src/test/java/kr/pickple/back/fixture/dto/GameDtoFixtures.java +++ b/src/test/java/kr/pickple/back/fixture/dto/GameDtoFixtures.java @@ -9,22 +9,25 @@ import kr.pickple.back.game.dto.request.GameMemberRegistrationStatusUpdateRequest; import kr.pickple.back.game.dto.request.MannerScoreReview; import kr.pickple.back.game.dto.request.MannerScoreReviewsRequest; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.member.repository.entity.MemberEntity; import kr.pickple.back.position.domain.Position; -public class GameDtoFixtures { +public final class GameDtoFixtures { + + private GameDtoFixtures() { + } public static GameCreateRequest gameCreateRequestBuild() { return GameCreateRequest.builder() - .content("재밌는 농구 경기 해요~! 다 초보입니다") - .playDate(LocalDate.of(2023, 2, 1)) - .playStartTime(LocalTime.of(11, 30)) - .playTimeMinutes(90) - .mainAddress("서울 영등포구 도림동 254") - .detailAddress("영등포 다목적 체육관 2층 201호") - .cost(100) - .maxMemberCount(5) - .positions(List.of(Position.CENTER, Position.POINT_GUARD)) + .content("pickple game") + .playDate(LocalDate.of(2024, 4, 1)) + .playStartTime(LocalTime.of(10, 0)) + .playTimeMinutes(60) + .mainAddress("서울시 강남구 논현동 1") + .detailAddress("201호") + .cost(0) + .maxMemberCount(20) + .positions(List.of(Position.CENTER)) .build(); } @@ -36,10 +39,10 @@ public static GameMemberRegistrationStatusUpdateRequest gameMemberRegistrationSt .build(); } - public static MannerScoreReviewsRequest mannerScoreReviewsRequestBuild(final List members) { - final List mannerScoreReviews = members.stream() - .map(member -> MannerScoreReview.builder() - .memberId(member.getId()) + public static MannerScoreReviewsRequest mannerScoreReviewsRequestBuild(final List guests) { + final List mannerScoreReviews = guests.stream() + .map(guest -> MannerScoreReview.builder() + .memberId(guest.getId()) .mannerScore(1) .build()) .toList(); diff --git a/src/test/java/kr/pickple/back/fixture/dto/MemberDtoFixtures.java b/src/test/java/kr/pickple/back/fixture/dto/MemberDtoFixtures.java index ee2f3c9e..4ba03a16 100644 --- a/src/test/java/kr/pickple/back/fixture/dto/MemberDtoFixtures.java +++ b/src/test/java/kr/pickple/back/fixture/dto/MemberDtoFixtures.java @@ -7,7 +7,10 @@ import kr.pickple.back.member.dto.response.AuthenticatedMemberResponse; import kr.pickple.back.position.domain.Position; -public class MemberDtoFixtures { +public final class MemberDtoFixtures { + + private MemberDtoFixtures() { + } public static MemberCreateRequest memberCreateRequestBuild() { return MemberCreateRequest.builder() diff --git a/src/test/java/kr/pickple/back/fixture/entity/chat/ChatMessageEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/chat/ChatMessageEntityFixtures.java new file mode 100644 index 00000000..da68696b --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/chat/ChatMessageEntityFixtures.java @@ -0,0 +1,19 @@ +package kr.pickple.back.fixture.entity.chat; + +import kr.pickple.back.chat.domain.MessageType; +import kr.pickple.back.chat.repository.entity.ChatMessageEntity; + +public final class ChatMessageEntityFixtures { + + private ChatMessageEntityFixtures() { + } + + public static ChatMessageEntity chatMessageEntityBuild(final Long senderId, final Long chatRoomId) { + return ChatMessageEntity.builder() + .type(MessageType.ENTER) + .content("pickple1님이 채팅방에 입장하셨습니다.") + .senderId(senderId) + .chatRoomId(chatRoomId) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/chat/ChatRoomEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/chat/ChatRoomEntityFixtures.java new file mode 100644 index 00000000..9e9a1221 --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/chat/ChatRoomEntityFixtures.java @@ -0,0 +1,27 @@ +package kr.pickple.back.fixture.entity.chat; + +import static kr.pickple.back.chat.domain.RoomType.*; + +import kr.pickple.back.chat.repository.entity.ChatRoomEntity; + +public final class ChatRoomEntityFixtures { + + private ChatRoomEntityFixtures() { + } + + public static ChatRoomEntity crewChatRoomEntityBuild() { + return ChatRoomEntity.builder() + .name("백둥크루1") + .type(CREW) + .maxMemberCount(10) + .build(); + } + + public static ChatRoomEntity gameChatRoomEntityBuild() { + return ChatRoomEntity.builder() + .name("11.10 영등포구") + .type(GAME) + .maxMemberCount(10) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/chat/ChatRoomMemberEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/chat/ChatRoomMemberEntityFixtures.java new file mode 100644 index 00000000..0812aa0d --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/chat/ChatRoomMemberEntityFixtures.java @@ -0,0 +1,16 @@ +package kr.pickple.back.fixture.entity.chat; + +import kr.pickple.back.chat.repository.entity.ChatRoomMemberEntity; + +public final class ChatRoomMemberEntityFixtures { + + private ChatRoomMemberEntityFixtures() { + } + + public static ChatRoomMemberEntity chatRoomMemberEntityBuild(final Long memberId, final Long chatRoomId) { + return ChatRoomMemberEntity.builder() + .memberId(memberId) + .chatRoomId(chatRoomId) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/crew/CrewEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/crew/CrewEntityFixtures.java new file mode 100644 index 00000000..a25c0f87 --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/crew/CrewEntityFixtures.java @@ -0,0 +1,23 @@ +package kr.pickple.back.fixture.entity.crew; + +import kr.pickple.back.crew.repository.entity.CrewEntity; + +public final class CrewEntityFixtures { + + private CrewEntityFixtures() { + } + + public static CrewEntity crewEntityBuild(final Long leaderId, final Long chatRoomId) { + return CrewEntity.builder() + .name("백둥크루1") + .content("안녕하세요 백둥크루1 입니다.") + .profileImageUrl("https://amazon.profileimage/1") + .backgroundImageUrl("https://amazon.backgroundimage/1") + .maxMemberCount(15) + .leaderId(leaderId) + .addressDepth1Id(1L) // 서울시 + .addressDepth2Id(1L) // 강남구 + .chatRoomId(chatRoomId) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/crew/CrewMemberEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/crew/CrewMemberEntityFixtures.java new file mode 100644 index 00000000..fae74040 --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/crew/CrewMemberEntityFixtures.java @@ -0,0 +1,33 @@ +package kr.pickple.back.fixture.entity.crew; + +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.crew.repository.entity.CrewMemberEntity; + +public final class CrewMemberEntityFixtures { + + private CrewMemberEntityFixtures() { + } + + public static CrewMemberEntity crewMemberEntityBuild(final Long memberId, final Long crewId) { + return CrewMemberEntity.builder() + .memberId(memberId) + .crewId(crewId) + .build(); + } + + public static CrewMemberEntity crewMemberEntityStatusWaitingBuild(final Long memberId, final Long crewId) { + return CrewMemberEntity.builder() + .memberId(memberId) + .crewId(crewId) + .status(RegistrationStatus.WAITING) + .build(); + } + + public static CrewMemberEntity crewMemberEntityStatusConfirmedBuild(final Long memberId, final Long crewId) { + return CrewMemberEntity.builder() + .memberId(memberId) + .crewId(crewId) + .status(RegistrationStatus.CONFIRMED) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/game/GameEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/game/GameEntityFixtures.java new file mode 100644 index 00000000..4dcb8030 --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/game/GameEntityFixtures.java @@ -0,0 +1,39 @@ +package kr.pickple.back.fixture.entity.game; + +import java.time.LocalDate; +import java.time.LocalTime; + +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.PrecisionModel; + +import kr.pickple.back.game.repository.entity.GameEntity; + +public final class GameEntityFixtures { + + private GameEntityFixtures() { + } + + public static GameEntity gameEntityBuild(final Long memberId, final Long chatRoomId) { + final GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326); + final Point point = geometryFactory.createPoint(new Coordinate(37.125, 126.75)); + + return GameEntity.builder() + .content("하이하이 즐겜 한 판해요") + .playDate(LocalDate.now().minusDays(1)) + .playStartTime(LocalTime.of(11, 30)) + .playEndTime(LocalTime.of(13, 0)) + .playTimeMinutes(90) + .mainAddress("서울 영등포구 도림동 254") + .detailAddress("영등포 다목적 체육관 2층 201호") + .cost(100) + .maxMemberCount(5) + .point(point) + .hostId(memberId) + .addressDepth1Id(1L) // 서울시 + .addressDepth2Id(1L) // 강남구 + .chatRoomId(chatRoomId) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/game/GameMemberEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/game/GameMemberEntityFixtures.java new file mode 100644 index 00000000..ba40abb5 --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/game/GameMemberEntityFixtures.java @@ -0,0 +1,33 @@ +package kr.pickple.back.fixture.entity.game; + +import kr.pickple.back.common.domain.RegistrationStatus; +import kr.pickple.back.game.repository.entity.GameMemberEntity; + +public final class GameMemberEntityFixtures { + + private GameMemberEntityFixtures() { + } + + public static GameMemberEntity gameMemberEntityBuild(final Long memberId, final Long gameId) { + return GameMemberEntity.builder() + .memberId(memberId) + .gameId(gameId) + .build(); + } + + public static GameMemberEntity gameMemberEntityStatusWaitingBuild(final Long memberId, final Long gameId) { + return GameMemberEntity.builder() + .memberId(memberId) + .gameId(gameId) + .status(RegistrationStatus.WAITING) + .build(); + } + + public static GameMemberEntity gameMemberEntityStatusConfirmedBuild(final Long memberId, final Long gameId) { + return GameMemberEntity.builder() + .memberId(memberId) + .gameId(gameId) + .status(RegistrationStatus.CONFIRMED) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/game/GamePositionEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/game/GamePositionEntityFixtures.java new file mode 100644 index 00000000..a392fbb4 --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/game/GamePositionEntityFixtures.java @@ -0,0 +1,17 @@ +package kr.pickple.back.fixture.entity.game; + +import kr.pickple.back.game.repository.entity.GamePositionEntity; +import kr.pickple.back.position.domain.Position; + +public final class GamePositionEntityFixtures { + + private GamePositionEntityFixtures() { + } + + public static GamePositionEntity gamePositionEntityBuild(final Long gameId) { + return GamePositionEntity.builder() + .position(Position.CENTER) + .gameId(gameId) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/member/MemberEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/member/MemberEntityFixtures.java new file mode 100644 index 00000000..70101a26 --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/member/MemberEntityFixtures.java @@ -0,0 +1,44 @@ +package kr.pickple.back.fixture.entity.member; + +import static java.text.MessageFormat.format; + +import java.util.List; +import java.util.stream.IntStream; + +import kr.pickple.back.auth.domain.oauth.OauthProvider; +import kr.pickple.back.member.domain.MemberStatus; +import kr.pickple.back.member.repository.entity.MemberEntity; + +public final class MemberEntityFixtures { + + private MemberEntityFixtures() { + } + + public static MemberEntity memberEntityBuild() { + return MemberEntity.builder() + .email("pickple1@pickple.kr") + .nickname("pickple1") + .profileImageUrl("https://amazon.image") + .status(MemberStatus.ACTIVE) + .oauthId(1L) + .oauthProvider(OauthProvider.KAKAO) + .addressDepth1Id(1L) // 서울시 + .addressDepth2Id(1L) // 강남구 + .build(); + } + + public static List memberEntitiesBuild(final int count) { + return IntStream.rangeClosed(1, count) + .mapToObj(i -> MemberEntity.builder() + .email(format("pickple{0}@pickple.kr", i)) + .nickname(format("pickple{0}", i)) + .profileImageUrl(format("https://amazon{0}.image", i)) + .status(MemberStatus.ACTIVE) + .oauthId((long)i) + .oauthProvider(OauthProvider.KAKAO) + .addressDepth1Id(1L) // 서울시 + .addressDepth2Id(1L) // 강남구 + .build()) + .toList(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/entity/member/MemberPositionEntityFixtures.java b/src/test/java/kr/pickple/back/fixture/entity/member/MemberPositionEntityFixtures.java new file mode 100644 index 00000000..432c8731 --- /dev/null +++ b/src/test/java/kr/pickple/back/fixture/entity/member/MemberPositionEntityFixtures.java @@ -0,0 +1,17 @@ +package kr.pickple.back.fixture.entity.member; + +import kr.pickple.back.member.repository.entity.MemberPositionEntity; +import kr.pickple.back.position.domain.Position; + +public final class MemberPositionEntityFixtures { + + private MemberPositionEntityFixtures() { + } + + public static MemberPositionEntity memberPositionEntityBuild(final Long memberId) { + return MemberPositionEntity.builder() + .position(Position.CENTER) + .memberId(memberId) + .build(); + } +} diff --git a/src/test/java/kr/pickple/back/fixture/setup/AddressSetup.java b/src/test/java/kr/pickple/back/fixture/setup/AddressSetup.java deleted file mode 100644 index fb511738..00000000 --- a/src/test/java/kr/pickple/back/fixture/setup/AddressSetup.java +++ /dev/null @@ -1,32 +0,0 @@ -package kr.pickple.back.fixture.setup; - -import static kr.pickple.back.address.exception.AddressExceptionCode.*; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.address.exception.AddressException; -import kr.pickple.back.address.repository.AddressDepth1Repository; -import kr.pickple.back.address.repository.AddressDepth2Repository; - -@Component -public class AddressSetup { - - @Autowired - private AddressDepth1Repository addressDepth1Repository; - - @Autowired - private AddressDepth2Repository addressDepth2Repository; - - public AddressDepth1 findAddressDepth1(String name) { - return addressDepth1Repository.findByName(name) - .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, name)); - } - - public AddressDepth2 findAddressDepth2(String name) { - return addressDepth2Repository.findByNameAndAddressDepth1(name, findAddressDepth1("서울시")) - .orElseThrow(() -> new AddressException(ADDRESS_NOT_FOUND, name)); - } -} diff --git a/src/test/java/kr/pickple/back/fixture/setup/CrewSetup.java b/src/test/java/kr/pickple/back/fixture/setup/CrewSetup.java index fca511e9..79465100 100644 --- a/src/test/java/kr/pickple/back/fixture/setup/CrewSetup.java +++ b/src/test/java/kr/pickple/back/fixture/setup/CrewSetup.java @@ -1,20 +1,24 @@ package kr.pickple.back.fixture.setup; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.chat.domain.ChatRoom; -import kr.pickple.back.chat.repository.ChatRoomRepository; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.crew.domain.CrewMember; -import kr.pickple.back.crew.repository.CrewRepository; -import kr.pickple.back.fixture.domain.CrewFixtures; -import kr.pickple.back.member.domain.Member; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; -import java.util.List; - -import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; +import kr.pickple.back.chat.repository.ChatMessageRepository; +import kr.pickple.back.chat.repository.ChatRoomMemberRepository; +import kr.pickple.back.chat.repository.ChatRoomRepository; +import kr.pickple.back.chat.repository.entity.ChatMessageEntity; +import kr.pickple.back.chat.repository.entity.ChatRoomEntity; +import kr.pickple.back.chat.repository.entity.ChatRoomMemberEntity; +import kr.pickple.back.crew.repository.CrewMemberRepository; +import kr.pickple.back.crew.repository.CrewRepository; +import kr.pickple.back.crew.repository.entity.CrewEntity; +import kr.pickple.back.crew.repository.entity.CrewMemberEntity; +import kr.pickple.back.fixture.entity.chat.ChatMessageEntityFixtures; +import kr.pickple.back.fixture.entity.chat.ChatRoomEntityFixtures; +import kr.pickple.back.fixture.entity.chat.ChatRoomMemberEntityFixtures; +import kr.pickple.back.fixture.entity.crew.CrewEntityFixtures; +import kr.pickple.back.fixture.entity.crew.CrewMemberEntityFixtures; @Component public class CrewSetup { @@ -22,54 +26,57 @@ public class CrewSetup { @Autowired private CrewRepository crewRepository; + @Autowired + private CrewMemberRepository crewMemberRepository; + @Autowired private ChatRoomRepository chatRoomRepository; @Autowired - private MemberSetup memberSetup; + private ChatRoomMemberRepository chatRoomMemberRepository; @Autowired - private AddressSetup addressSetup; + private ChatMessageRepository chatMessageRepository; - public Crew save(final Member leader) { - final AddressDepth1 addressDepth1 = addressSetup.findAddressDepth1("서울시"); - final AddressDepth2 addressDepth2 = addressSetup.findAddressDepth2("영등포구"); + @Transactional + public CrewEntity save(final Long memberId) { + final ChatRoomEntity chatRoomEntity = ChatRoomEntityFixtures.gameChatRoomEntityBuild(); + final ChatRoomEntity savedChatRoomEntity = chatRoomRepository.save(chatRoomEntity); - final Crew crew = CrewFixtures.crewBuild(addressDepth1, addressDepth2, leader); - final ChatRoom savedChatRoom = chatRoomRepository.save(CrewFixtures.crewChatRoomBuild()); + final CrewEntity crewEntity = CrewEntityFixtures.crewEntityBuild(memberId, savedChatRoomEntity.getId()); + final CrewEntity savedCrewEntity = crewRepository.save(crewEntity); - crew.addCrewMember(leader); - savedChatRoom.updateMaxMemberCount(crew.getMaxMemberCount()); - crew.makeNewCrewChatRoom(savedChatRoom); + final CrewMemberEntity crewMemberEntity = CrewMemberEntityFixtures.crewMemberEntityStatusConfirmedBuild( + memberId, savedCrewEntity.getId()); + crewMemberRepository.save(crewMemberEntity); - final CrewMember crewLeader = crew.getCrewMembers().get(0); - leader.addMemberCrew(crewLeader); - crewLeader.updateStatus(CONFIRMED); + final ChatRoomMemberEntity chatRoomMemberEntity = ChatRoomMemberEntityFixtures.chatRoomMemberEntityBuild( + memberId, savedChatRoomEntity.getId()); + chatRoomMemberRepository.save(chatRoomMemberEntity); - return crewRepository.save(crew); - } + final ChatMessageEntity chatMessageEntity = ChatMessageEntityFixtures.chatMessageEntityBuild(memberId, + savedChatRoomEntity.getId()); + chatMessageRepository.save(chatMessageEntity); - public Crew saveWithWaitingMembers(final Integer memberCount) { - final List members = memberSetup.save(memberCount); - final Crew crew = save(members.get(0)); - final List crewMembers = members.subList(1, members.size()); + return savedCrewEntity; + } - crewMembers.forEach(crew::addCrewMember); + @Transactional + public CrewMemberEntity registerWaiting(final Long memberId, final Long crewId) { + final CrewMemberEntity crewMemberEntity = CrewMemberEntityFixtures.crewMemberEntityStatusWaitingBuild( + memberId, crewId); - return crew; + return crewMemberRepository.save(crewMemberEntity); } - public Crew saveWithConfirmedMembers(final Integer memberCount) { - final Crew crew = saveWithWaitingMembers(memberCount); - final Member crewLeader = crew.getLeader(); - final List crewMembers = crew.getCrewMembers(); + @Transactional + public CrewMemberEntity registerConfirmed(final Long memberId, final Long crewId) { + final CrewEntity crewEntity = crewRepository.findById(crewId).get(); + crewRepository.updateMemberCountAndStatus(crewId, crewEntity.getMemberCount() + 1, crewEntity.getStatus()); - crewMembers.forEach(crewMember -> { - if (!crewLeader.equals(crewMember.getMember())) { - crewMember.updateStatus(CONFIRMED); - } - }); + final CrewMemberEntity crewMemberEntity = CrewMemberEntityFixtures.crewMemberEntityStatusConfirmedBuild( + memberId, crewId); - return crew; + return crewMemberRepository.save(crewMemberEntity); } } diff --git a/src/test/java/kr/pickple/back/fixture/setup/GameSetup.java b/src/test/java/kr/pickple/back/fixture/setup/GameSetup.java index 620547c3..bf7ab01f 100644 --- a/src/test/java/kr/pickple/back/fixture/setup/GameSetup.java +++ b/src/test/java/kr/pickple/back/fixture/setup/GameSetup.java @@ -1,21 +1,27 @@ package kr.pickple.back.fixture.setup; -import static kr.pickple.back.common.domain.RegistrationStatus.*; - -import java.util.List; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.chat.domain.ChatRoom; +import kr.pickple.back.chat.repository.ChatMessageRepository; +import kr.pickple.back.chat.repository.ChatRoomMemberRepository; import kr.pickple.back.chat.repository.ChatRoomRepository; -import kr.pickple.back.fixture.domain.GameFixtures; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.domain.GameMember; +import kr.pickple.back.chat.repository.entity.ChatMessageEntity; +import kr.pickple.back.chat.repository.entity.ChatRoomEntity; +import kr.pickple.back.chat.repository.entity.ChatRoomMemberEntity; +import kr.pickple.back.fixture.entity.chat.ChatMessageEntityFixtures; +import kr.pickple.back.fixture.entity.chat.ChatRoomEntityFixtures; +import kr.pickple.back.fixture.entity.chat.ChatRoomMemberEntityFixtures; +import kr.pickple.back.fixture.entity.game.GameEntityFixtures; +import kr.pickple.back.fixture.entity.game.GameMemberEntityFixtures; +import kr.pickple.back.fixture.entity.game.GamePositionEntityFixtures; +import kr.pickple.back.game.repository.GameMemberRepository; +import kr.pickple.back.game.repository.GamePositionRepository; import kr.pickple.back.game.repository.GameRepository; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.game.repository.entity.GameEntity; +import kr.pickple.back.game.repository.entity.GameMemberEntity; +import kr.pickple.back.game.repository.entity.GamePositionEntity; @Component public class GameSetup { @@ -24,53 +30,66 @@ public class GameSetup { private GameRepository gameRepository; @Autowired - private ChatRoomRepository chatRoomRepository; + private GameMemberRepository gameMemberRepository; + + @Autowired + private GamePositionRepository gamePositionRepository; @Autowired - private MemberSetup memberSetup; + private ChatRoomRepository chatRoomRepository; @Autowired - private AddressSetup addressSetup; + private ChatRoomMemberRepository chatRoomMemberRepository; - public Game save(final Member host) { - final AddressDepth1 addressDepth1 = addressSetup.findAddressDepth1("서울시"); - final AddressDepth2 addressDepth2 = addressSetup.findAddressDepth2("영등포구"); + @Autowired + private ChatMessageRepository chatMessageRepository; - final Game game = GameFixtures.gameBuild(addressDepth1, addressDepth2, host); - final ChatRoom savedChatRoom = chatRoomRepository.save(GameFixtures.gameChatRoomBuild()); + @Transactional + public GameEntity save(final Long memberId) { + final ChatRoomEntity chatRoomEntity = ChatRoomEntityFixtures.gameChatRoomEntityBuild(); + final ChatRoomEntity savedChatRoomEntity = chatRoomRepository.save(chatRoomEntity); - game.addGameMember(host); - savedChatRoom.updateMaxMemberCount(game.getMaxMemberCount()); - game.makeNewCrewChatRoom(savedChatRoom); + final GameEntity gameEntity = GameEntityFixtures.gameEntityBuild(memberId, savedChatRoomEntity.getId()); + final GameEntity savedGameEntity = gameRepository.save(gameEntity); - final GameMember gameHost = game.getGameMembers().get(0); - host.addMemberGame(gameHost); - gameHost.updateStatus(CONFIRMED); + final GameMemberEntity gameMemberEntity = GameMemberEntityFixtures.gameMemberEntityStatusConfirmedBuild( + memberId, savedGameEntity.getId()); + gameMemberRepository.save(gameMemberEntity); - return gameRepository.save(game); - } + final GamePositionEntity gamePositionEntity = GamePositionEntityFixtures.gamePositionEntityBuild( + savedGameEntity.getId()); + gamePositionRepository.save(gamePositionEntity); - public Game saveWithWaitingMembers(final Integer memberCount) { - final List members = memberSetup.save(memberCount); - final Game game = save(members.get(0)); - final List guests = members.subList(1, members.size()); + final ChatRoomMemberEntity chatRoomMemberEntity = ChatRoomMemberEntityFixtures.chatRoomMemberEntityBuild( + memberId, savedChatRoomEntity.getId()); + chatRoomMemberRepository.save(chatRoomMemberEntity); - guests.forEach(game::addGameMember); + final ChatMessageEntity chatMessageEntity = ChatMessageEntityFixtures.chatMessageEntityBuild(memberId, + savedChatRoomEntity.getId()); + chatMessageRepository.save(chatMessageEntity); - return game; + return savedGameEntity; } - public Game saveWithConfirmedMembers(final Integer memberCount) { - final Game game = saveWithWaitingMembers(memberCount); - final Member host = game.getHost(); - final List gameMembers = game.getGameMembers(); + @Transactional + public GameMemberEntity registerWaiting(final Long memberId, final Long gameId) { + final GameMemberEntity gameMemberEntity = GameMemberEntityFixtures.gameMemberEntityStatusWaitingBuild( + memberId, + gameId + ); + + return gameMemberRepository.save(gameMemberEntity); + } - gameMembers.forEach(gameMember -> { - if (!host.equals(gameMember.getMember())) { - gameMember.updateStatus(CONFIRMED); - } - }); + @Transactional + public GameMemberEntity registerConfirmed(final Long memberId, final Long gameId) { + final GameEntity gameEntity = gameRepository.findById(gameId).get(); + final GameMemberEntity gameMemberEntity = GameMemberEntityFixtures.gameMemberEntityStatusConfirmedBuild( + memberId, + gameId + ); + gameRepository.updateMemberCountAndStatus(gameId, gameEntity.getMemberCount() + 1, gameEntity.getStatus()); - return game; + return gameMemberRepository.save(gameMemberEntity); } } diff --git a/src/test/java/kr/pickple/back/fixture/setup/MemberSetup.java b/src/test/java/kr/pickple/back/fixture/setup/MemberSetup.java index f69e6260..0f8c493b 100644 --- a/src/test/java/kr/pickple/back/fixture/setup/MemberSetup.java +++ b/src/test/java/kr/pickple/back/fixture/setup/MemberSetup.java @@ -1,14 +1,17 @@ package kr.pickple.back.fixture.setup; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.fixture.domain.MemberFixtures; -import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.repository.MemberRepository; +import java.util.List; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; -import java.util.List; +import kr.pickple.back.fixture.entity.member.MemberEntityFixtures; +import kr.pickple.back.fixture.entity.member.MemberPositionEntityFixtures; +import kr.pickple.back.member.repository.MemberPositionRepository; +import kr.pickple.back.member.repository.MemberRepository; +import kr.pickple.back.member.repository.entity.MemberEntity; +import kr.pickple.back.member.repository.entity.MemberPositionEntity; @Component public class MemberSetup { @@ -17,27 +20,31 @@ public class MemberSetup { private MemberRepository memberRepository; @Autowired - private AddressSetup addressSetup; + private MemberPositionRepository memberPositionRepository; - public Member save() { - final AddressDepth1 addressDepth1 = addressSetup.findAddressDepth1("서울시"); - final AddressDepth2 addressDepth2 = addressSetup.findAddressDepth2("영등포구"); + @Transactional + public MemberEntity save() { + final MemberEntity memberEntity = MemberEntityFixtures.memberEntityBuild(); + final MemberEntity savedMemberEntity = memberRepository.save(memberEntity); - final Member member = MemberFixtures.memberBuild( - addressDepth1, - addressDepth2 - ); - return memberRepository.save(member); - } - - public List save(final int count) { - final AddressDepth1 addressDepth1 = addressSetup.findAddressDepth1("서울시"); - final AddressDepth2 addressDepth2 = addressSetup.findAddressDepth2("영등포구"); + final MemberPositionEntity memberPositionEntity = MemberPositionEntityFixtures.memberPositionEntityBuild( + savedMemberEntity.getId()); + memberPositionRepository.save(memberPositionEntity); - final List members = MemberFixtures.membersBuild(count, addressDepth1, addressDepth2); + return savedMemberEntity; + } - return members.stream() + @Transactional + public List save(final int count) { + final List savedMemberEntities = MemberEntityFixtures.memberEntitiesBuild(count) + .stream() .map(memberRepository::save) .toList(); + + savedMemberEntities.forEach(savedMemberEntity -> memberPositionRepository.save( + MemberPositionEntityFixtures.memberPositionEntityBuild(savedMemberEntity.getId()) + )); + + return savedMemberEntities; } } diff --git a/src/test/java/kr/pickple/back/game/controller/GameControllerTest.java b/src/test/java/kr/pickple/back/game/controller/GameControllerTest.java index 0a0ef5c5..6331715d 100644 --- a/src/test/java/kr/pickple/back/game/controller/GameControllerTest.java +++ b/src/test/java/kr/pickple/back/game/controller/GameControllerTest.java @@ -1,42 +1,75 @@ package kr.pickple.back.game.controller; -import static kr.pickple.back.common.domain.RegistrationStatus.*; -import static org.springframework.http.HttpHeaders.*; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; +import static kr.pickple.back.common.domain.RegistrationStatus.WAITING; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.List; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import org.springframework.transaction.annotation.Transactional; +import kr.pickple.back.address.repository.AddressDepth1Repository; +import kr.pickple.back.address.repository.AddressDepth2Repository; +import kr.pickple.back.address.repository.entity.AddressDepth1Entity; +import kr.pickple.back.address.repository.entity.AddressDepth2Entity; import kr.pickple.back.auth.domain.token.AuthTokens; import kr.pickple.back.fixture.dto.GameDtoFixtures; import kr.pickple.back.game.IntegrationGameTest; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.domain.GameMember; import kr.pickple.back.game.dto.request.GameMemberRegistrationStatusUpdateRequest; import kr.pickple.back.game.dto.request.MannerScoreReviewsRequest; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.game.repository.GamePositionRepository; +import kr.pickple.back.game.repository.GameRepository; +import kr.pickple.back.game.repository.entity.GameEntity; +import kr.pickple.back.game.repository.entity.GamePositionEntity; +import kr.pickple.back.member.repository.entity.MemberEntity; @Transactional class GameControllerTest extends IntegrationGameTest { private static final String BASE_URL = "/games"; + @Autowired + private AddressDepth1Repository addressDepth1Repository; + + @Autowired + private AddressDepth2Repository addressDepth2Repository; + + @Autowired + private GameRepository gameRepository; + + @Autowired + private GamePositionRepository gamePositionRepository; + @Test @DisplayName("사용자는 게스트 모집글의 상세 정보를 조회할 수 있다.") void findGameDetailsById_ReturnGameResponse() throws Exception { // given - final Game game = gameSetup.saveWithConfirmedMembers(2); - final Member host = game.getHost(); - final Member guest = game.getGameMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerConfirmed(guest.getId(), game.getId()); + game = gameRepository.findById(game.getId()).get(); + + final AddressDepth1Entity gameAddressDepth1 = addressDepth1Repository.findById(game.getAddressDepth1Id()) + .get(); + final AddressDepth2Entity gameAddressDepth2 = addressDepth2Repository.findById(game.getAddressDepth2Id()) + .get(); + final List gamePositions = gamePositionRepository.findAllByGameId(game.getId()); // when final ResultActions resultActions = mockMvc.perform(get(BASE_URL + "/{gameId}", game.getId())); @@ -66,10 +99,9 @@ void findGameDetailsById_ReturnGameResponse() throws Exception { .andExpect(jsonPath("host.profileImageUrl").value(host.getProfileImageUrl())) .andExpect(jsonPath("host.mannerScore").value(host.getMannerScore())) .andExpect(jsonPath("host.mannerScoreCount").value(host.getMannerScoreCount())) - .andExpect(jsonPath("addressDepth1").value(game.getAddressDepth1().getName())) - .andExpect(jsonPath("addressDepth2").value(game.getAddressDepth2().getName())) - .andExpect(jsonPath("positions[0]").value(game.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("positions[1]").value(game.getPositions().get(1).getAcronym())) + .andExpect(jsonPath("addressDepth1").value(gameAddressDepth1.getName())) + .andExpect(jsonPath("addressDepth2").value(gameAddressDepth2.getName())) + .andExpect(jsonPath("positions[0]").value(gamePositions.get(0).getPosition().getAcronym())) .andExpect(jsonPath("members[0].id").value(host.getId())) .andExpect(jsonPath("members[1].id").value(guest.getId())) .andDo(print()); @@ -79,10 +111,11 @@ void findGameDetailsById_ReturnGameResponse() throws Exception { @DisplayName("사용자는 게스트 모집글에 참여 신청을 할 수 있다.") void registerGameMember_Success() throws Exception { // given - final List members = memberSetup.save(2); - final Member host = members.get(0); - final Member guest = members.get(1); - final Game game = gameSetup.save(host); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + final GameEntity game = gameSetup.save(host.getId()); final String subject = String.valueOf(guest.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -101,11 +134,18 @@ void registerGameMember_Success() throws Exception { @DisplayName("호스트는 게스트 모집글에 참여 신청된 사용자 정보 목록을 조회할 수 있다.") void findAllGameMembers_WaitingStatus_ReturnGameResponse() throws Exception { // given - final Game game = gameSetup.saveWithWaitingMembers(2); - final Member host = game.getHost(); - final Member guest = game.getGameMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + final GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerWaiting(guest.getId(), game.getId()); + + final AddressDepth1Entity gameAddressDepth1 = addressDepth1Repository.findById(game.getAddressDepth1Id()) + .get(); + final AddressDepth2Entity gameAddressDepth2 = addressDepth2Repository.findById(game.getAddressDepth2Id()) + .get(); + final List gamePositions = gamePositionRepository.findAllByGameId(game.getId()); final String subject = String.valueOf(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -142,10 +182,9 @@ void findAllGameMembers_WaitingStatus_ReturnGameResponse() throws Exception { .andExpect(jsonPath("host.profileImageUrl").value(host.getProfileImageUrl())) .andExpect(jsonPath("host.mannerScore").value(host.getMannerScore())) .andExpect(jsonPath("host.mannerScoreCount").value(host.getMannerScoreCount())) - .andExpect(jsonPath("addressDepth1").value(game.getAddressDepth1().getName())) - .andExpect(jsonPath("addressDepth2").value(game.getAddressDepth2().getName())) - .andExpect(jsonPath("positions[0]").value(game.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("positions[1]").value(game.getPositions().get(1).getAcronym())) + .andExpect(jsonPath("addressDepth1").value(gameAddressDepth1.getName())) + .andExpect(jsonPath("addressDepth2").value(gameAddressDepth2.getName())) + .andExpect(jsonPath("positions[0]").value(gamePositions.get(0).getPosition().getAcronym())) .andExpect(jsonPath("members[0].id").value(guest.getId())) .andDo(print()); } @@ -154,11 +193,19 @@ void findAllGameMembers_WaitingStatus_ReturnGameResponse() throws Exception { @DisplayName("사용자는 게스트 모집글에 참여 확정된 사용자 정보 목록을 조회할 수 있다.") void findAllGameMembers_ConfirmedStatus_ReturnGameResponse() throws Exception { // given - final Game game = gameSetup.saveWithConfirmedMembers(2); - final Member host = game.getHost(); - final Member guest = game.getGameMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerConfirmed(guest.getId(), game.getId()); + game = gameRepository.findById(game.getId()).get(); + + final AddressDepth1Entity gameAddressDepth1 = addressDepth1Repository.findById(game.getAddressDepth1Id()) + .get(); + final AddressDepth2Entity gameAddressDepth2 = addressDepth2Repository.findById(game.getAddressDepth2Id()) + .get(); + final List gamePositions = gamePositionRepository.findAllByGameId(game.getId()); final String subject = String.valueOf(guest.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -195,10 +242,9 @@ void findAllGameMembers_ConfirmedStatus_ReturnGameResponse() throws Exception { .andExpect(jsonPath("host.profileImageUrl").value(host.getProfileImageUrl())) .andExpect(jsonPath("host.mannerScore").value(host.getMannerScore())) .andExpect(jsonPath("host.mannerScoreCount").value(host.getMannerScoreCount())) - .andExpect(jsonPath("addressDepth1").value(game.getAddressDepth1().getName())) - .andExpect(jsonPath("addressDepth2").value(game.getAddressDepth2().getName())) - .andExpect(jsonPath("positions[0]").value(game.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("positions[1]").value(game.getPositions().get(1).getAcronym())) + .andExpect(jsonPath("addressDepth1").value(gameAddressDepth1.getName())) + .andExpect(jsonPath("addressDepth2").value(gameAddressDepth2.getName())) + .andExpect(jsonPath("positions[0]").value(gamePositions.get(0).getPosition().getAcronym())) .andExpect(jsonPath("members[0].id").value(host.getId())) .andExpect(jsonPath("members[1].id").value(guest.getId())) .andDo(print()); @@ -208,11 +254,12 @@ void findAllGameMembers_ConfirmedStatus_ReturnGameResponse() throws Exception { @DisplayName("호스트는 다른 사용자의 게스트 모집글 참여 신청을 수락할 수 있다.") void updateGameMemberRegistrationStatus_Success() throws Exception { // given - final Game game = gameSetup.saveWithWaitingMembers(2); - final Member host = game.getHost(); - final Member guest = game.getGameMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + final GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerWaiting(guest.getId(), game.getId()); final String subject = String.valueOf(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -237,11 +284,12 @@ void updateGameMemberRegistrationStatus_Success() throws Exception { @DisplayName("호스트는 다른 사용자의 게스트 모집글 참여 신청을 거절할 수 있다.") void deleteGameMember_Host_Success() throws Exception { // given - final Game game = gameSetup.saveWithWaitingMembers(2); - final Member host = game.getHost(); - final Member guest = game.getGameMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + final GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerWaiting(guest.getId(), game.getId()); final String subject = String.valueOf(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -260,10 +308,12 @@ void deleteGameMember_Host_Success() throws Exception { @DisplayName("사용자는 자신의 게스트 모집글 참여 신청을 취소할 수 있다.") void deleteGameMember_GuestSelf_Success() throws Exception { // given - final Game game = gameSetup.saveWithWaitingMembers(2); - final Member guest = game.getGameMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + final GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerWaiting(guest.getId(), game.getId()); final String subject = String.valueOf(guest.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -282,19 +332,20 @@ void deleteGameMember_GuestSelf_Success() throws Exception { @DisplayName("사용자는 경기에 참여한 다른 사용자의 매너 스코어를 리뷰할 수 있다.") void reviewMannerScores_Success() throws Exception { // given - final Game game = gameSetup.saveWithConfirmedMembers(3); - final Member host = game.getHost(); - final List gameMembers = game.getGameMembers(); - final List guests = gameMembers.subList(1, gameMembers.size()) - .stream() - .map(GameMember::getMember) - .toList(); + final List members = memberSetup.save(3); + final MemberEntity host = members.get(0); + final MemberEntity guest1 = members.get(1); + final MemberEntity guest2 = members.get(2); + + GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerConfirmed(guest1.getId(), game.getId()); + gameSetup.registerConfirmed(guest2.getId(), game.getId()); final String subject = String.valueOf(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); final MannerScoreReviewsRequest mannerScoreReviewsRequest = GameDtoFixtures.mannerScoreReviewsRequestBuild( - guests); + List.of(guest1, guest2)); final String requestBody = objectMapper.writeValueAsString(mannerScoreReviewsRequest); // when diff --git a/src/test/java/kr/pickple/back/game/docs/GameDocumentTest.java b/src/test/java/kr/pickple/back/game/docs/GameDocumentTest.java index c5fbcbd2..47e6df66 100644 --- a/src/test/java/kr/pickple/back/game/docs/GameDocumentTest.java +++ b/src/test/java/kr/pickple/back/game/docs/GameDocumentTest.java @@ -1,15 +1,24 @@ package kr.pickple.back.game.docs; -import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.*; -import static com.epages.restdocs.apispec.ResourceDocumentation.*; -import static com.epages.restdocs.apispec.Schema.*; -import static kr.pickple.back.common.domain.RegistrationStatus.*; -import static kr.pickple.back.game.domain.Category.*; -import static org.springframework.http.HttpHeaders.*; -import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.payload.PayloadDocumentation.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document; +import static com.epages.restdocs.apispec.ResourceDocumentation.headerWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName; +import static com.epages.restdocs.apispec.ResourceDocumentation.resource; +import static com.epages.restdocs.apispec.Schema.schema; +import static kr.pickple.back.common.domain.RegistrationStatus.CONFIRMED; +import static kr.pickple.back.common.domain.RegistrationStatus.WAITING; +import static kr.pickple.back.game.domain.Category.ADDRESS; +import static org.springframework.http.HttpHeaders.AUTHORIZATION; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.util.List; @@ -26,11 +35,11 @@ import kr.pickple.back.auth.domain.token.AuthTokens; import kr.pickple.back.fixture.dto.GameDtoFixtures; import kr.pickple.back.game.IntegrationGameTest; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.domain.GameMember; +import kr.pickple.back.game.dto.request.GameCreateRequest; import kr.pickple.back.game.dto.request.GameMemberRegistrationStatusUpdateRequest; import kr.pickple.back.game.dto.request.MannerScoreReviewsRequest; -import kr.pickple.back.member.domain.Member; +import kr.pickple.back.game.repository.entity.GameEntity; +import kr.pickple.back.member.repository.entity.MemberEntity; @Transactional class GameDocumentTest extends IntegrationGameTest { @@ -38,65 +47,65 @@ class GameDocumentTest extends IntegrationGameTest { private static final String BASE_URL = "/games"; //todo: 카카오 외부 API 의존성으로 인한 테스트 실패 해결 필요. - // @Test // @DisplayName("게스트 모집글 생성") // void createGame_ReturnGameIdResponse() throws Exception { // // given + // final MemberEntity host = memberSetup.save(); + // // final GameCreateRequest gameCreateRequest = GameDtoFixtures.gameCreateRequestBuild(); // final String requestBody = objectMapper.writeValueAsString(gameCreateRequest); // - // final Member host = memberSetup.save(); // final String subject = String.valueOf(host.getId()); // final AuthTokens authTokens = jwtProvider.createLoginToken(subject); // // // when // final ResultActions resultActions = mockMvc.perform(post(BASE_URL) - // .contentType(MediaType.APPLICATION_JSON) - // .content(requestBody) - // .header(AUTHORIZATION, "Bearer " + authTokens.getAccessToken()) - // ) - // .andExpect(status().isCreated()); + // .contentType(MediaType.APPLICATION_JSON) + // .content(requestBody) + // .header(AUTHORIZATION, "Bearer " + authTokens.getAccessToken()) + // ) + // .andExpect(status().isCreated()); // // // then // resultActions.andDo(document("create-game", - // preprocessRequest(prettyPrint()), - // preprocessResponse(prettyPrint()), - // resource( - // ResourceSnippetParameters.builder() - // .tag("Game") - // .summary("게스트 모집 생성") - // .description("게스트 모집을 생성한다.") - // .requestSchema(schema("GameCreateRequest")) - // .responseSchema(schema("GameIdResponse")) - // .requestHeaders( - // headerWithName(AUTHORIZATION).type(SimpleType.STRING) - // .description("Access Token") + // preprocessRequest(prettyPrint()), + // preprocessResponse(prettyPrint()), + // resource( + // ResourceSnippetParameters.builder() + // .tag("Game") + // .summary("게스트 모집 생성") + // .description("게스트 모집을 생성한다.") + // .requestSchema(schema("GameCreateRequest")) + // .responseSchema(schema("GameIdResponse")) + // .requestHeaders( + // headerWithName(AUTHORIZATION).type(SimpleType.STRING) + // .description("Access Token") + // ) + // .requestFields( + // fieldWithPath("content").type(JsonFieldType.STRING) + // .description("게스트 모집글 내용"), + // fieldWithPath("playDate").type(JsonFieldType.STRING).description("경기 날짜"), + // fieldWithPath("playStartTime").type(JsonFieldType.STRING) + // .description("경기 시작 시간"), + // fieldWithPath("playTimeMinutes").type(JsonFieldType.NUMBER) + // .description("경기 진행 분"), + // fieldWithPath("mainAddress").type(JsonFieldType.STRING) + // .description("메인 주소(도/시, 구, 동, 번지)"), + // fieldWithPath("detailAddress").type(JsonFieldType.STRING) + // .description("상세 주소(층, 호수)"), + // fieldWithPath("latitude").type(JsonFieldType.VARIES).description("위도"), + // fieldWithPath("longitude").type(JsonFieldType.VARIES).description("경도"), + // fieldWithPath("cost").type(JsonFieldType.NUMBER).description("비용"), + // fieldWithPath("maxMemberCount").type(JsonFieldType.NUMBER).description("인원 제한"), + // fieldWithPath("positions").type(JsonFieldType.ARRAY).description("포지션 목록") + // ) + // .responseFields( + // fieldWithPath("gameId").type(JsonFieldType.NUMBER).description("게스트 모집 ID") + // ) + // .build() // ) - // .requestFields( - // fieldWithPath("content").type(JsonFieldType.STRING) - // .description("게스트 모집글 내용"), - // fieldWithPath("playDate").type(JsonFieldType.STRING).description("경기 날짜"), - // fieldWithPath("playStartTime").type(JsonFieldType.STRING) - // .description("경기 시작 시간"), - // fieldWithPath("playTimeMinutes").type(JsonFieldType.NUMBER) - // .description("경기 진행 분"), - // fieldWithPath("mainAddress").type(JsonFieldType.STRING) - // .description("메인 주소(도/시, 구, 동, 번지)"), - // fieldWithPath("detailAddress").type(JsonFieldType.STRING) - // .description("상세 주소(층, 호수)"), - // fieldWithPath("latitude").type(JsonFieldType.VARIES).description("위도"), - // fieldWithPath("longitude").type(JsonFieldType.VARIES).description("경도"), - // fieldWithPath("cost").type(JsonFieldType.NUMBER).description("비용"), - // fieldWithPath("maxMemberCount").type(JsonFieldType.NUMBER).description("인원 제한"), - // fieldWithPath("positions").type(JsonFieldType.ARRAY).description("포지션 목록") - // ) - // .responseFields( - // fieldWithPath("gameId").type(JsonFieldType.NUMBER).description("게스트 모집 ID") - // ) - // .build() // ) - // ) // ); // } @@ -104,13 +113,20 @@ class GameDocumentTest extends IntegrationGameTest { @DisplayName("조건별(장소) 게스트 모집글 조회") void findGamesByCategory_ReturnGameResponses() throws Exception { // given - final Game game = gameSetup.saveWithConfirmedMembers(3); + final List members = memberSetup.save(3); + final MemberEntity host = members.get(0); + final MemberEntity guest1 = members.get(1); + final MemberEntity guest2 = members.get(2); + + GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerConfirmed(guest1.getId(), game.getId()); + gameSetup.registerConfirmed(guest2.getId(), game.getId()); // when final ResultActions resultActions = mockMvc.perform( get(BASE_URL) .param("category", ADDRESS.getValue()) - .param("value", "서울시+영등포구") + .param("value", "서울시+강남구") .param("page", "0") .param("size", "1") ) @@ -215,7 +231,14 @@ void findGamesByCategory_ReturnGameResponses() throws Exception { @DisplayName("게스트 모집글 상세 조회") void findGameById_ReturnGameResponse() throws Exception { // given - final Game game = gameSetup.saveWithConfirmedMembers(3); + final List members = memberSetup.save(3); + final MemberEntity host = members.get(0); + final MemberEntity guest1 = members.get(1); + final MemberEntity guest2 = members.get(2); + + GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerConfirmed(guest1.getId(), game.getId()); + gameSetup.registerConfirmed(guest2.getId(), game.getId()); // when final ResultActions resultActions = mockMvc.perform(get(BASE_URL + "/{gameId}", game.getId())) @@ -309,17 +332,18 @@ void findGameById_ReturnGameResponse() throws Exception { @DisplayName("게스트 모집 참여 신청") void registerGameMember_ReturnVoid() throws Exception { // given - final List members = memberSetup.save(2); - final Member host = members.get(0); - final Member guest = members.get(1); - final Game game = gameSetup.save(host); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + final GameEntity gameEntity = gameSetup.save(host.getId()); final String subject = String.valueOf(guest.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); // when final ResultActions resultActions = mockMvc.perform( - post(BASE_URL + "/{gameId}/members", game.getId()) + post(BASE_URL + "/{gameId}/members", gameEntity.getId()) .header(AUTHORIZATION, "Bearer " + authTokens.getAccessToken()) ) .andExpect(status().isNoContent()); @@ -350,8 +374,14 @@ void registerGameMember_ReturnVoid() throws Exception { @DisplayName("게스트 모집에 참여 신청된 혹은 확정된 사용자 정보 목록 조회") void findAllGameMembers_ReturnGameResponseWithWaitingMembers() throws Exception { // given - final Game game = gameSetup.saveWithWaitingMembers(3); - final Member host = game.getHost(); + final List members = memberSetup.save(3); + final MemberEntity host = members.get(0); + final MemberEntity guest1 = members.get(1); + final MemberEntity guest2 = members.get(2); + + GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerWaiting(guest1.getId(), game.getId()); + gameSetup.registerWaiting(guest2.getId(), game.getId()); final String subject = String.valueOf(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -458,11 +488,12 @@ void findAllGameMembers_ReturnGameResponseWithWaitingMembers() throws Exception @DisplayName("게스트 모집 참여 신청 수락") void updateGameMemberRegistrationStatus_ReturnVoid() throws Exception { // given - final Game game = gameSetup.saveWithWaitingMembers(2); - final Member host = game.getHost(); - final Member guest = game.getGameMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + final GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerWaiting(guest.getId(), game.getId()); final String subject = String.valueOf(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -512,11 +543,12 @@ void updateGameMemberRegistrationStatus_ReturnVoid() throws Exception { @DisplayName("게스트 모집 참여 신청 거절/취소") void deleteGameMember_ReturnVoid() throws Exception { // given - final Game game = gameSetup.saveWithWaitingMembers(2); - final Member host = game.getHost(); - final Member guest = game.getGameMembers() - .get(1) - .getMember(); + final List members = memberSetup.save(2); + final MemberEntity host = members.get(0); + final MemberEntity guest = members.get(1); + + final GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerWaiting(guest.getId(), game.getId()); final String subject = String.valueOf(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); @@ -555,19 +587,20 @@ void deleteGameMember_ReturnVoid() throws Exception { @DisplayName("다른 사용자 매너 스코어 리뷰") void reviewMannerScores_ReturnVoid() throws Exception { // given - final Game game = gameSetup.saveWithConfirmedMembers(3); - final Member host = game.getHost(); - final List gameMembers = game.getGameMembers(); - final List guests = gameMembers.subList(1, gameMembers.size()) - .stream() - .map(GameMember::getMember) - .toList(); + final List members = memberSetup.save(3); + final MemberEntity host = members.get(0); + final MemberEntity guest1 = members.get(1); + final MemberEntity guest2 = members.get(2); + + GameEntity game = gameSetup.save(host.getId()); + gameSetup.registerConfirmed(guest1.getId(), game.getId()); + gameSetup.registerConfirmed(guest2.getId(), game.getId()); final String subject = String.valueOf(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(subject); final MannerScoreReviewsRequest mannerScoreReviewsRequest = GameDtoFixtures.mannerScoreReviewsRequestBuild( - guests); + List.of(guest1, guest2)); final String requestBody = objectMapper.writeValueAsString(mannerScoreReviewsRequest); // when diff --git a/src/test/java/kr/pickple/back/game/service/GameServiceTest.java b/src/test/java/kr/pickple/back/game/service/GameServiceTest.java deleted file mode 100644 index 2e9efc8c..00000000 --- a/src/test/java/kr/pickple/back/game/service/GameServiceTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package kr.pickple.back.game.service; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.BDDMockito.*; - -import java.util.Optional; - -import org.junit.jupiter.api.DisplayName; -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 kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.fixture.domain.GameFixtures; -import kr.pickple.back.fixture.domain.MemberFixtures; -import kr.pickple.back.game.domain.Game; -import kr.pickple.back.game.dto.response.GameResponse; -import kr.pickple.back.game.repository.GameRepository; -import kr.pickple.back.member.domain.Member; - -@ExtendWith(MockitoExtension.class) -public class GameServiceTest { - - @InjectMocks - private GameService gameService; - - @Mock - private GameRepository gameRepository; - - @Test - @DisplayName("게스트 모집을 상세 조회할 수 있다.") - void findGameById_ReturnGameResponse() { - // given - final AddressDepth1 addressDepth1 = AddressDepth1.builder() - .name("서울시") - .build(); - final AddressDepth2 addressDepth2 = AddressDepth2.builder() - .name("영등포구") - .addressDepth1(addressDepth1) - .build(); - - final Member host = MemberFixtures.memberBuild(addressDepth1, addressDepth2); - - final Game game = GameFixtures.gameBuild(addressDepth1, addressDepth2, host); - - given(gameRepository.findById(anyLong())).willReturn(Optional.ofNullable(game)); - - // when - final GameResponse gameResponse = gameService.findGameDetailsById(1L); - - // then - assertThat(gameResponse).isNotNull(); - } -} diff --git a/src/test/java/kr/pickple/back/member/IntegrationMemberTest.java b/src/test/java/kr/pickple/back/member/IntegrationMemberTest.java index 6124f914..670069e7 100644 --- a/src/test/java/kr/pickple/back/member/IntegrationMemberTest.java +++ b/src/test/java/kr/pickple/back/member/IntegrationMemberTest.java @@ -8,10 +8,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; +import kr.pickple.back.address.repository.AddressDepth1Repository; +import kr.pickple.back.address.repository.AddressDepth2Repository; import kr.pickple.back.auth.domain.token.JwtProvider; import kr.pickple.back.fixture.setup.CrewSetup; import kr.pickple.back.fixture.setup.GameSetup; import kr.pickple.back.fixture.setup.MemberSetup; +import kr.pickple.back.game.repository.GamePositionRepository; +import kr.pickple.back.member.repository.MemberPositionRepository; @SpringBootTest @AutoConfigureRestDocs @@ -21,6 +25,12 @@ public abstract class IntegrationMemberTest { @Autowired protected MockMvc mockMvc; + @Autowired + protected ObjectMapper objectMapper; + + @Autowired + protected JwtProvider jwtProvider; + @Autowired protected MemberSetup memberSetup; @@ -31,8 +41,14 @@ public abstract class IntegrationMemberTest { protected GameSetup gameSetup; @Autowired - protected ObjectMapper objectMapper; + protected AddressDepth1Repository addressDepth1Repository; @Autowired - protected JwtProvider jwtProvider; + protected AddressDepth2Repository addressDepth2Repository; + + @Autowired + protected GamePositionRepository gamePositionRepository; + + @Autowired + protected MemberPositionRepository memberPositionRepository; } diff --git a/src/test/java/kr/pickple/back/member/controller/MemberControllerTest.java b/src/test/java/kr/pickple/back/member/controller/MemberControllerTest.java index c9a91595..4dace6e3 100644 --- a/src/test/java/kr/pickple/back/member/controller/MemberControllerTest.java +++ b/src/test/java/kr/pickple/back/member/controller/MemberControllerTest.java @@ -4,18 +4,21 @@ import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import java.util.List; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.ResultActions; import org.springframework.transaction.annotation.Transactional; +import kr.pickple.back.auth.domain.oauth.OauthProvider; import kr.pickple.back.auth.domain.token.AuthTokens; -import kr.pickple.back.crew.domain.Crew; -import kr.pickple.back.fixture.dto.MemberDtoFixtures; +import kr.pickple.back.crew.repository.entity.CrewEntity; import kr.pickple.back.member.IntegrationMemberTest; -import kr.pickple.back.member.domain.Member; import kr.pickple.back.member.dto.request.MemberCreateRequest; +import kr.pickple.back.member.repository.entity.MemberEntity; +import kr.pickple.back.position.domain.Position; @Transactional class MemberControllerTest extends IntegrationMemberTest { @@ -23,10 +26,20 @@ class MemberControllerTest extends IntegrationMemberTest { private static final String BASE_URL = "/members"; @Test - @DisplayName("회원을 생성할 수 있다.") + @DisplayName("사용자가 회원가입을 한다.") void createMemberById_ReturnAuthenticatedMemberResponse() throws Exception { // given - final MemberCreateRequest memberCreateRequest = MemberDtoFixtures.memberCreateRequestBuild(); + final MemberCreateRequest memberCreateRequest = MemberCreateRequest.builder() + .nickname("강백호") + .profileImageUrl("https://pickple.com/image") + .email("kangbaekho@pickple.com") + .positions(List.of(Position.CENTER, Position.POINT_GUARD)) + .oauthId(12345678L) + .oauthProvider(OauthProvider.KAKAO) + .addressDepth1("서울시") + .addressDepth2("구로구") + .build(); + final String requestBody = objectMapper.writeValueAsString(memberCreateRequest); final String subject = memberCreateRequest.getOauthProvider().toString() + memberCreateRequest.getOauthId(); @@ -58,29 +71,39 @@ void createMemberById_ReturnAuthenticatedMemberResponse() throws Exception { } @Test - @DisplayName("회원 프로필을 조회할 수 있다.") + @DisplayName("사용자의 프로필을 조회한다.") void findMemberById_ReturnMemberProfileResponse() throws Exception { // given - final Member savedMember = memberSetup.save(); - final Crew savedCrew = crewSetup.save(savedMember); + final MemberEntity member = memberSetup.save(); + final CrewEntity savedCrew = crewSetup.save(member.getId()); + final String memberAddressDepth1Name = addressDepth1Repository.findById(member.getAddressDepth1Id()) + .get() + .getName(); + final String memberAddressDepth2Name = addressDepth2Repository.findById(member.getAddressDepth2Id()) + .get() + .getName(); + + final String memberPositionName = memberPositionRepository.findAllByMemberId(member.getId()) + .get(0) + .getPosition() + .getAcronym(); // when - final ResultActions resultActions = mockMvc.perform(get(BASE_URL + "/{members}", savedMember.getId())); + final ResultActions resultActions = mockMvc.perform(get(BASE_URL + "/{members}", member.getId())); // then resultActions.andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("id").value(savedMember.getId())) - .andExpect(jsonPath("email").value(savedMember.getEmail())) - .andExpect(jsonPath("nickname").value(savedMember.getNickname())) - .andExpect(jsonPath("introduction").value(savedMember.getIntroduction())) - .andExpect(jsonPath("profileImageUrl").value(savedMember.getProfileImageUrl())) - .andExpect(jsonPath("mannerScore").value(savedMember.getMannerScore())) - .andExpect(jsonPath("mannerScoreCount").value(savedMember.getMannerScoreCount())) - .andExpect(jsonPath("addressDepth1").value(savedMember.getAddressDepth1().getName())) - .andExpect(jsonPath("addressDepth2").value(savedMember.getAddressDepth2().getName())) - .andExpect(jsonPath("positions[0]").value(savedMember.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("positions[1]").value(savedMember.getPositions().get(1).getAcronym())) + .andExpect(jsonPath("id").value(member.getId())) + .andExpect(jsonPath("email").value(member.getEmail())) + .andExpect(jsonPath("nickname").value(member.getNickname())) + // .andExpect(jsonPath("introduction").value(member.getIntroduction)) 머지 후 수정 예정 + .andExpect(jsonPath("profileImageUrl").value(member.getProfileImageUrl())) + .andExpect(jsonPath("mannerScore").value(member.getMannerScore())) + .andExpect(jsonPath("mannerScoreCount").value(member.getMannerScoreCount())) + .andExpect(jsonPath("addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("addressDepth2").value(memberAddressDepth2Name)) + .andExpect(jsonPath("positions[0]").value(memberPositionName)) .andExpect(jsonPath("crews[0].id").value(savedCrew.getId())) .andExpect(jsonPath("crews[0].name").value(savedCrew.getName())) .andExpect(jsonPath("crews[0].content").value(savedCrew.getContent())) @@ -91,29 +114,37 @@ void findMemberById_ReturnMemberProfileResponse() throws Exception { .andExpect(jsonPath("crews[0].status").value(savedCrew.getStatus().getDescription())) .andExpect(jsonPath("crews[0].likeCount").value(savedCrew.getLikeCount())) .andExpect(jsonPath("crews[0].competitionPoint").value(savedCrew.getCompetitionPoint())) - .andExpect(jsonPath("crews[0].leader.id").value(savedMember.getId())) - .andExpect(jsonPath("crews[0].leader.nickname").value(savedMember.getNickname())) - .andExpect(jsonPath("crews[0].leader.email").value(savedMember.getEmail())) - .andExpect(jsonPath("crews[0].leader.introduction").value(savedMember.getIntroduction())) - .andExpect(jsonPath("crews[0].leader.profileImageUrl").value(savedMember.getProfileImageUrl())) - .andExpect(jsonPath("crews[0].leader.mannerScore").value(savedMember.getMannerScore())) - .andExpect(jsonPath("crews[0].leader.mannerScoreCount").value(savedMember.getMannerScoreCount())) - .andExpect(jsonPath("crews[0].leader.addressDepth1").value(savedMember.getAddressDepth1().getName())) - .andExpect(jsonPath("crews[0].leader.addressDepth2").value(savedMember.getAddressDepth2().getName())) - .andExpect( - jsonPath("crews[0].leader.positions[0]").value(savedMember.getPositions().get(0).getAcronym())) - .andExpect( - jsonPath("crews[0].leader.positions[1]").value(savedMember.getPositions().get(1).getAcronym())) - .andExpect(jsonPath("crews[0].addressDepth1").value(savedCrew.getAddressDepth1().getName())) - .andExpect(jsonPath("crews[0].addressDepth2").value(savedCrew.getAddressDepth2().getName())); + .andExpect(jsonPath("crews[0].leader.id").value(member.getId())) + .andExpect(jsonPath("crews[0].leader.nickname").value(member.getNickname())) + .andExpect(jsonPath("crews[0].leader.email").value(member.getEmail())) + .andExpect(jsonPath("crews[0].leader.introduction").value(member.getIntroduction())) + .andExpect(jsonPath("crews[0].leader.profileImageUrl").value(member.getProfileImageUrl())) + .andExpect(jsonPath("crews[0].leader.mannerScore").value(member.getMannerScore())) + .andExpect(jsonPath("crews[0].leader.mannerScoreCount").value(member.getMannerScoreCount())) + .andExpect(jsonPath("crews[0].leader.addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("crews[0].leader.addressDepth2").value(memberAddressDepth2Name)) + .andExpect(jsonPath("crews[0].leader.positions[0]").value(memberPositionName)) + .andExpect(jsonPath("crews[0].addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("crews[0].addressDepth2").value(memberAddressDepth2Name)); } @Test - @DisplayName("회원이 가입한 크루 목록을 조회할 수 있다.") + @DisplayName("사용자가 가입한 크루 목록을 조회한다.") void findAllCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { // given - final Member member = memberSetup.save(); - final Crew crew = crewSetup.save(member); + final MemberEntity member = memberSetup.save(); + final CrewEntity crew = crewSetup.save(member.getId()); + final String memberAddressDepth1Name = addressDepth1Repository.findById(member.getAddressDepth1Id()) + .get() + .getName(); + final String memberAddressDepth2Name = addressDepth2Repository.findById(member.getAddressDepth2Id()) + .get() + .getName(); + + final String memberPositionName = memberPositionRepository.findAllByMemberId(member.getId()) + .get(0) + .getPosition() + .getAcronym(); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(member.getId())); @@ -143,12 +174,11 @@ void findAllCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { .andExpect(jsonPath("[0].leader.profileImageUrl").value(member.getProfileImageUrl())) .andExpect(jsonPath("[0].leader.mannerScore").value(member.getMannerScore())) .andExpect(jsonPath("[0].leader.mannerScoreCount").value(member.getMannerScoreCount())) - .andExpect(jsonPath("[0].leader.addressDepth1").value(member.getAddressDepth1().getName())) - .andExpect(jsonPath("[0].leader.addressDepth2").value(member.getAddressDepth2().getName())) - .andExpect(jsonPath("[0].leader.positions[0]").value(member.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("[0].leader.positions[1]").value(member.getPositions().get(1).getAcronym())) - .andExpect(jsonPath("[0].addressDepth1").value(crew.getAddressDepth1().getName())) - .andExpect(jsonPath("[0].addressDepth2").value(crew.getAddressDepth2().getName())) + .andExpect(jsonPath("[0].leader.addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("[0].leader.addressDepth2").value(memberAddressDepth2Name)) + .andExpect(jsonPath("[0].leader.positions[0]").value(memberPositionName)) + .andExpect(jsonPath("[0].addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("[0].addressDepth2").value(memberAddressDepth2Name)) .andExpect(jsonPath("[0].members[0].id").value(member.getId())) .andExpect(jsonPath("[0].members[0].nickname").value(member.getNickname())) .andExpect(jsonPath("[0].members[0].email").value(member.getEmail())) @@ -156,16 +186,28 @@ void findAllCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { .andExpect(jsonPath("[0].members[0].profileImageUrl").value(member.getProfileImageUrl())) .andExpect(jsonPath("[0].members[0].mannerScore").value(member.getMannerScore())) .andExpect(jsonPath("[0].members[0].mannerScoreCount").value(member.getMannerScoreCount())) - .andExpect(jsonPath("[0].members[0].addressDepth1").value(member.getAddressDepth1().getName())) - .andExpect(jsonPath("[0].members[0].addressDepth2").value(member.getAddressDepth2().getName())); + .andExpect(jsonPath("[0].members[0].addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("[0].members[0].addressDepth2").value(memberAddressDepth2Name)); } @Test - @DisplayName("회원이 만든 크루 목록을 조회할 수 있다.") + @DisplayName("사용자가 만든 크루 목록을 조회한다.") void findCreatedCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { // given - final Member member = memberSetup.save(); - final Crew crew = crewSetup.save(member); + final MemberEntity member = memberSetup.save(); + final CrewEntity crew = crewSetup.save(member.getId()); + final String memberAddressDepth1Name = addressDepth1Repository.findById(member.getAddressDepth1Id()) + .get() + .getName(); + final String memberAddressDepth2Name = addressDepth2Repository.findById(member.getAddressDepth2Id()) + .get() + .getName(); + + System.out.println(member.getId()); + final String memberPositionName = memberPositionRepository.findAllByMemberId(member.getId()) + .get(0) + .getPosition() + .getAcronym(); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(member.getId())); @@ -194,12 +236,11 @@ void findCreatedCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { .andExpect(jsonPath("[0].leader.profileImageUrl").value(member.getProfileImageUrl())) .andExpect(jsonPath("[0].leader.mannerScore").value(member.getMannerScore())) .andExpect(jsonPath("[0].leader.mannerScoreCount").value(member.getMannerScoreCount())) - .andExpect(jsonPath("[0].leader.addressDepth1").value(member.getAddressDepth1().getName())) - .andExpect(jsonPath("[0].leader.addressDepth2").value(member.getAddressDepth2().getName())) - .andExpect(jsonPath("[0].leader.positions[0]").value(member.getPositions().get(0).getAcronym())) - .andExpect(jsonPath("[0].leader.positions[1]").value(member.getPositions().get(1).getAcronym())) - .andExpect(jsonPath("[0].addressDepth1").value(crew.getAddressDepth1().getName())) - .andExpect(jsonPath("[0].addressDepth2").value(crew.getAddressDepth2().getName())) + .andExpect(jsonPath("[0].leader.addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("[0].leader.addressDepth2").value(memberAddressDepth2Name)) + .andExpect(jsonPath("[0].leader.positions[0]").value(memberPositionName)) + .andExpect(jsonPath("[0].addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("[0].addressDepth2").value(memberAddressDepth2Name)) .andExpect(jsonPath("[0].members[0].id").value(member.getId())) .andExpect(jsonPath("[0].members[0].nickname").value(member.getNickname())) .andExpect(jsonPath("[0].members[0].email").value(member.getEmail())) @@ -207,7 +248,7 @@ void findCreatedCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { .andExpect(jsonPath("[0].members[0].profileImageUrl").value(member.getProfileImageUrl())) .andExpect(jsonPath("[0].members[0].mannerScore").value(member.getMannerScore())) .andExpect(jsonPath("[0].members[0].mannerScoreCount").value(member.getMannerScoreCount())) - .andExpect(jsonPath("[0].members[0].addressDepth1").value(member.getAddressDepth1().getName())) - .andExpect(jsonPath("[0].members[0].addressDepth2").value(member.getAddressDepth2().getName())); + .andExpect(jsonPath("[0].members[0].addressDepth1").value(memberAddressDepth1Name)) + .andExpect(jsonPath("[0].members[0].addressDepth2").value(memberAddressDepth2Name)); } } diff --git a/src/test/java/kr/pickple/back/member/docs/MemberDocumentTest.java b/src/test/java/kr/pickple/back/member/docs/MemberDocumentTest.java index 8151099f..7062f31a 100644 --- a/src/test/java/kr/pickple/back/member/docs/MemberDocumentTest.java +++ b/src/test/java/kr/pickple/back/member/docs/MemberDocumentTest.java @@ -23,14 +23,14 @@ import kr.pickple.back.auth.domain.token.AuthTokens; import kr.pickple.back.fixture.dto.MemberDtoFixtures; import kr.pickple.back.member.IntegrationMemberTest; -import kr.pickple.back.member.domain.Member; import kr.pickple.back.member.dto.request.MemberCreateRequest; +import kr.pickple.back.member.repository.entity.MemberEntity; @Transactional class MemberDocumentTest extends IntegrationMemberTest { @Test - @DisplayName("회원 생성") + @DisplayName("사용자 회원가입") void createMember_ReturnAuthenticatedMemberResponse() throws Exception { // given final MemberCreateRequest memberCreateRequest = MemberDtoFixtures.memberCreateRequestBuild(); @@ -102,11 +102,11 @@ void createMember_ReturnAuthenticatedMemberResponse() throws Exception { } @Test - @DisplayName("회원 프로필 조회") + @DisplayName("사용자 프로필 조회") void findMemberById_ReturnMemberProfileResponse() throws Exception { // given - final Member member = memberSetup.save(); - crewSetup.save(member); + final MemberEntity member = memberSetup.save(); + crewSetup.save(member.getId()); // when final ResultActions resultActions = mockMvc.perform(get("/members/{memberId}", member.getId())) @@ -191,11 +191,11 @@ void findMemberById_ReturnMemberProfileResponse() throws Exception { } @Test - @DisplayName("회원이 가입한 크루 목록 조회") + @DisplayName("사용자가 가입한 크루 목록 조회") void findAllCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { // given - final Member member = memberSetup.save(); - crewSetup.save(member); + final MemberEntity member = memberSetup.save(); + crewSetup.save(member.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(member.getId())); @@ -295,11 +295,11 @@ void findAllCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { } @Test - @DisplayName("회원이 만든 크루 목록 조회") + @DisplayName("사용자가 만든 크루 목록 조회") void findCreatedCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { // given - final Member member = memberSetup.save(); - crewSetup.save(member); + final MemberEntity member = memberSetup.save(); + crewSetup.save(member.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(member.getId())); @@ -395,11 +395,11 @@ void findCreatedCrewsByMemberId_ReturnCrewProfileResponses() throws Exception { } @Test - @DisplayName("회원이 참여 확정된 게스트 모집글 목록 조회") + @DisplayName("사용자의 참여 확정 게스트 모집글 목록 조회") void findAllMemberGames_ReturnGameResponses() throws Exception { // given - final Member host = memberSetup.save(); - gameSetup.save(host); + final MemberEntity host = memberSetup.save(); + gameSetup.save(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(host.getId())); @@ -448,7 +448,8 @@ void findAllMemberGames_ReturnGameResponses() throws Exception { fieldWithPath("[].latitude").type(JsonFieldType.VARIES).description("위도"), fieldWithPath("[].longitude").type(JsonFieldType.VARIES).description("경도"), fieldWithPath("[].status").type(JsonFieldType.STRING).description("게스트 모집 상태"), - fieldWithPath("[].isReviewDone").type(JsonFieldType.BOOLEAN).description("리뷰 완료 여부"), + fieldWithPath("[].isReviewDone").type(JsonFieldType.BOOLEAN) + .description("리뷰 완료 여부"), fieldWithPath("[].viewCount").type(JsonFieldType.NUMBER).description("조회 수"), fieldWithPath("[].cost").type(JsonFieldType.NUMBER).description("비용"), fieldWithPath("[].memberCount").type(JsonFieldType.NUMBER).description("인원 수"), @@ -510,11 +511,11 @@ void findAllMemberGames_ReturnGameResponses() throws Exception { } @Test - @DisplayName("회원이 만든 게스트 모집글 목록 조회") + @DisplayName("사용자가 만든 게스트 모집글 목록 조회") void findAllCreatedGames_ReturnGameResponses() throws Exception { // given - final Member host = memberSetup.save(); - gameSetup.save(host); + final MemberEntity host = memberSetup.save(); + gameSetup.save(host.getId()); final AuthTokens authTokens = jwtProvider.createLoginToken(String.valueOf(host.getId())); @@ -559,7 +560,8 @@ void findAllCreatedGames_ReturnGameResponses() throws Exception { fieldWithPath("[].latitude").type(JsonFieldType.VARIES).description("위도"), fieldWithPath("[].longitude").type(JsonFieldType.VARIES).description("경도"), fieldWithPath("[].status").type(JsonFieldType.STRING).description("게스트 모집 상태"), - fieldWithPath("[].isReviewDone").type(JsonFieldType.BOOLEAN).description("리뷰 완료 여부"), + fieldWithPath("[].isReviewDone").type(JsonFieldType.BOOLEAN) + .description("리뷰 완료 여부"), fieldWithPath("[].viewCount").type(JsonFieldType.NUMBER).description("조회 수"), fieldWithPath("[].cost").type(JsonFieldType.NUMBER).description("비용"), fieldWithPath("[].memberCount").type(JsonFieldType.NUMBER).description("인원 수"), diff --git a/src/test/java/kr/pickple/back/member/domain/MemberTest.java b/src/test/java/kr/pickple/back/member/domain/MemberTest.java index be73afa7..3a4dbcdc 100644 --- a/src/test/java/kr/pickple/back/member/domain/MemberTest.java +++ b/src/test/java/kr/pickple/back/member/domain/MemberTest.java @@ -1,7 +1,7 @@ package kr.pickple.back.member.domain; -import static kr.pickple.back.member.domain.Member.*; import static kr.pickple.back.member.exception.MemberExceptionCode.*; +import static kr.pickple.back.member.repository.entity.MemberEntity.*; import static org.assertj.core.api.Assertions.*; import org.junit.jupiter.api.DisplayName; @@ -9,39 +9,46 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.fixture.domain.AddressFixtures; import kr.pickple.back.fixture.domain.MemberFixtures; import kr.pickple.back.member.exception.MemberException; class MemberTest { @Test - @DisplayName("회원의 매너스코어를 업데이트 시킬 수 있다.") - void updateMannerScore() { + @DisplayName("멤버의 ID가 동일한지 확인한다.") + void checkMemberIdSame() { // given - final AddressDepth1 addressDepth1 = AddressFixtures.addressDepth1Build(); - final AddressDepth2 addressDepth2 = AddressFixtures.addressDepth2Build(); + final Long memberId = 1L; + final Member member = MemberFixtures.memberBuild(); - // when & then + // when + boolean isMemberIdMatched = member.isIdMatched(memberId); + + // then + assertThat(isMemberIdMatched).isTrue(); + } + + @Test + @DisplayName("멤버의 매너 점수를 업데이트한다.") + void updateMannerScore() { MANNER_SCORE_POINT_RANGE.forEach((mannerScorePoint) -> { - final Member member = MemberFixtures.memberBuild(addressDepth1, addressDepth2); + // given + final Member member = MemberFixtures.memberBuild(); + + // when member.updateMannerScore(mannerScorePoint); + // then assertThat(member.getMannerScore()).isEqualTo(mannerScorePoint); }); } @ParameterizedTest @ValueSource(ints = {-300, -2, 10, 10000}) - @DisplayName("회원의 매너스코어를 업데이트할 때 매너스코어 포인트가 범위에서 벗어난 경우 예외가 발생한다.") + @DisplayName("상대방이 준 매너 점수가 매너 점수 범위에 포함되지 않을 때 예외를 발생시킨다.") void updateMannerScore_ThrowException(int mannerScorePoint) { // given - final AddressDepth1 addressDepth1 = AddressFixtures.addressDepth1Build(); - final AddressDepth2 addressDepth2 = AddressFixtures.addressDepth2Build(); - - final Member member = MemberFixtures.memberBuild(addressDepth1, addressDepth2); + final Member member = MemberFixtures.memberBuild(); // when & then assertThatThrownBy(() -> member.updateMannerScore(mannerScorePoint)) diff --git a/src/test/java/kr/pickple/back/member/service/MemberServiceTest.java b/src/test/java/kr/pickple/back/member/service/MemberServiceTest.java deleted file mode 100644 index 7185b45a..00000000 --- a/src/test/java/kr/pickple/back/member/service/MemberServiceTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package kr.pickple.back.member.service; - -import static kr.pickple.back.common.domain.RegistrationStatus.*; -import static kr.pickple.back.member.exception.MemberExceptionCode.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.BDDMockito.*; - -import java.util.List; -import java.util.Optional; - -import org.junit.jupiter.api.DisplayName; -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 kr.pickple.back.address.domain.AddressDepth1; -import kr.pickple.back.address.domain.AddressDepth2; -import kr.pickple.back.address.dto.response.MainAddressResponse; -import kr.pickple.back.address.service.AddressService; -import kr.pickple.back.auth.config.property.JwtProperties; -import kr.pickple.back.auth.domain.token.AuthTokens; -import kr.pickple.back.auth.domain.token.JwtProvider; -import kr.pickple.back.auth.domain.token.RefreshToken; -import kr.pickple.back.auth.repository.RedisRepository; -import kr.pickple.back.crew.dto.response.CrewProfileResponse; -import kr.pickple.back.fixture.domain.MemberFixtures; -import kr.pickple.back.fixture.dto.MemberDtoFixtures; -import kr.pickple.back.member.domain.Member; -import kr.pickple.back.member.dto.request.MemberCreateRequest; -import kr.pickple.back.member.dto.response.AuthenticatedMemberResponse; -import kr.pickple.back.member.dto.response.MemberProfileResponse; -import kr.pickple.back.member.exception.MemberException; -import kr.pickple.back.member.repository.MemberRepository; - -@ExtendWith(MockitoExtension.class) -class MemberServiceTest { - - @InjectMocks - private MemberService memberService; - - @Mock - private MemberRepository memberRepository; - - @Mock - private JwtProvider jwtProvider; - - @Mock - private AddressService addressService; - - @Mock - private JwtProperties jwtProperties; - - @Mock - private RedisRepository redisRepository; - - @Test - @Transactional - @DisplayName("회원을 생성할 수 있다.") - void createMember_ReturnAuthenticatedMemberResponse() { - // given - final MemberCreateRequest memberCreateRequest = MemberDtoFixtures.memberCreateRequestBuild(); - final AddressDepth1 addressDepth1 = AddressDepth1.builder() - .name("서울시") - .build(); - final AddressDepth2 addressDepth2 = AddressDepth2.builder() - .name("영등포구") - .addressDepth1(addressDepth1) - .build(); - final MainAddressResponse mainAddressResponse = MainAddressResponse.builder() - .addressDepth1(addressDepth1) - .addressDepth2(addressDepth2) - .build(); - final AuthTokens authTokens = AuthTokens.builder() - .accessToken("accessToken") - .refreshToken("refreshToken") - .build(); - - final Member member = memberCreateRequest.toEntity(mainAddressResponse); - - given(addressService.findMainAddressByNames(anyString(), anyString())).willReturn(mainAddressResponse); - given(memberRepository.save(any(Member.class))).willReturn(member); - given(jwtProvider.createLoginToken(anyString())).willReturn(authTokens); - given(jwtProperties.getRefreshTokenExpirationTime()).willReturn(1000L); - - // when - final AuthenticatedMemberResponse authenticatedMemberResponse = memberService.createMember(memberCreateRequest); - - // then - verify(redisRepository).saveHash(anyString(), anyString(), any(RefreshToken.class), anyLong()); - assertThat(authenticatedMemberResponse).isNotNull(); - } - - @Test - @DisplayName("회원을 조회할 수 있다.") - void findMemberById_ReturnMemberProfileResponse() { - // given - final Long memberId = 1L; - final Member member = buildMember(); - given(memberRepository.findById(anyLong())).willReturn(Optional.ofNullable(member)); - - // when - final MemberProfileResponse memberProfileResponse = memberService.findMemberProfileById(memberId); - - // then - assertThat(memberProfileResponse).isNotNull(); - } - - @Test - @DisplayName("회원이 가입한 크루 목록을 조회할 수 있다.") - void findAllCrewsByMemberId_ReturnCrewProfileResponses() { - // given - final Long memberId = 1L; - final Long loggedInMemberId = 1L; - final Member member = buildMember(); - given(memberRepository.findById(anyLong())).willReturn(Optional.ofNullable(member)); - - // when - final List crewProfileResponses = memberService.findAllCrewsByMemberId(memberId, - loggedInMemberId, CONFIRMED); - - // then - assertThat(crewProfileResponses).isNotNull(); - } - - @Test - @DisplayName("회원이 만든 크루 목록을 조회할 수 있다.") - void findCreatedCrewsByMemberId_ReturnCrewProfileResponses() { - // given - final Long memberId = 1L; - final Long loggedInMemberId = 1L; - final Member member = buildMember(); - - given(memberRepository.findById(anyLong())).willReturn(Optional.ofNullable(member)); - - // when - final List crewProfileResponses = memberService.findCreatedCrewsByMemberId( - loggedInMemberId, - memberId - ); - - // then - assertThat(crewProfileResponses).isNotNull(); - } - - @Test - @DisplayName("회원이 만든 크루 목록을 조회할 때 본인이 만든 크루가 아닌 경우 예외가 발생한다.") - void findCreatedCrewsByMemberId_ThrowException() { - // given - final Long memberId = 1L; - final Long loggedInMemberId = 2L; - final Member member = buildMember(); - - // when && then - assertThatThrownBy(() -> memberService.findCreatedCrewsByMemberId( - loggedInMemberId, - memberId - )).isInstanceOf(MemberException.class) - .hasMessage(MEMBER_MISMATCH.getMessage()); - } - - private Member buildMember() { - final AddressDepth1 addressDepth1 = AddressDepth1.builder() - .name("서울시") - .build(); - final AddressDepth2 addressDepth2 = AddressDepth2.builder() - .name("영등포구") - .addressDepth1(addressDepth1) - .build(); - - return MemberFixtures.memberBuild(addressDepth1, addressDepth2); - } -} diff --git a/src/test/java/kr/pickple/back/position/service/PositionServiceTest.java b/src/test/java/kr/pickple/back/position/service/PositionServiceTest.java deleted file mode 100644 index 62bad2af..00000000 --- a/src/test/java/kr/pickple/back/position/service/PositionServiceTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package kr.pickple.back.position.service; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import kr.pickple.back.position.domain.Position; -import kr.pickple.back.position.dto.PositionResponse; - -@SpringBootTest -class PositionServiceTest { - - @Autowired - private PositionService positionService; - - @Test - @DisplayName("포지션 정보 조회 시, 전체 포지션 정보를 반환한다.") - void findAllPositions_Success() { - //when - List Positions = positionService.findAllPositions(); - - //then - assertThat(Positions.get(0).getName()).isEqualTo(Position.CENTER.getName()); - assertThat(Positions.get(0).getAcronym()).isEqualTo(Position.CENTER.getAcronym()); - assertThat(Positions.get(0).getDescription()).isEqualTo(Position.CENTER.getDescription()); - - assertThat(Positions.get(1).getName()).isEqualTo(Position.POWER_FORWARD.getName()); - assertThat(Positions.get(1).getAcronym()).isEqualTo(Position.POWER_FORWARD.getAcronym()); - assertThat(Positions.get(1).getDescription()).isEqualTo(Position.POWER_FORWARD.getDescription()); - - assertThat(Positions.get(2).getName()).isEqualTo(Position.SMALL_FORWARD.getName()); - assertThat(Positions.get(2).getAcronym()).isEqualTo(Position.SMALL_FORWARD.getAcronym()); - assertThat(Positions.get(2).getDescription()).isEqualTo(Position.SMALL_FORWARD.getDescription()); - - assertThat(Positions.get(3).getName()).isEqualTo(Position.POINT_GUARD.getName()); - assertThat(Positions.get(3).getAcronym()).isEqualTo(Position.POINT_GUARD.getAcronym()); - assertThat(Positions.get(3).getDescription()).isEqualTo(Position.POINT_GUARD.getDescription()); - - assertThat(Positions.get(4).getName()).isEqualTo(Position.SHOOTING_GUARD.getName()); - assertThat(Positions.get(4).getAcronym()).isEqualTo(Position.SHOOTING_GUARD.getAcronym()); - assertThat(Positions.get(4).getDescription()).isEqualTo(Position.SHOOTING_GUARD.getDescription()); - - assertThat(Positions.get(5).getName()).isEqualTo(Position.EMPTY.getName()); - assertThat(Positions.get(5).getAcronym()).isEqualTo(Position.EMPTY.getAcronym()); - assertThat(Positions.get(5).getDescription()).isEqualTo(Position.EMPTY.getDescription()); - } -} \ No newline at end of file