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 : 그룹 캡슐 개봉 기능 추가 #415

Merged
merged 7 commits into from
May 15, 2024
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 @@ -45,4 +45,12 @@ private GroupCapsuleOpen(Boolean isOpened, Capsule capsule, Member member) {
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);
}

public void open() {
this.isOpened = Boolean.TRUE;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package site.timecapsulearchive.core.domain.capsule.exception;

import site.timecapsulearchive.core.global.error.ErrorCode;
import site.timecapsulearchive.core.global.error.exception.BusinessException;

public class GroupCapsuleOpenNotFoundException extends BusinessException {

public GroupCapsuleOpenNotFoundException() {
super(ErrorCode.GROUP_CAPSULE_OPEN_NOT_FOUND_ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,32 @@ ResponseEntity<ApiSpec<MyGroupCapsuleSliceResponse>> getMyGroupCapsules(
ZonedDateTime createAt
);

@Operation(
summary = "그룹 캡슐 개봉",
description = """
그룹원이 그룹 캡슐을 개봉한다.<br> 캡슐을 만들 때의 그룹원 모두가 캡슐을 개봉해야만 캡슐이 개봉된다.
""",
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<String>> openCapsule(
Long memberId,

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

@Operation(
summary = "그룹 캡슐 24시간 이내 수정",
description = "사용자가 생성한 그룹 캡슐의 생성 시간이 24시간 이내라면 수정한다.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import site.timecapsulearchive.core.infra.s3.manager.S3PreSignedUrlManager;

@RestController
@RequestMapping("/groups-capsules")
@RequestMapping("/group-capsules")
@RequiredArgsConstructor
public class GroupCapsuleApiController implements GroupCapsuleApi {

Expand Down Expand Up @@ -137,6 +137,17 @@ public ResponseEntity<ApiSpec<MyGroupCapsuleSliceResponse>> getMyGroupCapsules(
);
}

@PostMapping("/{capsule_id}/open")
@Override
public ResponseEntity<ApiSpec<String>> openCapsule(
@AuthenticationPrincipal final Long memberId,
@PathVariable(value = "capsule_id") final Long capsuleId
) {
groupCapsuleService.openGroupCapsule(memberId, capsuleId);

return ResponseEntity.ok(ApiSpec.empty(SuccessCode.SUCCESS));
}

@Override
public ResponseEntity<GroupCapsuleSummaryResponse> updateGroupCapsuleByIdAndGroupId(
Long groupId, Long capsuleId, GroupCapsuleUpdateRequest request) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.repository;

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

import com.querydsl.jpa.impl.JPAQueryFactory;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
Expand All @@ -17,6 +20,7 @@
public class GroupCapsuleOpenQueryRepository {

private final JdbcTemplate jdbcTemplate;
private final JPAQueryFactory jpaQueryFactory;

public void bulkSave(final List<Long> groupMemberIds, final Capsule capsule) {
jdbcTemplate.batchUpdate(
Expand All @@ -43,4 +47,12 @@ public int getBatchSize() {
}
);
}

public List<Boolean> findIsOpenedByCapsuleId(final Long capsuleId) {
return jpaQueryFactory
.select(groupCapsuleOpen.isOpened)
.from(groupCapsuleOpen)
.where(groupCapsuleOpen.capsule.id.eq(capsuleId))
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.repository;

import java.util.Optional;
import org.springframework.data.repository.Repository;
import site.timecapsulearchive.core.domain.capsule.entity.GroupCapsuleOpen;

public interface GroupCapsuleOpenRepository extends Repository<GroupCapsuleOpen, Long> {

void save(GroupCapsuleOpen groupCapsuleOpen);

Optional<GroupCapsuleOpen> findByMemberIdAndCapsuleId(Long memberId, Long capsuleId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,24 @@

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;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import site.timecapsulearchive.core.domain.capsule.entity.Capsule;
import site.timecapsulearchive.core.domain.capsule.entity.CapsuleType;
import site.timecapsulearchive.core.domain.capsule.entity.GroupCapsuleOpen;
import site.timecapsulearchive.core.domain.capsule.exception.CapsuleNotFondException;
import site.timecapsulearchive.core.domain.capsule.exception.GroupCapsuleOpenNotFoundException;
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.MyGroupCapsuleDto;
import site.timecapsulearchive.core.domain.capsule.group_capsule.repository.GroupCapsuleOpenQueryRepository;
import site.timecapsulearchive.core.domain.capsule.group_capsule.repository.GroupCapsuleOpenRepository;
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;
Expand All @@ -27,6 +32,8 @@ public class GroupCapsuleService {

private final CapsuleRepository capsuleRepository;
private final GroupCapsuleQueryRepository groupCapsuleQueryRepository;
private final GroupCapsuleOpenRepository groupCapsuleOpenRepository;
private final GroupCapsuleOpenQueryRepository groupCapsuleOpenQueryRepository;

@Transactional
public Capsule saveGroupCapsule(
Expand Down Expand Up @@ -88,5 +95,29 @@ public Slice<MyGroupCapsuleDto> findMyGroupCapsuleSlice(
) {
return groupCapsuleQueryRepository.findMyGroupCapsuleSlice(memberId, size, createdAt);
}

/**
* 해당 그룹원이 그룹 캡슐을 개봉한다. 모든 그룹원이 개봉하면 캡슐이 개봉된다.
*
* @param memberId 캡슐을 개봉할 그룹원
* @param capsuleId 개봉할 캡슐 아이디
*/
@Transactional
public void openGroupCapsule(final Long memberId, final Long capsuleId) {
GroupCapsuleOpen groupCapsuleOpen = groupCapsuleOpenRepository.findByMemberIdAndCapsuleId(
memberId,
capsuleId)
.orElseThrow(GroupCapsuleOpenNotFoundException::new);
groupCapsuleOpen.open();

List<Boolean> capsuleOpens = groupCapsuleOpenQueryRepository.findIsOpenedByCapsuleId(
capsuleId);

boolean allGroupMemberOpened = capsuleOpens.stream()
.allMatch(isOpened -> isOpened.equals(Boolean.TRUE));
if (allGroupMemberOpened) {
capsuleRepository.updateIsOpenedTrue(memberId, capsuleId);
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public enum ErrorCode {
//capsule
CAPSULE_NOT_FOUND_ERROR(404, "CAPSULE-001", "캡슐을 찾지 못하였습니다."),
NO_CAPSULE_AUTHORITY_ERROR(403, "CAPSULE-002", "캡슐에 접근 권한이 없습니다."),
GROUP_CAPSULE_OPEN_NOT_FOUND_ERROR(404, "CAPSULE-003", "그룹 캡슐 개봉상태를 찾을 수 없습니다."),

//friend
FRIEND_NOT_FOUND_ERROR(404, "FRIEND-001", "친구를 찾지 못하였습니다"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package site.timecapsulearchive.core.common.fixture.domain;

import java.util.List;
import java.util.Optional;
import site.timecapsulearchive.core.domain.capsule.entity.Capsule;
import site.timecapsulearchive.core.domain.capsule.entity.CapsuleType;
import site.timecapsulearchive.core.domain.capsule.entity.GroupCapsuleOpen;
import site.timecapsulearchive.core.domain.capsuleskin.entity.CapsuleSkin;
import site.timecapsulearchive.core.domain.member.entity.Member;

public class GroupCapsuleOpenFixture {
Expand All @@ -18,4 +21,16 @@ public static List<GroupCapsuleOpen> groupCapsuleOpens(Boolean isOpened, Capsule
).toList();
}

public static Optional<GroupCapsuleOpen> groupCapsuleOpen(int dataPrefix) {
Member member = MemberFixture.member(dataPrefix);
CapsuleSkin capsuleSkin = CapsuleSkinFixture.capsuleSkin(member);

return Optional.of(
GroupCapsuleOpen.createOf(
MemberFixture.member(dataPrefix),
CapsuleFixture.capsule(member, capsuleSkin, CapsuleType.GROUP),
Boolean.FALSE
)
);
}
}
Loading
Loading