diff --git a/src/main/java/com/acquirex/approval/CardAcquisitionController.java b/src/main/java/com/acquirex/approval/CardAcquisitionController.java new file mode 100644 index 0000000..8d2e92b --- /dev/null +++ b/src/main/java/com/acquirex/approval/CardAcquisitionController.java @@ -0,0 +1,95 @@ +package com.acquirex.approval; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 카드 매입 승인 REST 컨트롤러. + * Spring Boot 3 전환: javax.validation → jakarta.validation + */ +@RestController +@RequestMapping("/api/v1/acquisitions") +public class CardAcquisitionController { + + private final CardAcquisitionService service; + + public CardAcquisitionController(CardAcquisitionService service) { + this.service = service; + } + + @PostMapping + public ResponseEntity createAcquisition( + @Valid @RequestBody CreateAcquisitionRequest request) { + CardAcquisition acquisition = service.createAcquisition( + request.cardNumber(), request.merchantId(), request.amount()); + return ResponseEntity.status(HttpStatus.CREATED) + .body(CardAcquisitionResponse.from(acquisition)); + } + + @GetMapping("/{id}") + public ResponseEntity getAcquisition(@PathVariable Long id) { + return service.findById(id) + .map(CardAcquisitionResponse::from) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } + + @GetMapping + public ResponseEntity> getAllAcquisitions( + @RequestParam(required = false) CardAcquisition.ApprovalStatus status) { + List acquisitions = (status != null) + ? service.findByStatus(status) : service.findAll(); + return ResponseEntity.ok(acquisitions.stream().map(CardAcquisitionResponse::from).toList()); + } + + @PostMapping("/{id}/approve") + public ResponseEntity approveAcquisition( + @PathVariable Long id, @RequestBody ApproveRequest request) { + return ResponseEntity.ok(CardAcquisitionResponse.from(service.approve(id, request.approver()))); + } + + @PostMapping("/{id}/reject") + public ResponseEntity rejectAcquisition( + @PathVariable Long id, @RequestBody RejectRequest request) { + return ResponseEntity.ok(CardAcquisitionResponse.from(service.reject(id, request.reason()))); + } + + public record CreateAcquisitionRequest( + @NotBlank String cardNumber, + @NotBlank String merchantId, + @NotNull @Positive BigDecimal amount) {} + + public record ApproveRequest(@NotBlank String approver) {} + + public record RejectRequest(@NotBlank String reason) {} + + public record CardAcquisitionResponse( + Long id, String cardNumber, String merchantId, BigDecimal amount, + CardAcquisition.ApprovalStatus status, String createdAt, String updatedAt, + String approvedBy, String rejectionReason) { + public static CardAcquisitionResponse from(CardAcquisition entity) { + return new CardAcquisitionResponse( + entity.getId(), + maskCardNumber(entity.getCardNumber()), + entity.getMerchantId(), + entity.getAmount(), + entity.getStatus(), + entity.getCreatedAt().toString(), + entity.getUpdatedAt() != null ? entity.getUpdatedAt().toString() : null, + entity.getApprovedBy(), + entity.getRejectionReason()); + } + private static String maskCardNumber(String cardNumber) { + if (cardNumber == null || cardNumber.length() < 8) return "****"; + return cardNumber.substring(0, 4) + "****" + cardNumber.substring(cardNumber.length() - 4); + } + } +}