Skip to content

Commit 122f5d6

Browse files
authored
feat : 레벨 도메인 작성 (#85)
* feat : 레벨 엔티티 생성 * feat : 레벨 리포지터리 생성 * feat : 레벨업 서비스 작성(리워드 도메인과는 아직 연결 x) * feat : 레벨별 요구 경험치 초기 데이터 추가
1 parent b7bbee4 commit 122f5d6

File tree

5 files changed

+184
-0
lines changed

5 files changed

+184
-0
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.back.domain.level.entity;
2+
3+
import jakarta.persistence.Column;
4+
import jakarta.persistence.Entity;
5+
import jakarta.persistence.Id;
6+
import jakarta.persistence.Table;
7+
import lombok.AllArgsConstructor;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
11+
@Entity
12+
@Table(name = "level_xp")
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
@Getter
16+
public class LevelXP {
17+
18+
@Id
19+
@Column(name = "level", nullable = false)
20+
private Integer level;
21+
22+
@Column(name = "xp_to_next", nullable = false)
23+
private Integer xpToNext;
24+
25+
@Column(name = "cumulative_xp", nullable = false)
26+
private Long cumulativeXP;
27+
28+
// Level 30 이후 고정 요구량 처리를 위한 상수 정의
29+
public static final int MAX_VARIABLE_LEVEL = 30;
30+
public static final int FIXED_XP_REQUIREMENT = 30000;
31+
32+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.back.domain.level.repository;
2+
3+
import com.back.domain.level.entity.LevelXP;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
public interface LevelXPRepository extends JpaRepository<LevelXP, Integer> {
7+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.back.domain.level.service;
2+
3+
import com.back.domain.level.entity.LevelXP;
4+
import com.back.domain.level.repository.LevelXPRepository;
5+
import com.back.domain.member.entity.Member;
6+
import com.back.domain.member.repository.MemberRepository;
7+
import com.back.global.exception.CustomException;
8+
import com.back.global.exception.ErrorCode;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.stereotype.Service;
11+
import org.springframework.transaction.annotation.Transactional;
12+
13+
import java.util.Optional;
14+
15+
@Service
16+
@RequiredArgsConstructor
17+
public class LevelUpService {
18+
19+
private final MemberRepository memberRepository;
20+
private final LevelXPRepository levelXPRepository;
21+
22+
// 레벨업 보상 서비스
23+
@Transactional
24+
public void checkLevelUp(Integer memberId) {
25+
26+
Member member = memberRepository.findById(memberId)
27+
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND, "Member not found with id: " + memberId));
28+
29+
// 1. 레벨업 체크 및 처리
30+
checkAndProcessLevelUp(member);
31+
32+
// 2. 변경된 Member 저장
33+
memberRepository.save(member);
34+
}
35+
36+
37+
private void checkAndProcessLevelUp(Member member) {
38+
int currentLevel = member.getLevel();
39+
long currentTotalXp = member.getXp();
40+
41+
while (true) {
42+
int nextLevel = currentLevel + 1;
43+
Long requiredXpForNextLevel; // 다음 레벨 달성에 필요한 총 누적 XP
44+
45+
// 30레벨 미만: LevelXP 테이블 조회 (변동 구간)
46+
if (currentLevel < LevelXP.MAX_VARIABLE_LEVEL) {
47+
Optional<LevelXP> nextLevelInfo = levelXPRepository.findById(nextLevel);
48+
if (nextLevelInfo.isEmpty()) break;
49+
50+
requiredXpForNextLevel = nextLevelInfo.get().getCumulativeXP();
51+
52+
}
53+
// 30레벨 이상: 고정 경험치 계산 (고정 구간)
54+
else {
55+
// 현재 레벨 시작 누적 XP를 계산 (DB 조회 필요)
56+
Long currentLevelStartXp = getCurrentLevelStartCumulativeXp(currentLevel);
57+
requiredXpForNextLevel = currentLevelStartXp + LevelXP.FIXED_XP_REQUIREMENT;
58+
}
59+
60+
if (currentTotalXp >= requiredXpForNextLevel) {
61+
// 레벨업 성공
62+
currentLevel = nextLevel;
63+
member.setLevel(currentLevel);
64+
65+
// 보상 지급 (보상 리워드 관련 메서드가 생성되면 해당 로직 추가)
66+
67+
} else {
68+
break; // 레벨업 실패, 반복 종료
69+
}
70+
}
71+
}
72+
73+
// 현재 레벨의 시작 누적 XP를 계산하는 헬퍼 메서드
74+
private Long getCurrentLevelStartCumulativeXp(int level) {
75+
if (level == 1) return 0L;
76+
77+
// 30레벨 이하: DB에서 해당 레벨의 누적 XP 조회
78+
if (level <= LevelXP.MAX_VARIABLE_LEVEL) {
79+
return levelXPRepository.findById(level)
80+
// CustomException 사용
81+
.orElseThrow(() -> new CustomException(ErrorCode.LEVEL_DATA_NOT_FOUND, "Level XP data for level " + level + " not found"))
82+
.getCumulativeXP();
83+
}
84+
85+
// 31레벨 이상: 고정 요구량으로 계산
86+
// 1. Level 30 달성 시점의 누적 XP를 DB에서 조회합니다.
87+
Long baseCumulativeXp = levelXPRepository.findById(LevelXP.MAX_VARIABLE_LEVEL)
88+
// CustomException 사용
89+
.orElseThrow(() -> new CustomException(ErrorCode.LEVEL_DATA_NOT_FOUND, "Base Level XP data (Level 30) not found"))
90+
.getCumulativeXP();
91+
92+
// 2. 30레벨을 초과한 레벨 차이만큼 고정 경험치를 더합니다.
93+
int levelDifference = level - LevelXP.MAX_VARIABLE_LEVEL;
94+
95+
return baseCumulativeXp + (long)levelDifference * LevelXP.FIXED_XP_REQUIREMENT;
96+
}
97+
}

backend/src/main/java/com/back/global/exception/ErrorCode.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public enum ErrorCode {
99
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, "AUTH-401", "인증되지 않았습니다."),
1010
NOT_FOUND(HttpStatus.NOT_FOUND, "USER-404", "리소스를 찾을 수 없습니다."),
1111
CONFLICT(HttpStatus.CONFLICT, "CONFLICT-409", "요청이 충돌합니다"),
12+
LEVEL_DATA_NOT_FOUND(HttpStatus.NOT_FOUND, "LEVEL-404", "레벨업 기준 데이터(LevelXP)를 찾을 수 없습니다."),
1213
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "SERVER-500", "서버 내부 오류가 발생하였습니다.");
1314

