-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathChatRequestValidator.cs
More file actions
101 lines (88 loc) · 4.95 KB
/
ChatRequestValidator.cs
File metadata and controls
101 lines (88 loc) · 4.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
using ProjectVG.Infrastructure.Persistence.Session;
using ProjectVG.Application.Services.Users;
using ProjectVG.Application.Services.Character;
using ProjectVG.Application.Services.Credit;
using Microsoft.Extensions.Logging;
using ProjectVG.Application.Models.Chat;
namespace ProjectVG.Application.Services.Chat.Validators
{
public class ChatRequestValidator
{
private readonly ISessionStorage _sessionStorage;
private readonly IUserService _userService;
private readonly ICharacterService _characterService;
private readonly ICreditManagementService _tokenManagementService;
private readonly ILogger<ChatRequestValidator> _logger;
// 채팅 기본 예상 비용 (실제 비용은 처리 후 결정됨)
private const decimal ESTIMATED_CHAT_COST = 10m;
public ChatRequestValidator(
ISessionStorage sessionStorage,
IUserService userService,
ICharacterService characterService,
ICreditManagementService tokenManagementService,
ILogger<ChatRequestValidator> logger)
{
_sessionStorage = sessionStorage;
_userService = userService;
_characterService = characterService;
_tokenManagementService = tokenManagementService;
_logger = logger;
}
public async Task ValidateAsync(ChatRequestCommand command)
{
// 세션 검증 - 사용자 활성 세션 확인
await ValidateUserSessionAsync(command.UserId).ConfigureAwait(false);
var userExists = await _userService.ExistsByIdAsync(command.UserId).ConfigureAwait(false);
if (!userExists) {
_logger.LogWarning("사용자 ID 검증 실패: {UserId}", command.UserId);
throw new NotFoundException(ErrorCode.USER_NOT_FOUND, command.UserId);
}
var characterExists = await _characterService.CharacterExistsAsync(command.CharacterId).ConfigureAwait(false);
if (!characterExists) {
_logger.LogWarning("캐릭터 ID 검증 실패: {CharacterId}", command.CharacterId);
throw new NotFoundException(ErrorCode.CHARACTER_NOT_FOUND, command.CharacterId);
}
// 토큰 잔액 검증 - 예상 비용으로 미리 확인
var balance = await _tokenManagementService.GetCreditBalanceAsync(command.UserId).ConfigureAwait(false);
var currentBalance = balance.CurrentBalance;
if (currentBalance <= 0) {
_logger.LogWarning("토큰 잔액 부족 (0 토큰): UserId={UserId}", command.UserId);
throw new ValidationException(ErrorCode.INSUFFICIENT_CREDIT_BALANCE, $"토큰이 부족합니다. 현재 잔액: {currentBalance} 토큰, 필요 토큰: {ESTIMATED_CHAT_COST} 토큰");
}
var hasSufficientTokens = currentBalance >= ESTIMATED_CHAT_COST;
if (!hasSufficientTokens) {
_logger.LogWarning("토큰 부족: UserId={UserId}, 현재잔액={CurrentBalance}, 필요토큰={RequiredTokens}",
command.UserId, currentBalance, ESTIMATED_CHAT_COST);
throw new ValidationException(ErrorCode.INSUFFICIENT_CREDIT_BALANCE, $"토큰이 부족합니다. 현재 잔액: {currentBalance} 토큰, 필요 토큰: {ESTIMATED_CHAT_COST} 토큰");
}
_logger.LogDebug("채팅 요청 검증 완료: {UserId}, {CharacterId}", command.UserId, command.CharacterId);
}
/// <summary>
/// 사용자 세션 유효성 검증
/// </summary>
private async Task ValidateUserSessionAsync(Guid userId)
{
try {
// 사용자 ID를 기반으로 세션 조회
var userSessions = (await _sessionStorage
.GetSessionsByUserIdAsync(userId.ToString())
.ConfigureAwait(false))
.ToList();
if (userSessions.Count == 0) {
_logger.LogWarning("유효하지 않은 사용자 세션: {UserId}", userId);
throw new ValidationException(ErrorCode.SESSION_EXPIRED, "세션이 만료되었습니다. 다시 로그인해 주세요.");
}
// 세션이 존재하면 로그 기록
_logger.LogDebug("세션 검증 성공: {UserId}, 활성 세션 수: {SessionCount}", userId, userSessions.Count);
}
catch (ValidationException) {
throw; // 검증 예외는 그대로 전파
}
catch (Exception ex) {
_logger.LogError(ex, "세션 검증 중 예상치 못한 오류: {UserId}", userId);
// 세션 스토리지 오류 시에는 검증을 통과시키되 로그는 남김 (서비스 가용성 우선)
_logger.LogWarning("세션 스토리지 오류로 인해 세션 검증을 건너뜁니다: {UserId}", userId);
}
}
}
}