Skip to content

Commit

Permalink
Merge pull request #493 from tukcomCD2024/feat/treasure_summary_B-#492
Browse files Browse the repository at this point in the history
feat : 보물 캡슐 요약 조회 기능 개발 및 보물 찾기 기능 응답 값 수정
  • Loading branch information
GaBaljaintheroom authored Jun 16, 2024
2 parents 84f03eb + 72e25dd commit f3d226e
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package site.timecapsulearchive.core.domain.capsule.generic_capsule.repository.capsule;

import java.util.List;
import java.util.Optional;
import org.locationtech.jts.geom.Polygon;
import site.timecapsulearchive.core.domain.capsule.entity.CapsuleType;
import site.timecapsulearchive.core.domain.capsule.generic_capsule.data.dto.NearbyARCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsule.generic_capsule.data.dto.NearbyCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto.TreasureCapsuleSummaryDto;

public interface CapsuleQueryRepository {

Expand All @@ -29,4 +31,6 @@ List<NearbyARCapsuleSummaryDto> findFriendsARCapsulesByCurrentLocation(
final List<Long> friendIds,
final Polygon mbr
);

Optional<TreasureCapsuleSummaryDto> findTreasureCapsuleSummary(final Long capsuleId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.springframework.stereotype.Repository;
import site.timecapsulearchive.core.domain.capsule.entity.CapsuleType;
import site.timecapsulearchive.core.domain.capsule.generic_capsule.data.dto.NearbyARCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsule.generic_capsule.data.dto.NearbyCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto.TreasureCapsuleSummaryDto;

@Repository
@RequiredArgsConstructor
Expand All @@ -33,6 +35,7 @@ public class CapsuleQueryRepositoryImpl implements CapsuleQueryRepository {
* @return 범위 내에 조회된 캡슐들의 요약 정보들을 반환한다.
* @see site.timecapsulearchive.core.global.geography.GeoTransformManager
*/
@Override
public List<NearbyARCapsuleSummaryDto> findARCapsuleSummaryDtosByCurrentLocationAndCapsuleType(
final Long memberId,
final Polygon mbr,
Expand Down Expand Up @@ -75,6 +78,7 @@ private BooleanExpression capsuleFilter(CapsuleType capsuleType, Long memberId)
* @param capsuleType 조회할 캡슐의 타입
* @return 범위 내에 조회된 캡슐들의 요약 정보들을 반환한다.
*/
@Override
public List<NearbyCapsuleSummaryDto> findCapsuleSummaryDtosByCurrentLocationAndCapsuleType(
final Long memberId,
final Polygon mbr,
Expand Down Expand Up @@ -103,6 +107,7 @@ public List<NearbyCapsuleSummaryDto> findCapsuleSummaryDtosByCurrentLocationAndC
* @param mbr 캡슐을 조회할 범위(최소사각형), <code>GeoTransformManager</code> 참조
* @return 범위 내에 조회된 캡슐들의 요약 정보들을 반환한다.
*/
@Override
public List<NearbyCapsuleSummaryDto> findFriendsCapsuleSummaryDtosByCurrentLocationAndCapsuleType(
final List<Long> friendIds,
final Polygon mbr
Expand Down Expand Up @@ -135,6 +140,7 @@ private BooleanExpression ST_Contains(Polygon mbr, ComparablePath<Point> point)
* @param mbr 캡슐을 조회할 범위(최소사각형), <code>GeoTransformManager</code> 참조
* @return 범위 내에 조회된 캡슐들의 요약 정보들을 반환한다.
*/
@Override
public List<NearbyARCapsuleSummaryDto> findFriendsARCapsulesByCurrentLocation(
final List<Long> friendIds,
final Polygon mbr
Expand All @@ -160,4 +166,23 @@ public List<NearbyARCapsuleSummaryDto> findFriendsARCapsulesByCurrentLocation(
.and(capsule.type.eq(CapsuleType.PUBLIC)))
.fetch();
}

@Override
public Optional<TreasureCapsuleSummaryDto> findTreasureCapsuleSummary(final Long capsuleId) {
return Optional.ofNullable(jpaQueryFactory
.select(
Projections.constructor(
TreasureCapsuleSummaryDto.class,
capsuleSkin.imageUrl,
capsule.dueDate,
capsule.address.fullRoadAddressName,
capsule.address.roadName
)
)
.from(capsule)
.join(capsule.capsuleSkin, capsuleSkin)
.where(capsule.id.eq(capsuleId))
.fetchOne()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.springframework.http.ResponseEntity;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.response.TreasureCapsuleOpenResponse;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.response.TreasureCapsuleSummaryResponse;
import site.timecapsulearchive.core.global.common.response.ApiSpec;
import site.timecapsulearchive.core.global.error.ErrorResponse;

Expand Down Expand Up @@ -36,11 +38,35 @@ public interface TreasureCapsuleApi {
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
)
})
ResponseEntity<ApiSpec<String>> openTreasureCapsule(
ResponseEntity<ApiSpec<TreasureCapsuleOpenResponse>> openTreasureCapsule(
Long memberId,

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

@Operation(
summary = "보물 캡슐 요약 조회",
description = "사용자는 보물 캡슐 정보를 요약 조회할 수 있다.",
security = {@SecurityRequirement(name = "user_token")},
tags = {"secret capsule"}
)
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "처리 완료"
),
@ApiResponse(
responseCode = "404",
description = "보물 캡슐 찾기 실패 예외",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))
)
})
ResponseEntity<ApiSpec<TreasureCapsuleSummaryResponse>> findTreasureCapsuleSummary(
Long memberId,

@Parameter(in = ParameterIn.PATH, description = "조회할 보물 캡슐 아이디", required = true)
Long capsuleId
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,67 @@
import lombok.RequiredArgsConstructor;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto.TreasureCapsuleOpenDto;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto.TreasureCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.response.TreasureCapsuleOpenResponse;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.response.TreasureCapsuleSummaryResponse;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.service.TreasureCapsuleService;
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("/treasure-capsules")
@RequiredArgsConstructor
public class TreasureCapsuleController implements TreasureCapsuleApi {

private final TreasureCapsuleService treasureCapsuleService;
private final S3PreSignedUrlManager s3PreSignedUrlManager;

@Override
@PostMapping("/{capsule_id}/open")
public ResponseEntity<ApiSpec<String>> openTreasureCapsule(
public ResponseEntity<ApiSpec<TreasureCapsuleOpenResponse>> openTreasureCapsule(
@AuthenticationPrincipal final Long memberId,
@PathVariable(value = "capsule_id") final Long capsuleId
) {
treasureCapsuleService.openTreasureCapsule(memberId, capsuleId);
final TreasureCapsuleOpenDto treasureCapsuleOpenDto = treasureCapsuleService.openTreasureCapsule(
memberId, capsuleId);

return ResponseEntity.accepted().body(
ApiSpec.success(
SuccessCode.SUCCESS,
TreasureCapsuleOpenResponse.createOf(
treasureCapsuleOpenDto,
s3PreSignedUrlManager::getS3PreSignedUrlForGet
)
)
);
}

@Override
@GetMapping(
value = "/{capsule_id}/summary",
produces = {"application/json"}
)
public ResponseEntity<ApiSpec<TreasureCapsuleSummaryResponse>> findTreasureCapsuleSummary(
@AuthenticationPrincipal final Long memberId,
@PathVariable("capsule_id") final Long capsuleId
) {
final TreasureCapsuleSummaryDto treasureCapsuleSummaryDto = treasureCapsuleService.findTreasureCapsuleSummary(
capsuleId);

return ResponseEntity.ok(
ApiSpec.empty(
SuccessCode.SUCCESS
ApiSpec.success(
SuccessCode.SUCCESS,
TreasureCapsuleSummaryResponse.createOf(
treasureCapsuleSummaryDto,
s3PreSignedUrlManager::getS3PreSignedUrlForGet
)
)
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto;

import java.util.function.Function;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.response.TreasureCapsuleOpenResponse;

public record TreasureCapsuleOpenDto(
String treasureImageUrl
) {

public TreasureCapsuleOpenResponse toResponse(final Function<String, String> s3PreSignedUrlForGet) {
String imageUrl = s3PreSignedUrlForGet.apply(treasureImageUrl);
return new TreasureCapsuleOpenResponse(imageUrl);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto;

import java.time.ZonedDateTime;
import java.util.function.Function;
import lombok.Builder;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.response.TreasureCapsuleSummaryResponse;

@Builder
public record TreasureCapsuleSummaryDto(
String skinUrl,
ZonedDateTime dueDate,
String address,
String roadName

) {

public TreasureCapsuleSummaryResponse toResponse(final Function<String, String> s3PreSignedUrlForGet) {
final String treasureSkinUrl = s3PreSignedUrlForGet.apply(skinUrl);

return TreasureCapsuleSummaryResponse.builder()
.skinUrl(treasureSkinUrl)
.dueDate(dueDate)
.address(address)
.roadName(roadName)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.function.Function;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto.TreasureCapsuleOpenDto;

@Schema(name = "보물 캡슐 개봉 응답")
public record TreasureCapsuleOpenResponse(
@Schema(name = "보물 이미지 URL")
String treasureImageUrl
) {

public static TreasureCapsuleOpenResponse createOf(
final TreasureCapsuleOpenDto dto,
final Function<String, String> s3PreSignedUrlForGet
) {
return dto.toResponse(s3PreSignedUrlForGet);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.ZonedDateTime;
import java.util.function.Function;
import lombok.Builder;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto.TreasureCapsuleSummaryDto;

@Schema(description = "보물 캡슐 요약 정보")
@Builder
public record TreasureCapsuleSummaryResponse(

@Schema(description = "보물 캡슐 스킨 url")
String skinUrl,

@Schema(description = "개봉일")
ZonedDateTime dueDate,

@Schema(description = "캡슐 생성 주소")
String address,

@Schema(description = "캡슐 생성 도로 이름")
String roadName

) {

public static TreasureCapsuleSummaryResponse createOf(
final TreasureCapsuleSummaryDto dto,
final Function<String, String> s3PreSignedUrlForGet
) {
return dto.toResponse(s3PreSignedUrlForGet);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import site.timecapsulearchive.core.domain.capsule.entity.Capsule;
import site.timecapsulearchive.core.domain.capsule.entity.Image;
import site.timecapsulearchive.core.domain.capsule.exception.CapsuleNotFondException;
import site.timecapsulearchive.core.domain.capsule.generic_capsule.repository.capsule.CapsuleRepository;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto.TreasureCapsuleOpenDto;
import site.timecapsulearchive.core.domain.capsule.treasure_capsule.data.dto.TreasureCapsuleSummaryDto;
import site.timecapsulearchive.core.domain.capsuleskin.entity.CapsuleSkin;
import site.timecapsulearchive.core.domain.capsuleskin.repository.CapsuleSkinRepository;
import site.timecapsulearchive.core.domain.member.entity.Member;
Expand All @@ -24,7 +27,7 @@ public class TreasureCapsuleService {
private final SocialNotificationManager socialNotificationManager;
private final TransactionTemplate transactionTemplate;

public void openTreasureCapsule(final Long memberId, final Long capsuleId) {
public TreasureCapsuleOpenDto openTreasureCapsule(final Long memberId, final Long capsuleId) {
final Member member = memberRepository.findMemberById(memberId).orElseThrow(
MemberNotFoundException::new);

Expand All @@ -48,5 +51,15 @@ public void openTreasureCapsule(final Long memberId, final Long capsuleId) {
// 알림 전송
socialNotificationManager.sendTreasureCaptureMessage(memberId, member.getNickname(),
treasureImageUrl);

return new TreasureCapsuleOpenDto(treasureImageUrl);
}

@Transactional(readOnly = true)
public TreasureCapsuleSummaryDto findTreasureCapsuleSummary(
final Long capsuleId
) {
return capsuleRepository.findTreasureCapsuleSummary(capsuleId)
.orElseThrow(CapsuleNotFondException::new);
}
}

0 comments on commit f3d226e

Please sign in to comment.