1415
private final HttpStatus httpStatus;

backend/src/main/java/com/back/global/initData/BaseInitData.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.back.domain.item.dto.CreateItemDto;
44
import com.back.domain.item.entity.ItemType;
55
import com.back.domain.item.service.ItemService;
6+
import com.back.domain.level.entity.LevelXP;
7+
import com.back.domain.level.repository.LevelXPRepository;
68
import com.back.domain.member.service.MemberService;
79
import lombok.RequiredArgsConstructor;
810
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,6 +15,9 @@
1315
import org.springframework.context.annotation.Profile;
1416
import org.springframework.transaction.annotation.Transactional;
1517

18+
import java.util.ArrayList;
19+
import java.util.List;
20+
1621
@Profile("!prod")
1722
@Configuration
1823
@RequiredArgsConstructor
@@ -22,6 +27,7 @@ public class BaseInitData {
2227
private BaseInitData self;
2328
private final MemberService memberService;
2429
private final ItemService itemService;
30+
private final LevelXPRepository levelXPRepository;
2531

2632
@Bean
2733
ApplicationRunner baseInitDataApplicationRunner() {
@@ -35,6 +41,7 @@ public void initAllData() {
3541
try {
3642
createMember();
3743
createItem();
44+
createLevelXP();
3845
} catch (Exception e) {
3946
throw new RuntimeException("[initData] Fail: 초기 데이터 생성 실패", e);
4047
}
@@ -63,4 +70,44 @@ private void createItem() {
6370
itemService.createItem(new CreateItemDto("다람쥐먹는중", ItemType.FURNITURE) );
6471
itemService.createItem(new CreateItemDto("다람쥐그림", ItemType.FURNITURE) );
6572
}
73+
74+
private void createLevelXP() {
75+
if(levelXPRepository.count() > 0) {
76+
return;
77+
}
78+
79+
List<LevelXP> xpList = new ArrayList<>();
80+
81+
xpList.add(new LevelXP(2, 5332, 5000L));
82+
xpList.add(new LevelXP(3, 5685, 10332L));
83+
xpList.add(new LevelXP(4, 6060, 16017L));
84+
xpList.add(new LevelXP(5, 6461, 22077L));
85+
xpList.add(new LevelXP(6, 6887, 28538L));
86+
xpList.add(new LevelXP(7, 7341, 35425L));
87+
xpList.add(new LevelXP(8, 7823, 42766L));
88+
xpList.add(new LevelXP(9, 8336, 50589L));
89+
xpList.add(new LevelXP(10, 8881, 58925L));
90+
xpList.add(new LevelXP(11, 9461, 67806L));
91+
xpList.add(new LevelXP(12, 10077, 77267L));
92+
xpList.add(new LevelXP(13, 10732, 87344L));
93+
xpList.add(new LevelXP(14, 11429, 98076L));
94+
xpList.add(new LevelXP(15, 12170, 109505L));
95+
xpList.add(new LevelXP(16, 12958, 121675L));
96+
xpList.add(new LevelXP(17, 13795, 134633L));
97+
xpList.add(new LevelXP(18, 14685, 148428L));
98+
xpList.add(new LevelXP(19, 15630, 163113L));
99+
xpList.add(new LevelXP(20, 16634, 178743L));
100+
xpList.add(new LevelXP(21, 17699, 195377L));
101+
xpList.add(new LevelXP(22, 18830, 213076L));
102+
xpList.add(new LevelXP(23, 20031, 231906L));
103+
xpList.add(new LevelXP(24, 21305, 251937L));
104+
xpList.add(new LevelXP(25, 22657, 273242L));
105+
xpList.add(new LevelXP(26, 24091, 295899L));
106+
xpList.add(new LevelXP(27, 25612, 319990L));
107+
xpList.add(new LevelXP(28, 27225, 345602L));
108+
xpList.add(new LevelXP(29, 28935, 372827L));
109+
xpList.add(new LevelXP(30, 30000, 401762L));
110+
111+
levelXPRepository.saveAll(xpList);
112+
}
66113
}

0 commit comments

Comments
 (0)