Member를 만들던 것과 비슷하므로 추가적인 개념이 있다면 그 구분에 대해서만 설명하겠습니다.
TeamFounder = Team의 요구사항과 같다고 할 수 있습니다.
TeamFounder는 하나의 Team만 만들 수 있습니다.
TeamFounder는 Member가 신청한 리스트를 확인할 수 있으며 수락이 가능합니다.
TeamFounder는 Team해체가 가능합니다.
TeamFounder는 League 참가 신청을 할 수 있습니다.
LeagueHost가 수락을 해야 League에 참가가 완료됩니다.
여러 리그에 신청이 가능하지만 한 League에만 참석이 가능합니다.
1. TeamService, TeamServiceImpl
public interface TeamRepository extends JpaRepository < Team , Long > {
@Query("select t from Team t where t.name= :teamName")
Optional<Team> findByName ( @Param("teamName") String teamName) ;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TeamDto {
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class CreateOrUpdateTeamDto{
public String name ;
public int size ;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public static class RestTeamDto {
public String teamName ;
public Integer maxSize ;
public Integer nowSize ;
}
}
@Override
public Long createTeam ( String name, int size, Long userId ) {
User user = userRepository.findById(userId).get();
checkDuplicateTeamName(name);
Team team = Team.createTeam(user, name, size);
return teamRepository.save(team).getId();
}
private void checkDuplicateTeamName ( String name ) {
teamRepository.findByName(name).ifPresent(
a -> {
throw new DuplicateTeamNameException("이미 같은 이름의 팀이 있습니다." );
}
);
}
Team.createTeam()을 이용하여 User(TeamFounder)와의 연관 관계를 설정하며, Team을 저장해줍니다.
단 중복되는 Team이름을 예외 처리는 해줍니다.
RequestTeamRepository query 추가
@Query ("select re from RequestTeam re join fetch re.team where re.team.id= :teamId" )
List<RequestTeam> findByTeamId(@Param ("teamId" ) Long teamId, Sort sort);
추가 사항을 활용하여 Team에게 Member들이 신청한 리스트 보기
@Override
public List<RequestTeamDto> findRequestList(Long teamId) {
return requestTeamRepository.findByTeamId (teamId, Sort.by(Sort.Direction.DESC
, "createdTime")).stream ().map (a -> new RequestTeamDto(a.getRequest(),a .getTeam ().getName ()))
.collect (Collectors.toList());
}
fetch join을 통하여 최적화합니다.
Sort를 이용하여 요청한 시간 순서대로 RequestTeam을 받고 stream().map()을 사용하고, Entity 외부 노출을 숨기기 위해 List<RequestTeamDto>로 변경해줍니다.
RequestTeamRepository query 추가
@Query ("select re from RequestTeam re join fetch re.user join fetch re.team where re.id= :requestId" )
Optional<RequestTeam> findWithFetchJoinUserById(@Param ("requestId" )Long requestId);
public void acceptRequest ( ) {
request=true ;
team.setUser(user);
team.addTeam(user);
team.increaseSize();
}
public void increaseSize ( ) {
nowSize++;
}
AlreadyAcceptMemberException 추가
public class AlreadyAcceptMemberException extends RuntimeException {
public AlreadyAcceptMemberException ( ) {
super ();
}
public AlreadyAcceptMemberException ( String message ) {
super (message);
}
public AlreadyAcceptMemberException ( String message, Throwable cause ) {
super (message, cause);
}
public AlreadyAcceptMemberException ( Throwable cause ) {
super (cause);
}
protected AlreadyAcceptMemberException ( String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace ) {
super (message, cause, enableSuppression, writableStackTrace);
}
}
추사 사항을 활용하여 Team이 Member의 가입 신청을 받는 로직입니다.
@Override
public void acceptMember(Long requestTeamId) {
RequestTeam requestTeam = requestTeamRepository.findWithFetchJoinUserById(requestTeamId).orElseThrow(
() -> {
throw new NotExistRequestException("존재하지 않는 요청입니다." );
}
);
User user = requestTeam.getUser();
if (user.getTeam()!=null ){
throw new AlreadyTeamException("이미 팀에 속해있습니다." );
}
if (requestTeam.getRequest()){
throw new AlreadyAcceptMemberException("이미 승인된 요청입니다." );
}
requestTeam.acceptRequest();
}
fetch join을 이용하여 최적화합니다.
만약 요청을 한 Member가 팀을 이미 가지고 있다면 여러 팀에 속할 수 없으므로 예외를 터트립니다.
requestTeam은 영속성 콘테스트에서 관리하는 객체이므로 requestTeam과 team에 대한 비즈니스 로직을 이용하여 dirty checking을 이용해줍니다.
public void removeTeam ( ) {
for (User user: userList){
user.setTeam(null );
}
userList=null ;
}
추가 사항을 활용하여 자신의 팀을 삭제하는 로직입니다.
@Override
public void deleteTeam ( Long teamId ) {
Team team = teamRepository.findById(teamId).get();
team.removeTeam();
teamRepository.deleteById(teamId);
}
마찬가지로 dirty checking을 이용합니다.
public void update ( String name, int size ) {
this .name=name;
this .maxSize=size;
}
추가 사항을 활용하여 TeamFounder가 자신의 팀의 이름이나 팀 규모를 바꾸는 것입니다.
@Override
public Long updateTeam(String name, int size, Long teamId, Long userId) {
User user = userRepository.findById(userId).get ();
Team team = teamRepository.findById(teamId).get ();
checkDuplicateTeamName(name);
team.update(name,size);
return team.getId();
}
@Query("select t from Team t where t.maxSize>t.nowSize")
List<Team> findByRestSeat () ;
위의 query를 통해 현재 팀 최대 규모를 다 채우지 못한 팀들의 리스트를 불러오는 로직입니다.
@Override
public List<TeamDto.RestTeamDto> findRestTeamSeat() {
return teamRepository.findByRestSeat ().stream ().map ( a -> new TeamDto.RestTeamDto(a.getName(),a .getMaxSize (),a .getNowSize ()))
.collect (Collectors.toList());
}
NotMatchingTeamNameException 추가
public class NotMatchingTeamNameException extends RuntimeException {
public NotMatchingTeamNameException ( ) {
super ();
}
public NotMatchingTeamNameException ( String message ) {
super (message);
}
public NotMatchingTeamNameException ( String message, Throwable cause ) {
super (message, cause);
}
public NotMatchingTeamNameException ( Throwable cause ) {
super (cause);
}
protected NotMatchingTeamNameException ( String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace ) {
super (message, cause, enableSuppression, writableStackTrace);
}
}
Team의 이름을 가지고 검색을 하는 요청을 수행하는 로직입니다.
@Override
public TeamDto.RestTeamDto findTeamByName(String teamName) {
Team team = teamRepository.findByName(teamName).orElseThrow(
() -> {
throw new NotMatchingTeamNameException("일치하는 이름의 팀이 없습니다." );
}
);
return new TeamDto.RestTeamDto(team.getName(),team.getMaxSize(),team.getNowSize());
}
검색한 팀의 이름이 없을 경우를 대비하여 예외를 작성합니다.
public interface LeagueRepository extends JpaRepository < League , Long > {
}
RequestLeagueRepository 추가
public interface RequestLeagueRepository extends JpaRepository < RequestLeague , Long > {
@Query("select re from RequestLeague re join fetch re.team where re.team.id= :teamId")
Optional<RequestLeague> findByTeamId ( @Param("teamId") Long teamId) ;
}
DuplicateRequestToLeagueException 추가
public class DuplicateRequestToLeagueException extends RuntimeException {
public DuplicateRequestToLeagueException ( ) {
super ();
}
public DuplicateRequestToLeagueException ( String message ) {
super (message);
}
public DuplicateRequestToLeagueException ( String message, Throwable cause ) {
super (message, cause);
}
public DuplicateRequestToLeagueException ( Throwable cause ) {
super (cause);
}
protected DuplicateRequestToLeagueException ( String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace ) {
super (message, cause, enableSuppression, writableStackTrace);
}
}
위의 추가한 사항들을 활용하여 Team이 Leauge에게 참가 신청을 하는 로직입니다. League에게 신청을 하므로 League와 RequestLeague Repository들이 필요합니다.
@Override
public Long requestLeague(Long leagueId, Long teamId) {
League league = leagueRepository.findById(leagueId).get ();
Team team = teamRepository.findById(teamId).get ();
RequestLeague requestLeague = RequestLeague.createRequestLeague(team, league);
if (team.getLeague()!=null ){
throw new AlreadyTeamException("이미 리그에 참가하고 있습니다." );
}
requestLeagueRepository.findByTeamId(teamId).ifPresent(
a -> {
throw new DuplicateRequestToLeagueException("이미 리그에 신청한 기록이 있습니다." );
}
);
return requestLeagueRepository.save(requestLeague).getId();
}
Team이 이미 League에 속해있다면 예외를 터트립니다.
findByTeamId()를 fetch join을 이용하여 최적화하며, 이미 신청한 기록이 있다면 예외를 터트립니다.
RequestLeagueRepository query 추가
@Query (" select re from RequestLeague re join fetch re.team join fetch re.league where re.team.id= :teamId and " +
"re.league.id= :leagueId" )
Optional<RequestLeague> findByMemberIdAndTeamId(@Param ("teamId" ) Long teamId, @Param ("leagueId" ) Long leagueId);
RequestLeague의 비즈니스 로직 추가
public void removeRequest ( ) {
league.getRequestLeagueList().remove (this );
team.getRequestLeagueList().remove (this );
}
추가한 사항을 활용하여 Team이 League에 참가 신청을 취소할 수 있습니다.
@Override
public void withdrawalLeagueRequest(Long teamId, Long leagueId) {
RequestLeague requestLeague = requestLeagueRepository.findByMemberIdAndTeamId(leagueId, teamId).get ();
requestLeague.removeRequest();
requestLeagueRepository.deleteById(requestLeague.getId());
}
fetch join을 이용하여 최적화 하며, dirty checking을 이용하여 상태를 변경합니다.
requestLeagueRepository.deleteById()를 이용하여 삭제합니다.
2. TeamService Test code (Member Test code와 유사하므로 설명은 생략하겠습니다.)
@SpringBootTest
@Transactional
class TeamServiceImplTest {
@Autowired
TeamRepository teamRepository;
@Autowired
TeamService teamService;
@Autowired
UserRepository userRepository;
@Autowired
MemberService memberService;
@Test
void createTeamSuccess(){
Address address = getAddress();
User user = getTeamFounder1(address);
Long teamFounderId = userRepository.save(user).getId();
Long teamId = teamService.createTeam("상명" , 10 , teamFounderId);
Team team = teamRepository.findById(teamId).get ();
assertThat(team.getMaxSize()).isEqualTo(10 );
assertThat(team.getEmail()).isEqualTo(user.getEmail());
assertThat(team.getUserList().size()).isEqualTo(1 );
}
@Test
void createTeamFailDuplicateTeamName(){
Address address = getAddress();
User user = getTeamFounder1(address);
Long teamFounderId1 = userRepository.save(user).getId();
User user2 = getTeamFounder2(address);
Long teamFounderId2 = userRepository.save(user2).getId();
Long teamId = teamService.createTeam("상명" , 10 , teamFounderId1);
assertThatThrownBy(() -> teamService.createTeam("상명" ,10 ,teamFounderId2))
.isInstanceOf(DuplicateTeamNameException.class );
}
@Test
void DeleteTeam(){
Address address = getAddress();
User user = getTeamFounder1(address);
Long teamFounderId1 = userRepository.save(user).getId();
Long teamId = teamService.createTeam("상명" , 10 , teamFounderId1);
teamService.deleteTeam(teamId);
assertThatThrownBy(() ->teamRepository.findById(teamId).orElseThrow(
() ->
{
throw new IllegalStateException("없는 팀입니다." );
}
)).isInstanceOf(IllegalStateException.class );
}
@Test
void updateTeam(){
Address address = getAddress();
User user = getTeamFounder1(address);
Long teamFounderId1 = userRepository.save(user).getId();
Long teamId1 = teamService.createTeam("상명" , 10 , teamFounderId1);
User user2 = getTeamFounder2(address);
Long teamFounderId2 = userRepository.save(user2).getId();
Long teamId2 = teamService.createTeam("국민" , 10 , teamFounderId1);
Long updateId = teamService.updateTeam("경복" , 10 , teamId2, teamFounderId2);
Team team = teamRepository.findById(updateId).get ();
assertThat(team.getEmail()).isEqualTo(user2.getEmail());
assertThat(team.getName()).isEqualTo("경복" );
}
@Test
void updateTeamFailDuplicateName(){
Address address = getAddress();
User user = getTeamFounder1(address);
Long teamFounderId1 = userRepository.save(user).getId();
Long teamId1 = teamService.createTeam("상명" , 10 , teamFounderId1);
User user2 = getTeamFounder2(address);
Long teamFounderId2 = userRepository.save(user2).getId();
Long teamId2 = teamService.createTeam("국민" , 10 , teamFounderId2);
assertThatThrownBy(() -> teamService.updateTeam("상명" , 10 , teamId2, teamFounderId1))
.isInstanceOf(DuplicateTeamNameException.class );
}
@Test
void acceptRequest(){
Address address = getAddress();
User user1 = getTeamFounder1(address);
Long teamFounderId = userRepository.save(user1).getId();
Long teamId = teamService.createTeam("상명" , 10 , teamFounderId);
User user2 = getMember(address);
Long memberId = userRepository.save(user2).getId();
Long requestTeamId = memberService.requestTeam(teamId, memberId);
teamService.acceptMember(requestTeamId);
Team team = teamRepository.findById(teamId).get ();
User resultUser = userRepository.findById(memberId).get ();
assertThat(resultUser.getTeam().getId()).isEqualTo(teamId);
assertThat(resultUser.getTeam()).isEqualTo(team);
assertThat(resultUser.getTeam().getNowSize()).isEqualTo(1 );
}
@Test
void findRequest(){
Address address = getAddress();
User user1 = getTeamFounder1(address);
Long teamFounderId = userRepository.save(user1).getId();
Long teamId = teamService.createTeam("상명" , 10 , teamFounderId);
User user2 = getMember(address);
Long memberId = userRepository.save(user2).getId();
Long requestTeamId = memberService.requestTeam(teamId, memberId);
List<RequestTeamDto> result = teamService.findRequestList(teamId);
assertThat(result.size()).isEqualTo(1 );
assertThat(result.get (0 ).MemberName).isEqualTo("kim" );
}
@Test
void findRestTeamSeat(){
Address address = getAddress();
User user = getTeamFounder1(address);
Long teamFounderId1 = userRepository.save(user).getId();
Long teamId1 = teamService.createTeam("상명" , 1 , teamFounderId1);
User user2 = getTeamFounder2(address);
Long teamFounderId2 = userRepository.save(user2).getId();
Long teamId2 = teamService.createTeam("국민" , 10 , teamFounderId2);
User user3 = getMember(address);
Long memberId = userRepository.save(user3).getId();
Long requestTeamId = memberService.requestTeam(teamId1, memberId);
teamService.acceptMember(requestTeamId);
List<TeamDto.RestTeamDto> result = teamService.findRestTeamSeat();
assertThat(result.size()).isEqualTo(1 );
assertThat(result.get (0 ).getTeamName()).isEqualTo("국민" );
}
@Test
void findTeamByNameSuccess(){
Address address = getAddress();
User user = getTeamFounder1(address);
Long teamFounderId1 = userRepository.save(user).getId();
Long teamId1 = teamService.createTeam("상명" , 10 , teamFounderId1);
TeamDto.RestTeamDto result = teamService.findTeamByName("상명" );
assertThat(result.getTeamName()).isEqualTo("상명" );
assertThat(result.getMaxSize()).isEqualTo(10 );
assertThat(result.getNowSize()).isEqualTo(0 );
}
@Test
void findTeamByNameFailNotExistName(){
Address address = getAddress();
User user = getTeamFounder1(address);
Long teamFounderId1 = userRepository.save(user).getId();
Long teamId1 = teamService.createTeam("상명" , 10 , teamFounderId1);
assertThatThrownBy(() -> teamService.findTeamByName("국민" ))
.isInstanceOf(NotMatchingTeamNameException.class );
}
private Address getAddress() {
return Address.builder()
.street("s" )
.city("a" )
.build();
}
private User getTeamFounder1(Address address) {
return User.createUser("rkdlem48" , "asdf" , "kim" , address, ROLE.TEAM_FOUNDER);
}
private User getTeamFounder2(Address address){
return User.createUser("rkdlem49" , "asdf" , "kim" , address, ROLE.TEAM_FOUNDER);
}
private User getMember(Address address){
return User.createUser("rkdlem48" , "asdf" , "kim" , address, ROLE.MEMBER);
}
}
지금까지 TeamService에서 실행되는 로직들과 로직에 필요한 Repository, query 들을 만들어봤습니다. Member를 만들 때와 유사하여 설명을 많이 생략한 점 이해해주시면 감사하겠습니다. 다음으로는 TeamController, TeamControllerAdvice, TeamInterceptor를 만들어보겠습니다. 감사합니다.
모든 코드는 아래 링크에서 확인 가능합니다.
https://github.com/rlaehdals/blogProject
GitHub - rlaehdals/blogProject
Contribute to rlaehdals/blogProject development by creating an account on GitHub.
github.com
댓글을 사용할 수 없습니다.