主要变更: 1. 重构多处Provider初始化逻辑,使用Future.microtask避免build阶段修改state 2. 重命名"灵感"模块为"工作流","天气诗词"改为"情景诗词" 3. 新增插件系统页面与路由,添加speech_to_text_windows插件支持 4. 优化玻璃容器性能,添加性能节流与模糊值适配 5. 新增离线横幅、阅读体验控制器、手势控制器等组件 6. 完善头像审核状态展示与缓存管理 7. 修复键盘弹出与页面路由问题 8. 更新依赖与项目配置,优化widget默认数据
196 lines
5.9 KiB
Dart
196 lines
5.9 KiB
Dart
/// ============================================================
|
|
/// 闲言APP — 壁纸图库服务
|
|
/// 创建时间: 2026-05-01
|
|
/// 更新时间: 2026-05-26
|
|
/// 作用: 壁纸图库API对接 — 12个源 + 分页 + 搜索 + 缓存
|
|
/// 上次更新: 增加超时时间(connect 8s/receive 12s)避免慢源请求失败
|
|
/// ============================================================
|
|
|
|
import 'package:dio/dio.dart';
|
|
|
|
import '../../../core/utils/logger.dart';
|
|
import '../models/template_models.dart';
|
|
|
|
class WallpaperService {
|
|
WallpaperService._();
|
|
|
|
static final Dio _dio = Dio(
|
|
BaseOptions(
|
|
baseUrl: 'http://bz.wktyl.com',
|
|
connectTimeout: const Duration(seconds: 8),
|
|
receiveTimeout: const Duration(seconds: 12),
|
|
headers: {'User-Agent': 'Xianyan/1.0'},
|
|
),
|
|
);
|
|
|
|
// ============================================================
|
|
// 获取壁纸列表
|
|
// ============================================================
|
|
|
|
static Future<WallpaperResult> fetchWallpapers({
|
|
WallpaperSource source = WallpaperSource.unsplash,
|
|
int page = 1,
|
|
int limit = 20,
|
|
WallpaperCategory category = WallpaperCategory.all,
|
|
String sort = 'hot',
|
|
String? search,
|
|
}) async {
|
|
try {
|
|
final url = source.buildUrl(
|
|
limit: limit,
|
|
page: page,
|
|
category: category.id,
|
|
sort: sort,
|
|
search: search,
|
|
);
|
|
|
|
final response = await _dio.get<Map<String, dynamic>>(url);
|
|
final data = response.data;
|
|
|
|
if (data == null || data['success'] != true) {
|
|
Log.w('壁纸API返回失败: source=${source.label}');
|
|
return const WallpaperResult(items: [], currentPage: 1, totalPages: 0);
|
|
}
|
|
|
|
final pagination = data['pagination'] as Map<String, dynamic>? ?? {};
|
|
final rawData = data['data'];
|
|
final List<WallpaperItem> list;
|
|
if (rawData is List<dynamic>) {
|
|
list = rawData
|
|
.whereType<Map<String, dynamic>>()
|
|
.map((e) => WallpaperItem.fromApi(e))
|
|
.toList();
|
|
} else if (rawData is Map<String, dynamic>) {
|
|
list = [WallpaperItem.fromApi(rawData)];
|
|
} else {
|
|
list = [];
|
|
}
|
|
|
|
return WallpaperResult(
|
|
items: list,
|
|
currentPage: pagination['current_page'] as int? ?? page,
|
|
totalPages: pagination['total_pages'] as int? ?? 1,
|
|
totalItems: pagination['total_items'] as int? ?? list.length,
|
|
hasNext: pagination['has_next'] as bool? ?? false,
|
|
);
|
|
} on DioException catch (e) {
|
|
Log.e('壁纸请求失败 [${source.label}]', e);
|
|
return const WallpaperResult(items: [], currentPage: 1, totalPages: 0);
|
|
} catch (e) {
|
|
Log.e('壁纸解析失败 [${source.label}]', e);
|
|
return const WallpaperResult(items: [], currentPage: 1, totalPages: 0);
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// 快速获取 (极速源)
|
|
// ============================================================
|
|
|
|
static Future<List<WallpaperItem>> fetchQuick({
|
|
int limit = 10,
|
|
WallpaperCategory category = WallpaperCategory.all,
|
|
}) async {
|
|
final result = await fetchWallpapers(limit: limit, category: category);
|
|
return result.items;
|
|
}
|
|
|
|
// ============================================================
|
|
// 快速源聚合 (avgMs < 500)
|
|
// ============================================================
|
|
|
|
static Future<List<WallpaperItem>> fetchQuickSources({
|
|
int limitPerSource = 6,
|
|
WallpaperCategory category = WallpaperCategory.all,
|
|
}) async {
|
|
final quickSources = WallpaperSource.values.where((s) => s.isFast).toList();
|
|
return fetchMultiSource(
|
|
sources: quickSources,
|
|
limitPerSource: limitPerSource,
|
|
category: category,
|
|
);
|
|
}
|
|
|
|
// ============================================================
|
|
// 多源聚合
|
|
// ============================================================
|
|
|
|
static Future<List<WallpaperItem>> fetchMultiSource({
|
|
List<WallpaperSource>? sources,
|
|
int limitPerSource = 6,
|
|
WallpaperCategory category = WallpaperCategory.all,
|
|
}) async {
|
|
final effectiveSources =
|
|
sources ?? WallpaperSource.values.where((s) => s.isFast).toList();
|
|
final allItems = <WallpaperItem>[];
|
|
|
|
final futures = effectiveSources.map(
|
|
(source) =>
|
|
fetchWallpapers(
|
|
source: source,
|
|
limit: limitPerSource,
|
|
category: category,
|
|
)
|
|
.timeout(
|
|
const Duration(seconds: 5),
|
|
onTimeout: () => const WallpaperResult(
|
|
items: [],
|
|
currentPage: 1,
|
|
totalPages: 0,
|
|
),
|
|
)
|
|
.catchError(
|
|
(_) => const WallpaperResult(
|
|
items: [],
|
|
currentPage: 1,
|
|
totalPages: 0,
|
|
),
|
|
),
|
|
);
|
|
|
|
final results = await Future.wait(futures);
|
|
for (final result in results) {
|
|
allItems.addAll(result.items);
|
|
}
|
|
|
|
allItems.shuffle();
|
|
return allItems;
|
|
}
|
|
|
|
// ============================================================
|
|
// 搜索壁纸
|
|
// ============================================================
|
|
|
|
static Future<List<WallpaperItem>> searchWallpapers(
|
|
String keyword, {
|
|
WallpaperSource source = WallpaperSource.unsplash,
|
|
int limit = 20,
|
|
}) async {
|
|
final result = await fetchWallpapers(
|
|
source: source,
|
|
limit: limit,
|
|
search: keyword,
|
|
);
|
|
return result.items;
|
|
}
|
|
}
|
|
|
|
// ============================================================
|
|
// 壁纸分页结果
|
|
// ============================================================
|
|
|
|
class WallpaperResult {
|
|
const WallpaperResult({
|
|
required this.items,
|
|
required this.currentPage,
|
|
required this.totalPages,
|
|
this.totalItems = 0,
|
|
this.hasNext = false,
|
|
});
|
|
|
|
final List<WallpaperItem> items;
|
|
final int currentPage;
|
|
final int totalPages;
|
|
final int totalItems;
|
|
final bool hasNext;
|
|
}
|