From 25d09ce593e650586056aff92cf79b58de0bca90 Mon Sep 17 00:00:00 2001 From: hong seokho Date: Wed, 15 May 2024 12:13:27 +0900 Subject: [PATCH] =?UTF-8?q?fix=20:=20read/write,=20group/group=5Fmember=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../facade/GroupCapsuleFacade.java | 6 +- .../core/domain/group/api/GroupApi.java | 274 ------------------ .../domain/group/api/GroupApiController.java | 175 ----------- .../group/api/command/GroupCommandApi.java | 109 +++++++ .../command/GroupCommandApiController.java | 63 ++++ .../domain/group/api/query/GroupQueryApi.java | 66 +++++ .../api/query/GroupQueryApiController.java | 73 +++++ .../GroupQueryRepository.java | 2 +- .../GroupQueryRepositoryImpl.java | 4 +- .../GroupRepository.java | 2 +- .../MemberGroupQueryRepository.java | 10 - .../domain/group/service/GroupService.java | 8 - .../group/service/GroupServiceImpl.java | 65 ----- .../GroupCommandService.java} | 82 +----- .../GroupQueryService.java} | 6 +- .../group/service/read/GroupReadService.java | 20 -- .../service/write/GroupWriteService.java | 16 - .../api/command/GroupMemberCommandApi.java | 128 ++++++++ .../GroupMemberCommandApiController.java | 78 +++++ .../data}/GroupOwnerSummaryDto.java | 2 +- .../entity/GroupInvite.java | 3 +- .../entity/MemberGroup.java | 3 +- .../GroupInviteNotFoundException.java | 2 +- .../GroupOwnerAuthenticateException.java | 2 +- .../GroupInviteQueryRepository.java | 2 +- .../GroupInviteQueryRepositoryImpl.java | 5 +- .../GroupInviteRepository.java | 4 +- .../MemberGroupQueryRepository.java | 10 + .../MemberGroupQueryRepositoryImpl.java | 6 +- .../MemberGroupRepository.java | 4 +- .../service/GroupMemberCommandService.java | 101 +++++++ .../core/domain/member/entity/Member.java | 2 +- .../repository/MemberQueryRepository.java | 2 +- 33 files changed, 664 insertions(+), 671 deletions(-) delete mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/GroupApi.java delete mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/GroupApiController.java create mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/command/GroupCommandApi.java create mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/command/GroupCommandApiController.java create mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/query/GroupQueryApi.java create mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/query/GroupQueryApiController.java rename backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/{groupRepository => }/GroupQueryRepository.java (86%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/{groupRepository => }/GroupQueryRepositoryImpl.java (95%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/{groupRepository => }/GroupRepository.java (81%) delete mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupQueryRepository.java delete mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/GroupService.java delete mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/GroupServiceImpl.java rename backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/{write/GroupWriteServiceImpl.java => command/GroupCommandService.java} (57%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/{read/GroupReadServiceImpl.java => query/GroupQueryService.java} (87%) delete mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/read/GroupReadService.java delete mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/write/GroupWriteService.java create mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/api/command/GroupMemberCommandApi.java create mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/api/command/GroupMemberCommandApiController.java rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group/data/dto => group_member/data}/GroupOwnerSummaryDto.java (64%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/entity/GroupInvite.java (92%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/entity/MemberGroup.java (92%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/exception/GroupInviteNotFoundException.java (82%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/exception/GroupOwnerAuthenticateException.java (82%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/repository/groupInviteRepository/GroupInviteQueryRepository.java (73%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/repository/groupInviteRepository/GroupInviteQueryRepositoryImpl.java (91%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/repository/groupInviteRepository/GroupInviteRepository.java (80%) create mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupQueryRepository.java rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/repository/memberGroupRepository/MemberGroupQueryRepositoryImpl.java (82%) rename backend/core/src/main/java/site/timecapsulearchive/core/domain/{group => group_member}/repository/memberGroupRepository/MemberGroupRepository.java (67%) create mode 100644 backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/service/GroupMemberCommandService.java diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/group_capsule/facade/GroupCapsuleFacade.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/group_capsule/facade/GroupCapsuleFacade.java index ba2164109..de10d78e8 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/group_capsule/facade/GroupCapsuleFacade.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/capsule/group_capsule/facade/GroupCapsuleFacade.java @@ -13,7 +13,7 @@ import site.timecapsulearchive.core.domain.capsuleskin.entity.CapsuleSkin; import site.timecapsulearchive.core.domain.capsuleskin.service.CapsuleSkinService; import site.timecapsulearchive.core.domain.group.entity.Group; -import site.timecapsulearchive.core.domain.group.service.GroupService; +import site.timecapsulearchive.core.domain.group.service.query.GroupQueryService; import site.timecapsulearchive.core.domain.member.entity.Member; import site.timecapsulearchive.core.domain.member.service.MemberService; import site.timecapsulearchive.core.global.geography.GeoTransformManager; @@ -24,7 +24,7 @@ public class GroupCapsuleFacade { private final MemberService memberService; private final GroupCapsuleService groupCapsuleService; - private final GroupService groupService; + private final GroupQueryService groupQueryService; private final ImageService imageService; private final VideoService videoService; private final GeoTransformManager geoTransformManager; @@ -38,7 +38,7 @@ public void saveGroupCapsule( final Long groupId ) { final Member member = memberService.findMemberById(memberId); - final Group group = groupService.findGroupById(groupId); + final Group group = groupQueryService.findGroupById(groupId); final CapsuleSkin capsuleSkin = capsuleSkinService.findCapsuleSkinById(dto.capsuleSkinId()); final Point point = geoTransformManager.changePoint4326To3857(dto.latitude(), diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/GroupApi.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/GroupApi.java deleted file mode 100644 index 0fd57eb75..000000000 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/GroupApi.java +++ /dev/null @@ -1,274 +0,0 @@ -package site.timecapsulearchive.core.domain.group.api; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import java.time.ZonedDateTime; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import site.timecapsulearchive.core.domain.group.data.reqeust.GroupCreateRequest; -import site.timecapsulearchive.core.domain.group.data.reqeust.GroupUpdateRequest; -import site.timecapsulearchive.core.domain.group.data.response.GroupDetailResponse; -import site.timecapsulearchive.core.domain.group.data.response.GroupsSliceResponse; -import site.timecapsulearchive.core.global.common.response.ApiSpec; -import site.timecapsulearchive.core.global.error.ErrorResponse; - -public interface GroupApi { - - @Operation( - summary = "그룹 삭제", - description = """ - 그룹 삭제를 요청한 사용자가 해당 그룹의 그룹장인 경우 그룹을 삭제한다.
- 주의 - - """, - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "202", - description = "처리 시작" - ), - @ApiResponse( - responseCode = "400", - description = """ - 다음의 경우 예외가 발생한다. - - """, - content = @Content(schema = @Schema(implementation = ErrorResponse.class)) - ), - @ApiResponse( - responseCode = "404", - description = "그룹이 존재하지 않으면 발생한다.", - content = @Content(schema = @Schema(implementation = ErrorResponse.class)) - ), - }) - ResponseEntity> deleteGroupById( - Long memberId, - - @Parameter(in = ParameterIn.PATH, description = "삭제할 그룹 아이디", required = true) - Long groupId - ); - - @Operation( - summary = "그룹 요청 수락", - description = "특정 그룹으로부터 그룹 요청을 수락한다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "처리 완료" - ), - @ApiResponse( - responseCode = "404", - description = "그룹 초대 찾기 실패" - ), - @ApiResponse( - responseCode = "500", - description = "외부 API 요청 실패" - ) - }) - ResponseEntity> acceptGroupInvitation( - Long memberId, - - @Parameter(in = ParameterIn.PATH, description = "그룹 아이디", required = true) - Long groupId, - - @Parameter(in = ParameterIn.PATH, description = "대상 회원 아이디", required = true) - Long targetId - ); - - @Operation( - summary = "그룹 생성", - description = "친구로부터 그룹을 생성한다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "처리완료" - ), - @ApiResponse( - responseCode = "500", - description = "외부 API 요청 실패" - ) - }) - ResponseEntity> createGroup( - Long memberId, - GroupCreateRequest request - ); - - @Operation( - summary = "그룹원 삭제", - description = "그룹장인 경우 특정 그룹원을 삭제한다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "204", - description = "처리 완료" - ) - }) - @DeleteMapping(value = "/groups/{group_id}/members/{member_id}") - ResponseEntity deleteGroupMember( - @Parameter(in = ParameterIn.PATH, description = "그룹 아이디", required = true, schema = @Schema()) - @PathVariable("group_id") Long groupId, - - @Parameter(in = ParameterIn.PATH, description = "삭제할 멤버 아이디", required = true, schema = @Schema()) - @PathVariable("member_id") Long memberId - ); - - @Operation( - summary = "그룹 요청 거부", - description = "특정 그룹으로부터 초대 요청을 거부한다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "처리 완료" - ) - }) - ResponseEntity> rejectGroupInvitation( - Long memberId, - - Long groupId, - - @Parameter(in = ParameterIn.PATH, description = "그룹 초대 대상 아이디", required = true) - Long groupOwnerId - ); - - @Operation( - summary = "그룹 상세 조회", - description = "그룹의 상세 정보를 보여준다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "ok" - ), - @ApiResponse( - responseCode = "400", - description = "잘못된 파라미터를 받았을 때 발생하는 오류" - ), - @ApiResponse( - responseCode = "403", - description = "그룹에 포함된 사용자가 아닌 경우 발생하는 오류" - ) - }) - ResponseEntity> findGroupDetailById( - Long memberId, - - @Parameter(in = ParameterIn.PATH, description = "조회할 그룹 아이디", required = true, schema = @Schema()) - Long groupId - ); - - @Operation( - summary = "그룹 목록 조회", - description = "사용자가 소속된 그룹의 목록을 조회한다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "200", - description = "ok" - ) - }) - ResponseEntity> findGroups( - Long memberId, - - @Parameter(in = ParameterIn.QUERY, description = "페이지 크기", required = true) - int size, - - @Parameter(in = ParameterIn.QUERY, description = "마지막 데이터의 시간", required = true) - ZonedDateTime createdAt - ); - - @Operation( - summary = "그룹 요청", - description = "그룹장인 경우 친구에게 그룹 가입 요청을 할 수 있다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "202", - description = "처리 시작" - ) - }) - ResponseEntity> inviteGroup( - Long memberId, - - @Parameter(in = ParameterIn.PATH, description = "그룹 아이디", required = true) - Long groupId, - - @Parameter(in = ParameterIn.PATH, description = "대상 회원 아이디", required = true) - Long targetId - ); - - @Operation( - summary = "그룹 탈퇴", - description = "사용자가 속한 그룹을 탈퇴한다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "204", - description = "처리 완료" - ) - }) - @DeleteMapping(value = "/groups/{group_id}/members/quit") - ResponseEntity quitGroup( - @Parameter(in = ParameterIn.PATH, description = "조회할 그룹 아이디", required = true, schema = @Schema()) - @PathVariable("group_id") Long groupId - ); - - @Operation( - summary = "그룹 수정", - description = "그룹장인 경우에 그룹의 기본 정보들을 수정한다.", - security = {@SecurityRequirement(name = "user_token")}, - tags = {"group"} - ) - @ApiResponses(value = { - @ApiResponse( - responseCode = "202", - description = "처리 시작" - ) - }) - @PatchMapping( - value = "/groups/{group_id}", - consumes = {"multipart/form-data"} - ) - ResponseEntity updateGroupById( - @Parameter(in = ParameterIn.PATH, description = "수정할 그룹 아이디", required = true, schema = @Schema()) - @PathVariable("group_id") Long groupId, - - @ModelAttribute GroupUpdateRequest request - ); -} \ No newline at end of file diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/GroupApiController.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/GroupApiController.java deleted file mode 100644 index bf7e50c5d..000000000 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/GroupApiController.java +++ /dev/null @@ -1,175 +0,0 @@ -package site.timecapsulearchive.core.domain.group.api; - -import jakarta.validation.Valid; -import java.time.ZonedDateTime; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Slice; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import site.timecapsulearchive.core.domain.group.data.dto.GroupCreateDto; -import site.timecapsulearchive.core.domain.group.data.dto.GroupDetailDto; -import site.timecapsulearchive.core.domain.group.data.dto.GroupSummaryDto; -import site.timecapsulearchive.core.domain.group.data.reqeust.GroupCreateRequest; -import site.timecapsulearchive.core.domain.group.data.reqeust.GroupUpdateRequest; -import site.timecapsulearchive.core.domain.group.data.response.GroupDetailResponse; -import site.timecapsulearchive.core.domain.group.data.response.GroupsSliceResponse; -import site.timecapsulearchive.core.domain.group.service.GroupService; -import site.timecapsulearchive.core.global.common.response.ApiSpec; -import site.timecapsulearchive.core.global.common.response.SuccessCode; -import site.timecapsulearchive.core.infra.s3.manager.S3PreSignedUrlManager; -import site.timecapsulearchive.core.infra.s3.manager.S3UrlGenerator; - - -@RestController -@RequestMapping("/groups") -@RequiredArgsConstructor -public class GroupApiController implements GroupApi { - - private final GroupService groupService; - private final S3UrlGenerator s3UrlGenerator; - private final S3PreSignedUrlManager s3PreSignedUrlManager; - - @PostMapping(value = "/accept/{group_id}/member/{target_id}") - @Override - public ResponseEntity> acceptGroupInvitation( - @AuthenticationPrincipal final Long memberId, - @PathVariable("group_id") final Long groupId, - @PathVariable("target_id") final Long targetId - ) { - groupService.acceptGroupInvite(memberId, groupId, targetId); - - return ResponseEntity.ok( - ApiSpec.empty( - SuccessCode.ACCEPTED - ) - ); - } - - @Override - @PostMapping - public ResponseEntity> createGroup( - @AuthenticationPrincipal final Long memberId, - @Valid @RequestBody GroupCreateRequest request - ) { - final String groupProfileUrl = s3UrlGenerator.generateFileName(memberId, - request.groupDirectory(), request.groupImage()); - final GroupCreateDto dto = request.toDto(groupProfileUrl); - - groupService.createGroup(memberId, dto); - - return ResponseEntity.ok( - ApiSpec.empty( - SuccessCode.ACCEPTED - ) - ); - } - - @DeleteMapping(value = "/{group_id}") - @Override - public ResponseEntity> deleteGroupById( - @AuthenticationPrincipal final Long memberId, - @PathVariable("group_id") final Long groupId - ) { - groupService.deleteGroup(memberId, groupId); - - return ResponseEntity.ok(ApiSpec.empty(SuccessCode.SUCCESS)); - } - - @Override - public ResponseEntity deleteGroupMember(Long groupId, Long memberId) { - return null; - } - - @DeleteMapping(value = "/reject/{group_id}/member/{target_id}") - public ResponseEntity> rejectGroupInvitation( - @AuthenticationPrincipal final Long memberId, - @PathVariable("group_id") final Long groupId, - @PathVariable("target_id") final Long targetId) { - - groupService.rejectRequestGroup(memberId, groupId, targetId); - - return ResponseEntity.ok( - ApiSpec.empty( - SuccessCode.SUCCESS - ) - ); - } - - @GetMapping( - value = "/{group_id}", - produces = {"application/json"} - ) - @Override - public ResponseEntity> findGroupDetailById( - @AuthenticationPrincipal final Long memberId, - @PathVariable("group_id") final Long groupId - ) { - final GroupDetailDto groupDetailDto = groupService.findGroupDetailByGroupId(memberId, - groupId); - - return ResponseEntity.ok( - ApiSpec.success( - SuccessCode.SUCCESS, - groupDetailDto.toResponse(s3PreSignedUrlManager::getS3PreSignedUrlForGet) - ) - ); - } - - @GetMapping( - produces = {"application/json"} - ) - @Override - public ResponseEntity> findGroups( - @AuthenticationPrincipal final Long memberId, - @RequestParam(defaultValue = "20", value = "size") final int size, - @RequestParam(value = "created_at") final ZonedDateTime createdAt - ) { - final Slice groupsSlice = groupService.findGroupsSlice(memberId, size, - createdAt); - - return ResponseEntity.ok( - ApiSpec.success( - SuccessCode.SUCCESS, - GroupsSliceResponse.createOf( - groupsSlice.getContent(), - groupsSlice.hasNext(), - s3PreSignedUrlManager::getS3PreSignedUrlForGet - ) - ) - ); - } - - @PostMapping(value = "/invite/{group_id}/member/{target_id}") - @Override - public ResponseEntity> inviteGroup( - @AuthenticationPrincipal final Long memberId, - @PathVariable("group_id") final Long groupId, - @PathVariable("target_id") final Long targetId - ) { - groupService.inviteGroup(memberId, groupId, targetId); - - return ResponseEntity.ok( - ApiSpec.empty( - SuccessCode.ACCEPTED - ) - ); - } - - @Override - public ResponseEntity quitGroup(Long groupId) { - return null; - } - - @Override - public ResponseEntity updateGroupById(Long groupId, GroupUpdateRequest request) { - return null; - } -} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/command/GroupCommandApi.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/command/GroupCommandApi.java new file mode 100644 index 000000000..bee129442 --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/command/GroupCommandApi.java @@ -0,0 +1,109 @@ +package site.timecapsulearchive.core.domain.group.api.command; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import site.timecapsulearchive.core.domain.group.data.reqeust.GroupCreateRequest; +import site.timecapsulearchive.core.domain.group.data.reqeust.GroupUpdateRequest; +import site.timecapsulearchive.core.global.common.response.ApiSpec; +import site.timecapsulearchive.core.global.error.ErrorResponse; + +public interface GroupCommandApi { + + @Operation( + summary = "그룹 삭제", + description = """ + 그룹 삭제를 요청한 사용자가 해당 그룹의 그룹장인 경우 그룹을 삭제한다.
+ 주의 +
    +
  • 그룹장이 아닌 경우 그룹을 삭제할 수 없다.
  • +
  • 그룹에 포함된 멤버가 그룹장을 제외하고 존재하면 그룹을 삭제할 수 없다.
  • +
  • 그룹에 그룹 캡슐이 남아있는 경우 삭제할 수 없다.
  • +
+ """, + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "202", + description = "처리 시작" + ), + @ApiResponse( + responseCode = "400", + description = """ + 다음의 경우 예외가 발생한다. +
    +
  • 삭제를 요청한 그룹에서 요청한 사용자가 그룹장이 아닌 경우
  • +
  • 삭제를 요청한 그룹에 그룹장을 제외한 그룹원이 존재하는 경우
  • +
  • 삭제를 요청한 그룹에 그룹 캡슐이 존재하는 경우
  • +
+ """, + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + @ApiResponse( + responseCode = "404", + description = "그룹이 존재하지 않으면 발생한다.", + content = @Content(schema = @Schema(implementation = ErrorResponse.class)) + ), + }) + ResponseEntity> deleteGroupById( + Long memberId, + + @Parameter(in = ParameterIn.PATH, description = "삭제할 그룹 아이디", required = true) + Long groupId + ); + + @Operation( + summary = "그룹 생성", + description = "친구로부터 그룹을 생성한다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "처리완료" + ), + @ApiResponse( + responseCode = "500", + description = "외부 API 요청 실패" + ) + }) + ResponseEntity> createGroup( + Long memberId, + GroupCreateRequest request + ); + + @Operation( + summary = "그룹 수정", + description = "그룹장인 경우에 그룹의 기본 정보들을 수정한다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "202", + description = "처리 시작" + ) + }) + @PatchMapping( + value = "/groups/{group_id}", + consumes = {"multipart/form-data"} + ) + ResponseEntity updateGroupById( + @Parameter(in = ParameterIn.PATH, description = "수정할 그룹 아이디", required = true, schema = @Schema()) + @PathVariable("group_id") Long groupId, + + @ModelAttribute GroupUpdateRequest request + ); +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/command/GroupCommandApiController.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/command/GroupCommandApiController.java new file mode 100644 index 000000000..3dc4ade88 --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/command/GroupCommandApiController.java @@ -0,0 +1,63 @@ +package site.timecapsulearchive.core.domain.group.api.command; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import site.timecapsulearchive.core.domain.group.data.dto.GroupCreateDto; +import site.timecapsulearchive.core.domain.group.data.reqeust.GroupCreateRequest; +import site.timecapsulearchive.core.domain.group.data.reqeust.GroupUpdateRequest; +import site.timecapsulearchive.core.domain.group.service.command.GroupCommandService; +import site.timecapsulearchive.core.global.common.response.ApiSpec; +import site.timecapsulearchive.core.global.common.response.SuccessCode; +import site.timecapsulearchive.core.infra.s3.manager.S3UrlGenerator; + +@RestController +@RequestMapping("/groups") +@RequiredArgsConstructor +public class GroupCommandApiController implements GroupCommandApi { + + private final GroupCommandService groupCommandService; + private final S3UrlGenerator s3UrlGenerator; + + @Override + @PostMapping + public ResponseEntity> createGroup( + @AuthenticationPrincipal final Long memberId, + @Valid @RequestBody GroupCreateRequest request + ) { + final String groupProfileUrl = s3UrlGenerator.generateFileName(memberId, + request.groupDirectory(), request.groupImage()); + final GroupCreateDto dto = request.toDto(groupProfileUrl); + + groupCommandService.createGroup(memberId, dto); + + return ResponseEntity.ok( + ApiSpec.empty( + SuccessCode.ACCEPTED + ) + ); + } + + @DeleteMapping(value = "/{group_id}") + @Override + public ResponseEntity> deleteGroupById( + @AuthenticationPrincipal final Long memberId, + @PathVariable("group_id") final Long groupId + ) { + groupCommandService.deleteGroup(memberId, groupId); + + return ResponseEntity.ok(ApiSpec.empty(SuccessCode.SUCCESS)); + } + + @Override + public ResponseEntity updateGroupById(Long groupId, GroupUpdateRequest request) { + return null; + } +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/query/GroupQueryApi.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/query/GroupQueryApi.java new file mode 100644 index 000000000..6c67826dc --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/query/GroupQueryApi.java @@ -0,0 +1,66 @@ +package site.timecapsulearchive.core.domain.group.api.query; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import java.time.ZonedDateTime; +import org.springframework.http.ResponseEntity; +import site.timecapsulearchive.core.domain.group.data.response.GroupDetailResponse; +import site.timecapsulearchive.core.domain.group.data.response.GroupsSliceResponse; +import site.timecapsulearchive.core.global.common.response.ApiSpec; + +public interface GroupQueryApi { + + @Operation( + summary = "그룹 상세 조회", + description = "그룹의 상세 정보를 보여준다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "ok" + ), + @ApiResponse( + responseCode = "400", + description = "잘못된 파라미터를 받았을 때 발생하는 오류" + ), + @ApiResponse( + responseCode = "403", + description = "그룹에 포함된 사용자가 아닌 경우 발생하는 오류" + ) + }) + ResponseEntity> findGroupDetailById( + Long memberId, + + @Parameter(in = ParameterIn.PATH, description = "조회할 그룹 아이디", required = true, schema = @Schema()) + Long groupId + ); + + @Operation( + summary = "그룹 목록 조회", + description = "사용자가 소속된 그룹의 목록을 조회한다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "ok" + ) + }) + ResponseEntity> findGroups( + Long memberId, + + @Parameter(in = ParameterIn.QUERY, description = "페이지 크기", required = true) + int size, + + @Parameter(in = ParameterIn.QUERY, description = "마지막 데이터의 시간", required = true) + ZonedDateTime createdAt + ); +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/query/GroupQueryApiController.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/query/GroupQueryApiController.java new file mode 100644 index 000000000..befb57525 --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/api/query/GroupQueryApiController.java @@ -0,0 +1,73 @@ +package site.timecapsulearchive.core.domain.group.api.query; + +import java.time.ZonedDateTime; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Slice; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import site.timecapsulearchive.core.domain.group.data.dto.GroupDetailDto; +import site.timecapsulearchive.core.domain.group.data.dto.GroupSummaryDto; +import site.timecapsulearchive.core.domain.group.data.response.GroupDetailResponse; +import site.timecapsulearchive.core.domain.group.data.response.GroupsSliceResponse; +import site.timecapsulearchive.core.domain.group.service.query.GroupQueryService; +import site.timecapsulearchive.core.global.common.response.ApiSpec; +import site.timecapsulearchive.core.global.common.response.SuccessCode; +import site.timecapsulearchive.core.infra.s3.manager.S3PreSignedUrlManager; + +@RestController +@RequestMapping("/groups") +@RequiredArgsConstructor +public class GroupQueryApiController implements GroupQueryApi { + + private final GroupQueryService groupQueryService; + private final S3PreSignedUrlManager s3PreSignedUrlManager; + + @GetMapping( + value = "/{group_id}", + produces = {"application/json"} + ) + @Override + public ResponseEntity> findGroupDetailById( + @AuthenticationPrincipal final Long memberId, + @PathVariable("group_id") final Long groupId + ) { + final GroupDetailDto groupDetailDto = groupQueryService.findGroupDetailByGroupId(memberId, + groupId); + + return ResponseEntity.ok( + ApiSpec.success( + SuccessCode.SUCCESS, + groupDetailDto.toResponse(s3PreSignedUrlManager::getS3PreSignedUrlForGet) + ) + ); + } + + @GetMapping( + produces = {"application/json"} + ) + @Override + public ResponseEntity> findGroups( + @AuthenticationPrincipal final Long memberId, + @RequestParam(defaultValue = "20", value = "size") final int size, + @RequestParam(value = "created_at") final ZonedDateTime createdAt + ) { + final Slice groupsSlice = groupQueryService.findGroupsSlice(memberId, size, + createdAt); + + return ResponseEntity.ok( + ApiSpec.success( + SuccessCode.SUCCESS, + GroupsSliceResponse.createOf( + groupsSlice.getContent(), + groupsSlice.hasNext(), + s3PreSignedUrlManager::getS3PreSignedUrlForGet + ) + ) + ); + } +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupQueryRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepository.java similarity index 86% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupQueryRepository.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepository.java index 0ce6327a9..a19bcb478 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupQueryRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepository.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.repository.groupRepository; +package site.timecapsulearchive.core.domain.group.repository; import java.time.ZonedDateTime; import java.util.Optional; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupQueryRepositoryImpl.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryImpl.java similarity index 95% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupQueryRepositoryImpl.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryImpl.java index 21ff3e10b..f24ce647b 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupQueryRepositoryImpl.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupQueryRepositoryImpl.java @@ -1,9 +1,9 @@ -package site.timecapsulearchive.core.domain.group.repository.groupRepository; +package site.timecapsulearchive.core.domain.group.repository; import static com.querydsl.core.group.GroupBy.groupBy; import static com.querydsl.core.group.GroupBy.list; import static site.timecapsulearchive.core.domain.group.entity.QGroup.group; -import static site.timecapsulearchive.core.domain.group.entity.QMemberGroup.memberGroup; +import static site.timecapsulearchive.core.domain.group_member.entity.QMemberGroup.memberGroup; import static site.timecapsulearchive.core.domain.member.entity.QMember.member; import com.querydsl.core.types.Projections; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupRepository.java similarity index 81% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupRepository.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupRepository.java index ac60cac3c..d8ddde133 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupRepository/GroupRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/GroupRepository.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.repository.groupRepository; +package site.timecapsulearchive.core.domain.group.repository; import java.util.Optional; import org.springframework.data.repository.Repository; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupQueryRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupQueryRepository.java deleted file mode 100644 index c39ef58b2..000000000 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupQueryRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package site.timecapsulearchive.core.domain.group.repository.memberGroupRepository; - -import java.util.Optional; -import site.timecapsulearchive.core.domain.group.data.dto.GroupOwnerSummaryDto; - -public interface MemberGroupQueryRepository { - - Optional findOwnerInMemberGroup(Long groupId, Long memberId); - -} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/GroupService.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/GroupService.java deleted file mode 100644 index 5fa63bedf..000000000 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/GroupService.java +++ /dev/null @@ -1,8 +0,0 @@ -package site.timecapsulearchive.core.domain.group.service; - -import site.timecapsulearchive.core.domain.group.service.read.GroupReadService; -import site.timecapsulearchive.core.domain.group.service.write.GroupWriteService; - -public interface GroupService extends GroupReadService, GroupWriteService { - -} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/GroupServiceImpl.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/GroupServiceImpl.java deleted file mode 100644 index c4a66d617..000000000 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/GroupServiceImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -package site.timecapsulearchive.core.domain.group.service; - -import java.time.ZonedDateTime; -import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Slice; -import org.springframework.stereotype.Service; -import site.timecapsulearchive.core.domain.group.data.dto.GroupCreateDto; -import site.timecapsulearchive.core.domain.group.data.dto.GroupDetailDto; -import site.timecapsulearchive.core.domain.group.data.dto.GroupSummaryDto; -import site.timecapsulearchive.core.domain.group.entity.Group; -import site.timecapsulearchive.core.domain.group.service.read.GroupReadService; -import site.timecapsulearchive.core.domain.group.service.write.GroupWriteService; - -@Service -@RequiredArgsConstructor -public class GroupServiceImpl implements GroupService { - - private final GroupReadService groupReadService; - private final GroupWriteService groupWriteService; - - @Override - public Group findGroupById(final Long groupId) { - return groupReadService.findGroupById(groupId); - } - - @Override - public Slice findGroupsSlice( - final Long memberId, - final int size, - final ZonedDateTime createdAt - ) { - return groupReadService.findGroupsSlice(memberId, size, createdAt); - } - - @Override - public GroupDetailDto findGroupDetailByGroupId(final Long memberId, final Long groupId) { - return groupReadService.findGroupDetailByGroupId(memberId, groupId); - } - - @Override - public void createGroup(final Long memberId, final GroupCreateDto dto) { - groupWriteService.createGroup(memberId, dto); - } - - @Override - public void inviteGroup(final Long memberId, final Long groupId, final Long targetId) { - groupWriteService.inviteGroup(memberId, groupId, targetId); - } - - @Override - public void rejectRequestGroup(final Long groupMemberId, final Long groupId, - final Long targetId) { - groupWriteService.rejectRequestGroup(groupMemberId, groupId, targetId); - } - - @Override - public void acceptGroupInvite(final Long memberId, final Long groupId, final Long targetId) { - groupWriteService.acceptGroupInvite(memberId, groupId, targetId); - } - - @Override - public void deleteGroup(final Long memberId, final Long groupId) { - groupWriteService.deleteGroup(memberId, groupId); - } -} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/write/GroupWriteServiceImpl.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/command/GroupCommandService.java similarity index 57% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/write/GroupWriteServiceImpl.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/command/GroupCommandService.java index 88fa65842..d1afbc627 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/write/GroupWriteServiceImpl.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/command/GroupCommandService.java @@ -1,25 +1,20 @@ -package site.timecapsulearchive.core.domain.group.service.write; +package site.timecapsulearchive.core.domain.group.service.command; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import site.timecapsulearchive.core.domain.capsule.group_capsule.repository.GroupCapsuleQueryRepository; import site.timecapsulearchive.core.domain.group.data.dto.GroupCreateDto; -import site.timecapsulearchive.core.domain.group.data.dto.GroupOwnerSummaryDto; import site.timecapsulearchive.core.domain.group.entity.Group; -import site.timecapsulearchive.core.domain.group.entity.GroupInvite; -import site.timecapsulearchive.core.domain.group.entity.MemberGroup; import site.timecapsulearchive.core.domain.group.exception.GroupDeleteFailException; -import site.timecapsulearchive.core.domain.group.exception.GroupInviteNotFoundException; import site.timecapsulearchive.core.domain.group.exception.GroupNotFoundException; -import site.timecapsulearchive.core.domain.group.exception.GroupOwnerAuthenticateException; -import site.timecapsulearchive.core.domain.group.repository.groupInviteRepository.GroupInviteRepository; -import site.timecapsulearchive.core.domain.group.repository.groupRepository.GroupRepository; -import site.timecapsulearchive.core.domain.group.repository.memberGroupRepository.MemberGroupRepository; +import site.timecapsulearchive.core.domain.group.repository.GroupRepository; +import site.timecapsulearchive.core.domain.group_member.entity.MemberGroup; +import site.timecapsulearchive.core.domain.group_member.repository.groupInviteRepository.GroupInviteRepository; +import site.timecapsulearchive.core.domain.group_member.repository.memberGroupRepository.MemberGroupRepository; import site.timecapsulearchive.core.domain.member.entity.Member; import site.timecapsulearchive.core.domain.member.exception.MemberNotFoundException; import site.timecapsulearchive.core.domain.member.repository.MemberRepository; @@ -29,7 +24,7 @@ @Service @RequiredArgsConstructor -public class GroupWriteServiceImpl implements GroupWriteService { +public class GroupCommandService { private final MemberRepository memberRepository; private final GroupRepository groupRepository; @@ -61,71 +56,6 @@ protected void doInTransactionWithoutResult(TransactionStatus status) { dto.groupProfileUrl(), dto.targetIds()); } - public void inviteGroup(final Long memberId, final Long groupId, final Long targetId) { - final Member groupOwner = memberRepository.findMemberById(memberId).orElseThrow( - MemberNotFoundException::new); - - final Member groupMember = memberRepository.findMemberById(targetId).orElseThrow( - MemberNotFoundException::new); - - final Group group = groupRepository.findGroupById(groupId) - .orElseThrow(GroupNotFoundException::new); - - final GroupInvite groupInvite = GroupInvite.createOf(group, groupOwner, groupMember); - - final GroupOwnerSummaryDto[] summaryDto = new GroupOwnerSummaryDto[1]; - - transactionTemplate.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - summaryDto[0] = memberGroupRepository.findOwnerInMemberGroup( - groupId, memberId).orElseThrow(GroupNotFoundException::new); - - if (!summaryDto[0].isOwner()) { - throw new GroupOwnerAuthenticateException(); - } - - groupInviteRepository.save(groupInvite); - } - }); - - socialNotificationManager.sendGroupInviteMessage(summaryDto[0].nickname(), - summaryDto[0].groupProfileUrl(), List.of(targetId)); - } - - @Transactional - public void rejectRequestGroup(final Long memberId, final Long groupId, final Long targetId) { - final int isDenyRequest = groupInviteRepository.deleteGroupInviteByGroupIdAndGroupOwnerIdAndGroupMemberId( - groupId, targetId, memberId); - - if (isDenyRequest != 1) { - throw new GroupInviteNotFoundException(); - } - } - - public void acceptGroupInvite(final Long memberId, final Long groupId, final Long targetId) { - final Member groupMember = memberRepository.findMemberById(memberId) - .orElseThrow(MemberNotFoundException::new); - final Group group = groupRepository.findGroupById(groupId) - .orElseThrow(GroupNotFoundException::new); - - transactionTemplate.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus status) { - final int isDenyRequest = groupInviteRepository.deleteGroupInviteByGroupIdAndGroupOwnerIdAndGroupMemberId( - groupId, targetId, memberId); - - if (isDenyRequest != 1) { - throw new GroupInviteNotFoundException(); - } - - memberGroupRepository.save(MemberGroup.createGroupMember(groupMember, group)); - } - }); - - socialNotificationManager.sendGroupAcceptMessage(groupMember.getNickname(), targetId); - } - /** * 사용자가 그룹장인 그룹을 삭제한다. *
주의 - 그룹 삭제 시 아래 조건에 해당하면 예외가 발생한다. diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/read/GroupReadServiceImpl.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/query/GroupQueryService.java similarity index 87% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/read/GroupReadServiceImpl.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/query/GroupQueryService.java index d3066d287..29049385f 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/read/GroupReadServiceImpl.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/query/GroupQueryService.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.service.read; +package site.timecapsulearchive.core.domain.group.service.query; import java.time.ZonedDateTime; import lombok.RequiredArgsConstructor; @@ -9,12 +9,12 @@ import site.timecapsulearchive.core.domain.group.data.dto.GroupSummaryDto; import site.timecapsulearchive.core.domain.group.entity.Group; import site.timecapsulearchive.core.domain.group.exception.GroupNotFoundException; -import site.timecapsulearchive.core.domain.group.repository.groupRepository.GroupRepository; +import site.timecapsulearchive.core.domain.group.repository.GroupRepository; @Service @RequiredArgsConstructor @Transactional(readOnly = true) -public class GroupReadServiceImpl implements GroupReadService { +public class GroupQueryService { private final GroupRepository groupRepository; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/read/GroupReadService.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/read/GroupReadService.java deleted file mode 100644 index 635b1dbb1..000000000 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/read/GroupReadService.java +++ /dev/null @@ -1,20 +0,0 @@ -package site.timecapsulearchive.core.domain.group.service.read; - -import java.time.ZonedDateTime; -import org.springframework.data.domain.Slice; -import site.timecapsulearchive.core.domain.group.data.dto.GroupDetailDto; -import site.timecapsulearchive.core.domain.group.data.dto.GroupSummaryDto; -import site.timecapsulearchive.core.domain.group.entity.Group; - -public interface GroupReadService { - - Group findGroupById(final Long groupId); - - Slice findGroupsSlice( - final Long memberId, - final int size, - final ZonedDateTime createdAt - ); - - GroupDetailDto findGroupDetailByGroupId(final Long memberId, final Long groupId); -} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/write/GroupWriteService.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/write/GroupWriteService.java deleted file mode 100644 index d7eb52fcb..000000000 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/service/write/GroupWriteService.java +++ /dev/null @@ -1,16 +0,0 @@ -package site.timecapsulearchive.core.domain.group.service.write; - -import site.timecapsulearchive.core.domain.group.data.dto.GroupCreateDto; - -public interface GroupWriteService { - - void createGroup(final Long memberId, final GroupCreateDto dto); - - void inviteGroup(final Long memberId, final Long groupId, final Long targetId); - - void rejectRequestGroup(final Long memberId, final Long groupId, final Long targetId); - - void acceptGroupInvite(final Long memberId, final Long groupId, final Long targetId); - - void deleteGroup(final Long memberId, final Long groupId); -} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/api/command/GroupMemberCommandApi.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/api/command/GroupMemberCommandApi.java new file mode 100644 index 000000000..e32783d4b --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/api/command/GroupMemberCommandApi.java @@ -0,0 +1,128 @@ +package site.timecapsulearchive.core.domain.group_member.api.command; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import site.timecapsulearchive.core.global.common.response.ApiSpec; + +public interface GroupMemberCommandApi { + + @Operation( + summary = "그룹원 삭제", + description = "그룹장인 경우 특정 그룹원을 삭제한다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group member"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "처리 완료" + ) + }) + @DeleteMapping(value = "/groups/{group_id}/members/{member_id}") + ResponseEntity deleteGroupMember( + @Parameter(in = ParameterIn.PATH, description = "그룹 아이디", required = true, schema = @Schema()) + @PathVariable("group_id") Long groupId, + + @Parameter(in = ParameterIn.PATH, description = "삭제할 멤버 아이디", required = true, schema = @Schema()) + @PathVariable("member_id") Long memberId + ); + + @Operation( + summary = "그룹 탈퇴", + description = "사용자가 속한 그룹을 탈퇴한다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group member"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "204", + description = "처리 완료" + ) + }) + @DeleteMapping(value = "/groups/{group_id}/members/quit") + ResponseEntity quitGroup( + @Parameter(in = ParameterIn.PATH, description = "조회할 그룹 아이디", required = true, schema = @Schema()) + @PathVariable("group_id") Long groupId + ); + + @Operation( + summary = "그룹 요청", + description = "그룹장인 경우 친구에게 그룹 가입 요청을 할 수 있다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group member"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "202", + description = "처리 시작" + ) + }) + ResponseEntity> inviteGroup( + Long memberId, + + @Parameter(in = ParameterIn.PATH, description = "그룹 아이디", required = true) + Long groupId, + + @Parameter(in = ParameterIn.PATH, description = "대상 회원 아이디", required = true) + Long targetId + ); + + @Operation( + summary = "그룹 요청 수락", + description = "특정 그룹으로부터 그룹 요청을 수락한다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group member"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "처리 완료" + ), + @ApiResponse( + responseCode = "404", + description = "그룹 초대 찾기 실패" + ), + @ApiResponse( + responseCode = "500", + description = "외부 API 요청 실패" + ) + }) + ResponseEntity> acceptGroupInvitation( + Long memberId, + + @Parameter(in = ParameterIn.PATH, description = "그룹 아이디", required = true) + Long groupId, + + @Parameter(in = ParameterIn.PATH, description = "대상 회원 아이디", required = true) + Long targetId + ); + + @Operation( + summary = "그룹 요청 거부", + description = "특정 그룹으로부터 초대 요청을 거부한다.", + security = {@SecurityRequirement(name = "user_token")}, + tags = {"group member"} + ) + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "처리 완료" + ) + }) + ResponseEntity> rejectGroupInvitation( + Long memberId, + + Long groupId, + + @Parameter(in = ParameterIn.PATH, description = "그룹 초대 대상 아이디", required = true) + Long groupOwnerId + ); +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/api/command/GroupMemberCommandApiController.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/api/command/GroupMemberCommandApiController.java new file mode 100644 index 000000000..be6d3795b --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/api/command/GroupMemberCommandApiController.java @@ -0,0 +1,78 @@ +package site.timecapsulearchive.core.domain.group_member.api.command; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import site.timecapsulearchive.core.domain.group_member.service.GroupMemberCommandService; +import site.timecapsulearchive.core.global.common.response.ApiSpec; +import site.timecapsulearchive.core.global.common.response.SuccessCode; + +@RestController +@RequestMapping("/group-members") +@RequiredArgsConstructor +public class GroupMemberCommandApiController implements GroupMemberCommandApi { + + private final GroupMemberCommandService groupMemberCommandService; + + @Override + public ResponseEntity deleteGroupMember(Long groupId, Long memberId) { + return null; + } + + @Override + public ResponseEntity quitGroup(Long groupId) { + return null; + } + + @PostMapping(value = "/invite/{group_id}/member/{target_id}") + @Override + public ResponseEntity> inviteGroup( + @AuthenticationPrincipal final Long memberId, + @PathVariable("group_id") final Long groupId, + @PathVariable("target_id") final Long targetId + ) { + groupMemberCommandService.inviteGroup(memberId, groupId, targetId); + + return ResponseEntity.ok( + ApiSpec.empty( + SuccessCode.ACCEPTED + ) + ); + } + + @PostMapping(value = "/accept/{group_id}/member/{target_id}") + @Override + public ResponseEntity> acceptGroupInvitation( + @AuthenticationPrincipal final Long memberId, + @PathVariable("group_id") final Long groupId, + @PathVariable("target_id") final Long targetId + ) { + groupMemberCommandService.acceptGroupInvite(memberId, groupId, targetId); + + return ResponseEntity.ok( + ApiSpec.empty( + SuccessCode.ACCEPTED + ) + ); + } + + @DeleteMapping(value = "/reject/{group_id}/member/{target_id}") + public ResponseEntity> rejectGroupInvitation( + @AuthenticationPrincipal final Long memberId, + @PathVariable("group_id") final Long groupId, + @PathVariable("target_id") final Long targetId) { + + groupMemberCommandService.rejectRequestGroup(memberId, groupId, targetId); + + return ResponseEntity.ok( + ApiSpec.empty( + SuccessCode.SUCCESS + ) + ); + } +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupOwnerSummaryDto.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/data/GroupOwnerSummaryDto.java similarity index 64% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupOwnerSummaryDto.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/data/GroupOwnerSummaryDto.java index 3496d3ec8..cbe65a301 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/data/dto/GroupOwnerSummaryDto.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/data/GroupOwnerSummaryDto.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.data.dto; +package site.timecapsulearchive.core.domain.group_member.data; public record GroupOwnerSummaryDto( String nickname, diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/entity/GroupInvite.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/entity/GroupInvite.java similarity index 92% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/entity/GroupInvite.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/entity/GroupInvite.java index 140c53763..02a39f238 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/entity/GroupInvite.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/entity/GroupInvite.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.entity; +package site.timecapsulearchive.core.domain.group_member.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -12,6 +12,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import site.timecapsulearchive.core.domain.group.entity.Group; import site.timecapsulearchive.core.domain.member.entity.Member; import site.timecapsulearchive.core.global.entity.BaseEntity; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/entity/MemberGroup.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/entity/MemberGroup.java similarity index 92% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/entity/MemberGroup.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/entity/MemberGroup.java index 67c37404c..c1c8b3cec 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/entity/MemberGroup.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/entity/MemberGroup.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.entity; +package site.timecapsulearchive.core.domain.group_member.entity; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -12,6 +12,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import site.timecapsulearchive.core.domain.group.entity.Group; import site.timecapsulearchive.core.domain.member.entity.Member; import site.timecapsulearchive.core.global.entity.BaseEntity; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/exception/GroupInviteNotFoundException.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/exception/GroupInviteNotFoundException.java similarity index 82% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/exception/GroupInviteNotFoundException.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/exception/GroupInviteNotFoundException.java index 39ad8bb61..41201b335 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/exception/GroupInviteNotFoundException.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/exception/GroupInviteNotFoundException.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.exception; +package site.timecapsulearchive.core.domain.group_member.exception; import site.timecapsulearchive.core.global.error.ErrorCode; import site.timecapsulearchive.core.global.error.exception.BusinessException; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/exception/GroupOwnerAuthenticateException.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/exception/GroupOwnerAuthenticateException.java similarity index 82% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/exception/GroupOwnerAuthenticateException.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/exception/GroupOwnerAuthenticateException.java index 2c6382b85..ae2cc2b2f 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/exception/GroupOwnerAuthenticateException.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/exception/GroupOwnerAuthenticateException.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.exception; +package site.timecapsulearchive.core.domain.group_member.exception; import site.timecapsulearchive.core.global.error.ErrorCode; import site.timecapsulearchive.core.global.error.exception.BusinessException; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteQueryRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteQueryRepository.java similarity index 73% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteQueryRepository.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteQueryRepository.java index 279dd9d5e..de9bbc52d 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteQueryRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteQueryRepository.java @@ -1,4 +1,4 @@ -package site.timecapsulearchive.core.domain.group.repository.groupInviteRepository; +package site.timecapsulearchive.core.domain.group_member.repository.groupInviteRepository; import java.util.List; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteQueryRepositoryImpl.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteQueryRepositoryImpl.java similarity index 91% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteQueryRepositoryImpl.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteQueryRepositoryImpl.java index 6f6c78ec2..6e1c973dd 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteQueryRepositoryImpl.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteQueryRepositoryImpl.java @@ -1,6 +1,7 @@ -package site.timecapsulearchive.core.domain.group.repository.groupInviteRepository; +package site.timecapsulearchive.core.domain.group_member.repository.groupInviteRepository; -import static site.timecapsulearchive.core.domain.group.entity.QGroupInvite.groupInvite; + +import static site.timecapsulearchive.core.domain.group_member.entity.QGroupInvite.groupInvite; import com.querydsl.jpa.impl.JPAQueryFactory; import java.sql.PreparedStatement; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteRepository.java similarity index 80% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteRepository.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteRepository.java index 258563ef1..f411caa1a 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/groupInviteRepository/GroupInviteRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/groupInviteRepository/GroupInviteRepository.java @@ -1,11 +1,11 @@ -package site.timecapsulearchive.core.domain.group.repository.groupInviteRepository; +package site.timecapsulearchive.core.domain.group_member.repository.groupInviteRepository; import java.util.List; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.Repository; import org.springframework.data.repository.query.Param; -import site.timecapsulearchive.core.domain.group.entity.GroupInvite; +import site.timecapsulearchive.core.domain.group_member.entity.GroupInvite; public interface GroupInviteRepository extends Repository, GroupInviteQueryRepository { diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupQueryRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupQueryRepository.java new file mode 100644 index 000000000..01e11dff6 --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupQueryRepository.java @@ -0,0 +1,10 @@ +package site.timecapsulearchive.core.domain.group_member.repository.memberGroupRepository; + +import java.util.Optional; +import site.timecapsulearchive.core.domain.group_member.data.GroupOwnerSummaryDto; + +public interface MemberGroupQueryRepository { + + Optional findOwnerInMemberGroup(Long groupId, Long memberId); + +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupQueryRepositoryImpl.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupQueryRepositoryImpl.java similarity index 82% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupQueryRepositoryImpl.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupQueryRepositoryImpl.java index 6e240515a..afdb563ff 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupQueryRepositoryImpl.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupQueryRepositoryImpl.java @@ -1,7 +1,7 @@ -package site.timecapsulearchive.core.domain.group.repository.memberGroupRepository; +package site.timecapsulearchive.core.domain.group_member.repository.memberGroupRepository; import static site.timecapsulearchive.core.domain.group.entity.QGroup.group; -import static site.timecapsulearchive.core.domain.group.entity.QMemberGroup.memberGroup; +import static site.timecapsulearchive.core.domain.group_member.entity.QMemberGroup.memberGroup; import static site.timecapsulearchive.core.domain.member.entity.QMember.member; import com.querydsl.core.types.Projections; @@ -9,7 +9,7 @@ import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; -import site.timecapsulearchive.core.domain.group.data.dto.GroupOwnerSummaryDto; +import site.timecapsulearchive.core.domain.group_member.data.GroupOwnerSummaryDto; @Repository @RequiredArgsConstructor diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupRepository.java similarity index 67% rename from backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupRepository.java rename to backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupRepository.java index fdd73b90d..cfe9af47e 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group/repository/memberGroupRepository/MemberGroupRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/repository/memberGroupRepository/MemberGroupRepository.java @@ -1,8 +1,8 @@ -package site.timecapsulearchive.core.domain.group.repository.memberGroupRepository; +package site.timecapsulearchive.core.domain.group_member.repository.memberGroupRepository; import java.util.List; import org.springframework.data.repository.Repository; -import site.timecapsulearchive.core.domain.group.entity.MemberGroup; +import site.timecapsulearchive.core.domain.group_member.entity.MemberGroup; public interface MemberGroupRepository extends Repository, MemberGroupQueryRepository { diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/service/GroupMemberCommandService.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/service/GroupMemberCommandService.java new file mode 100644 index 000000000..5e85ba734 --- /dev/null +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/group_member/service/GroupMemberCommandService.java @@ -0,0 +1,101 @@ +package site.timecapsulearchive.core.domain.group_member.service; + +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallbackWithoutResult; +import org.springframework.transaction.support.TransactionTemplate; +import site.timecapsulearchive.core.domain.group_member.data.GroupOwnerSummaryDto; +import site.timecapsulearchive.core.domain.group.entity.Group; +import site.timecapsulearchive.core.domain.group_member.entity.GroupInvite; +import site.timecapsulearchive.core.domain.group_member.entity.MemberGroup; +import site.timecapsulearchive.core.domain.group_member.exception.GroupInviteNotFoundException; +import site.timecapsulearchive.core.domain.group.exception.GroupNotFoundException; +import site.timecapsulearchive.core.domain.group_member.exception.GroupOwnerAuthenticateException; +import site.timecapsulearchive.core.domain.group.repository.GroupRepository; +import site.timecapsulearchive.core.domain.group_member.repository.groupInviteRepository.GroupInviteRepository; +import site.timecapsulearchive.core.domain.group_member.repository.memberGroupRepository.MemberGroupRepository; +import site.timecapsulearchive.core.domain.member.entity.Member; +import site.timecapsulearchive.core.domain.member.exception.MemberNotFoundException; +import site.timecapsulearchive.core.domain.member.repository.MemberRepository; +import site.timecapsulearchive.core.infra.queue.manager.SocialNotificationManager; + +@Service +@RequiredArgsConstructor +public class GroupMemberCommandService { + + private final MemberRepository memberRepository; + private final GroupRepository groupRepository; + private final MemberGroupRepository memberGroupRepository; + private final GroupInviteRepository groupInviteRepository; + private final TransactionTemplate transactionTemplate; + private final SocialNotificationManager socialNotificationManager; + + public void inviteGroup(final Long memberId, final Long groupId, final Long targetId) { + final Member groupOwner = memberRepository.findMemberById(memberId).orElseThrow( + MemberNotFoundException::new); + + final Member groupMember = memberRepository.findMemberById(targetId).orElseThrow( + MemberNotFoundException::new); + + final Group group = groupRepository.findGroupById(groupId) + .orElseThrow(GroupNotFoundException::new); + + final GroupInvite groupInvite = GroupInvite.createOf(group, groupOwner, groupMember); + + final GroupOwnerSummaryDto[] summaryDto = new GroupOwnerSummaryDto[1]; + + transactionTemplate.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + summaryDto[0] = memberGroupRepository.findOwnerInMemberGroup( + groupId, memberId).orElseThrow(GroupNotFoundException::new); + + if (!summaryDto[0].isOwner()) { + throw new GroupOwnerAuthenticateException(); + } + + groupInviteRepository.save(groupInvite); + } + }); + + socialNotificationManager.sendGroupInviteMessage(summaryDto[0].nickname(), + summaryDto[0].groupProfileUrl(), List.of(targetId)); + } + + @Transactional + public void rejectRequestGroup(final Long memberId, final Long groupId, final Long targetId) { + final int isDenyRequest = groupInviteRepository.deleteGroupInviteByGroupIdAndGroupOwnerIdAndGroupMemberId( + groupId, targetId, memberId); + + if (isDenyRequest != 1) { + throw new GroupInviteNotFoundException(); + } + } + + public void acceptGroupInvite(final Long memberId, final Long groupId, final Long targetId) { + final Member groupMember = memberRepository.findMemberById(memberId) + .orElseThrow(MemberNotFoundException::new); + final Group group = groupRepository.findGroupById(groupId) + .orElseThrow(GroupNotFoundException::new); + + transactionTemplate.execute(new TransactionCallbackWithoutResult() { + @Override + protected void doInTransactionWithoutResult(TransactionStatus status) { + final int isDenyRequest = groupInviteRepository.deleteGroupInviteByGroupIdAndGroupOwnerIdAndGroupMemberId( + groupId, targetId, memberId); + + if (isDenyRequest != 1) { + throw new GroupInviteNotFoundException(); + } + + memberGroupRepository.save(MemberGroup.createGroupMember(groupMember, group)); + } + }); + + socialNotificationManager.sendGroupAcceptMessage(groupMember.getNickname(), targetId); + } + +} diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/member/entity/Member.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/member/entity/Member.java index a99bd8fff..432cb03d1 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/member/entity/Member.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/member/entity/Member.java @@ -19,7 +19,7 @@ import site.timecapsulearchive.core.domain.capsule.entity.Capsule; import site.timecapsulearchive.core.domain.friend.entity.FriendInvite; import site.timecapsulearchive.core.domain.friend.entity.MemberFriend; -import site.timecapsulearchive.core.domain.group.entity.MemberGroup; +import site.timecapsulearchive.core.domain.group_member.entity.MemberGroup; import site.timecapsulearchive.core.domain.history.entity.History; import site.timecapsulearchive.core.global.entity.BaseEntity; import site.timecapsulearchive.core.global.util.NullCheck; diff --git a/backend/core/src/main/java/site/timecapsulearchive/core/domain/member/repository/MemberQueryRepository.java b/backend/core/src/main/java/site/timecapsulearchive/core/domain/member/repository/MemberQueryRepository.java index 56d405fa0..cb36a5ec6 100644 --- a/backend/core/src/main/java/site/timecapsulearchive/core/domain/member/repository/MemberQueryRepository.java +++ b/backend/core/src/main/java/site/timecapsulearchive/core/domain/member/repository/MemberQueryRepository.java @@ -1,7 +1,7 @@ package site.timecapsulearchive.core.domain.member.repository; import static site.timecapsulearchive.core.domain.friend.entity.QMemberFriend.memberFriend; -import static site.timecapsulearchive.core.domain.group.entity.QMemberGroup.memberGroup; +import static site.timecapsulearchive.core.domain.group_member.entity.QMemberGroup.memberGroup; import static site.timecapsulearchive.core.domain.member.entity.QMember.member; import static site.timecapsulearchive.core.domain.member.entity.QNotification.notification; import static site.timecapsulearchive.core.domain.member.entity.QNotificationCategory.notificationCategory;