games/packages/service_api/lib/services/identity_service.dart
2025-12-15 18:18:17 +09:00

246 lines
8.6 KiB
Dart

import 'dart:convert';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:uuid/uuid.dart';
import '../models/cognitive_type.dart';
import '../models/assessment_data.dart';
// -----------------------------------------------------------------------------
// [Fix] UserSession 정의 및 isGuest 추가
// -----------------------------------------------------------------------------
class UserSession {
final String userId;
final String? userName;
final String? email;
final String? photoUrl;
final String? provider; // 'google', 'apple', 'guest'
UserSession({
required this.userId,
this.userName,
this.email,
this.photoUrl,
this.provider,
});
// [Fix] 에러 해결: isGuest 게터 추가
bool get isGuest => provider == 'guest' || provider == null;
Map<String, dynamic> toJson() => {
'userId': userId,
'userName': userName,
'email': email,
'photoUrl': photoUrl,
'provider': provider,
};
factory UserSession.fromJson(Map<String, dynamic> json) => UserSession(
userId: json['userId'],
userName: json['userName'],
email: json['email'],
photoUrl: json['photoUrl'],
provider: json['provider'],
);
}
// -----------------------------------------------------------------------------
// IdentityService 구현
// -----------------------------------------------------------------------------
class IdentityService {
static const String _userIdKey = 'app_user_id';
static const String _userSessionKey = 'app_user_session';
static const String _userNameKey = 'app_user_name'; // 추가
static const String _assessmentHistoryKey = 'cognitive_assessment_history';
final _storage = const FlutterSecureStorage();
final _uuid = const Uuid();
IOSOptions _getIOSOptions() => const IOSOptions(accessibility: KeychainAccessibility.first_unlock);
AndroidOptions _getAndroidOptions() => const AndroidOptions(encryptedSharedPreferences: true);
// ===========================================================================
// 1. 유저 세션 관리 (호환성 복구)
// ===========================================================================
Future<String> getOrCreateUser() async {
String? userId = await _storage.read(key: _userIdKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
if (userId == null) {
userId = _uuid.v4();
await _storage.write(key: _userIdKey, value: userId, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
}
return userId;
}
/// [Fix] SessionNotifier 에러 해결
Future<UserSession?> getUserSession() async {
String? jsonStr = await _storage.read(key: _userSessionKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
if (jsonStr == null) return null;
try {
return UserSession.fromJson(jsonDecode(jsonStr));
} catch (e) {
return null;
}
}
/// [Fix] SessionNotifier 에러 해결
Future<UserSession> saveSocialLogin({
required String userId,
String? email,
String? name,
String? photoUrl,
required String provider,
}) async {
final session = UserSession(
userId: userId,
email: email,
userName: name,
photoUrl: photoUrl,
provider: provider,
);
await _storage.write(
key: _userSessionKey,
value: jsonEncode(session.toJson()),
iOptions: _getIOSOptions(),
aOptions: _getAndroidOptions()
);
await _storage.write(key: _userIdKey, value: userId, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
return session;
}
/// [Fix] SessionNotifier 에러 해결
Future<void> logout() async {
await _storage.delete(key: _userSessionKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
}
/// [Fix] GameCompletionScreen 에러 해결
Future<void> saveUserName(String name) async {
await _storage.write(key: _userNameKey, value: name, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
// 세션이 있다면 세션 이름도 업데이트
final currentSession = await getUserSession();
if (currentSession != null) {
final newSession = UserSession(
userId: currentSession.userId,
userName: name,
email: currentSession.email,
photoUrl: currentSession.photoUrl,
provider: currentSession.provider,
);
await _storage.write(
key: _userSessionKey,
value: jsonEncode(newSession.toJson()),
iOptions: _getIOSOptions(),
aOptions: _getAndroidOptions()
);
}
}
Future<String?> getUserName() async {
return await _storage.read(key: _userNameKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
}
// ===========================================================================
// 2. 진단 기록 (Assessment)
// ===========================================================================
Future<void> saveAssessmentResult(Map<CognitiveArea, int> scores) async {
final record = AssessmentRecord(
id: _uuid.v4(),
date: DateTime.now(),
scores: scores,
);
final history = await getAssessmentHistory();
history.add(record);
final jsonString = jsonEncode(history.map((e) => e.toJson()).toList());
await _storage.write(key: _assessmentHistoryKey, value: jsonString, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
}
Future<List<AssessmentRecord>> getAssessmentHistory() async {
final jsonString = await _storage.read(key: _assessmentHistoryKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
if (jsonString == null) return [];
try {
final List<dynamic> jsonList = jsonDecode(jsonString);
return jsonList.map((e) => AssessmentRecord.fromJson(e)).toList();
} catch (e) {
return [];
}
}
/// [Fix] SettingsScreen 에러 해결
Future<void> clearAssessmentHistory() async {
await _storage.delete(key: _assessmentHistoryKey, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
}
Future<Map<CognitiveArea, int>?> getCognitiveScores() async {
final history = await getAssessmentHistory();
if (history.isEmpty) return null;
history.sort((a, b) => b.date.compareTo(a.date));
return history.first.scores;
}
// ===========================================================================
// 3. 게임 데이터 관리 (통합 + 레거시 호환)
// ===========================================================================
String _getMaxLevelKey(String gameType) => 'max_level_${gameType.toLowerCase()}';
String _getRankMapKey(String gameType) => 'rank_map_${gameType.toLowerCase()}';
Future<int> getMaxUnlockedLevel({String gameType = 'SUDOKU'}) async {
final key = _getMaxLevelKey(gameType);
String? levelString = await _storage.read(key: key, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
return int.parse(levelString ?? '1');
}
/// [Fix] 기존 게임들이 호출하는 메서드 복구
Future<void> saveMaxUnlockedLevel(int level, {String gameType = 'SUDOKU'}) async {
await _storage.write(
key: _getMaxLevelKey(gameType),
value: level.toString(),
iOptions: _getIOSOptions(),
aOptions: _getAndroidOptions()
);
}
Future<Map<int, int>> getLastSavedRankMap({required String gameType}) async {
final key = _getRankMapKey(gameType);
String? jsonString = await _storage.read(key: key, iOptions: _getIOSOptions(), aOptions: _getAndroidOptions());
if (jsonString == null) return {};
try {
final Map<String, dynamic> decodedMap = jsonDecode(jsonString);
return decodedMap.map((k, v) => MapEntry(int.parse(k), v as int));
} catch (e) {
return {};
}
}
/// [Fix] LobbyHelper 에러 해결
Future<void> saveLastRankMap(Map<int, int> rankMap, {required String gameType}) async {
final String jsonString = jsonEncode(rankMap.map((k, v) => MapEntry(k.toString(), v)));
await _storage.write(
key: _getRankMapKey(gameType),
value: jsonString,
iOptions: _getIOSOptions(),
aOptions: _getAndroidOptions()
);
}
/// [신규] 게임 결과 통합 처리
Future<void> submitGameResult({
required String gameType,
required int level,
required int stars,
}) async {
final rankMap = await getLastSavedRankMap(gameType: gameType);
final int oldStars = rankMap[level] ?? 0;
if (stars > oldStars) {
rankMap[level] = stars;
await saveLastRankMap(rankMap, gameType: gameType);
}
final int currentMax = await getMaxUnlockedLevel(gameType: gameType);
if (level >= currentMax) {
await saveMaxUnlockedLevel(level + 1, gameType: gameType);
}
}
}