Skip to content

Commit

Permalink
Merge pull request #249 from Codiary-UMC-6th/feature/#248-team-member
Browse files Browse the repository at this point in the history
Feat: νŒ€μ› μΆ”κ°€/μ‚­μ œ/쑰회 API 개발
  • Loading branch information
yumzen authored Oct 27, 2024
2 parents 2561c7f + 270da0c commit e3681f5
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,15 @@ public static List<MemberResponseDTO.SimpleMemberDTO> toSimpleFollowResponseDto(
.collect(Collectors.toList());
}

public static List<MemberResponseDTO.SimpleMemberProfileDTO> toSimpleMemberProfileResponseDto(List<Member> members){
public static MemberResponseDTO.SimpleMemberProfileDTO tosimpleMemberProfileResponseDto(Member member){
return MemberResponseDTO.SimpleMemberProfileDTO.builder()
.userId(member.getMemberId())
.userName(member.getNickname())
.photoUrl(member.getImage() != null ? member.getImage().getImageUrl() : "")
.build();
}

public static List<MemberResponseDTO.SimpleMemberProfileDTO> toSimpleMemberProfileListResponseDto(List<Member> members){
return members.stream()
.map(member -> MemberResponseDTO.SimpleMemberProfileDTO.builder()
.userId(member.getMemberId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ public interface MemberRepository extends JpaRepository<Member, Long>, MemberRep
Boolean existsByNickname(String nickname);

Optional<Member> findByEmail(String email);

Optional<Member> findByNicknameIgnoreCase(String nickname);
}
Original file line number Diff line number Diff line change
@@ -1,57 +1,94 @@
package com.codiary.backend.domain.team.controller;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.member.security.CustomMemberDetails;
import com.codiary.backend.domain.member.service.MemberCommandService;
import com.codiary.backend.domain.team.converter.TeamConverter;
import com.codiary.backend.domain.team.dto.request.TeamRequestDTO;
import com.codiary.backend.domain.team.dto.response.TeamResponseDTO;
import com.codiary.backend.domain.team.entity.Team;
import com.codiary.backend.domain.team.entity.TeamMember;
import com.codiary.backend.domain.team.service.TeamMemberService;
import com.codiary.backend.domain.team.service.TeamService;
import com.codiary.backend.global.apiPayload.ApiResponse;
import com.codiary.backend.global.apiPayload.code.status.SuccessStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/teams")
@RequestMapping("/api/v2/teams")
@RequiredArgsConstructor
@Tag(name = "νŒ€ API", description = "νŒ€ 생성/쑰회/μˆ˜μ • κ΄€λ ¨ API μž…λ‹ˆλ‹€.")
public class TeamController {
private final TeamService teamService;
private final TeamMemberService teamMemberService;
private final MemberCommandService memberCommandService;

@PostMapping("")
@Operation(summary = "νŒ€ 생성")
public ApiResponse<TeamResponseDTO.TeamDTO> createTeam(@RequestBody TeamRequestDTO.CreateTeamDTO request){
Member member = memberCommandService.getRequester();
Team newTeam = teamService.createTeam(request, member);
public ApiResponse<TeamResponseDTO.TeamDTO> createTeam(@RequestBody TeamRequestDTO.CreateTeamDTO request,
@AuthenticationPrincipal CustomMemberDetails memberDetails){
Team newTeam = teamService.createTeam(request, memberDetails.getId());
return ApiResponse.onSuccess(SuccessStatus.TEAM_OK, TeamConverter.toTeamResponseDto(newTeam));
}

@GetMapping("/profile/{team_id}")
@Operation(summary = "νŒ€ ν”„λ‘œν•„ 쑰회")
public ApiResponse<TeamResponseDTO.TeamProfileDTO> getTeamProfile(@PathVariable("team_id") Long teamId){
Member member = memberCommandService.getRequester();
Team fetchedTeam = teamService.getTeamProfile(teamId, member);
public ApiResponse<TeamResponseDTO.TeamProfileDTO> getTeamProfile(@PathVariable("team_id") Long teamId,
@AuthenticationPrincipal CustomMemberDetails memberDetails){
Team fetchedTeam = teamService.getTeamProfile(teamId, memberDetails.getId());
return ApiResponse.onSuccess(SuccessStatus.TEAM_OK, TeamConverter.toTeamProfileResponseDto(fetchedTeam));
}

@GetMapping("/{team_id}")
@Operation(summary = "νŒ€ 정보 쑰회")
public ApiResponse<TeamResponseDTO.TeamDTO> getTeam(@PathVariable("team_id") Long teamId){
Member member = memberCommandService.getRequester();
Team fetchedTeam = teamService.getTeam(teamId, member);
public ApiResponse<TeamResponseDTO.TeamDTO> getTeam(@PathVariable("team_id") Long teamId,
@AuthenticationPrincipal CustomMemberDetails memberDetails){
Team fetchedTeam = teamService.getTeam(teamId, memberDetails.getId());
return ApiResponse.onSuccess(SuccessStatus.TEAM_OK, TeamConverter.toTeamResponseDto(fetchedTeam));
}

@PutMapping("/{team_id}")
@Operation(summary = "νŒ€ 정보 μˆ˜μ •")
public ApiResponse<TeamResponseDTO.TeamDTO> updateTeam(@PathVariable("team_id") Long teamId, @RequestBody TeamRequestDTO.UpdateTeamDTO request){
Member member = memberCommandService.getRequester();
Team updatedTeam = teamService.updateTeam(request, teamId, member);
public ApiResponse<TeamResponseDTO.TeamDTO> updateTeam(@PathVariable("team_id") Long teamId,
@RequestBody TeamRequestDTO.UpdateTeamDTO request,
@AuthenticationPrincipal CustomMemberDetails memberDetails){
Team updatedTeam = teamService.updateTeam(request, teamId, memberDetails.getId());
return ApiResponse.onSuccess(SuccessStatus.TEAM_OK, TeamConverter.toTeamResponseDto(updatedTeam));
}

@PostMapping("/team_member")
@Operation(summary = "νŒ€μ› μΆ”κ°€")
public ApiResponse<TeamResponseDTO.TeamMemberDTO> addTeamMember(
@RequestParam("team_id") Long teamId,
@RequestBody TeamRequestDTO.TeamMemberDTO request,
@AuthenticationPrincipal CustomMemberDetails memberDetails
){
TeamMember teamMember = teamMemberService.addTeamMember(memberDetails.getId(), teamId, request);
return ApiResponse.onSuccess(SuccessStatus.TEAM_OK, TeamConverter.toTeamMemberResponseDTO(teamMember));
}

@DeleteMapping("/team_member")
@Operation(summary = "νŒ€μ› μ‚­μ œ")
public ApiResponse<String> deleteTeamMember(
@RequestParam("team_id") Long teamId,
@RequestParam("member_id") Long memberId,
@AuthenticationPrincipal CustomMemberDetails memberDetails
){
teamMemberService.deleteTeamMember(memberDetails.getId(), teamId, memberId);
return ApiResponse.onSuccess(SuccessStatus.TEAM_OK, "νŒ€μ› μ‚­μ œκ°€ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
}

@GetMapping("/team_member")
@Operation(summary = "νŒ€μ› 쑰회")
public ApiResponse<List<TeamResponseDTO.TeamMemberDTO>> getTeamMember(
@RequestParam("team_id") Long teamId
){
Team team = teamMemberService.getTeamMember(teamId);
return ApiResponse.onSuccess(SuccessStatus.TEAM_OK, TeamConverter.toTeamMemberListResponseDto(team));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static TeamResponseDTO.TeamProfileDTO toTeamProfileResponseDto(Team team)
.instagram(team.getInstagram())
.isFollowed(false)
.teamMemberList(team.getTeamMemberList() == null ? null :
MemberConverter.toSimpleMemberProfileResponseDto(team.getTeamMemberList()
MemberConverter.toSimpleMemberProfileListResponseDto(team.getTeamMemberList()
.stream()
.map(TeamMember::getMember)
.collect(Collectors.toList())))
Expand Down Expand Up @@ -70,4 +70,19 @@ public static TeamResponseDTO.TeamFollowersDTO toTeamFollowersResponseDTO(Long t
)
.build();
}

public static TeamResponseDTO.TeamMemberDTO toTeamMemberResponseDTO(TeamMember teamMember){
return TeamResponseDTO.TeamMemberDTO.builder()
.teamMemberId(teamMember.getTeamMemberId())
.member(MemberConverter.tosimpleMemberProfileResponseDto(teamMember.getMember()))
.teamMemberRole(teamMember.getTeamMemberRole().name())
.build();

}

public static List<TeamResponseDTO.TeamMemberDTO> toTeamMemberListResponseDto(Team team) {
return team.getTeamMemberList().stream()
.map(TeamConverter::toTeamMemberResponseDTO)
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import io.swagger.v3.oas.annotations.media.Schema;

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class TeamRequestDTO {

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record CreateTeamDTO(
String name,
String intro,
Expand All @@ -16,6 +18,8 @@ public record CreateTeamDTO(
String instagram
) {}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record UpdateTeamDTO(
String name,
String email,
Expand All @@ -25,4 +29,13 @@ public record UpdateTeamDTO(
String discord,
String instagram
) {}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record TeamMemberDTO(
@Schema(description = "μΆ”κ°€ν•  νŒ€μ› λ‹‰λ„€μž„", example = "abc123*")
String memberNickName,
@Schema(description = "μΆ”κ°€ν•  νŒ€μ› μ—­ν• ", example = "MEMBER | ADMIN")
String memberRole
){}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,12 @@ public record TeamFollowDTO(
public record TeamFollowersDTO(
Long teamId,
List<MemberResponseDTO.SimpleMemberDTO> followers) {}

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
@Builder
public record TeamMemberDTO(
Long teamMemberId,
String teamMemberRole,
MemberResponseDTO.SimpleMemberProfileDTO member) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,6 @@ public class TeamMember {
@JoinColumn(name = "team_id")
private Team team;

@Column(name = "member_position", columnDefinition = "varchar(500)")
private String memberPosition;

@Builder
public TeamMember(TeamMemberRole teamMemberRole, Member member, Team team) {
this.teamMemberRole = teamMemberRole;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

public interface TeamFollowRepository extends JpaRepository<TeamFollow, Long>, TeamFollowRepositoryCustom {

Boolean existsByTeamAndAndMember(Team team, Member member);
Boolean existsByTeamAndMember(Team team, Member member);

Optional<TeamFollow> findTeamFollowByTeamAndMember(Team team, Member member);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.codiary.backend.domain.team.repository;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.team.entity.Team;
import com.codiary.backend.domain.team.entity.TeamMember;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface TeamMemberRepository extends JpaRepository<TeamMember, Long> {
boolean existsByTeamAndMember(Team team, Member member);

Optional<TeamMember> findByTeamAndMember(Team team, Member member);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface TeamRepositoryCustom {
boolean isTeamMember(Team team, Member member);

Optional<Team> findByIdWithFollowers(Long teamId);

Optional<Team> findByIdWithTeamMemberList(Long teamId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,14 @@ public Optional<Team> findByIdWithFollowers(Long teamId) {
.where(team.teamId.eq(teamId))
.fetchOne());
}

@Override
public Optional<Team> findByIdWithTeamMemberList(Long teamId) {
return Optional.ofNullable(queryFactory
.selectFrom(team)
.leftJoin(team.teamMemberList, teamMember).fetchJoin()
.leftJoin(teamMember.member).fetchJoin()
.where(team.teamId.eq(teamId))
.fetchOne());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public TeamFollow followTeam(Long toId, Member fromMember) {
TeamFollow teamFollow;

// business logic: νŒ”λ‘œμš°/μ–ΈνŒ”λ‘œμš°
if (teamFollowRepository.existsByTeamAndAndMember(toTeam, fromMember)) {
if (teamFollowRepository.existsByTeamAndMember(toTeam, fromMember)) {
// 관계 있음
teamFollow = teamFollowRepository.findTeamFollowByTeamAndMember(toTeam, fromMember).get();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.codiary.backend.domain.team.service;

import com.codiary.backend.domain.member.entity.Member;
import com.codiary.backend.domain.member.repository.MemberRepository;
import com.codiary.backend.domain.team.dto.request.TeamRequestDTO;
import com.codiary.backend.domain.team.entity.Team;
import com.codiary.backend.domain.team.entity.TeamMember;
import com.codiary.backend.domain.team.enumerate.TeamMemberRole;
import com.codiary.backend.domain.team.repository.TeamMemberRepository;
import com.codiary.backend.domain.team.repository.TeamRepository;
import com.codiary.backend.global.apiPayload.code.status.ErrorStatus;
import com.codiary.backend.global.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TeamMemberService {
private final TeamMemberRepository teamMemberRepository;
private final TeamRepository teamRepository;
private final MemberRepository memberRepository;

@Transactional
public TeamMember addTeamMember(Long requestMemberId, Long teamId, TeamRequestDTO.TeamMemberDTO request) {
//validation: νŒ€/멀버 μœ νš¨μ„± 및 νŒ€μ› 접근인지, μ‘΄μž¬ν•˜λŠ” λ‹‰λ„€μž„μΈμ§€, 이미 μΆ”κ°€ν•œ νŒ€μ›μΈμ§€ μœ νš¨μ„± 검사
Team team = teamRepository.findById(teamId)
.orElseThrow(() -> new GeneralException(ErrorStatus.TEAM_NOT_FOUND));

Member requestMember = memberRepository.findById(requestMemberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

if(!teamMemberRepository.existsByTeamAndMember(team, requestMember)){
throw new GeneralException(ErrorStatus.TEAM_MEMBER_ONLY_ACCESS);
}

Member newMember = memberRepository.findByNicknameIgnoreCase(request.memberNickName().toString())
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

if(teamMemberRepository.existsByTeamAndMember(team, newMember)){
throw new GeneralException(ErrorStatus.TEAM_MEMBER_ALREADY_EXISTS);
}

//business logic: νŒ€μ› μΆ”κ°€
TeamMember teamMember = TeamMember.builder()
.team(team)
.member(newMember)
.teamMemberRole(TeamMemberRole.valueOf(request.memberRole()))
.build();

//return
return teamMemberRepository.save(teamMember);
}

@Transactional
public void deleteTeamMember(Long requestMemberId, Long teamId, Long memberId) {
//validation: νŒ€/μš”μ²­μž/νŒ€μ› μœ νš¨μ„± 및 μš”μ²­μžκ°€ νŒ€μ›μΈμ§€, μ‘΄μž¬ν•˜λŠ” νŒ€μ›μΈμ§€ μœ νš¨μ„± 검사
Team team = teamRepository.findById(teamId)
.orElseThrow(() -> new GeneralException(ErrorStatus.TEAM_NOT_FOUND));

Member requester = memberRepository.findById(requestMemberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

if(!teamMemberRepository.existsByTeamAndMember(team, requester)){
throw new GeneralException(ErrorStatus.TEAM_MEMBER_NOT_FOUND);
}

TeamMember teamMember = teamMemberRepository.findByTeamAndMember(team, member)
.orElseThrow(() -> new GeneralException(ErrorStatus.TEAM_MEMBER_NOT_FOUND));

//business logic: νŒ€μ› μ‚­μ œ
teamMemberRepository.delete(teamMember);
}

public Team getTeamMember(Long teamId){
return teamRepository.findByIdWithTeamMemberList(teamId)
.orElseThrow(() -> new GeneralException(ErrorStatus.TEAM_NOT_FOUND));
}
}
Loading

0 comments on commit e3681f5

Please sign in to comment.