Skip to content

Commit

Permalink
[BE] Member, MemberTeamPlace, TeamPlace 구조 및 연관관계 리팩터링 (#239)
Browse files Browse the repository at this point in the history
* feat: MemberTeamPlace 필드 추가

- DisplayMemberName 필드 추가
- DisplayTeamPlaceName 필드 추가

* refactor: TeamPlace -> MemberTeamPlace 의존관계 제거

* feat: 팀플레이스 소속멤버 정보 조회 repository 기능 구현

* refactor: 멤버 팀플레이스 소속 확인 인터셉터 네이밍 변경

* style: 공백줄 제거
  • Loading branch information
pilyang authored Jul 31, 2023
1 parent e0eb4e0 commit 6c9fb8a
Show file tree
Hide file tree
Showing 16 changed files with 376 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@
import org.springframework.web.servlet.HandlerMapping;
import team.teamby.teambyteam.auth.jwt.JwtTokenExtractor;
import team.teamby.teambyteam.auth.jwt.JwtTokenProvider;
import team.teamby.teambyteam.member.domain.Member;
import team.teamby.teambyteam.member.domain.MemberRepository;
import team.teamby.teambyteam.member.domain.vo.Email;
import team.teamby.teambyteam.teamplace.domain.TeamPlace;
import team.teamby.teambyteam.teamplace.domain.TeamPlaceRepository;
import team.teamby.teambyteam.teamplace.exception.TeamPlaceException;

import java.util.Map;

@Component
@RequiredArgsConstructor
public final class TeamPlaceInterceptor implements HandlerInterceptor {
public final class TeamPlaceParticipationInterceptor implements HandlerInterceptor {

private final JwtTokenExtractor jwtTokenExtractor;
private final JwtTokenProvider jwtTokenProvider;
private final TeamPlaceRepository teamPlaceRepository;
private final MemberRepository memberRepository;

@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
Expand All @@ -37,8 +37,8 @@ public boolean preHandle(final HttpServletRequest request, final HttpServletResp
}

private boolean hasNotMemberInTeamPlace(final Long teamPlaceId, final String email) {
final TeamPlace teamPlace = teamPlaceRepository.findById(teamPlaceId)
final Member member = memberRepository.findByEmail(new Email(email))
.orElseThrow(TeamPlaceException.NotFoundException::new);
return !teamPlace.hasMemberByMemberEmail(new Email(email));
return !member.isMemberOf(teamPlaceId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import team.teamby.teambyteam.member.configuration.MemberArgumentResolver;
import team.teamby.teambyteam.auth.presentation.MemberInterceptor;
import team.teamby.teambyteam.auth.presentation.TeamPlaceInterceptor;
import team.teamby.teambyteam.auth.presentation.TeamPlaceParticipationInterceptor;

import java.util.List;

Expand All @@ -16,7 +16,7 @@
public class WebMvcConfiguration implements WebMvcConfigurer {

private final MemberInterceptor memberInterceptor;
private final TeamPlaceInterceptor teamPlaceInterceptor;
private final TeamPlaceParticipationInterceptor teamPlaceParticipationInterceptor;

@Override
public void addArgumentResolvers(final List<HandlerMethodArgumentResolver> resolvers) {
Expand All @@ -28,7 +28,7 @@ public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(memberInterceptor)
.order(1)
.addPathPatterns("/api/**");
registry.addInterceptor(teamPlaceInterceptor)
registry.addInterceptor(teamPlaceParticipationInterceptor)
.order(2)
.addPathPatterns("/**/team-place/**");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package team.teamby.teambyteam.member.domain;

import jakarta.persistence.*;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down Expand Up @@ -51,4 +58,10 @@ public List<TeamPlace> getTeamPlaces() {
.map(MemberTeamPlace::getTeamPlace)
.toList();
}

public boolean isMemberOf(final Long targetTeamPlaceId) {
return getMemberTeamPlaces().stream()
.mapToLong(memberTeamPlace -> memberTeamPlace.getMember().getId())
.anyMatch(teamPlaceId -> teamPlaceId == targetTeamPlaceId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package team.teamby.teambyteam.member.domain;

import team.teamby.teambyteam.member.domain.vo.DisplayMemberName;

public record MemberIdAndDisplayNameOnly(
Long id,
DisplayMemberName displayMemberName
) {
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package team.teamby.teambyteam.member.domain;

import jakarta.persistence.*;
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 lombok.Getter;
import lombok.NoArgsConstructor;
import team.teamby.teambyteam.global.domain.BaseEntity;
import team.teamby.teambyteam.member.domain.vo.DisplayMemberName;
import team.teamby.teambyteam.member.domain.vo.DisplayTeamPlaceName;
import team.teamby.teambyteam.teamplace.domain.TeamPlace;

@Getter
Expand All @@ -16,17 +25,24 @@ public class MemberTeamPlace extends BaseEntity {
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = false)
@JoinColumn(nullable = false, updatable = false)
private Member member;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(nullable = false)
@JoinColumn(nullable = false, updatable = false)
private TeamPlace teamPlace;

@Embedded
private DisplayMemberName displayMemberName;

@Embedded
private DisplayTeamPlaceName displayTeamPlaceName;

public void setMemberAndTeamPlace(final Member member, final TeamPlace teamPlace) {
this.member = member;
this.teamPlace = teamPlace;
this.displayMemberName = new DisplayMemberName(member.getName().getValue());
this.displayTeamPlaceName = new DisplayTeamPlaceName(teamPlace.getName().getValue());
member.getMemberTeamPlaces().add(this);
teamPlace.getMemberTeamPlaces().add(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,12 @@

import org.springframework.data.jpa.repository.JpaRepository;

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

public interface MemberTeamPlaceRepository extends JpaRepository<MemberTeamPlace, Long> {

Optional<MemberIdAndDisplayNameOnly> findByTeamPlaceIdAndMemberId(Long teamPlaceId, Long memberId);

List<MemberIdAndDisplayNameOnly> findAllByTeamPlaceId(Long teamPlaceId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package team.teamby.teambyteam.member.domain.vo;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import team.teamby.teambyteam.member.exception.MemberTeamPlaceException;

import java.util.Objects;

@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode
@Getter
public class DisplayMemberName {

private static final int MAX_LENGTH = 20;

@Column(name = "display_member_name", nullable = false, length = MAX_LENGTH)
private String value;

public DisplayMemberName(final String value) {
validate(value);
this.value = value;
}

private void validate(final String value) {
if (Objects.isNull(value)) {
throw new NullPointerException("멤버 이름은 null일 수 없습니다.");
}
if (value.length() > MAX_LENGTH) {
throw new MemberTeamPlaceException.MemberDisplayNameLengthException();
}
if (value.isBlank()) {
throw new MemberTeamPlaceException.MemberNameBlankException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package team.teamby.teambyteam.member.domain.vo;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import team.teamby.teambyteam.member.exception.MemberTeamPlaceException;

import java.util.Objects;

@Embeddable
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode
@Getter
public class DisplayTeamPlaceName {

private static final int MAX_LENGTH = 30;

@Column(name = "display_team_place_name", nullable = false, length = MAX_LENGTH)
private String value;

public DisplayTeamPlaceName(final String value) {
validate(value);
this.value = value;
}

private void validate(final String value) {
if (Objects.isNull(value)) {
throw new NullPointerException("팀플레이스의 이름은 null일 수 없습니다.");
}
if (value.length() > MAX_LENGTH) {
throw new MemberTeamPlaceException.TeamPlaceDisplayNameLengthException();
}
if (value.isBlank()) {
throw new MemberTeamPlaceException.TeamPlaceNameBlankException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package team.teamby.teambyteam.member.exception;

public class MemberTeamPlaceException extends RuntimeException {

public MemberTeamPlaceException(final String message) {
super(message);
}

public static class MemberDisplayNameLengthException extends MemberTeamPlaceException {
public MemberDisplayNameLengthException() {
super("멤버 이름의 길이가 최대 이름 길이를 초과했습니다.");
}
}

public static class MemberNameBlankException extends MemberTeamPlaceException {
public MemberNameBlankException() {
super("멤버 이름은 공백을 제외한 1자 이상이어야합니다.");
}
}

public static class TeamPlaceDisplayNameLengthException extends MemberTeamPlaceException {
public TeamPlaceDisplayNameLengthException() {
super("팀플레이스의 이름의 길이가 최대 이름 길이를 초과했습니다.");
}
}

public static class TeamPlaceNameBlankException extends MemberTeamPlaceException {
public TeamPlaceNameBlankException() {
super("팀플레이스의 이름은 공백을 제외한 1자 이상이어야합니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package team.teamby.teambyteam.teamplace.domain;

import jakarta.persistence.*;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import team.teamby.teambyteam.global.domain.BaseEntity;
import team.teamby.teambyteam.member.domain.MemberTeamPlace;
import team.teamby.teambyteam.member.domain.vo.Email;
import team.teamby.teambyteam.teamplace.domain.vo.Name;

import java.util.ArrayList;
import java.util.List;

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand All @@ -24,18 +23,7 @@ public class TeamPlace extends BaseEntity {
@Embedded
private Name name;

@OneToMany(mappedBy = "teamPlace", cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true)
private List<MemberTeamPlace> memberTeamPlaces;

public TeamPlace(final Name name) {
this.name = name;
this.memberTeamPlaces = new ArrayList<>();
}

public boolean hasMemberByMemberEmail(final Email email) {
return memberTeamPlaces.stream()
.anyMatch(memberTeamPlace -> memberTeamPlace.getMember()
.getEmail()
.equals(email));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package team.teamby.teambyteam.member.domain;

import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import team.teamby.teambyteam.common.RepositoryTest;
import team.teamby.teambyteam.common.fixtures.MemberFixtures;
import team.teamby.teambyteam.common.fixtures.TeamPlaceFixtures;
import team.teamby.teambyteam.member.domain.vo.DisplayMemberName;
import team.teamby.teambyteam.teamplace.domain.TeamPlace;

import java.util.List;

class MemberTeamPlaceRepositoryTest extends RepositoryTest {

@Autowired
private MemberTeamPlaceRepository memberTeamPlaceRepository;

@Test
@DisplayName("멤버아이디와 소속된 팀의 아이디로 해당 팀에서의 사용자 이름을 조회한다.")
void findMemberIdAndDisplayNameByTeamPlaceIdAndMemberId() {
// given
final Member PHILIP = testFixtureBuilder.buildMember(MemberFixtures.PHILIP());
final TeamPlace ENGLISH_TEAM_PLACE = testFixtureBuilder.buildTeamPlace(TeamPlaceFixtures.ENGLISH_TEAM_PLACE());
testFixtureBuilder.buildMemberTeamPlace(PHILIP, ENGLISH_TEAM_PLACE);

// when
final MemberIdAndDisplayNameOnly actual = memberTeamPlaceRepository.findByTeamPlaceIdAndMemberId(ENGLISH_TEAM_PLACE.getId(), PHILIP.getId()).get();

//then
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(actual.id()).isEqualTo(PHILIP.getId());
softly.assertThat(actual.displayMemberName().getValue()).isEqualTo(PHILIP.getName().getValue());
});
}

@Test
@DisplayName("팀플레이스에 소속된 모든 멤버들의 아이디와 해당 팀에서의 사용자 이름을 조회한다.")
void findAllMemberIdAndDisplayNameByTeamPlace() {
// given
final Member PHILIP = testFixtureBuilder.buildMember(MemberFixtures.PHILIP());
final Member ENDLE = testFixtureBuilder.buildMember(MemberFixtures.ENDEL());
final TeamPlace ENGLISH_TEAM_PLACE = testFixtureBuilder.buildTeamPlace(TeamPlaceFixtures.ENGLISH_TEAM_PLACE());
testFixtureBuilder.buildMemberTeamPlace(PHILIP, ENGLISH_TEAM_PLACE);
testFixtureBuilder.buildMemberTeamPlace(ENDLE, ENGLISH_TEAM_PLACE);

// when
final List<MemberIdAndDisplayNameOnly> actual = memberTeamPlaceRepository.findAllByTeamPlaceId(ENGLISH_TEAM_PLACE.getId());
final List<String> displayNames = actual.stream()
.map(MemberIdAndDisplayNameOnly::displayMemberName)
.map(DisplayMemberName::getValue)
.toList();

//then
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(actual).hasSize(2);
softly.assertThat(displayNames).containsExactlyInAnyOrder(PHILIP.getName().getValue(), ENDLE.getName().getValue());
});
}
}
Loading

0 comments on commit 6c9fb8a

Please sign in to comment.