Compare commits
8 commits
main
...
forge/ACX-
| Author | SHA1 | Date | |
|---|---|---|---|
| b96b455f65 | |||
| 1e65c277a5 | |||
| 4146f9ec3b | |||
| a098d222b2 | |||
| 33b519a72c | |||
| 6bb9a58a18 | |||
| 56533bbd44 | |||
| 435fc528ac |
8 changed files with 611 additions and 0 deletions
57
pom.xml
Normal file
57
pom.xml
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.2.5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>com.acquirex</groupId>
|
||||
<artifactId>proj-acquirex</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<name>proj-acquirex</name>
|
||||
<description>카드 매입 승인 시스템 - Spring Boot 3</description>
|
||||
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
11
src/main/java/com/acquirex/AcquirexApplication.java
Normal file
11
src/main/java/com/acquirex/AcquirexApplication.java
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package com.acquirex;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class AcquirexApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AcquirexApplication.class, args);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.acquirex.controller;
|
||||
|
||||
import com.acquirex.dto.ApprovalRequestDto;
|
||||
import com.acquirex.dto.ApprovalResponseDto;
|
||||
import com.acquirex.service.ApprovalService;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/v1/approvals")
|
||||
public class ApprovalController {
|
||||
|
||||
private final ApprovalService approvalService;
|
||||
|
||||
public ApprovalController(ApprovalService approvalService) {
|
||||
this.approvalService = approvalService;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<ApprovalResponseDto> requestApproval(@Valid @RequestBody ApprovalRequestDto request) {
|
||||
ApprovalResponseDto response = approvalService.requestApproval(request);
|
||||
return ResponseEntity.status(HttpStatus.CREATED).body(response);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
public ResponseEntity<ApprovalResponseDto> approveRequest(@PathVariable Long id) {
|
||||
ApprovalResponseDto response = approvalService.approveRequest(id);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reject")
|
||||
public ResponseEntity<ApprovalResponseDto> rejectRequest(@PathVariable Long id, @RequestBody Map<String, String> body) {
|
||||
String reason = body.getOrDefault("reason", "사유 미지정");
|
||||
ApprovalResponseDto response = approvalService.rejectRequest(id, reason);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ResponseEntity<ApprovalResponseDto> getApprovalStatus(@PathVariable Long id) {
|
||||
ApprovalResponseDto response = approvalService.getApprovalStatus(id);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
}
|
||||
131
src/main/java/com/acquirex/domain/DomainClasses.java
Normal file
131
src/main/java/com/acquirex/domain/DomainClasses.java
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package com.acquirex.domain;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// ===== AcquisitionCard Entity =====
|
||||
@Entity
|
||||
@Table(name = "acquisition_cards")
|
||||
class AcquisitionCard {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "card_number", length = 20)
|
||||
private String cardNumber;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "card_company_code", length = 10)
|
||||
private String cardCompanyCode;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "merchant_number", length = 15)
|
||||
private String merchantNumber;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "status", length = 20)
|
||||
private CardStatus status = CardStatus.PENDING;
|
||||
|
||||
@Column(name = "created_at")
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "updated_at")
|
||||
private LocalDateTime updatedAt;
|
||||
|
||||
public enum CardStatus { PENDING, APPROVED, REJECTED, CANCELLED }
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createdAt = LocalDateTime.now();
|
||||
updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
updatedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCardNumber() { return cardNumber; }
|
||||
public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; }
|
||||
public String getCardCompanyCode() { return cardCompanyCode; }
|
||||
public void setCardCompanyCode(String cardCompanyCode) { this.cardCompanyCode = cardCompanyCode; }
|
||||
public String getMerchantNumber() { return merchantNumber; }
|
||||
public void setMerchantNumber(String merchantNumber) { this.merchantNumber = merchantNumber; }
|
||||
public CardStatus getStatus() { return status; }
|
||||
public void setStatus(CardStatus status) { this.status = status; }
|
||||
public LocalDateTime getCreatedAt() { return createdAt; }
|
||||
public LocalDateTime getUpdatedAt() { return updatedAt; }
|
||||
}
|
||||
|
||||
// ===== ApprovalRequest Entity =====
|
||||
@Entity
|
||||
@Table(name = "approval_requests")
|
||||
class ApprovalRequest {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "card_id")
|
||||
private Long cardId;
|
||||
|
||||
@NotBlank
|
||||
@Column(name = "transaction_id", length = 30, unique = true)
|
||||
private String transactionId;
|
||||
|
||||
@NotNull
|
||||
@Column(name = "amount", precision = 15, scale = 2)
|
||||
private BigDecimal amount;
|
||||
|
||||
@Column(name = "vat", precision = 15, scale = 2)
|
||||
private BigDecimal vat;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "approval_status", length = 20)
|
||||
private ApprovalStatus approvalStatus = ApprovalStatus.REQUESTED;
|
||||
|
||||
@Column(name = "approval_number", length = 20)
|
||||
private String approvalNumber;
|
||||
|
||||
@Column(name = "requested_at")
|
||||
private LocalDateTime requestedAt;
|
||||
|
||||
@Column(name = "approved_at")
|
||||
private LocalDateTime approvedAt;
|
||||
|
||||
@Column(name = "rejection_reason", length = 200)
|
||||
private String rejectionReason;
|
||||
|
||||
public enum ApprovalStatus { REQUESTED, APPROVED, REJECTED, CANCELLED }
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
requestedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getCardId() { return cardId; }
|
||||
public void setCardId(Long cardId) { this.cardId = cardId; }
|
||||
public String getTransactionId() { return transactionId; }
|
||||
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
|
||||
public BigDecimal getAmount() { return amount; }
|
||||
public void setAmount(BigDecimal amount) { this.amount = amount; }
|
||||
public BigDecimal getVat() { return vat; }
|
||||
public void setVat(BigDecimal vat) { this.vat = vat; }
|
||||
public ApprovalStatus getApprovalStatus() { return approvalStatus; }
|
||||
public void setApprovalStatus(ApprovalStatus approvalStatus) { this.approvalStatus = approvalStatus; }
|
||||
public String getApprovalNumber() { return approvalNumber; }
|
||||
public void setApprovalNumber(String approvalNumber) { this.approvalNumber = approvalNumber; }
|
||||
public LocalDateTime getRequestedAt() { return requestedAt; }
|
||||
public LocalDateTime getApprovedAt() { return approvedAt; }
|
||||
public void setApprovedAt(LocalDateTime approvedAt) { this.approvedAt = approvedAt; }
|
||||
public String getRejectionReason() { return rejectionReason; }
|
||||
public void setRejectionReason(String rejectionReason) { this.rejectionReason = rejectionReason; }
|
||||
}
|
||||
84
src/main/java/com/acquirex/dto/Dtos.java
Normal file
84
src/main/java/com/acquirex/dto/Dtos.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package com.acquirex.dto;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
// ===== ApprovalRequestDto =====
|
||||
class ApprovalRequestDto {
|
||||
@NotBlank(message = "카드 번호는 필수입니다")
|
||||
@Size(max = 20)
|
||||
private String cardNumber;
|
||||
|
||||
@NotBlank(message = "카드사 코드는 필수입니다")
|
||||
@Size(max = 10)
|
||||
private String cardCompanyCode;
|
||||
|
||||
@NotBlank(message = "가맹점 번호는 필수입니다")
|
||||
@Size(max = 15)
|
||||
private String merchantNumber;
|
||||
|
||||
@NotNull(message = "거래 금액은 필수입니다")
|
||||
@DecimalMin(value = "0.01")
|
||||
@Digits(integer = 13, fraction = 2)
|
||||
private BigDecimal amount;
|
||||
|
||||
@DecimalMin(value = "0.00")
|
||||
@Digits(integer = 13, fraction = 2)
|
||||
private BigDecimal vat;
|
||||
|
||||
public String getCardNumber() { return cardNumber; }
|
||||
public void setCardNumber(String cardNumber) { this.cardNumber = cardNumber; }
|
||||
public String getCardCompanyCode() { return cardCompanyCode; }
|
||||
public void setCardCompanyCode(String cardCompanyCode) { this.cardCompanyCode = cardCompanyCode; }
|
||||
public String getMerchantNumber() { return merchantNumber; }
|
||||
public void setMerchantNumber(String merchantNumber) { this.merchantNumber = merchantNumber; }
|
||||
public BigDecimal getAmount() { return amount; }
|
||||
public void setAmount(BigDecimal amount) { this.amount = amount; }
|
||||
public BigDecimal getVat() { return vat; }
|
||||
public void setVat(BigDecimal vat) { this.vat = vat; }
|
||||
}
|
||||
|
||||
// ===== ApprovalResponseDto =====
|
||||
class ApprovalResponseDto {
|
||||
private Long approvalId;
|
||||
private String transactionId;
|
||||
private String approvalNumber;
|
||||
private String status;
|
||||
private BigDecimal amount;
|
||||
private BigDecimal vat;
|
||||
private LocalDateTime approvedAt;
|
||||
private String message;
|
||||
|
||||
public ApprovalResponseDto() {}
|
||||
|
||||
public ApprovalResponseDto(Long approvalId, String transactionId, String approvalNumber,
|
||||
String status, BigDecimal amount, BigDecimal vat,
|
||||
LocalDateTime approvedAt, String message) {
|
||||
this.approvalId = approvalId;
|
||||
this.transactionId = transactionId;
|
||||
this.approvalNumber = approvalNumber;
|
||||
this.status = status;
|
||||
this.amount = amount;
|
||||
this.vat = vat;
|
||||
this.approvedAt = approvedAt;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Long getApprovalId() { return approvalId; }
|
||||
public void setApprovalId(Long approvalId) { this.approvalId = approvalId; }
|
||||
public String getTransactionId() { return transactionId; }
|
||||
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
|
||||
public String getApprovalNumber() { return approvalNumber; }
|
||||
public void setApprovalNumber(String approvalNumber) { this.approvalNumber = approvalNumber; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public BigDecimal getAmount() { return amount; }
|
||||
public void setAmount(BigDecimal amount) { this.amount = amount; }
|
||||
public BigDecimal getVat() { return vat; }
|
||||
public void setVat(BigDecimal vat) { this.vat = vat; }
|
||||
public LocalDateTime getApprovedAt() { return approvedAt; }
|
||||
public void setApprovedAt(LocalDateTime approvedAt) { this.approvedAt = approvedAt; }
|
||||
public String getMessage() { return message; }
|
||||
public void setMessage(String message) { this.message = message; }
|
||||
}
|
||||
21
src/main/java/com/acquirex/repository/Repositories.java
Normal file
21
src/main/java/com/acquirex/repository/Repositories.java
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package com.acquirex.repository;
|
||||
|
||||
import com.acquirex.domain.AcquisitionCard;
|
||||
import com.acquirex.domain.ApprovalRequest;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
interface AcquisitionCardRepository extends JpaRepository<AcquisitionCard, Long> {
|
||||
Optional<AcquisitionCard> findByCardNumberAndCardCompanyCode(String cardNumber, String cardCompanyCode);
|
||||
Optional<AcquisitionCard> findByMerchantNumber(String merchantNumber);
|
||||
}
|
||||
|
||||
@Repository
|
||||
interface ApprovalRequestRepository extends JpaRepository<ApprovalRequest, Long> {
|
||||
Optional<ApprovalRequest> findByTransactionId(String transactionId);
|
||||
List<ApprovalRequest> findByCardId(Long cardId);
|
||||
List<ApprovalRequest> findByApprovalStatus(ApprovalRequest.ApprovalStatus status);
|
||||
}
|
||||
110
src/main/java/com/acquirex/service/ApprovalService.java
Normal file
110
src/main/java/com/acquirex/service/ApprovalService.java
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
package com.acquirex.service;
|
||||
|
||||
import com.acquirex.domain.AcquisitionCard;
|
||||
import com.acquirex.domain.ApprovalRequest;
|
||||
import com.acquirex.dto.ApprovalRequestDto;
|
||||
import com.acquirex.dto.ApprovalResponseDto;
|
||||
import com.acquirex.repository.AcquisitionCardRepository;
|
||||
import com.acquirex.repository.ApprovalRequestRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class ApprovalService {
|
||||
|
||||
private final ApprovalRequestRepository approvalRequestRepository;
|
||||
private final AcquisitionCardRepository acquisitionCardRepository;
|
||||
|
||||
public ApprovalService(ApprovalRequestRepository approvalRequestRepository,
|
||||
AcquisitionCardRepository acquisitionCardRepository) {
|
||||
this.approvalRequestRepository = approvalRequestRepository;
|
||||
this.acquisitionCardRepository = acquisitionCardRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ApprovalResponseDto requestApproval(ApprovalRequestDto dto) {
|
||||
String transactionId = generateTransactionId();
|
||||
|
||||
AcquisitionCard card = acquisitionCardRepository
|
||||
.findByCardNumberAndCardCompanyCode(dto.getCardNumber(), dto.getCardCompanyCode())
|
||||
.orElseGet(() -> createNewCard(dto));
|
||||
|
||||
ApprovalRequest request = new ApprovalRequest();
|
||||
request.setCardId(card.getId());
|
||||
request.setTransactionId(transactionId);
|
||||
request.setAmount(dto.getAmount());
|
||||
request.setVat(dto.getVat());
|
||||
request.setApprovalStatus(ApprovalRequest.ApprovalStatus.REQUESTED);
|
||||
|
||||
ApprovalRequest saved = approvalRequestRepository.save(request);
|
||||
return buildApprovalResponse(saved, "매입 승인 요청이 완료되었습니다");
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ApprovalResponseDto approveRequest(Long requestId) {
|
||||
ApprovalRequest request = approvalRequestRepository.findById(requestId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("승인 요청을 찾을 수 없습니다: " + requestId));
|
||||
|
||||
if (request.getApprovalStatus() != ApprovalRequest.ApprovalStatus.REQUESTED) {
|
||||
throw new IllegalStateException("이미 처리된 요청입니다");
|
||||
}
|
||||
|
||||
request.setApprovalNumber(generateApprovalNumber());
|
||||
request.setApprovalStatus(ApprovalRequest.ApprovalStatus.APPROVED);
|
||||
request.setApprovedAt(LocalDateTime.now());
|
||||
|
||||
ApprovalRequest saved = approvalRequestRepository.save(request);
|
||||
return buildApprovalResponse(saved, "매입 승인이 완료되었습니다");
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public ApprovalResponseDto rejectRequest(Long requestId, String reason) {
|
||||
ApprovalRequest request = approvalRequestRepository.findById(requestId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("승인 요청을 찾을 수 없습니다: " + requestId));
|
||||
|
||||
if (request.getApprovalStatus() != ApprovalRequest.ApprovalStatus.REQUESTED) {
|
||||
throw new IllegalStateException("이미 처리된 요청입니다");
|
||||
}
|
||||
|
||||
request.setApprovalStatus(ApprovalRequest.ApprovalStatus.REJECTED);
|
||||
request.setRejectionReason(reason);
|
||||
|
||||
ApprovalRequest saved = approvalRequestRepository.save(request);
|
||||
return buildApprovalResponse(saved, "매입 승인이 거절되었습니다");
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public ApprovalResponseDto getApprovalStatus(Long requestId) {
|
||||
ApprovalRequest request = approvalRequestRepository.findById(requestId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("승인 요청을 찾을 수 없습니다: " + requestId));
|
||||
return buildApprovalResponse(request, "조회가 완료되었습니다");
|
||||
}
|
||||
|
||||
private AcquisitionCard createNewCard(ApprovalRequestDto dto) {
|
||||
AcquisitionCard card = new AcquisitionCard();
|
||||
card.setCardNumber(dto.getCardNumber());
|
||||
card.setCardCompanyCode(dto.getCardCompanyCode());
|
||||
card.setMerchantNumber(dto.getMerchantNumber());
|
||||
card.setStatus(AcquisitionCard.CardStatus.PENDING);
|
||||
return acquisitionCardRepository.save(card);
|
||||
}
|
||||
|
||||
private String generateTransactionId() {
|
||||
return "TX" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
|
||||
}
|
||||
|
||||
private String generateApprovalNumber() {
|
||||
return "AP" + System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private ApprovalResponseDto buildApprovalResponse(ApprovalRequest request, String message) {
|
||||
return new ApprovalResponseDto(
|
||||
request.getId(), request.getTransactionId(), request.getApprovalNumber(),
|
||||
request.getApprovalStatus().name(), request.getAmount(), request.getVat(),
|
||||
request.getApprovedAt(), message
|
||||
);
|
||||
}
|
||||
}
|
||||
150
src/test/java/com/acquirex/ApprovalServiceTest.java
Normal file
150
src/test/java/com/acquirex/ApprovalServiceTest.java
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package com.acquirex;
|
||||
|
||||
import com.acquirex.domain.AcquisitionCard;
|
||||
import com.acquirex.domain.ApprovalRequest;
|
||||
import com.acquirex.dto.ApprovalRequestDto;
|
||||
import com.acquirex.dto.ApprovalResponseDto;
|
||||
import com.acquirex.repository.AcquisitionCardRepository;
|
||||
import com.acquirex.repository.ApprovalRequestRepository;
|
||||
import com.acquirex.service.ApprovalService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ApprovalServiceTest {
|
||||
|
||||
@Mock
|
||||
private ApprovalRequestRepository approvalRequestRepository;
|
||||
|
||||
@Mock
|
||||
private AcquisitionCardRepository acquisitionCardRepository;
|
||||
|
||||
private ApprovalService approvalService;
|
||||
private ApprovalRequestDto validRequestDto;
|
||||
private AcquisitionCard testCard;
|
||||
private ApprovalRequest testRequest;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
approvalService = new ApprovalService(approvalRequestRepository, acquisitionCardRepository);
|
||||
|
||||
validRequestDto = new ApprovalRequestDto();
|
||||
validRequestDto.setCardNumber("1234567890123456");
|
||||
validRequestDto.setCardCompanyCode("KB");
|
||||
validRequestDto.setMerchantNumber("123456789012345");
|
||||
validRequestDto.setAmount(new BigDecimal("50000.00"));
|
||||
validRequestDto.setVat(new BigDecimal("5000.00"));
|
||||
|
||||
testCard = new AcquisitionCard();
|
||||
testCard.setId(1L);
|
||||
testCard.setCardNumber("1234567890123456");
|
||||
testCard.setCardCompanyCode("KB");
|
||||
testCard.setMerchantNumber("123456789012345");
|
||||
testCard.setStatus(AcquisitionCard.CardStatus.PENDING);
|
||||
|
||||
testRequest = new ApprovalRequest();
|
||||
testRequest.setId(1L);
|
||||
testRequest.setCardId(1L);
|
||||
testRequest.setTransactionId("TX12345678");
|
||||
testRequest.setAmount(new BigDecimal("50000.00"));
|
||||
testRequest.setVat(new BigDecimal("5000.00"));
|
||||
testRequest.setApprovalStatus(ApprovalRequest.ApprovalStatus.REQUESTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("매입 승인 요청 성공 - 기존 카드")
|
||||
void requestApproval_WithExistingCard_Success() {
|
||||
when(acquisitionCardRepository.findByCardNumberAndCardCompanyCode(any(), any()))
|
||||
.thenReturn(Optional.of(testCard));
|
||||
when(approvalRequestRepository.save(any(ApprovalRequest.class))).thenReturn(testRequest);
|
||||
|
||||
ApprovalResponseDto response = approvalService.requestApproval(validRequestDto);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals("TX12345678", response.getTransactionId());
|
||||
assertEquals("REQUESTED", response.getStatus());
|
||||
verify(acquisitionCardRepository, never()).save(any());
|
||||
verify(approvalRequestRepository, times(1)).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("매입 승인 요청 성공 - 신규 카드")
|
||||
void requestApproval_WithNewCard_Success() {
|
||||
when(acquisitionCardRepository.findByCardNumberAndCardCompanyCode(any(), any()))
|
||||
.thenReturn(Optional.empty());
|
||||
when(acquisitionCardRepository.save(any(AcquisitionCard.class))).thenReturn(testCard);
|
||||
when(approvalRequestRepository.save(any(ApprovalRequest.class))).thenReturn(testRequest);
|
||||
|
||||
ApprovalResponseDto response = approvalService.requestApproval(validRequestDto);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals("REQUESTED", response.getStatus());
|
||||
verify(acquisitionCardRepository, times(1)).save(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("매입 승인 처리 성공")
|
||||
void approveRequest_Success() {
|
||||
when(approvalRequestRepository.findById(1L)).thenReturn(Optional.of(testRequest));
|
||||
when(approvalRequestRepository.save(any(ApprovalRequest.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
ApprovalResponseDto response = approvalService.approveRequest(1L);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals("APPROVED", response.getStatus());
|
||||
assertNotNull(response.getApprovalNumber());
|
||||
assertNotNull(response.getApprovedAt());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("매입 승인 처리 실패 - 요청 없음")
|
||||
void approveRequest_NotFound() {
|
||||
when(approvalRequestRepository.findById(999L)).thenReturn(Optional.empty());
|
||||
assertThrows(IllegalArgumentException.class, () -> approvalService.approveRequest(999L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("매입 승인 처리 실패 - 이미 처리됨")
|
||||
void approveRequest_AlreadyProcessed() {
|
||||
testRequest.setApprovalStatus(ApprovalRequest.ApprovalStatus.APPROVED);
|
||||
when(approvalRequestRepository.findById(1L)).thenReturn(Optional.of(testRequest));
|
||||
assertThrows(IllegalStateException.class, () -> approvalService.approveRequest(1L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("매입 승인 거절 성공")
|
||||
void rejectRequest_Success() {
|
||||
when(approvalRequestRepository.findById(1L)).thenReturn(Optional.of(testRequest));
|
||||
when(approvalRequestRepository.save(any(ApprovalRequest.class)))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
|
||||
ApprovalResponseDto response = approvalService.rejectRequest(1L, "한도 초과");
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals("REJECTED", response.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("승인 상태 조회 성공")
|
||||
void getApprovalStatus_Success() {
|
||||
when(approvalRequestRepository.findById(1L)).thenReturn(Optional.of(testRequest));
|
||||
|
||||
ApprovalResponseDto response = approvalService.getApprovalStatus(1L);
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(1L, response.getApprovalId());
|
||||
assertEquals("TX12345678", response.getTransactionId());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue