Skip to content

Commit 7ffcaa6

Browse files
authored
feat : 방어 전투 로그 조회할 수 있는 기능 추가, 성능 개선 (#90)
* feat : 방어 전투 로그 조회할 수 있는 기능 추가, 성능 개선 * fix : 웹소켓 세션 해제시 예외처리
1 parent e29aa84 commit 7ffcaa6

File tree

16 files changed

+289
-129
lines changed

16 files changed

+289
-129
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.ezcode.codetest.application.game.dto.response.encounter;
2+
3+
import java.time.LocalDateTime;
4+
5+
import org.ezcode.codetest.domain.game.model.encounter.BattleHistory;
6+
7+
import io.swagger.v3.oas.annotations.media.Schema;
8+
import lombok.Builder;
9+
10+
@Builder
11+
@Schema(description = "캐릭터 방어 전투 결과 응답")
12+
public record DefenceBattleHistoryResponse(
13+
14+
@Schema(description = "공격자 캐릭터 닉네임")
15+
String attackerNickName,
16+
17+
@Schema(description = "플레이어 캐릭터 닉네임")
18+
String PlayerNickName,
19+
20+
@Schema(description = "전투 로그")
21+
String battleLog,
22+
23+
@Schema(description = "플레이어 승패 여부(이겼을시 true, 패배시 false)")
24+
boolean isDefenderWin,
25+
26+
@Schema(description = "PVP 가 일어난 시점")
27+
LocalDateTime battleCreatedAt
28+
29+
) {
30+
public static DefenceBattleHistoryResponse from(BattleHistory history) {
31+
return DefenceBattleHistoryResponse.builder()
32+
.attackerNickName(history.getAttacker().getName())
33+
.PlayerNickName(history.getDefender().getName())
34+
.battleLog(history.getBattleLog())
35+
.isDefenderWin(!history.getIsAttackerWin())
36+
.battleCreatedAt(history.getCreatedAt())
37+
.build();
38+
}
39+
}

src/main/java/org/ezcode/codetest/application/game/play/GamePlayUseCase.java

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.ezcode.codetest.application.game.dto.request.skill.SkillUnEquipRequest;
1010
import org.ezcode.codetest.application.game.dto.response.character.CharacterStatusResponse;
1111
import org.ezcode.codetest.application.game.dto.response.encounter.BattleHistoryResponse;
12+
import org.ezcode.codetest.application.game.dto.response.encounter.DefenceBattleHistoryResponse;
1213
import org.ezcode.codetest.application.game.dto.response.encounter.EncounterResultResponse;
1314
import org.ezcode.codetest.application.game.dto.response.encounter.MatchingBattleResponse;
1415
import org.ezcode.codetest.application.game.dto.response.encounter.MatchingEncounterResponse;
@@ -18,6 +19,7 @@
1819
import org.ezcode.codetest.application.game.dto.response.skill.SkillResponse;
1920
import org.ezcode.codetest.common.security.util.JwtUtil;
2021
import org.ezcode.codetest.domain.game.model.character.GameCharacter;
22+
import org.ezcode.codetest.domain.game.model.encounter.BattleHistory;
2123
import org.ezcode.codetest.domain.game.model.encounter.EncounterHistory;
2224
import org.ezcode.codetest.domain.game.model.encounter.EncounterLog;
2325
import org.ezcode.codetest.domain.game.model.encounter.MatchMessageTemplate;
@@ -160,25 +162,6 @@ public BattleHistoryResponse battle(Long playerId, BattleRequest request) {
160162
);
161163
}
162164

163-
@Transactional
164-
public BattleHistoryResponse randomBattle(Long userId) {
165-
166-
GameCharacter player = characterService.getGameCharacter(userId);
167-
168-
GameCharacter enemy = encounterDomainService.getRandomEnemyCharacter(userId, player.getId());
169-
170-
BattleLog log = encounterDomainService.battle(player, enemy);
171-
172-
encounterDomainService.createBattleHistory(player, enemy, log);
173-
174-
return BattleHistoryResponse.of(
175-
player.getName(),
176-
enemy.getName(),
177-
log.getMessages(),
178-
log.getPlayerWin()
179-
);
180-
}
181-
182165
@Transactional
183166
public MatchingBattleResponse randomBattleMatching(Long userId) {
184167

@@ -222,4 +205,14 @@ public EncounterResultResponse encounterChoice(Long userId, EncounterChoiceReque
222205
return EncounterResultResponse.from(history);
223206
}
224207

208+
@Transactional
209+
public List<DefenceBattleHistoryResponse> getPlayerDefenceHistory(Long userId) {
210+
211+
GameCharacter playerCharacter = characterService.getGameCharacter(userId);
212+
213+
List<BattleHistory> history = encounterDomainService.getCharacterBattleHistory(playerCharacter);
214+
215+
return history.stream().map(DefenceBattleHistoryResponse::from).toList();
216+
}
217+
225218
}

