Files
UnionApp/lib/providers/user_provider.dart
spasolreisa f5f62c828d ver1.00.00
update
2026-04-21 00:28:41 +08:00

263 lines
7.4 KiB
Dart

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../model/user_model.dart';
import '../service/user_service.dart';
import '../tool/encryption_util.dart';
import 'dart:async'; // 必须加这个
class UserProvider with ChangeNotifier {
UserModel? _user;
String? _token;
List<String> _sexTags = [];
String _scoreDataSource = 'cn';
String? _selectedSegaId;
String? _selectedCnUserName;
// ===================== 【新增:初始化等待控制】 =====================
bool _isInitialized = false;
Completer<void>? _initCompleter;
// 外部等待初始化完成的方法
Future<void> waitInit() async {
if (_isInitialized) return;
if (_initCompleter == null) {
_initCompleter = Completer<void>();
}
await _initCompleter!.future;
}
UserModel? get user => _user;
String? get token => _token;
bool get isLogin => _token != null && _user != null;
String get username => _user?.name ?? "未登录";
List<String> get sexTags => _sexTags;
String get scoreDataSource => _scoreDataSource;
String? get selectedSegaId => _selectedSegaId;
String? get selectedCnUserName => _selectedCnUserName;
List<SegaCard> get availableSegaCards => _user?.segaCards ?? [];
List<String> get availableCnUserNames {
final map = _user?.userName2userId;
if (map == null || map.isEmpty) return [];
return map.keys.toList();
}
String get avatarUrl {
if (_user?.iconId != null) {
return "https://cdn.godserver.cn/resource/static/coll/Icon/UI_Icon_${_user!.iconId!}.png";
} else {
return "https://cdn.godserver.cn/resource/static/coll/Icon/UI_Icon_605526.png";
}
}
static UserProvider? _instance;
static UserProvider get instance {
_instance ??= UserProvider();
return _instance!;
}
Future<void> initUser() async {
try {
final prefs = await SharedPreferences.getInstance();
_token = prefs.getString("token");
_scoreDataSource = prefs.getString("scoreDataSource") ?? 'cn';
_selectedSegaId = prefs.getString("selectedSegaId");
_selectedCnUserName = prefs.getString("selectedCnUserName");
if (_token != null) {
try {
_user = await UserService.getUserInfo(_token!);
await fetchSexTags();
if (_selectedSegaId != null && !availableSegaCards.any((c) => c.segaId == _selectedSegaId)) {
_selectedSegaId = null;
await prefs.remove("selectedSegaId");
}
if (_selectedCnUserName != null && !availableCnUserNames.contains(_selectedCnUserName)) {
_selectedCnUserName = null;
await prefs.remove("selectedCnUserName");
}
} catch (e) {
await logout();
}
}
} finally {
// ===================== 【关键:标记初始化完成】 =====================
_isInitialized = true;
_initCompleter?.complete();
notifyListeners();
}
}
Future<void> login(String username, String twoKeyCode) async {
try {
_token = await UserService.login(username, twoKeyCode);
if (_token != null && _token!.isNotEmpty) {
final prefs = await SharedPreferences.getInstance();
await prefs.setString("token", _token!);
_user = await UserService.getUserInfo(_token!);
await fetchSexTags();
}
} catch (e) {
rethrow;
} finally {
notifyListeners();
}
}
Future<void> register(String username, String password, String inviter) async {
await UserService.register(username, password, inviter);
}
Future<void> logout() async {
_token = null;
_user = null;
_selectedSegaId = null;
_selectedCnUserName = null;
final prefs = await SharedPreferences.getInstance();
await prefs.remove("token");
await prefs.remove("selectedSegaId");
await prefs.remove("selectedCnUserName");
notifyListeners();
}
Future<void> saveUserInfo() async {
if (_token == null || _user == null) return;
try {
final userJson = _user!.toJson();
final encrypted = EncryptionUtil.encryptAndCompress(jsonEncode(userJson));
await UserService.updateUser(_token!, encrypted);
_user = await UserService.getUserInfo(_token!);
notifyListeners();
} catch (e) {
rethrow;
}
}
Future<bool> refreshToken() async {
if (_token == null) return false;
try {
final newToken = await UserService.refreshToken(_token!);
if (newToken != null) {
_token = newToken;
final prefs = await SharedPreferences.getInstance();
await prefs.setString("token", newToken);
notifyListeners();
return true;
}
} catch (e) {
await logout();
}
return false;
}
Future<Map<String, dynamic>> fetchRadarData(String id) async {
if (_token == null) throw "请先登录";
try {
return await UserService.getRadarData(_token!, id);
} catch (e) {
rethrow;
}
}
Future<void> fetchSexTags() async {
_sexTags = await UserService.fetchSexTags();
notifyListeners();
}
void updateUser(UserModel? newUser) {
_user = newUser;
notifyListeners();
}
Future<void> setScoreDataSource(String source) async {
_scoreDataSource = source;
final prefs = await SharedPreferences.getInstance();
await prefs.setString("scoreDataSource", source);
notifyListeners();
}
Future<void> setSelectedSegaId(String? segaId) async {
_selectedSegaId = segaId;
final prefs = await SharedPreferences.getInstance();
if (segaId != null) {
await prefs.setString("selectedSegaId", segaId);
} else {
await prefs.remove("selectedSegaId");
}
notifyListeners();
}
Future<void> setSelectedCnUserName(String? userName) async {
_selectedCnUserName = userName;
final prefs = await SharedPreferences.getInstance();
if (userName != null && userName.isNotEmpty) {
await prefs.setString("selectedCnUserName", userName);
} else {
await prefs.remove("selectedCnUserName");
}
notifyListeners();
}
Future<void> syncSegaScore(Map<String, dynamic> data) async {
if (_token == null || _user == null) {
throw "请先登录";
}
if (_selectedSegaId == null || _selectedSegaId!.isEmpty) {
throw "请先选择要同步的 SegaID";
}
try {
final result = await UserService.uploadSegaRating(
_token!,
_selectedSegaId!,
data,
);
if (result["code"] == 200) {
print("✅ 同步成功:${result["msg"]}");
} else {
print("❌ 同步失败:${result["msg"]}");
throw result["msg"] ?? "同步失败";
}
} catch (e) {
print("❌ 同步异常:$e");
rethrow;
}
}
Future<Map<String, dynamic>> uploadStegImage(Uint8List imageBytes, {String? segaId}) async {
if (_token == null) {
throw Exception("请先登录");
}
final targetSegaId = segaId ?? _selectedSegaId;
if (targetSegaId == null || targetSegaId.isEmpty) {
throw Exception("请提供或选择要隐藏的 SegaID");
}
try {
print("🚀 开始隐写并上传... SegaID: $targetSegaId");
final result = await UserService.uploadStegImage(
token: _token!,
segaId: targetSegaId,
originalImageBytes: imageBytes,
);
print("✅ 隐写上传成功: ${result['msg']}");
return result;
} catch (e) {
print("❌ 隐写上传失败: $e");
rethrow;
}
}
}