Skip to content

Commit

Permalink
Merge pull request #485 from tukcomCD2024/feat/group_member_capsule_o…
Browse files Browse the repository at this point in the history
…pen_status-B-#484

feat : 그룹 캡슐 개봉 상태 조회 API 추가 #484
  • Loading branch information
seokho-1116 authored Jun 11, 2024
2 parents 889f189 + 3a22e75 commit ed7fd18
Show file tree
Hide file tree
Showing 19 changed files with 364 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.Builder;
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;

Expand All @@ -39,16 +38,9 @@ public class GroupCapsuleOpen extends BaseEntity {
@JoinColumn(name = "member_id", nullable = false)
private Member member;

@Builder
private GroupCapsuleOpen(Boolean isOpened, Capsule capsule, Member member) {
this.isOpened = Objects.requireNonNull(isOpened);
this.capsule = Objects.requireNonNull(capsule);
this.member = Objects.requireNonNull(member);
}

public static GroupCapsuleOpen createOf(Member member, Capsule capsule, Boolean isOpened) {
return new GroupCapsuleOpen(isOpened, capsule, member);
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id", nullable = false)
private Group group;

public void open() {
this.isOpened = Boolean.TRUE;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.GroupMemberCapsuleOpenStatusListResponse;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.reqeust.GroupCapsuleCreateRequest;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.reqeust.GroupCapsuleUpdateRequest;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.GroupCapsuleDetailResponse;
Expand Down Expand Up @@ -154,6 +155,35 @@ ResponseEntity<ApiSpec<MyGroupCapsuleSliceResponse>> getMyGroupCapsules(
ZonedDateTime createAt
);

@Operation(
summary = "그룹원의 그룹 캡슐 개봉 상태 확인",
description = """
그룹원의 그룹 캡슐 개봉 상태를 확인한다.
""",
security = {@SecurityRequirement(name = "user_token")},
tags = {"group capsule"}
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "처리 완료"
),
@ApiResponse(
responseCode = "404",
description = "그룹 캡슐의 개봉 상태를 찾을 수 없는 경우 예외가 발생한다.",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
)
})
ResponseEntity<ApiSpec<GroupMemberCapsuleOpenStatusListResponse>> getGroupCapsuleOpenStatus(
Long memberId,

@Parameter(in = ParameterIn.PATH, description = "개봉 상태를 확인할 캡슐 아이디", required = true)
Long capsuleId,

@Parameter(in = ParameterIn.QUERY, description = "생성할 그룹 아이디", required = true)
Long groupId
);

@Operation(
summary = "그룹 캡슐 개봉",
description = """
Expand All @@ -177,7 +207,7 @@ ResponseEntity<ApiSpec<GroupCapsuleOpenStateResponse>> openCapsule(
Long memberId,

@Parameter(in = ParameterIn.PATH, description = "개봉할 그룹 캡슐 아이디", required = true)
@PathVariable("capsule_id") Long capsuleId
Long capsuleId
);

@Operation(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.validation.Valid;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Slice;
import org.springframework.http.ResponseEntity;
Expand All @@ -17,12 +18,15 @@
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupCapsuleDetailDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupCapsuleOpenStateDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupMemberCapsuleOpenStatusDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.MyGroupCapsuleDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.reqeust.GroupCapsuleCreateRequest;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.reqeust.GroupCapsuleUpdateRequest;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.GroupCapsuleDetailResponse;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.GroupCapsuleOpenStateResponse;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.GroupCapsulePageResponse;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.GroupCapsuleSummaryResponse;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.GroupMemberCapsuleOpenStatusListResponse;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.MyGroupCapsuleSliceResponse;
import site.timecapsulearchive.core.domain.capsule.group_capsule.facade.GroupCapsuleFacade;
import site.timecapsulearchive.core.domain.capsule.group_capsule.service.GroupCapsuleService;
Expand Down Expand Up @@ -139,6 +143,24 @@ public ResponseEntity<ApiSpec<MyGroupCapsuleSliceResponse>> getMyGroupCapsules(
);
}

@GetMapping(value = "/{capsule_id}/open-status", produces = {"application/json"})
@Override
public ResponseEntity<ApiSpec<GroupMemberCapsuleOpenStatusListResponse>> getGroupCapsuleOpenStatus(
@AuthenticationPrincipal final Long memberId,
@PathVariable("capsule_id") final Long capsuleId,
@RequestParam("group_id") final Long groupId
) {
List<GroupMemberCapsuleOpenStatusDto> groupMemberCapsuleOpenStatus = groupCapsuleService.findGroupMemberCapsuleOpenStatus(
memberId, capsuleId, groupId);

return ResponseEntity.ok(
ApiSpec.success(
SuccessCode.SUCCESS,
GroupMemberCapsuleOpenStatusListResponse.create(groupMemberCapsuleOpenStatus)
)
);
}

@PostMapping("/{capsule_id}/open")
@Override
public ResponseEntity<ApiSpec<GroupCapsuleOpenStateResponse>> openCapsule(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto;

import site.timecapsulearchive.core.domain.capsule.group_capsule.data.response.GroupMemberCapsuleOpenStatusResponse;

public record GroupMemberCapsuleOpenStatusDto(
Long memberId,
String nickname,
String profileUrl,
boolean isOpened
) {

public GroupMemberCapsuleOpenStatusResponse toResponse() {
return GroupMemberCapsuleOpenStatusResponse.builder()
.memberId(memberId)
.nickname(nickname)
.profileUrl(profileUrl)
.isOpened(isOpened)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.data.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupMemberCapsuleOpenStatusDto;

@Schema(description = "그룹원들 캡슐 개봉 상태")
public record GroupMemberCapsuleOpenStatusListResponse(
List<GroupMemberCapsuleOpenStatusResponse> groupMemberCapsuleOpenStatus
) {

public static GroupMemberCapsuleOpenStatusListResponse create(
List<GroupMemberCapsuleOpenStatusDto> groupMemberCapsuleOpenStatus) {
List<GroupMemberCapsuleOpenStatusResponse> groupMemberCapsuleOpenStatusResponses = groupMemberCapsuleOpenStatus.stream()
.map(GroupMemberCapsuleOpenStatusDto::toResponse)
.toList();

return new GroupMemberCapsuleOpenStatusListResponse(groupMemberCapsuleOpenStatusResponses);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.data.response;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Schema(description = "그룹원별 캡슐 개봉 상태")
@Builder
public record GroupMemberCapsuleOpenStatusResponse(

@Schema(description = "회원 아이디")
Long memberId,

@Schema(description = "회원 닉네임")
String nickname,

@Schema(description = "회원 프로필")
String profileUrl,

@Schema(description = "개봉 상태")
boolean isOpened
) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ public void saveGroupCapsule(

final List<Long> groupMemberIds = memberGroupQueryService.findGroupMemberIds(groupId);

groupCapsuleOpenService.bulkSave(groupMemberIds, capsule);
groupCapsuleOpenService.bulkSave(groupId, groupMemberIds, capsule);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.repository;

import static site.timecapsulearchive.core.domain.capsule.entity.QGroupCapsuleOpen.groupCapsuleOpen;
import static site.timecapsulearchive.core.domain.member.entity.QMember.member;

import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
Expand All @@ -11,19 +16,26 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import site.timecapsulearchive.core.domain.capsule.entity.Capsule;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupMemberCapsuleOpenStatusDto;
import site.timecapsulearchive.core.domain.member.entity.QMember;

@Repository
@RequiredArgsConstructor
public class GroupCapsuleOpenQueryRepository {

private final JdbcTemplate jdbcTemplate;
private final JPAQueryFactory jpaQueryFactory;

public void bulkSave(final List<Long> groupMemberIds, final Capsule capsule) {
public void bulkSave(
final Long groupId,
final List<Long> groupMemberIds,
final Capsule capsule
) {
jdbcTemplate.batchUpdate(
"""
INSERT INTO group_capsule_open (
group_capsule_open_id, is_opened, member_id, capsule_id, created_at, updated_at
) values (?, ? ,? ,? ,?, ?)
group_capsule_open_id, is_opened, member_id, capsule_id, group_id, created_at, updated_at
) values (?, ? ,? ,? ,?, ?, ?)
""",
new BatchPreparedStatementSetter() {
@Override
Expand All @@ -34,8 +46,9 @@ public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setBoolean(2, isOpened);
ps.setLong(3, groupMemberIds.get(i));
ps.setLong(4, capsule.getId());
ps.setTimestamp(5, Timestamp.valueOf(ZonedDateTime.now().toLocalDateTime()));
ps.setLong(5, groupId);
ps.setTimestamp(6, Timestamp.valueOf(ZonedDateTime.now().toLocalDateTime()));
ps.setTimestamp(7, Timestamp.valueOf(ZonedDateTime.now().toLocalDateTime()));
}

@Override
Expand All @@ -45,4 +58,26 @@ public int getBatchSize() {
}
);
}

public List<GroupMemberCapsuleOpenStatusDto> findGroupMemberCapsuleOpenStatus(
final Long capsuleId,
final Long groupId
) {
return jpaQueryFactory
.select(
Projections.constructor(
GroupMemberCapsuleOpenStatusDto.class,
groupCapsuleOpen.member.id,
groupCapsuleOpen.member.nickname,
groupCapsuleOpen.member.profileUrl,
groupCapsuleOpen.isOpened
)
)
.from(groupCapsuleOpen)
.join(groupCapsuleOpen.member, member)
.where(groupCapsuleOpen.group.id.eq(groupId)
.and(groupCapsuleOpen.capsule.id.eq(capsuleId))
)
.fetch();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ public class GroupCapsuleOpenService {

private final GroupCapsuleOpenQueryRepository repository;

public void bulkSave(final List<Long> groupMemberIds, final Capsule capsule) {
repository.bulkSave(groupMemberIds, capsule);
public void bulkSave(
final Long groupId,
final List<Long> groupMemberIds,
final Capsule capsule
) {
repository.bulkSave(groupId, groupMemberIds, capsule);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.locationtech.jts.geom.Point;
import org.springframework.data.domain.Slice;
Expand All @@ -14,12 +15,17 @@
import site.timecapsulearchive.core.domain.capsule.generic_capsule.repository.CapsuleRepository;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupCapsuleCreateRequestDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupCapsuleDetailDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupCapsuleOpenStateDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.GroupMemberCapsuleOpenStatusDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.MyGroupCapsuleDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.repository.GroupCapsuleOpenQueryRepository;
import site.timecapsulearchive.core.domain.capsule.group_capsule.repository.GroupCapsuleQueryRepository;
import site.timecapsulearchive.core.domain.capsuleskin.entity.CapsuleSkin;
import site.timecapsulearchive.core.domain.group.entity.Group;
import site.timecapsulearchive.core.domain.member.entity.Member;
import site.timecapsulearchive.core.domain.member_group.exception.NoGroupAuthorityException;
import site.timecapsulearchive.core.domain.member_group.repository.member_group_repository.MemberGroupRepository;

@Service
@Transactional(readOnly = true)
Expand All @@ -28,6 +34,8 @@ public class GroupCapsuleService {

private final CapsuleRepository capsuleRepository;
private final GroupCapsuleQueryRepository groupCapsuleQueryRepository;
private final GroupCapsuleOpenQueryRepository groupCapsuleOpenQueryRepository;
private final MemberGroupRepository memberGroupRepository;

@Transactional
public Capsule saveGroupCapsule(
Expand Down Expand Up @@ -98,7 +106,8 @@ public Slice<CapsuleBasicInfoDto> findMyGroupCapsuleSlice(
*/
@Transactional
public GroupCapsuleOpenStateDto openGroupCapsule(final Long memberId, final Long capsuleId) {
Capsule groupCapsule = capsuleRepository.findNotOpenedGroupCapsuleByMemberIdAndCapsuleId(memberId,
Capsule groupCapsule = capsuleRepository.findNotOpenedGroupCapsuleByMemberIdAndCapsuleId(
memberId,
capsuleId)
.orElseThrow(CapsuleNotFondException::new);

Expand All @@ -119,5 +128,18 @@ public GroupCapsuleOpenStateDto openGroupCapsule(final Long memberId, final Long
groupCapsule.open();
return GroupCapsuleOpenStateDto.opened();
}

public List<GroupMemberCapsuleOpenStatusDto> findGroupMemberCapsuleOpenStatus(
final Long memberId,
final Long capsuleId,
final Long groupId
) {
boolean isGroupMember = memberGroupRepository.existMemberGroupByMemberIdAndGroupId(memberId, groupId);
if (!isGroupMember) {
throw new NoGroupAuthorityException();
}

return groupCapsuleOpenQueryRepository.findGroupMemberCapsuleOpenStatus(capsuleId, groupId);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ public interface MemberGroupQueryRepository {
Optional<Long> findGroupMembersCount(Long groupId);

List<Long> findGroupMemberIdsByGroupId(final Long groupId);

boolean existMemberGroupByMemberIdAndGroupId(Long memberId, Long groupId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,22 @@ public Optional<Long> findGroupMembersCount(final Long groupId) {
);
}

@Override
public List<Long> findGroupMemberIdsByGroupId(final Long groupId) {
return jpaQueryFactory
.select(memberGroup.member.id)
.from(memberGroup)
.where(memberGroup.group.id.eq(groupId))
.fetch();
}

@Override
public boolean existMemberGroupByMemberIdAndGroupId(Long memberId, Long groupId) {
final Integer count = jpaQueryFactory.selectOne()
.from(memberGroup)
.where(memberGroup.member.id.eq(memberId).and(memberGroup.group.id.eq(groupId)))
.fetchFirst();

return count != null;
}
}
Loading

0 comments on commit ed7fd18

Please sign in to comment.