feat: complete Spring Boot 카드 매입·정산 system (acquire-core migration target, mvn verify green, 24 tests)

This commit is contained in:
forge-bot 2026-07-18 15:27:41 +00:00
commit 0f6e3acfe6
92 changed files with 4320 additions and 0 deletions

42
modules/master/pom.xml Normal file
View file

@ -0,0 +1,42 @@
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.klaro.acquiring</groupId>
<artifactId>card-acquiring-boot</artifactId>
<version>1.0.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>master</artifactId>
<name>master</name>
<dependencies>
<dependency>
<groupId>com.klaro.acquiring</groupId>
<artifactId>common-framework</artifactId>
</dependency>
<dependency>
<groupId>com.klaro.acquiring</groupId>
<artifactId>domain</artifactId>
</dependency>
<dependency>
<groupId>com.klaro.acquiring</groupId>
<artifactId>persistence</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</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>
</dependencies>
</project>

View file

@ -0,0 +1,48 @@
package com.klaro.acquiring.master;
import com.klaro.acquiring.common.dto.ApiResponse;
import com.klaro.acquiring.master.dto.MerchantRequest;
import com.klaro.acquiring.master.dto.MerchantResponse;
import jakarta.validation.Valid;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/** 가맹점 마스터 REST. */
@RestController
@RequestMapping("/api/master/merchants")
public class MasterController {
private final MasterService masterService;
public MasterController(MasterService masterService) {
this.masterService = masterService;
}
@PostMapping
public ApiResponse<MerchantResponse> register(@Valid @RequestBody MerchantRequest req) {
return ApiResponse.ok(MerchantResponse.from(masterService.register(req)));
}
@PutMapping("/{merchId}")
public ApiResponse<MerchantResponse> update(@PathVariable String merchId,
@Valid @RequestBody MerchantRequest req) {
return ApiResponse.ok(MerchantResponse.from(masterService.update(merchId, req)));
}
@GetMapping("/{merchId}")
public ApiResponse<MerchantResponse> get(@PathVariable String merchId) {
return ApiResponse.ok(MerchantResponse.from(masterService.get(merchId)));
}
@GetMapping
public ApiResponse<List<MerchantResponse>> list() {
return ApiResponse.ok(masterService.list().stream().map(MerchantResponse::from).toList());
}
}

View file

@ -0,0 +1,60 @@
package com.klaro.acquiring.master;
import com.klaro.acquiring.common.error.ErrorCode;
import com.klaro.acquiring.common.error.AcquiringException;
import com.klaro.acquiring.common.error.NotFoundException;
import com.klaro.acquiring.domain.entity.Merchant;
import com.klaro.acquiring.master.dto.MerchantRequest;
import com.klaro.acquiring.persistence.repository.MerchantRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 가맹점/수수료율/한도 마스터(mm) CRUD 서비스.
*/
@Service
public class MasterService {
private final MerchantRepository merchantRepository;
public MasterService(MerchantRepository merchantRepository) {
this.merchantRepository = merchantRepository;
}
@Transactional
public Merchant register(MerchantRequest req) {
if (merchantRepository.existsById(req.merchId())) {
throw new AcquiringException(ErrorCode.DUPLICATE, "이미 존재하는 가맹점: " + req.merchId());
}
Merchant m = new Merchant(req.merchId(), req.merchName(), req.mdrRate(), req.vanFee(),
req.settlementCycle(), req.bankCode(), req.accountNo());
m.setBizNo(req.bizNo());
return merchantRepository.save(m);
}
@Transactional
public Merchant update(String merchId, MerchantRequest req) {
Merchant m = get(merchId);
m.setMerchName(req.merchName());
m.setBizNo(req.bizNo());
m.setMdrRate(req.mdrRate());
m.setVanFee(req.vanFee());
m.setSettlementCycle(req.settlementCycle());
m.setBankCode(req.bankCode());
m.setAccountNo(req.accountNo());
return merchantRepository.save(m);
}
@Transactional(readOnly = true)
public Merchant get(String merchId) {
return merchantRepository.findById(merchId)
.orElseThrow(() -> new NotFoundException("가맹점 없음: " + merchId));
}
@Transactional(readOnly = true)
public List<Merchant> list() {
return merchantRepository.findAll();
}
}

View file

@ -0,0 +1,20 @@
package com.klaro.acquiring.master.dto;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
/** 가맹점 등록/수정 요청. */
public record MerchantRequest(
@NotBlank String merchId,
@NotBlank String merchName,
String bizNo,
@NotNull @DecimalMin("0.0") BigDecimal mdrRate,
@NotNull @DecimalMin("0.0") BigDecimal vanFee,
@Min(0) int settlementCycle,
String bankCode,
String accountNo) {
}

View file

@ -0,0 +1,17 @@
package com.klaro.acquiring.master.dto;
import com.klaro.acquiring.domain.entity.Merchant;
import java.math.BigDecimal;
/** 가맹점 응답. */
public record MerchantResponse(String merchId, String merchName, String bizNo, String status,
BigDecimal mdrRate, BigDecimal vanFee, int settlementCycle,
String bankCode, String accountNo) {
public static MerchantResponse from(Merchant m) {
return new MerchantResponse(m.getMerchId(), m.getMerchName(), m.getBizNo(),
m.getStatus().name(), m.getMdrRate(), m.getVanFee(), m.getSettlementCycle(),
m.getBankCode(), m.getAccountNo());
}
}