src/main/java/org/ezcode/codetest/domain/game/model/character/CharacterRealStat.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,12 @@ public void increase(Stat stat, double rate) {
8686
case DATA_STRUCTURE:
8787
this.def += rate / 10;
8888
this.atk += rate / 10;
89-
this.accuracy += rate / 10;
89+
this.accuracy += rate / 5;
9090
break;
9191
case SPEED:
92-
this.speed += rate / 10;
92+
this.speed += rate / 5;
9393
this.atk += rate / 10;
94-
this.accuracy += rate / 10;
94+
this.accuracy += rate / 5;
9595
break;
9696
case DEBUGGING:
9797
this.crit += rate / 5;
@@ -101,7 +101,7 @@ public void increase(Stat stat, double rate) {
101101
case OPTIMIZATION:
102102
this.evasion += rate / 5;
103103
this.def += rate / 10;
104-
this.accuracy += rate / 10;
104+
this.accuracy += rate / 5;
105105
break;
106106
default:
107107
break;

src/main/java/org/ezcode/codetest/domain/game/repository/BattleHistoryRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ public interface BattleHistoryRepository {
2020
void delete(BattleHistory battleHistory);
2121

2222
List<BattleHistory> findAll();
23+
24+
List<BattleHistory> findCreatedInLast24Hours(Long playerId);
2325
}

src/main/java/org/ezcode/codetest/domain/game/service/GameEncounterDomainService.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
public class GameEncounterDomainService {
4141

4242
private final CharacterEquipService characterEquipService;
43-
private final SkillStrategyFactory skillStrategyFactory;
43+
private final SkillStrategyFactory skillStrategyFactory;
4444
private final EncounterStrategyFactory encounterFactory;
4545

4646
private final BattleHistoryRepository battleHistoryRepository;
@@ -65,13 +65,13 @@ public BattleLog battle(GameCharacter player, GameCharacter opponent) {
6565

6666
WeaponType playerWeaponType = playerItems.stream()
6767
.filter(item -> item instanceof Weapon)
68-
.map(item -> (WeaponType) item.getItemType())
68+
.map(item -> (WeaponType)item.getItemType())
6969
.findFirst()
7070
.orElse(WeaponType.NOTHING);
7171

72-
WeaponType opponentWeaponType = opponentItems.stream()
72+
WeaponType opponentWeaponType = opponentItems.stream()
7373
.filter(item -> item instanceof Weapon)
74-
.map(item -> (WeaponType) item.getItemType())
74+
.map(item -> (WeaponType)item.getItemType())
7575
.findFirst()
7676
.orElse(WeaponType.NOTHING);
7777

@@ -106,10 +106,12 @@ public BattleLog battle(GameCharacter player, GameCharacter opponent) {
106106

107107
if (attacker == playerContext) {
108108
player.earnGold(100L);
109-
battleLog.add("전투 승리보상으로 100 골드가 지급되었습니다.");
109+
battleLog.add("%s(이)가 전투 승리보상으로 100 골드가 지급되었습니다.", playerContext.getName());
110110
} else {
111111
player.loseGold(25L);
112-
battleLog.add("패배하여 25 골드를 갈취당했습니다.");
112+
opponent.earnGold(25L);
113+
battleLog.add("%s님이 %s님에게 패배하여 25 골드를 갈취당했습니다.", playerContext.getName(),
114+
opponentContext.getName());
113115
}
114116

115117
return battleLog;
@@ -128,10 +130,12 @@ public BattleLog battle(GameCharacter player, GameCharacter opponent) {
128130

129131
if (defender == playerContext) {
130132
player.earnGold(100L);
131-
battleLog.add("전투 승리보상으로 100 골드가 지급되었습니다.");
133+
battleLog.add("%s님에게 전투 승리보상으로 100 골드가 지급되었습니다.", playerContext.getName());
132134
} else {
133135
player.loseGold(25L);
134-
battleLog.add("패배하여 25 골드를 갈취당했습니다.");
136+
opponent.earnGold(25L);
137+
battleLog.add("%s님이 %s님에게 패배하여 25 골드를 갈취당했습니다.", playerContext.getName(),
138+
opponentContext.getName());
135139
}
136140

137141
return battleLog;
@@ -140,7 +144,7 @@ public BattleLog battle(GameCharacter player, GameCharacter opponent) {
140144
currentSkillIndex = (currentSkillIndex + 1) % 3;
141145
}
142146

143-
battleLog.add("전투가 종료되었습니다. 양쪽 모두 살아남았습니다. 무승부입니다!");
147+
battleLog.add("전투가 종료되었습니다. 양쪽 모두 살아남았습니다. 무승부입니다.");
144148
battleLog.setPlayerWin(false);
145149
return battleLog;
146150
}
@@ -175,7 +179,7 @@ public List<BattleHistory> getBattleHistory(GameCharacter character) {
175179

176180
public GameCharacter getRandomEnemyCharacter(Long userId, Long playerId) {
177181

178-
GameCharacterMatchTokenBucket playerTokenBucket = matchTokenBucketRepository.findByCharacterId(playerId)
182+
GameCharacterMatchTokenBucket playerTokenBucket = matchTokenBucketRepository.findByCharacterId(playerId)
179183
.orElseThrow(() -> new GameException(GameExceptionCode.PLAYER_TOKEN_BUCKET_NOT_EXISTS));
180184

181185
playerTokenBucket.consumeBattleToken();
@@ -186,7 +190,7 @@ public GameCharacter getRandomEnemyCharacter(Long userId, Long playerId) {
186190

187191
public RandomEncounter getRandomEncounter(Long playerId) {
188192

189-
GameCharacterMatchTokenBucket playerTokenBucket = matchTokenBucketRepository.findByCharacterId(playerId)
193+
GameCharacterMatchTokenBucket playerTokenBucket = matchTokenBucketRepository.findByCharacterId(playerId)
190194
.orElseThrow(() -> new GameException(GameExceptionCode.PLAYER_TOKEN_BUCKET_NOT_EXISTS));
191195

192196
playerTokenBucket.consumeEncounterToken();
@@ -217,7 +221,7 @@ public EncounterLog encounterHappen(GameCharacter player, Long encounterId, bool
217221
CharacterContext playerContext = CharacterContext.from(player.getName(), playerStats);
218222

219223
Inventory playerInventory = inventoryRepository.findByGameCharacterId(player.getId())
220-
.orElseThrow(() -> new GameException(GameExceptionCode.INVENTORY_NOT_FOUND));
224+
.orElseThrow(() -> new GameException(GameExceptionCode.INVENTORY_NOT_FOUND));
221225

222226
EncounterLog resultLog = new EncounterLog();
223227

@@ -235,4 +239,9 @@ public EncounterHistory createEncounterHistory(GameCharacter player, EncounterLo
235239
.build());
236240
}
237241

242+
public List<BattleHistory> getCharacterBattleHistory(GameCharacter player) {
243+
244+
return battleHistoryRepository.findCreatedInLast24Hours(player.getId());
245+
}
246+
238247
}

src/main/java/org/ezcode/codetest/domain/game/strategy/skill/skillstrategyimpl/LifeStealSkill.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public boolean useSkill(CharacterContext attacker, CharacterContext defender, Ba
3939

4040
if (RNG.nextDouble() * 100 < attacker.getStun()) {
4141
defender.consumeActionPoints();
42-
log.add("스턴! %s가 잠시 머리를 잃었습니다 — 행동력 1 감소 → 남은 AP %d", defender.getName(), defender.getAp());
42+
log.add("스턴! %s(이)가 잠시 정신을 잃었습니다 — 행동력 1 감소 → 남은 AP %d", defender.getName(), defender.getAp());
4343
}
4444

4545
log.add("[%s] 남은 체력: %,.1f | [%s] 남은 체력: %,.1f", attacker.getName(), attacker.getHp(), defender.getName(), defender.getHp());

src/main/java/org/ezcode/codetest/domain/game/util/StatUpdateUtil.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ public StatUpdateUtil() {
111111
}
112112

113113
public Map<Stat, Double> getStatIncreasePerProblem(String categoryDescription) {
114-
//TODO : 나중에 NULL 처리하기
115114
return increasedStatRate.get(categoryDescription).asImmutableMap();
116115
}
117116

src/main/java/org/ezcode/codetest/infrastructure/cache/config/CaffeineCacheConfig.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ public CacheManager cacheManager() {
2323
.expireAfterWrite(Duration.ofMinutes(1))
2424
.build());
2525

26+
CaffeineCache historyCache = new CaffeineCache("histories",
27+
Caffeine.newBuilder()
28+
.expireAfterWrite(Duration.ofMinutes(1))
29+
.build());
30+
2631
CaffeineCache skillCache = new CaffeineCache("skill",
2732
Caffeine.newBuilder()
2833
.expireAfterWrite(Duration.ofMinutes(10))
@@ -38,8 +43,14 @@ public CacheManager cacheManager() {
3843
.expireAfterWrite(Duration.ofMinutes(10))
3944
.build());
4045

46+
CaffeineCache choiceCache = new CaffeineCache("choices",
47+
Caffeine.newBuilder()
48+
.expireAfterWrite(Duration.ofMinutes(10))
49+
.build());
50+
4151
SimpleCacheManager manager = new SimpleCacheManager();
42-
manager.setCaches(List.of(skillCache, itemsByCategoryCache, encountersCache, countCache));
52+
manager.setCaches(
53+
List.of(skillCache, itemsByCategoryCache, encountersCache, countCache, historyCache, choiceCache));
4354
return manager;
4455
}
4556
}

src/main/java/org/ezcode/codetest/infrastructure/event/config/CustomHandShakeHandler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ protected Principal determineUser(
3131
if (query != null && query.startsWith("token=")) {
3232
tokenParam = query.substring(6);
3333
}
34-
//TODO : 토큰의 대한 예외처리 아직 구현 x
3534

3635
Claims claims = jwtUtil.extractClaims(tokenParam);
3736
String email = claims.get("email", String.class);

src/main/java/org/ezcode/codetest/infrastructure/event/listener/WebSocketEventListener.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
import org.springframework.web.socket.messaging.SessionDisconnectEvent;
1717

1818
import lombok.RequiredArgsConstructor;
19+
import lombok.extern.slf4j.Slf4j;
1920

21+
@Slf4j
2022
@Component
2123
@RequiredArgsConstructor
2224
public class WebSocketEventListener implements ApplicationListener<SessionDisconnectEvent> {
@@ -29,23 +31,27 @@ public class WebSocketEventListener implements ApplicationListener<SessionDiscon
2931
@Override
3032
public void onApplicationEvent(SessionDisconnectEvent event) {
3133

32-
StompHeaderAccessor h = StompHeaderAccessor.wrap(event.getMessage());
33-
String sessionId = h.getSessionId();
34+
try {
35+
StompHeaderAccessor h = StompHeaderAccessor.wrap(event.getMessage());
36+
String sessionId = h.getSessionId();
3437

35-
String email = h.getUser().getName();
36-
String nickName = userDomainService.getUser(email).getNickname();
38+
String email = h.getUser().getName();
39+
String nickName = userDomainService.getUser(email).getNickname();
3740

38-
RoomSessionInfo roomData = sessionService.removeSessionCount(sessionId);
39-
ChatRoom chatRoom = chattingDomainService.getChatRoom(roomData.roomId());
41+
RoomSessionInfo roomData = sessionService.removeSessionCount(sessionId);
42+
ChatRoom chatRoom = chattingDomainService.getChatRoom(roomData.roomId());
4043

41-
Map<String, Object> payload = new HashMap<>();
42-
payload.put("roomId", chatRoom.getId());
43-
payload.put("title", chatRoom.getTitle());
44-
payload.put("headCount", roomData.headCount());
45-
payload.put("eventType", "UPDATE");
44+
Map<String, Object> payload = new HashMap<>();
45+
payload.put("roomId", chatRoom.getId());
46+
payload.put("title", chatRoom.getTitle());
47+
payload.put("headCount", roomData.headCount());
48+
payload.put("eventType", "UPDATE");
4649

47-
messageService.handleChatRoomParticipantCountChange(payload);
48-
messageService.handleChatRoomEntryExitMessage(ChatMessageTemplate.CHAT_ROOM_LEFT.format(nickName),
49-
chatRoom.getId());
50+
messageService.handleChatRoomParticipantCountChange(payload);
51+
messageService.handleChatRoomEntryExitMessage(ChatMessageTemplate.CHAT_ROOM_LEFT.format(nickName),
52+
chatRoom.getId());
53+
} catch (Exception e) {
54+
log.warn("SessionDisconnectEvent 처리 중 예외 발생, 채팅 관련 웹소켓 세션이 아닙니다.", e);
55+
}
5056
}
5157
}

0 commit comments

Comments
 (0)