feat(shop): 포인트 상점 구현 — 계좌 슬롯(500P)·AI 통계 크레딧(1000P→10회)
- member 테이블: extra_wallet_slots, ai_stat_credits 컬럼 추가(ALTER IF NOT EXISTS) - MemberMapper: 상점 관련 5개 메서드 추가(getExtraWalletSlots/getAiStatCredits/incrementExtraWalletSlots/addAiStatCredits/decrementAiStatCredit) - ShopService: buyWalletSlot(500P, max 3개), buyAiStatPack(1000P→10회) 구현 - ShopController: GET /api/account/shop/status, POST /api/account/shop/buy - AccountService.freeWalletLimit: bonus=0 TODO → memberMapper.getExtraWalletSlots 실제 조회 - AccountService.aiComment: FREE 회원 크레딧 차감 로직 추가, 소진 시 402 반환 - AccountController.aiComment: current 파라미터 전달 - WebConfig: /api/account/ai-comment 를 premiumInterceptor 에서 제거(서비스 레이어에서 처리) - BackupController: @DeleteMapping import 누락 수정 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
75d5c9fa9b
commit
6db6cb055e
@@ -21,6 +21,8 @@ import com.sb.web.account.dto.WalletResponse;
|
||||
import com.sb.web.account.mapper.AccountEntryMapper;
|
||||
import com.sb.web.account.mapper.AccountTagMapper;
|
||||
import com.sb.web.account.mapper.WalletMapper;
|
||||
import com.sb.web.auth.dto.SessionUser;
|
||||
import com.sb.web.auth.mapper.MemberMapper;
|
||||
import com.sb.web.common.exception.ApiException;
|
||||
import com.sb.web.point.PointService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -51,6 +53,7 @@ public class AccountService {
|
||||
private final CardNotificationParser notificationParser;
|
||||
private final AiEntryParser aiEntryParser;
|
||||
private final PointService pointService;
|
||||
private final MemberMapper memberMapper;
|
||||
|
||||
/* ===================== 항목 ===================== */
|
||||
|
||||
@@ -341,11 +344,26 @@ public class AccountService {
|
||||
.build();
|
||||
}
|
||||
|
||||
/** 통계 화면 집계 → AI 재무 코멘트. 집계만 JSON 으로 직렬화해 전달(거래 원문 미전송). 미설정/실패 시 빈 코멘트. */
|
||||
public com.sb.web.account.dto.AiCommentResponse aiComment(com.sb.web.account.dto.AiCommentRequest req) {
|
||||
/** 통계 화면 집계 → AI 재무 코멘트.
|
||||
* - PREMIUM: 무제한.
|
||||
* - FREE + ai_stat_credits > 0: 크레딧 1 차감 후 호출.
|
||||
* - FREE + credits == 0: 402 에러(프론트가 상점으로 유도).
|
||||
*/
|
||||
@Transactional
|
||||
public com.sb.web.account.dto.AiCommentResponse aiComment(
|
||||
com.sb.web.account.dto.AiCommentRequest req, SessionUser current) {
|
||||
if ("FREE".equals(current.getPlan())) {
|
||||
int credits = memberMapper.getAiStatCredits(current.getId());
|
||||
if (credits <= 0) {
|
||||
throw new ApiException(HttpStatus.PAYMENT_REQUIRED, "AI_STAT_CREDIT_EMPTY");
|
||||
}
|
||||
memberMapper.decrementAiStatCredit(current.getId());
|
||||
}
|
||||
try {
|
||||
String json = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(req);
|
||||
return aiEntryParser.financeComment(json);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
return com.sb.web.account.dto.AiCommentResponse.builder()
|
||||
.bullets(java.util.List.of()).tips(java.util.List.of()).build();
|
||||
@@ -561,9 +579,9 @@ public class AccountService {
|
||||
public static final int FREE_WALLET_BASE_LIMIT = 2;
|
||||
public static final int FREE_WALLET_MAX_LIMIT = 5;
|
||||
|
||||
/** 무료 회원의 해당 타입 등록 한도 = 기본 2 + 구매한 영구 보너스 슬롯(추후 상점), 최대 5. */
|
||||
/** 무료 회원의 해당 타입 등록 한도 = 기본 2 + 포인트 상점 구매 슬롯, 최대 5. */
|
||||
private int freeWalletLimit(Long memberId, String type) {
|
||||
int bonus = 0; // TODO: 포인트 상점 구현 시 회원×종류별 구매 슬롯 수를 조회해 더함
|
||||
int bonus = memberMapper.getExtraWalletSlots(memberId);
|
||||
return Math.min(FREE_WALLET_BASE_LIMIT + bonus, FREE_WALLET_MAX_LIMIT);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user