initial
This commit is contained in:
0
lib/service/recommendation_helper.dart
Normal file
0
lib/service/recommendation_helper.dart
Normal file
107
lib/service/song_service.dart
Normal file
107
lib/service/song_service.dart
Normal file
@@ -0,0 +1,107 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../model/song_model.dart';
|
||||
import 'user_service.dart'; // 引用 baseUrl
|
||||
|
||||
class SongService {
|
||||
static final Dio _dio = new Dio();
|
||||
static const String _cacheKeySongs = 'cached_songs_json';
|
||||
static const String _cacheKeyInfo = 'cached_song_info';
|
||||
|
||||
/// 获取所有歌曲数据(带缓存逻辑)
|
||||
static Future<List<SongModel>> getAllSongs() async {
|
||||
try {
|
||||
// 1. 获取远程元数据 (判断是否需要更新)
|
||||
final metaRes = await _dio.get('${UserService.baseUrl}/api/union/');
|
||||
final remoteSize = metaRes.data['songSize'] as int? ?? 0;
|
||||
final remoteVersion = metaRes.data['version'] as String? ?? '';
|
||||
|
||||
// 2. 获取本地缓存信息
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final cachedInfoJson = prefs.getString(_cacheKeyInfo);
|
||||
|
||||
bool needFetch = true;
|
||||
|
||||
if (cachedInfoJson != null) {
|
||||
final cachedInfo = SongCacheInfo.fromJson(jsonDecode(cachedInfoJson));
|
||||
// 如果本地歌曲数量与远程一致,且版本号一致,则使用缓存
|
||||
// 注意:这里简单判断 size 相同即认为无需更新,实际业务中可能需要更严格的版本对比
|
||||
if (cachedInfo.songSize == remoteSize && cachedInfo.version == remoteVersion) {
|
||||
needFetch = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (needFetch) {
|
||||
print("SongService: Cache miss or outdated. Fetching full song list...");
|
||||
// 3. 拉取最新全量数据
|
||||
final songsRes = await _dio.get('${UserService.baseUrl}/api/union/uni');
|
||||
final List<dynamic> songsJson = songsRes.data as List;
|
||||
|
||||
// 4. 保存新数据和元数据到本地
|
||||
final songsList = songsJson.map((e) => SongModel.fromJson(e)).toList();
|
||||
final newInfo = SongCacheInfo(
|
||||
songSize: remoteSize,
|
||||
version: remoteVersion,
|
||||
lastUpdate: DateTime.now()
|
||||
);
|
||||
|
||||
await prefs.setString(_cacheKeySongs, jsonEncode(songsJson));
|
||||
await prefs.setString(_cacheKeyInfo, jsonEncode(newInfo.toJson()));
|
||||
|
||||
return songsList;
|
||||
} else {
|
||||
print("SongService: Using cached song list.");
|
||||
// 5. 使用缓存
|
||||
final cachedSongsJson = prefs.getString(_cacheKeySongs);
|
||||
if (cachedSongsJson != null) {
|
||||
final List<dynamic> songsJson = jsonDecode(cachedSongsJson);
|
||||
return songsJson.map((e) => SongModel.fromJson(e)).toList();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print("SongService Error: $e");
|
||||
// 如果出错,尝试返回旧缓存以防万一
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final cachedSongsJson = prefs.getString(_cacheKeySongs);
|
||||
if (cachedSongsJson != null) {
|
||||
final List<dynamic> songsJson = jsonDecode(cachedSongsJson);
|
||||
return songsJson.map((e) => SongModel.fromJson(e)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
static Future<Map<String, dynamic>> getUserAllScores(String token, {String? name}) async {
|
||||
try {
|
||||
if(name!=null) {
|
||||
return await UserService.getUserAllScores(token,name: name);
|
||||
}else{
|
||||
return await UserService.getUserAllScores(token);
|
||||
}
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
static String _getErrorMessage(DioException e) {
|
||||
if (e.response?.data != null) {
|
||||
final data = e.response!.data;
|
||||
if (data is Map && data["msg"] != null) {
|
||||
return data["msg"].toString();
|
||||
}
|
||||
}
|
||||
switch (e.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
case DioExceptionType.receiveTimeout:
|
||||
case DioExceptionType.sendTimeout:
|
||||
return "网络超时";
|
||||
case DioExceptionType.connectionError:
|
||||
return "网络异常";
|
||||
default:
|
||||
return "请求失败";
|
||||
}
|
||||
}
|
||||
}
|
||||
286
lib/service/user_service.dart
Normal file
286
lib/service/user_service.dart
Normal file
@@ -0,0 +1,286 @@
|
||||
import 'dart:convert';
|
||||
import 'package:dio/dio.dart';
|
||||
import '../model/user_model.dart';
|
||||
import '../tool/encryption_util.dart';
|
||||
|
||||
class UserService {
|
||||
static final Dio _dio = Dio();
|
||||
static const String baseUrl = "https://union.godserver.cn";
|
||||
|
||||
// 注册
|
||||
static Future<void> register(String username, String password, String inviter) async {
|
||||
try {
|
||||
await _dio.post(
|
||||
'$baseUrl/user/register',
|
||||
data: {
|
||||
"username": username,
|
||||
"password": password,
|
||||
"inviter": inviter,
|
||||
},
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 登录
|
||||
static Future<String> login(String username, String twoKeyCode) async {
|
||||
try {
|
||||
final raw = jsonEncode({
|
||||
"username": username,
|
||||
"twoKeyCode": twoKeyCode,
|
||||
});
|
||||
final encrypted = EncryptionUtil.encrypt(raw);
|
||||
|
||||
final res = await _dio.post(
|
||||
'$baseUrl/api/user/user',
|
||||
data: {
|
||||
"code": 200,
|
||||
"timestamp": DateTime.now().millisecondsSinceEpoch,
|
||||
"msg": "login",
|
||||
"data": encrypted,
|
||||
},
|
||||
);
|
||||
|
||||
return EncryptionUtil.decrypt(res.data["data"]?.toString() ?? '');
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
static Future<UserModel> getUserInfo(String token) async {
|
||||
try {
|
||||
final res = await _dio.post(
|
||||
'$baseUrl/api/user/data',
|
||||
data: {
|
||||
"msg": "getUser",
|
||||
"timestamp": DateTime.now().millisecondsSinceEpoch,
|
||||
"data": token,
|
||||
},
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
|
||||
final decrypted = EncryptionUtil.decrypt(res.data["data"]?.toString() ?? '');
|
||||
return UserModel.fromJson(jsonDecode(decrypted));
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存/修改用户信息(updateUser)
|
||||
static Future<String> updateUser(String token, String encryptedData) async {
|
||||
try {
|
||||
final res = await _dio.put(
|
||||
'$baseUrl/api/user/data',
|
||||
data: {
|
||||
"msg": "updateUser",
|
||||
"timestamp": DateTime.now().millisecondsSinceEpoch,
|
||||
"data": encryptedData,
|
||||
},
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
return res.data.toString();
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 检查登录状态
|
||||
static Future<bool> isLogin(String token) async {
|
||||
try {
|
||||
final res = await _dio.get(
|
||||
'$baseUrl/api/user/isLogin',
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
return res.data["code"] == 200;
|
||||
} on DioException {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新Token(接口通用逻辑)
|
||||
static Future<String?> refreshToken(String oldToken) async {
|
||||
try {
|
||||
final res = await _dio.post(
|
||||
'$baseUrl/api/user/refreshToken',
|
||||
options: Options(headers: {"Authorization": oldToken}),
|
||||
);
|
||||
if (res.data["code"] == 200) {
|
||||
return res.data["data"]?.toString();
|
||||
}
|
||||
return null;
|
||||
} on DioException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取角色
|
||||
static Future<String?> getUserRole(String token) async {
|
||||
try {
|
||||
final res = await _dio.get(
|
||||
'$baseUrl/api/user/role',
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
if (res.data["code"] == 200) {
|
||||
return res.data["data"]?.toString();
|
||||
}
|
||||
return null;
|
||||
} on DioException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取性别标签
|
||||
static Future<List<String>> fetchSexTags() async {
|
||||
try {
|
||||
final res = await _dio.get('$baseUrl/api/union/sextag');
|
||||
if (res.data is List) {
|
||||
return List<String>.from(res.data);
|
||||
}
|
||||
return [];
|
||||
} on DioException {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 获取好友列表
|
||||
static Future<List<dynamic>> fetchFriends(String token) async {
|
||||
try {
|
||||
final res = await _dio.get(
|
||||
'$baseUrl/api/user/friend',
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
if (res.data is List) {
|
||||
return res.data;
|
||||
}
|
||||
return [];
|
||||
} on DioException {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// 更新API Key
|
||||
static Future<void> updateApiKey(String token) async {
|
||||
try {
|
||||
await _dio.post(
|
||||
'$baseUrl/api/user/updateKey',
|
||||
data: {
|
||||
"msg": "updateApiKey",
|
||||
"timestamp": DateTime.now().millisecondsSinceEpoch,
|
||||
},
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 绑定GitHub - 获取跳转链接
|
||||
static Future<String> bindGithub(String token) async {
|
||||
try {
|
||||
final res = await _dio.get(
|
||||
'$baseUrl/api/user/bind/github',
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
return res.realUri.toString();
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 确认绑定GitHub
|
||||
static Future<void> confirmBindGithub(String token, String uuid) async {
|
||||
try {
|
||||
await _dio.post(
|
||||
'$baseUrl/api/user/bind/github',
|
||||
options: Options(headers: {
|
||||
"Authorization": token,
|
||||
"uuid": uuid,
|
||||
}),
|
||||
);
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
// 验证SegaId
|
||||
static Future<Map<String, dynamic>> verifySegaId(String token, String segaId) async {
|
||||
try {
|
||||
final res = await _dio.post(
|
||||
'$baseUrl/api/user/verifySegaId',
|
||||
queryParameters: {"useSegaId": segaId},
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
return res.data;
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
// 获取雷达图数据
|
||||
static Future<Map<String, dynamic>> getRadarData(String token, String id) async {
|
||||
try {
|
||||
final res = await _dio.get(
|
||||
'$baseUrl/api/union/radar',
|
||||
queryParameters: {"id": id},
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
// 返回雷达图原始数据
|
||||
return Map<String, dynamic>.from(res.data);
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
// 统一错误处理
|
||||
static String _getErrorMessage(DioException e) {
|
||||
if (e.response?.data != null) {
|
||||
final data = e.response!.data;
|
||||
if (data is Map && data["msg"] != null) {
|
||||
return data["msg"].toString();
|
||||
}
|
||||
}
|
||||
switch (e.type) {
|
||||
case DioExceptionType.connectionTimeout:
|
||||
case DioExceptionType.receiveTimeout:
|
||||
case DioExceptionType.sendTimeout:
|
||||
return "网络超时";
|
||||
case DioExceptionType.connectionError:
|
||||
return "网络异常";
|
||||
default:
|
||||
return "请求失败";
|
||||
}
|
||||
}
|
||||
static Future<Map<String, dynamic>> getUserAllScores(String token, {String? name}) async {
|
||||
try {
|
||||
// 构建查询参数
|
||||
final queryParameters = <String, dynamic>{};
|
||||
if (name != null && name.isNotEmpty) {
|
||||
queryParameters['name'] = name;
|
||||
}
|
||||
final res = await _dio.get(
|
||||
'$baseUrl/api/union/reisaRating',
|
||||
queryParameters: queryParameters,
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
if (res.data is Map) {
|
||||
return Map<String, dynamic>.from(res.data);
|
||||
}
|
||||
return {};
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
static Future<Map<String, dynamic>> getSegaRatingData(String token, String segaId) async {
|
||||
try {
|
||||
final res = await _dio.get(
|
||||
'$baseUrl/api/union/segaReisaRating',
|
||||
queryParameters: {"segaId": segaId},
|
||||
options: Options(headers: {"Authorization": token}),
|
||||
);
|
||||
return Map<String, dynamic>.from(res.data);
|
||||
} on DioException catch (e) {
|
||||
throw _getErrorMessage(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user