Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat : 그룹 캡슐 개봉 상태 조회 API 추가 #484 #485

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading