SpringBoot [스프링부트] 시작하기(9) Team 내부 로직 만들기
Member를 만들던 것과 비슷하므로 추가적인 개념이 있다면 그 구분에 대해서만 설명하겠습니다.
TeamFounder = Team의 요구사항과 같다고 할 수 있습니다.
- TeamFounder는 하나의 Team만 만들 수 있습니다.
- TeamFounder는 Member가 신청한 리스트를 확인할 수 있으며 수락이 가능합니다.
- TeamFounder는 Team해체가 가능합니다.
- TeamFounder는 League 참가 신청을 할 수 있습니다.
- LeagueHost가 수락을 해야 League에 참가가 완료됩니다.
- 여러 리그에 신청이 가능하지만 한 League에만 참석이 가능합니다.
1. TeamService, TeamServiceImpl
- TeamRepository 추가
public interface TeamRepository extends JpaRepository<Team, Long> {
@Query("select t from Team t where t.name= :teamName")
Optional<Team> findByName(@Param("teamName") String teamName);
}
- TeamDto
@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;
}
}
- 추가 사항을 활용하여 Team 만들기
@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) { //팀에 신청한 request 목록
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);
- RequestTeam 비즈니스 로직 추가
public void acceptRequest(){
request=true;
team.setUser(user);
team.addTeam(user);
team.increaseSize();
}
- Team 비즈니스 로직 추가
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을 이용해줍니다.
- Team에 비즈니스 로직을 추가해줍니다.
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을 이용합니다.
- Team에 비즈니스 로직을 추가합니다.
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();
}
- TeamRepository query 추가
@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());
}
- 검색한 팀의 이름이 없을 경우를 대비하여 예외를 작성합니다.
- LeagueRepository 추가
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
'SpringBoot > spring 프로젝트 만들기' 카테고리의 다른 글
SpringBoot [스프링부트] 시작하기(12) LeagueController, Interceptor, ControllerAdvice 만들기 (0) | 2022.01.18 |
---|---|
SpringBoot [스프링부트] 시작하기(10) TeamController, Interceptor, Advice만들기 (0) | 2022.01.17 |
SpringBoot [스프링부트] 시작하기(8) Interceptor 만들기 (0) | 2022.01.16 |
SpringBoot [스프링부트] 시작하기(7) ArgumentResolver 만들기 (0) | 2022.01.15 |
SpringBoot [스프링부트] 시작하기(6) MemberController 만들기 (0) | 2022.01.15 |
댓글
이 글 공유하기
다른 글
-
SpringBoot [스프링부트] 시작하기(12) LeagueController, Interceptor, ControllerAdvice 만들기
SpringBoot [스프링부트] 시작하기(12) LeagueController, Interceptor, ControllerAdvice 만들기
2022.01.18 -
SpringBoot [스프링부트] 시작하기(10) TeamController, Interceptor, Advice만들기
SpringBoot [스프링부트] 시작하기(10) TeamController, Interceptor, Advice만들기
2022.01.17 -
SpringBoot [스프링부트] 시작하기(8) Interceptor 만들기
SpringBoot [스프링부트] 시작하기(8) Interceptor 만들기
2022.01.16 -
SpringBoot [스프링부트] 시작하기(7) ArgumentResolver 만들기
SpringBoot [스프링부트] 시작하기(7) ArgumentResolver 만들기
2022.01.15