diff --git a/layer-api/src/main/java/org/layer/domain/space/api/SpaceApi.java b/layer-api/src/main/java/org/layer/domain/space/api/SpaceApi.java index 334e6940..fbb33022 100644 --- a/layer-api/src/main/java/org/layer/domain/space/api/SpaceApi.java +++ b/layer-api/src/main/java/org/layer/domain/space/api/SpaceApi.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -49,7 +50,7 @@ public interface SpaceApi { ) } ) - void createSpace(@MemberId Long memberId, @RequestBody @Validated SpaceRequest.CreateSpaceRequest createSpaceRequest); + ResponseEntity createSpace(@MemberId Long memberId, @RequestBody @Validated SpaceRequest.CreateSpaceRequest createSpaceRequest); @Operation(summary = "스페이스 수정하기", method = "POST", description = """ 스페이스를 수정합니다.
@@ -66,7 +67,7 @@ public interface SpaceApi { ) } ) - void updateSpace(@MemberId Long memberId, @RequestBody @Validated SpaceRequest.UpdateSpaceRequest updateSpaceRequest); + ResponseEntity updateSpace(@MemberId Long memberId, @RequestBody @Validated SpaceRequest.UpdateSpaceRequest updateSpaceRequest); @Operation(summary = "스페이스 단건 조회하기", method = "GET", description = """ 스페이스 아이디를 통해 하나의 스페이스를 조회합니다. @@ -78,4 +79,93 @@ public interface SpaceApi { }) }) ResponseEntity getSpaceById(@MemberId Long memberId, @PathVariable Long spaceId); + + + @Operation(summary = "스페이스 입장하기", method = "POST", description = """ + 공유받은 링크를 통해 스페이스에 입장합니다. + category가 TEAM인 경우만 가능합니다. + 회원이 아닐 경우 입장할 수 없습니다. + """) + @ApiResponses({ + @ApiResponse(responseCode = "202", content = { + @Content(mediaType = "application/json", schema = @Schema) + }), + @ApiResponse( + responseCode = "400", + content = @Content( + mediaType = "application/json", + schema = @Schema(), + examples = { + @ExampleObject( + name = "SPACE_ALREADY_JOINED", + description = "이미 참여중인 스페이스", + value = """ + { + "name": "SPACE_ALREADY_JOINED", + "message": "이미 가입한 스페이스에요." + } + """ + ), + @ExampleObject( + name = "SPACE_NOT_FOUND", + description = "존재하지 않는 스페이스", + value = """ + { + "name": "SPACE_NOT_FOUND", + "message": "스페이스를 찾을 수 없어요." + } + """ + ) + + + } + ) + + ) + }) + ResponseEntity createMemberSpace(@MemberId Long memberId, @PathVariable Long spaceId); + + @Operation(summary = "스페이스 탈퇴하기", method = "POST", description = """ + 내가 속한 스페이스를 탈퇴합니다. + category가 TEAM인 경우만 가능합니다. + 회원이 아닐 경우 입장할 수 없습니다. + """) + @ApiResponses({ + @ApiResponse(responseCode = "202", content = { + @Content(mediaType = "application/json", schema = @Schema) + }), + @ApiResponse( + responseCode = "400", + content = @Content( + mediaType = "application/json", + schema = @Schema(), + examples = { + @ExampleObject( + name = "SPACE_NOT_JOINED", + description = "참여중인 스페이스가 아닐경우.", + value = """ + { + "name": "SPACE_NOT_JOINED", + "message": "참여중인 스페이스가 아니에요." + } + """ + ), + @ExampleObject( + name = "SPACE_NOT_FOUND", + description = "존재하지 않는 스페이스", + value = """ + { + "name": "SPACE_NOT_FOUND", + "message": "스페이스를 찾을 수 없어요." + } + """ + ) + + + } + ) + + ) + }) + ResponseEntity removeMemberSpace(@MemberId Long memberId, @PathVariable Long spaceId); } diff --git a/layer-api/src/main/java/org/layer/domain/space/controller/SpaceController.java b/layer-api/src/main/java/org/layer/domain/space/controller/SpaceController.java index ed6526ea..0052e1b8 100644 --- a/layer-api/src/main/java/org/layer/domain/space/controller/SpaceController.java +++ b/layer-api/src/main/java/org/layer/domain/space/controller/SpaceController.java @@ -30,15 +30,17 @@ public ResponseEntity getMySpaceList(@MemberId Long mem @Override @PutMapping("") - public void createSpace(@MemberId Long memberId, @RequestBody @Validated SpaceRequest.CreateSpaceRequest createSpaceRequest) { + public ResponseEntity createSpace(@MemberId Long memberId, @RequestBody @Validated SpaceRequest.CreateSpaceRequest createSpaceRequest) { spaceService.createSpace(memberId, createSpaceRequest); + return ResponseEntity.ok().build(); } @Override @PostMapping("") @ResponseStatus(HttpStatus.ACCEPTED) - public void updateSpace(@MemberId Long memberId, @RequestBody @Validated SpaceRequest.UpdateSpaceRequest updateSpaceRequest) { + public ResponseEntity updateSpace(@MemberId Long memberId, @RequestBody @Validated SpaceRequest.UpdateSpaceRequest updateSpaceRequest) { spaceService.updateSpace(memberId, updateSpaceRequest); + return ResponseEntity.ok().build(); } @GetMapping("/{spaceId}") @@ -46,5 +48,21 @@ public ResponseEntity getSpaceById(@Memb var foundSpace = spaceService.getSpaceById(memberId, spaceId); return ResponseEntity.ok((foundSpace)); } + + @Override + @PostMapping("/join") + @ResponseStatus(HttpStatus.ACCEPTED) + public ResponseEntity createMemberSpace(@MemberId Long memberId, @RequestParam Long spaceId) { + spaceService.createMemberSpace(memberId, spaceId); + return ResponseEntity.ok().build(); + } + + @Override + @PostMapping("/leave") + @ResponseStatus(HttpStatus.ACCEPTED) + public ResponseEntity removeMemberSpace(@MemberId Long memberId, @RequestParam Long spaceId) { + spaceService.removeMemberSpace(memberId, spaceId); + return ResponseEntity.ok().build(); + } } diff --git a/layer-api/src/main/java/org/layer/domain/space/exception/SpaceExceptionType.java b/layer-api/src/main/java/org/layer/domain/space/exception/SpaceExceptionType.java index 5e72a106..5b48e927 100644 --- a/layer-api/src/main/java/org/layer/domain/space/exception/SpaceExceptionType.java +++ b/layer-api/src/main/java/org/layer/domain/space/exception/SpaceExceptionType.java @@ -9,11 +9,13 @@ @RequiredArgsConstructor public enum SpaceExceptionType implements ExceptionType { - /** - * - */ - SPACE_NOT_FOUND(BAD_REQUEST, "스페이스를 찾을 수 없어요."); + SPACE_NOT_FOUND(BAD_REQUEST, "스페이스를 찾을 수 없어요."), + + SPACE_NOT_JOINED(BAD_REQUEST, "참여중인 스페이스가 아니에요."), + SPACE_LEADER_CANNOT_LEAVE(BAD_REQUEST, "스페이스를 팀장은 떠날 수 없어요."), + SPACE_ALREADY_JOINED(BAD_REQUEST, "이미 가입한 스페이스에요."); + private final HttpStatus status; private final String message; diff --git a/layer-api/src/main/java/org/layer/domain/space/service/SpaceService.java b/layer-api/src/main/java/org/layer/domain/space/service/SpaceService.java index 787d31cf..2bdc0246 100644 --- a/layer-api/src/main/java/org/layer/domain/space/service/SpaceService.java +++ b/layer-api/src/main/java/org/layer/domain/space/service/SpaceService.java @@ -7,6 +7,7 @@ import org.layer.domain.space.dto.SpaceRequest; import org.layer.domain.space.dto.SpaceResponse; import org.layer.domain.space.entity.MemberSpaceRelation; +import org.layer.domain.space.entity.SpaceCategory; import org.layer.domain.space.exception.SpaceException; import org.layer.domain.space.repository.MemberSpaceRelationRepository; import org.layer.domain.space.repository.SpaceRepository; @@ -15,7 +16,7 @@ import java.util.stream.Collectors; -import static org.layer.domain.space.exception.SpaceExceptionType.SPACE_NOT_FOUND; +import static org.layer.domain.space.exception.SpaceExceptionType.*; @Service @RequiredArgsConstructor @@ -62,4 +63,76 @@ public SpaceResponse.SpaceWithMemberCountInfo getSpaceById(Long memberId, Long s return SpaceResponse.SpaceWithMemberCountInfo.toResponse(foundSpace); } + @Transactional + public void createMemberSpace(Long memberId, Long spaceId) { + + /* + 존재하는 스페이스 여부 확인 + */ + var foundSpace = spaceRepository.findById(spaceId) + .orElseThrow(() -> new SpaceException(SPACE_NOT_FOUND) + ); + + if (foundSpace.getCategory() == SpaceCategory.INDIVIDUAL) { + throw new SpaceException(SPACE_NOT_FOUND); + } + + + /* + 이미 참여중인 스페이스 여부 확인 + */ + memberSpaceRelationRepository.findBySpaceIdAndMemberId(spaceId, memberId).ifPresent(it -> { + throw new SpaceException(SPACE_ALREADY_JOINED); + }); + + /* + 스페이스 참여여부 저장 + */ + var joinedSpace = MemberSpaceRelation.builder() + .space(foundSpace) + .memberId(memberId) + .build(); + memberSpaceRelationRepository.save(joinedSpace); + } + + @Transactional + public void removeMemberSpace(Long memberId, Long spaceId) { + /* + 존재하는 스페이스 여부 확인 + */ + var foundSpace = spaceRepository.findById(spaceId) + .orElseThrow(() -> new SpaceException(SPACE_NOT_FOUND) + ); + + /* + 스페이스 팀장 여부 확인 + */ + foundSpace.isLeaderSpace(memberId) + .orElseThrow(() -> new SpaceException(SPACE_LEADER_CANNOT_LEAVE)); + + /* + 개인 스페이스의 경우, 이탈 시 Space 엔티티의 로직이 된다. + 따라서 INDIVIDUAL인 Space인 경우 가능한 케이스는 2가지로 + 1. Space 삭제 + 2. 속한 스페이스 떠나기, 스페이스 삭제하기 분리 + 현재는 2번 케이스를 기준으로 구현되어 있음 + */ + if (foundSpace.getCategory() == SpaceCategory.INDIVIDUAL) { + throw new SpaceException(SPACE_NOT_FOUND); + } + + /* + 이미 참여중인 스페이스 여부 확인 + */ + var foundMemberSpaceRelation = memberSpaceRelationRepository.findBySpaceIdAndMemberId(spaceId, memberId) + .orElseThrow( + () -> new SpaceException(SPACE_ALREADY_JOINED) + ); + + /* + 스페이스 참여여부 삭제 ( 스페이스 떠나기 ) + */ + memberSpaceRelationRepository.deleteById(foundMemberSpaceRelation.getId()); + } + } diff --git a/layer-domain/src/main/java/org/layer/domain/space/entity/Space.java b/layer-domain/src/main/java/org/layer/domain/space/entity/Space.java index 6946afd3..b6f971a7 100644 --- a/layer-domain/src/main/java/org/layer/domain/space/entity/Space.java +++ b/layer-domain/src/main/java/org/layer/domain/space/entity/Space.java @@ -8,6 +8,8 @@ import lombok.experimental.SuperBuilder; import org.layer.domain.BaseEntity; +import java.util.Optional; + @Getter @Entity @AllArgsConstructor @@ -36,4 +38,11 @@ public class Space extends BaseEntity { * Form Relationid */ private Long formId; + + public Optional isLeaderSpace(Long memberId) { + if (leaderId.equals(memberId)) { + return Optional.empty(); + } + return Optional.of(true); + } }