Skip to content

Commit

Permalink
Merge pull request #475 from tukcomCD2024/fix/group_capsule_open-B-#470
Browse files Browse the repository at this point in the history
fix : 그룹 캡슐 개봉 로직 수정
  • Loading branch information
seokho-1116 authored Jun 7, 2024
2 parents 7e793f8 + f9839b4 commit eada8af
Show file tree
Hide file tree
Showing 17 changed files with 480 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,20 @@
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.locationtech.jts.geom.Point;
import site.timecapsulearchive.core.domain.capsule.exception.GroupCapsuleOpenNotFoundException;
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.global.common.supplier.ZonedDateTimeSupplier;
import site.timecapsulearchive.core.global.entity.BaseEntity;

@Entity
Expand Down Expand Up @@ -60,13 +64,13 @@ public class Capsule extends BaseEntity {
private Address address;

@OneToMany(mappedBy = "capsule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Image> images;
private List<Image> images = new ArrayList<>();

@OneToMany(mappedBy = "capsule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Video> videos;
private List<Video> videos = new ArrayList<>();

@OneToMany(mappedBy = "capsule", cascade = CascadeType.ALL, orphanRemoval = true)
private List<GroupCapsuleOpen> groupCapsuleOpens;
private List<GroupCapsuleOpen> groupCapsuleOpens = new ArrayList<>();

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "group_id")
Expand Down Expand Up @@ -109,6 +113,33 @@ public boolean isNotCapsuleOpened() {
return false;
}

return dueDate.isAfter(ZonedDateTime.now());
return dueDate.isAfter(ZonedDateTimeSupplier.utc().get());
}

public void open() {
this.isOpened = Boolean.TRUE;
}

public boolean isTimeCapsule() {
return dueDate != null;
}

public boolean canOpen() {
return dueDate == null || dueDate.isBefore(ZonedDateTimeSupplier.utc().get());
}

public boolean isAllGroupMemberOpened(Long memberId, Long capsuleId) {
if (groupCapsuleOpens.isEmpty()) {
throw new GroupCapsuleOpenNotFoundException();
}

return groupCapsuleOpens.stream()
.allMatch(groupCapsuleOpen -> {
if (groupCapsuleOpen.matched(capsuleId, memberId)) {
groupCapsuleOpen.open();
}

return groupCapsuleOpen.getIsOpened();
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,8 @@ public static GroupCapsuleOpen createOf(Member member, Capsule capsule, Boolean
public void open() {
this.isOpened = Boolean.TRUE;
}

public boolean matched(Long capsuleId, Long memberId) {
return capsule.getId().equals(capsuleId) && member.getId().equals(memberId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,14 @@ int updateIsOpenedTrue(
@Param("memberId") Long memberId,
@Param("capsuleId") Long capsuleId
);

@Query("select c "
+ "from Capsule c "
+ "join fetch c.groupCapsuleOpens "
+ "where c.id = :capsuleId and c.member.id = :memberId "
+ "and c.type = 'GROUP' and c.group.id is not null and c.isOpened = false")
Optional<Capsule> findNotOpenedGroupCapsuleByMemberIdAndCapsuleId(
@Param("memberId") Long memberId,
@Param("capsuleId") Long capsuleId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
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.MyGroupCapsuleSliceResponse;
Expand Down Expand Up @@ -172,7 +173,7 @@ ResponseEntity<ApiSpec<MyGroupCapsuleSliceResponse>> getMyGroupCapsules(
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
)
})
ResponseEntity<ApiSpec<String>> openCapsule(
ResponseEntity<ApiSpec<GroupCapsuleOpenStateResponse>> openCapsule(
Long memberId,

@Parameter(in = ParameterIn.PATH, description = "개봉할 그룹 캡슐 아이디", required = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
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.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.MyGroupCapsuleSliceResponse;
Expand Down Expand Up @@ -139,13 +141,19 @@ public ResponseEntity<ApiSpec<MyGroupCapsuleSliceResponse>> getMyGroupCapsules(

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

return ResponseEntity.ok(ApiSpec.empty(SuccessCode.SUCCESS));
return ResponseEntity.ok(
ApiSpec.success(
SuccessCode.SUCCESS,
groupCapsuleOpenStateDto.toResponse()
)
);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto;

import lombok.Getter;

@Getter
public enum CapsuleOpenStatus {
OPEN("캡슐이 개봉되었습니다."), NOT_OPEN("캡슐이 개봉되지 않았습니다.");

private final String statusMessage;

CapsuleOpenStatus(String statusMessage) {
this.statusMessage = statusMessage;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto;

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

public record GroupCapsuleOpenStateDto(
CapsuleOpenStatus capsuleOpenStatus
) {

public static GroupCapsuleOpenStateDto opened() {
return new GroupCapsuleOpenStateDto(CapsuleOpenStatus.OPEN);
}

public static GroupCapsuleOpenStateDto notOpened() {
return new GroupCapsuleOpenStateDto(CapsuleOpenStatus.NOT_OPEN);
}

public GroupCapsuleOpenStateResponse toResponse() {
return new GroupCapsuleOpenStateResponse(
capsuleOpenStatus,
capsuleOpenStatus.getStatusMessage()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package site.timecapsulearchive.core.domain.capsule.group_capsule.data.response;

import site.timecapsulearchive.core.domain.capsule.group_capsule.data.dto.CapsuleOpenStatus;

public record GroupCapsuleOpenStateResponse(
CapsuleOpenStatus capsuleOpenStatus,
String statusMessage
) {

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
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 @@ -20,7 +17,6 @@
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 @@ -32,8 +28,10 @@ INSERT INTO group_capsule_open (
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
boolean isOpened = capsule.getDueDate() == null;

ps.setNull(1, Types.BIGINT);
ps.setBoolean(2, false);
ps.setBoolean(2, isOpened);
ps.setLong(3, groupMemberIds.get(i));
ps.setLong(4, capsule.getId());
ps.setTimestamp(5, Timestamp.valueOf(ZonedDateTime.now().toLocalDateTime()));
Expand All @@ -47,12 +45,4 @@ 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,12 +1,9 @@
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,24 +2,20 @@

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.data.dto.GroupCapsuleOpenStateDto;
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 @@ -32,8 +28,6 @@ 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 @@ -103,21 +97,27 @@ public Slice<MyGroupCapsuleDto> findMyGroupCapsuleSlice(
* @param capsuleId 개봉할 캡슐 아이디
*/
@Transactional
public void openGroupCapsule(final Long memberId, final Long capsuleId) {
GroupCapsuleOpen groupCapsuleOpen = groupCapsuleOpenRepository.findByMemberIdAndCapsuleId(
memberId,
public GroupCapsuleOpenStateDto openGroupCapsule(final Long memberId, final Long capsuleId) {
Capsule groupCapsule = capsuleRepository.findNotOpenedGroupCapsuleByMemberIdAndCapsuleId(memberId,
capsuleId)
.orElseThrow(GroupCapsuleOpenNotFoundException::new);
groupCapsuleOpen.open();
.orElseThrow(CapsuleNotFondException::new);

List<Boolean> capsuleOpens = groupCapsuleOpenQueryRepository.findIsOpenedByCapsuleId(
capsuleId);
if (!groupCapsule.canOpen()) {
return GroupCapsuleOpenStateDto.notOpened();
}

boolean allGroupMemberOpened = capsuleOpens.stream()
.allMatch(isOpened -> isOpened.equals(Boolean.TRUE));
if (allGroupMemberOpened) {
capsuleRepository.updateIsOpenedTrue(memberId, capsuleId);
if (!groupCapsule.isTimeCapsule()) {
groupCapsule.open();
return GroupCapsuleOpenStateDto.opened();
}

boolean allGroupMemberOpened = groupCapsule.isAllGroupMemberOpened(memberId, capsuleId);
if (!allGroupMemberOpened) {
return GroupCapsuleOpenStateDto.notOpened();
}

groupCapsule.open();
return GroupCapsuleOpenStateDto.opened();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package site.timecapsulearchive.core.global.common.supplier;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.function.Supplier;

public class ZonedDateTimeSupplier {

private static final String TIME_ZONE = "UTC";

public static Supplier<ZonedDateTime> utc() {
return () -> ZonedDateTime.now(ZoneId.of(TIME_ZONE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public enum ErrorCode {
CAPSULE_NOT_FOUND_ERROR(404, "CAPSULE-001", "캡슐을 찾지 못하였습니다."),
NO_CAPSULE_AUTHORITY_ERROR(403, "CAPSULE-002", "캡슐에 접근 권한이 없습니다."),
GROUP_CAPSULE_OPEN_NOT_FOUND_ERROR(404, "CAPSULE-003", "그룹 캡슐 개봉상태를 찾을 수 없습니다."),
CAPSULE_OPEN_STATE_ERROR(400, "CAPSULE-004", "캡슐을 개봉할 수 없는 상태입니다."),

//friend
FRIEND_NOT_FOUND_ERROR(404, "FRIEND-001", "친구를 찾지 못하였습니다"),
Expand Down
Loading

0 comments on commit eada8af

Please sign in to comment.