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;