Files
UnionApp/lib/providers/user_provider.dart
spasolreisa c5c3f7c8f5 ver1.00.00
bindQR
2026-04-19 22:22:04 +08:00

261 lines
7.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:convert';
import 'dart:typed_data'; // 确保导入,因为 uploadStegImage 需要 Uint8List
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';
class UserProvider with ChangeNotifier {
UserModel? _user;
String? _token;
List<String> _sexTags = [];
// --- 成绩数据源相关状态 ---
String _scoreDataSource = 'cn'; // 'cn' or 'sega'
String? _selectedSegaId; // 选中的 SegaID
// --- 新增:国服多账号支持 ---
String? _selectedCnUserName; // 选中的国服用户名 (对应 userName2userId 的 key)
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 {
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();
// 校验 SegaID 有效性
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();
}
}
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);
// 注意:注册后通常不直接登录,或者根据业务需求决定。这里保持原逻辑。
// await login(username, password);
}
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();
}
// --- 设置选中的 SegaID ---
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;
}
}
// ================= 新增:上传隐写图片 =================
/// 上传带有隐写数据的图片
/// [imageBytes] 原始图片的字节数据 (Uint8List)
/// [segaId] 要隐藏进图片的 SegaID (如果为 null则使用当前选中的 _selectedSegaId)
Future<Map<String, dynamic>> uploadStegImage(Uint8List imageBytes, {String? segaId}) async {
if (_token == null) {
throw Exception("请先登录");
}
// 如果未指定 segaId则使用当前选中的
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;
}
}
}