This commit is contained in:
2025-11-11 14:38:15 +09:00
parent 1883fef583
commit 5a0785f262
9 changed files with 358 additions and 208 deletions
+15 -3
View File
@@ -1,12 +1,11 @@
// lib/services/identity_service.dart
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uuid/uuid.dart';
// 앱-고유 ID와 사용자 이름을 관리하는 서비스
// 앱-고유 ID와 사용자 이름, 레벨 진행 상황을 관리하는 서비스
class IdentityService {
static const String _userIdKey = 'app_user_id';
static const String _userNameKey = 'app_user_name';
static const String _maxLevelKey = 'max_unlocked_level'; // 👈 [추가]
// 1. 앱-고유 ID 가져오기 (없으면 생성)
Future<String> getOrCreateUserId() async {
@@ -32,4 +31,17 @@ class IdentityService {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(_userNameKey, name);
}
// 4. 🔽 [추가] 현재 잠금 해제된 최고 레벨 가져오기
Future<int> getMaxUnlockedLevel() async {
final prefs = await SharedPreferences.getInstance();
// 최초 실행 시 1 (L1) 반환, 9레벨 클리어 시 99 반환
return prefs.getInt(_maxLevelKey) ?? 1;
}
// 5. 🔽 [추가] 새 레벨 잠금 해제
Future<void> saveMaxUnlockedLevel(int level) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt(_maxLevelKey, level);
}
}
+9 -11
View File
@@ -1,5 +1,5 @@
import 'dart:convert';
import 'dart:developer'; // 👈 [추가] log 함수를 위한 임포트
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:sudoku_app/models/sudoku_game_dto.dart';
import 'package:sudoku_app/models/unified_rank_dto.dart';
@@ -8,11 +8,13 @@ import 'package:sudoku_app/models/game_rank_dto.dart';
class PuzzleService {
final String _baseUrl = "https://lunaticbum.kr";
// ... (startGame 함수는 동일) ...
// 🔽 [수정] 'difficulty' 파라미터 1개만 받음 (1~11)
Future<SudokuGameDto> startGame(String difficulty) async {
final response = await http.get(
// 🔽 [수정] 'difficulty' 파라미터만 전달
Uri.parse('$_baseUrl/puzzle/sudoku/start?difficulty=$difficulty'),
);
if (response.statusCode == 200) {
final data = jsonDecode(utf8.decode(response.bodyBytes));
return SudokuGameDto.fromJson(data);
@@ -21,7 +23,7 @@ class PuzzleService {
}
}
// ... (validateSolution 함수는 동일) ...
// 'puzzleId'를 받아 검증 (서버 DTO와 일치)
Future<bool> validateSolution(int puzzleId, String answer) async {
final response = await http.post(
Uri.parse('$_baseUrl/puzzle/sudoku/validate'),
@@ -31,6 +33,7 @@ class PuzzleService {
'answer': answer,
}),
);
if (response.statusCode == 200) {
return jsonDecode(response.body)['correct'] ?? false;
} else {
@@ -40,12 +43,10 @@ class PuzzleService {
}
}
// POST /api/ranks/submit
// 랭킹 등록
Future<void> submitRank(UnifiedRankDto rankDto) async {
final requestBody = jsonEncode(rankDto.toJson());
// 🔽 [로그 추가] 1. 서버로 전송하는 JSON 데이터 출력
log(">>> 랭킹 등록 요청: $requestBody");
final response = await http.post(
@@ -55,22 +56,19 @@ class PuzzleService {
);
if (response.statusCode != 200) {
// 🔽 [로그 추가] 2. 서버가 200(OK)이 아닌 응답을 줬을 때
log("<<< 랭킹 등록 실패: ${response.statusCode}");
try {
final errorBody = utf8.decode(response.bodyBytes);
log("<<< 서버 에러 메시지: $errorBody"); // 👈 (예: "이미 사용 중인 이름입니다.")
log("<<< 서버 에러 메시지: $errorBody");
throw Exception(errorBody);
} catch (e) {
throw Exception('랭킹 등록 실패: ${response.reasonPhrase}');
}
}
// 🔽 [로그 추가] 3. 성공 시
log("<<< 랭킹 등록 성공: 200 OK");
}
// ... (fetchRanks 함수는 동일) ...
// 랭킹 조회
Future<List<GameRankDto>> fetchRanks(String gameType, String? contextId) async {
final queryParams = {
'gameType': gameType,