Skip to content
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
2 changes: 2 additions & 0 deletions http/shipping.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
### 대여 Id로 운송장 번호와 발송일 조회
GET http://localhost:8080/api/v1/shipping/1
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ok.cherry.global.swagger.shipping;

import org.springframework.http.ProblemDetail;
import org.springframework.http.ResponseEntity;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import ok.cherry.shipping.application.response.TrackingNumberResponse;

@Tag(name = "Shipping")
public interface ShippingControllerDoc {

@Operation(method = "GET", summary = "운송장 번호 및 발송일 조회", description = "대여 Id로 배송 정보의 운송장 번호와 발송일을 조회합니다")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "운송장 번호 및 발송일 조회 성공",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = TrackingNumberResponse.class))),
@ApiResponse(responseCode = "404", description = "운송장 번호 및 발송일 조회 실패 - 배송 정보를 찾을 수 없음",
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ProblemDetail.class)))
})
ResponseEntity<TrackingNumberResponse> getTrackingNumber(
@Parameter(description = "조회할 대여 Id") Long rentalId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import ok.cherry.member.domain.Member;
import ok.cherry.rental.domain.Rental;
import ok.cherry.shipping.application.command.CreateShippingCommand;
import ok.cherry.shipping.application.response.TrackingNumberResponse;
import ok.cherry.shipping.domain.Shipping;
import ok.cherry.shipping.domain.status.ShippingStatus;
import ok.cherry.shipping.domain.type.Direction;
Expand Down Expand Up @@ -44,6 +45,12 @@ public Shipping createShipping(
return shippingRepository.save(shipping);
}

public TrackingNumberResponse getTrackingNumber(Long rentalId) {
Shipping shipping = shippingRepository.findByRentalIdAndDirection(rentalId, Direction.OUTBOUND)
.orElseThrow(() -> new BusinessException(ShippingError.SHIPPING_NOT_FOUND));
return TrackingNumberResponse.of(shipping.getTrackingNumber(), shipping.getDetail().getStartAt());
}

@Transactional
public void startShipping(Long shippingId) {
Shipping shipping = shippingRepository.findById(shippingId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ok.cherry.shipping.application.response;

import java.time.LocalDate;
import java.time.LocalDateTime;

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

@Schema(description = "운송장 번호 및 발송일 조회 응답 DTO")
public record TrackingNumberResponse(

@Schema(description = "운송장 번호", example = "25090213363012345678")
String trackingNumber,

@Schema(description = "발송일자", example = "2025-09-02")
LocalDate startAt
) {
public static TrackingNumberResponse of(String trackingNumber, LocalDateTime startAt) {
return new TrackingNumberResponse(trackingNumber, startAt.toLocalDate());
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
package ok.cherry.shipping.presentation;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import ok.cherry.global.swagger.shipping.ShippingControllerDoc;
import ok.cherry.shipping.application.ShippingService;
import ok.cherry.shipping.application.response.TrackingNumberResponse;

@RestController
@RequestMapping("/api/v1/shipping")
@RequiredArgsConstructor
public class ShippingController {
public class ShippingController implements ShippingControllerDoc {

private final ShippingService shippingService;

@GetMapping("/{rentalId}")
public ResponseEntity<TrackingNumberResponse> getTrackingNumber(@PathVariable Long rentalId) {
TrackingNumberResponse response = shippingService.getTrackingNumber(rentalId);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.transaction.annotation.Transactional;

import jakarta.persistence.EntityManager;
import ok.cherry.global.exception.error.BusinessException;
import ok.cherry.global.exception.error.DomainException;
import ok.cherry.member.MemberBuilder;
import ok.cherry.member.domain.Member;
Expand All @@ -22,6 +23,7 @@
import ok.cherry.rental.infrastructure.RentalRepository;
import ok.cherry.shipping.ShippingBuilder;
import ok.cherry.shipping.application.command.CreateShippingCommand;
import ok.cherry.shipping.application.response.TrackingNumberResponse;
import ok.cherry.shipping.domain.Address;
import ok.cherry.shipping.domain.Shipping;
import ok.cherry.shipping.domain.status.ShippingStatus;
Expand Down Expand Up @@ -353,6 +355,47 @@ void completeShipping_notInDelivery() {
.hasMessage(ShippingError.NOT_IN_DELIVERY.getMessage());
}

@Test
@DisplayName("대여 Id로 운송장 번호와 발송일 조회에 성공한다")
void getTrackingNumber_success() {
// given
Member member = memberRepository.save(MemberBuilder.create());
Product product = productRepository.save(ProductBuilder.create());
Rental rental = rentalRepository.save(
RentalBuilder.builder()
.withMember(member)
.withProduct(product)
.build()
);
Shipping shipping = shippingRepository.save(
ShippingBuilder.builder()
.withRental(rental)
.withDirection(Direction.OUTBOUND)
.build()
);
shipping.startShipping();

// when
TrackingNumberResponse response = shippingService.getTrackingNumber(rental.getId());

// then
Shipping savedShipping = shippingRepository.findById(shipping.getId()).orElseThrow();
assertThat(response.trackingNumber()).isEqualTo(savedShipping.getTrackingNumber());
assertThat(response.startAt()).isEqualTo(savedShipping.getDetail().getStartAt().toLocalDate());
}

@Test
@DisplayName("대여 Id로 OutBound 배송 정보를 찾을 수 없어 예외가 발생한다")
void getTrackingNumber_notFoundShipping() {
// given
Long nonExistRentalId = 999L;

// when & then
assertThatThrownBy(() -> shippingService.getTrackingNumber(nonExistRentalId))
.isInstanceOf(BusinessException.class)
.hasMessage(ShippingError.SHIPPING_NOT_FOUND.getMessage());
}

private void flushAndClear() {
entityManager.flush();
entityManager.clear();
Expand Down
Loading