Skip to content

Commit

Permalink
Merge pull request #17 from f-lab-edu/feature/15
Browse files Browse the repository at this point in the history
[#15] 그룹 요청 처리 기능 구현
  • Loading branch information
hyeok-kong authored Jun 2, 2024
2 parents 7419d18 + 8150a0b commit fd3767d
Show file tree
Hide file tree
Showing 15 changed files with 365 additions and 9 deletions.
3 changes: 3 additions & 0 deletions group-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ configurations {
}

repositories {
mavenLocal()
mavenCentral()
}

Expand All @@ -30,6 +31,8 @@ dependencies {
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

implementation 'me.kong:common-library:0.0.1-SNAPSHOT'
}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,34 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.kong.groupservice.domain.entity.group.Group;
import me.kong.groupservice.dto.request.GroupJoinProcessDto;
import me.kong.groupservice.dto.request.GroupJoinRequestDto;
import me.kong.groupservice.dto.request.enums.JoinRequestSearchCondition;
import me.kong.groupservice.dto.request.SaveGroupRequestDto;
import me.kong.groupservice.dto.response.GroupJoinResponseDto;
import me.kong.groupservice.dto.response.GroupResponseDto;
import me.kong.groupservice.mapper.GroupJoinRequestMapper;
import me.kong.groupservice.mapper.GroupMapper;
import me.kong.groupservice.service.GroupJoinRequestService;
import me.kong.groupservice.service.GroupService;
import me.kong.groupservice.service.ProfileService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

import static me.kong.commonlibrary.constant.HttpStatusResponseEntity.RESPONSE_OK;

@Slf4j
@RestController
@RequestMapping("/api/groups")
@RequiredArgsConstructor
public class GroupController {

private final GroupService groupService;

private final GroupJoinRequestService joinRequestService;
private final GroupMapper groupMapper;
private final GroupJoinRequestMapper requestMapper;

@PostMapping
public ResponseEntity<GroupResponseDto> addGroup(@RequestBody @Valid SaveGroupRequestDto dto) {
Expand All @@ -39,4 +48,21 @@ public ResponseEntity<HttpStatus> joinGroup(@PathVariable Long groupId, @Request
return ResponseEntity.status(HttpStatus.OK).build();
}

@GetMapping("{groupId}/requests")
public ResponseEntity<List<GroupJoinResponseDto>> getGroupRequests(
@PathVariable Long groupId,
@RequestParam(name = "status", defaultValue = "PENDING") JoinRequestSearchCondition condition) {
List<GroupJoinResponseDto> requests = requestMapper.toDtoList(joinRequestService.getGroupJoinRequestsByGroupIdAndCondition(groupId, condition));

return new ResponseEntity<>(requests, HttpStatus.OK);
}

@PatchMapping("{groupId}/requests/{requestId}")
public ResponseEntity<HttpStatus> handleGroupJoinRequest(@PathVariable Long groupId,
@PathVariable Long requestId,
@RequestBody GroupJoinProcessDto processDto) {
joinRequestService.processGroupJoinRequest(requestId, processDto);

return RESPONSE_OK;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,12 @@ public GroupJoinRequest(Long id, String requestInfo, String nickname, JoinRespon
this.userId = userId;
this.group = group;
}

public void approveJoinRequest() {
response = JoinResponse.APPROVED;
}

public void rejectJoinRequest() {
response = JoinResponse.REJECTED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,18 @@ default boolean pendingRequestExists(Long userId, Long groupId) {
}

Optional<GroupJoinRequest> findByResponseAndUserIdAndGroupId(JoinResponse response, Long userId, Long groupId);

List<GroupJoinRequest> findAllByGroupId(Long groupId);

default List<GroupJoinRequest> findPendingGroupJoinRequests(Long groupId) {
return findAllByGroupIdAndResponse(groupId, JoinResponse.PENDING);
}

default List<GroupJoinRequest> findProcessedGroupJoinRequests(Long groupId) {
return findAllByGroupIdAndResponseNot(groupId, JoinResponse.PENDING);
}

List<GroupJoinRequest> findAllByGroupIdAndResponse(Long groupId, JoinResponse response);

List<GroupJoinRequest> findAllByGroupIdAndResponseNot(Long groupId, JoinResponse response);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package me.kong.groupservice.dto.request;


import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import me.kong.groupservice.dto.request.enums.GroupJoinProcessAction;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class GroupJoinProcessDto {

@NotNull
private GroupJoinProcessAction action;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package me.kong.groupservice.dto.request.enums;

public enum GroupJoinProcessAction {
APPROVE,
REJECT;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package me.kong.groupservice.dto.request.enums;

public enum JoinRequestSearchCondition {
PENDING,
PROCESSED,
ALL;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package me.kong.groupservice.dto.response;

import lombok.Builder;
import lombok.Getter;
import me.kong.groupservice.domain.entity.GroupJoinRequest.JoinResponse;

@Getter
@Builder
public class GroupJoinResponseDto {
private Long requestId;
private String nickname;
private String requestInfo;
private JoinResponse status;
private Long userId;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
import me.kong.groupservice.domain.entity.State;
import me.kong.groupservice.domain.entity.group.Group;
import me.kong.groupservice.dto.request.GroupJoinRequestDto;
import me.kong.groupservice.dto.response.GroupJoinResponseDto;
import me.kong.groupservice.dto.response.GroupResponseDto;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
@RequiredArgsConstructor
public class GroupJoinRequestMapper {
Expand All @@ -25,4 +28,20 @@ public GroupJoinRequest toEntity(GroupJoinRequestDto dto, Group group) {
.group(group)
.build();
}

public GroupJoinResponseDto toDto(GroupJoinRequest request) {
return GroupJoinResponseDto.builder()
.requestId(request.getId())
.nickname(request.getNickname())
.requestInfo(request.getRequestInfo())
.status(request.getResponse())
.userId(request.getUserId())
.build();
}

public List<GroupJoinResponseDto> toDtoList(List<GroupJoinRequest> requests) {
return requests.stream()
.map(this::toDto)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,37 @@

import lombok.RequiredArgsConstructor;
import me.kong.groupservice.common.JwtReader;
import me.kong.groupservice.common.exception.DuplicateElementException;
import me.kong.groupservice.domain.entity.GroupJoinRequest.GroupJoinRequest;
import me.kong.groupservice.domain.entity.GroupJoinRequest.JoinResponse;
import me.kong.groupservice.domain.entity.group.Group;
import me.kong.groupservice.domain.entity.profile.GroupRole;
import me.kong.groupservice.domain.repository.GroupJoinRequestRepository;
import me.kong.groupservice.dto.request.GroupJoinProcessDto;
import me.kong.groupservice.dto.request.GroupJoinRequestDto;
import me.kong.groupservice.dto.request.enums.JoinRequestSearchCondition;
import me.kong.groupservice.mapper.GroupJoinRequestMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.NoSuchElementException;

@Service
@RequiredArgsConstructor
public class GroupJoinRequestService {

private final JwtReader jwtReader;
private final GroupJoinRequestRepository joinRequestRepository;
private final GroupJoinRequestMapper joinRequestMapper;
private final GroupJoinRequestRepository groupJoinRequestRepository;
private final ProfileService profileService;

@Transactional(readOnly = true)
public GroupJoinRequest getGroupJoinRequestByRequestId(Long requestId) {
return joinRequestRepository.findById(requestId)
.orElseThrow(() -> new NoSuchElementException("해당하는 가입 요청이 없습니다. id : " + requestId));
}

@Transactional(readOnly = true)
public boolean pendingRequestExists(Long groupId) {
Expand All @@ -26,4 +43,51 @@ public boolean pendingRequestExists(Long groupId) {
public void createNewGroupJoinRequest(GroupJoinRequestDto dto, Group group) {
joinRequestRepository.save(joinRequestMapper.toEntity(dto, group));
}

@Transactional(readOnly = true)
public List<GroupJoinRequest> getGroupJoinRequestsByGroupIdAndCondition(Long groupId, JoinRequestSearchCondition condition) {
List<GroupJoinRequest> requests;

profileService.checkLoggedInProfileIsGroupManager(groupId);

switch (condition) {
case PENDING -> {
requests = groupJoinRequestRepository.findPendingGroupJoinRequests(groupId);
}
case PROCESSED -> {
requests = groupJoinRequestRepository.findProcessedGroupJoinRequests(groupId);
}
case ALL -> {
requests = groupJoinRequestRepository.findAllByGroupId(groupId);
}
default -> {
throw new IllegalArgumentException("지원하지 않는 그룹 가입 검색 조건. status : " + condition);
}
}
return requests;
}

@Transactional
public void processGroupJoinRequest(Long requestId, GroupJoinProcessDto dto) {
GroupJoinRequest joinRequest = getGroupJoinRequestByRequestId(requestId);

if (joinRequest.getResponse() != JoinResponse.PENDING) {
throw new DuplicateElementException("이미 처리된 가입 요청입니다. 요청 id : " + requestId);
}

profileService.checkLoggedInProfileIsGroupManager(joinRequest.getGroup().getId());

switch (dto.getAction()) {
case APPROVE -> {
joinRequest.approveJoinRequest();
profileService.createNewProfile(joinRequest.getNickname(), GroupRole.MEMBER, joinRequest.getGroup());
}
case REJECT -> {
joinRequest.rejectJoinRequest();
}
default -> {
throw new IllegalArgumentException("지원하지 않는 처리 요청. action : " + dto.getAction());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package me.kong.groupservice.service;

import lombok.RequiredArgsConstructor;
import me.kong.commonlibrary.exception.auth.UnAuthorizedException;
import me.kong.groupservice.common.JwtReader;
import me.kong.groupservice.common.exception.NoLoggedInProfileException;
import me.kong.groupservice.domain.entity.State;
Expand All @@ -11,7 +12,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;


@Service
Expand Down Expand Up @@ -40,4 +40,14 @@ public Profile getLoggedInProfile(Long groupId) {

return profileRepository.findByUserIdAndGroupId(userId, groupId).orElseThrow(NoLoggedInProfileException::new);
}

@Transactional(readOnly = true)
public void checkLoggedInProfileIsGroupManager(Long groupId) {
Profile profile = getLoggedInProfile(groupId);

if (profile.getGroupRole() != GroupRole.MANAGER) {
throw new UnAuthorizedException("권한이 없습니다. groupId : "
+ groupId + ", profileId : " + profile.getId() + " , userId : " + profile.getUserId());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
import me.kong.groupservice.domain.repository.GroupRepository;
import me.kong.groupservice.domain.repository.ProfileRepository;
import me.kong.groupservice.dto.request.SaveGroupRequestDto;
import me.kong.groupservice.service.GroupJoinRequestService;
import me.kong.groupservice.service.GroupService;
import me.kong.groupservice.service.ProfileService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
Expand All @@ -29,6 +28,9 @@ public class GroupManagementServiceTest {
@Autowired
private GroupService groupService;

@Autowired
private GroupJoinRequestService joinRequestService;

@MockBean
private ProfileService profileService;

Expand Down
Loading

0 comments on commit fd3767d

Please sign in to comment.