diff --git a/src/main/java/com/sb/web/account/domain/Wallet.java b/src/main/java/com/sb/web/account/domain/Wallet.java index 8bd3b03..06a6b56 100644 --- a/src/main/java/com/sb/web/account/domain/Wallet.java +++ b/src/main/java/com/sb/web/account/domain/Wallet.java @@ -6,6 +6,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; +import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; @@ -29,6 +30,11 @@ public class Wallet implements Serializable { private Long openingBalance; // 초기 잔액(부호있음: 자산+, 부채-) private LocalDate openingDate; private Long currentValue; // 현재 평가금액 (INVEST 전용, 수동 갱신) + private Long loanAmount; // 대출 실행 금액(원금, 처음 빌린 금액) (LOAN 전용) + private BigDecimal loanRate; // 연이자율(%) e.g. 5.25 (LOAN 전용) + private String loanMethod; // 상환방식: EQUAL_PAYMENT / EQUAL_PRINCIPAL / BULLET (LOAN 전용) + private Integer loanMonths; // 대출기간(개월) (LOAN 전용) + private LocalDate loanStart; // 대출시작일 (LOAN 전용) private Integer sortOrder; // 표시 순서 (타입 내 정렬) private LocalDateTime createdAt; private LocalDateTime updatedAt; diff --git a/src/main/java/com/sb/web/account/dto/WalletRequest.java b/src/main/java/com/sb/web/account/dto/WalletRequest.java index 5cb3c18..a940aa1 100644 --- a/src/main/java/com/sb/web/account/dto/WalletRequest.java +++ b/src/main/java/com/sb/web/account/dto/WalletRequest.java @@ -1,10 +1,13 @@ package com.sb.web.account.dto; +import jakarta.validation.constraints.DecimalMax; +import jakarta.validation.constraints.DecimalMin; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import lombok.Data; +import java.math.BigDecimal; import java.time.LocalDate; /** @@ -37,4 +40,17 @@ public class WalletRequest { /** 현재 평가금액 (INVEST 전용, 수동 갱신) */ private Long currentValue; + + // ===== 대출(LOAN) 전용 ===== + private Long loanAmount; // 대출 실행 금액(원금) + + @DecimalMin(value = "0.0", inclusive = true) + @DecimalMax(value = "100.0", message = "이자율은 100% 이하여야 합니다.") + private BigDecimal loanRate; // 연이자율(%) + + @Pattern(regexp = "EQUAL_PAYMENT|EQUAL_PRINCIPAL|BULLET|", message = "상환방식이 올바르지 않습니다.") + private String loanMethod; // 상환방식 + + private Integer loanMonths; // 대출기간(개월) + private LocalDate loanStart; // 대출시작일 } diff --git a/src/main/java/com/sb/web/account/dto/WalletResponse.java b/src/main/java/com/sb/web/account/dto/WalletResponse.java index 408d67c..2ec3b39 100644 --- a/src/main/java/com/sb/web/account/dto/WalletResponse.java +++ b/src/main/java/com/sb/web/account/dto/WalletResponse.java @@ -4,6 +4,7 @@ import com.sb.web.account.domain.Wallet; import lombok.Builder; import lombok.Data; +import java.math.BigDecimal; import java.time.LocalDate; /** @@ -31,6 +32,13 @@ public class WalletResponse { private Long currentValue; // 평가액 직접입력값(퇴직연금·연금 등 수동 갱신). 있으면 총평가로 사용 private Boolean manualValuation; // true면 평가액 직접입력형(종목 자동계산 대신) + // 대출(LOAN) 전용 + private Long loanAmount; // 대출 실행 금액(원금) + private BigDecimal loanRate; // 연이자율(%) e.g. 5.25 + private String loanMethod; // EQUAL_PAYMENT / EQUAL_PRINCIPAL / BULLET + private Integer loanMonths; // 대출기간(개월) + private LocalDate loanStart; // 대출시작일 + public static WalletResponse from(Wallet w, long balance) { return WalletResponse.builder() .id(w.getId()) @@ -43,6 +51,11 @@ public class WalletResponse { .openingDate(w.getOpeningDate()) .balance(balance) .currentValue(w.getCurrentValue()) + .loanAmount(w.getLoanAmount()) + .loanRate(w.getLoanRate()) + .loanMethod(w.getLoanMethod()) + .loanMonths(w.getLoanMonths()) + .loanStart(w.getLoanStart()) .build(); } } diff --git a/src/main/java/com/sb/web/account/service/AccountService.java b/src/main/java/com/sb/web/account/service/AccountService.java index 308bdb2..2868871 100644 --- a/src/main/java/com/sb/web/account/service/AccountService.java +++ b/src/main/java/com/sb/web/account/service/AccountService.java @@ -535,6 +535,12 @@ public class AccountService { wallet.setOpeningBalance(req.getOpeningBalance() != null ? req.getOpeningBalance() : 0L); wallet.setOpeningDate(req.getOpeningDate()); wallet.setCurrentValue("INVEST".equals(req.getType()) ? req.getCurrentValue() : null); + boolean isLoan = "LOAN".equals(req.getType()); + wallet.setLoanAmount(isLoan ? req.getLoanAmount() : null); + wallet.setLoanRate(isLoan ? req.getLoanRate() : null); + wallet.setLoanMethod(isLoan ? blankToNull(req.getLoanMethod()) : null); + wallet.setLoanMonths(isLoan ? req.getLoanMonths() : null); + wallet.setLoanStart(isLoan ? req.getLoanStart() : null); walletMapper.update(wallet); return WalletResponse.from(wallet, balanceMap(memberId).getOrDefault(id, wallet.getOpeningBalance())); } @@ -564,6 +570,7 @@ public class AccountService { } private Wallet toWallet(WalletRequest req) { + boolean isLoan = "LOAN".equals(req.getType()); return Wallet.builder() .type(req.getType()) .name(req.getName().trim()) @@ -573,6 +580,11 @@ public class AccountService { .openingBalance(req.getOpeningBalance() != null ? req.getOpeningBalance() : 0L) .openingDate(req.getOpeningDate()) .currentValue("INVEST".equals(req.getType()) ? req.getCurrentValue() : null) + .loanAmount(isLoan ? req.getLoanAmount() : null) + .loanRate(isLoan ? req.getLoanRate() : null) + .loanMethod(isLoan ? blankToNull(req.getLoanMethod()) : null) + .loanMonths(isLoan ? req.getLoanMonths() : null) + .loanStart(isLoan ? req.getLoanStart() : null) .build(); } diff --git a/src/main/resources/db/account.sql b/src/main/resources/db/account.sql index 823290a..2f24f1c 100644 --- a/src/main/resources/db/account.sql +++ b/src/main/resources/db/account.sql @@ -28,6 +28,11 @@ ALTER TABLE wallet ADD COLUMN IF NOT EXISTS opening_balance BIGINT NOT NULL DEFA ALTER TABLE wallet ADD COLUMN IF NOT EXISTS opening_date DATE NULL; ALTER TABLE wallet ADD COLUMN IF NOT EXISTS current_value BIGINT NULL; ALTER TABLE wallet ADD COLUMN IF NOT EXISTS sort_order INT NOT NULL DEFAULT 0; +ALTER TABLE wallet ADD COLUMN IF NOT EXISTS loan_rate DECIMAL(7,4) NULL COMMENT '연이자율(%) 예: 5.2500'; +ALTER TABLE wallet ADD COLUMN IF NOT EXISTS loan_method VARCHAR(20) NULL COMMENT '상환방식: EQUAL_PAYMENT/EQUAL_PRINCIPAL/BULLET'; +ALTER TABLE wallet ADD COLUMN IF NOT EXISTS loan_months INT NULL COMMENT '대출기간(개월)'; +ALTER TABLE wallet ADD COLUMN IF NOT EXISTS loan_start DATE NULL COMMENT '대출시작일'; +ALTER TABLE wallet ADD COLUMN IF NOT EXISTS loan_amount BIGINT NULL COMMENT '대출 실행 금액(원금, 처음 빌린 금액)'; -- 계좌번호 암호화 저장(AES-GCM)으로 평문보다 길어 VARCHAR(255) 필요 — 운영 적용 완료(CREATE TABLE 정의에 반영). -- 매 기동 MODIFY 는 라이브 테이블 락 위험이 있어 제거. 기존 환경 확장이 필요하면 1회 수동 적용: -- ALTER TABLE wallet MODIFY account_number VARCHAR(255) NULL; diff --git a/src/main/resources/mapper/WalletMapper.xml b/src/main/resources/mapper/WalletMapper.xml index 609f25d..a8454de 100644 --- a/src/main/resources/mapper/WalletMapper.xml +++ b/src/main/resources/mapper/WalletMapper.xml @@ -15,13 +15,20 @@ + + + + + id, member_id, type, name, issuer, account_number, card_type, - opening_balance, opening_date, current_value, sort_order, created_at, updated_at + opening_balance, opening_date, current_value, + loan_amount, loan_rate, loan_method, loan_months, loan_start, + sort_order, created_at, updated_at