2026-09-19 02:50:40 +08:00
|
|
|
|
/// 上传域 Provider 装配(P3 计划 Task 6)。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// 生产装配:TaskStore.app()(documents/upload_tasks.sqlite)+ ApiClient
|
|
|
|
|
|
/// (Bearer 自动注入)+ DeviceId(shared_preferences)。引擎生命周期
|
2026-09-19 04:47:31 +08:00
|
|
|
|
/// (start(accountId)/stop,随登录态切换)在 engine_scope.dart 绑定
|
|
|
|
|
|
/// (Task 9 接线)。
|
2026-09-19 02:50:40 +08:00
|
|
|
|
library;
|
|
|
|
|
|
|
2026-09-19 04:47:31 +08:00
|
|
|
|
import 'dart:async';
|
|
|
|
|
|
|
2026-09-19 02:50:40 +08:00
|
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
|
|
|
|
|
|
|
|
import '../../core/api_client.dart';
|
|
|
|
|
|
import '../../core/device_id.dart';
|
|
|
|
|
|
import 'preparer.dart';
|
|
|
|
|
|
import 'task_store.dart';
|
|
|
|
|
|
import 'upload_engine.dart';
|
|
|
|
|
|
|
|
|
|
|
|
/// 任务库(生产:app documents dir 持久库;测试 override 为 memory)。
|
|
|
|
|
|
final taskStoreProvider = Provider<TaskStore>((ref) {
|
|
|
|
|
|
final store = TaskStore.app();
|
|
|
|
|
|
ref.onDispose(store.close);
|
|
|
|
|
|
return store;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-09-19 04:47:31 +08:00
|
|
|
|
/// 单任务传输进度事件(队列 UI 行级进度条消费)。
|
|
|
|
|
|
class UploadTaskProgress {
|
|
|
|
|
|
const UploadTaskProgress({
|
|
|
|
|
|
required this.taskId,
|
|
|
|
|
|
required this.sent,
|
|
|
|
|
|
required this.total,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
final String taskId;
|
|
|
|
|
|
final int sent;
|
|
|
|
|
|
final int total;
|
|
|
|
|
|
|
|
|
|
|
|
/// 0~1 进度分数;total 未知(0)返回 null(UI 转不确定态进度条)。
|
|
|
|
|
|
double? get fraction =>
|
|
|
|
|
|
total > 0 ? (sent / total).clamp(0.0, 1.0).toDouble() : null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 进度事件总线:引擎 onProgress 回调 → 广播流(行级 StreamBuilder 按
|
|
|
|
|
|
/// taskId 过滤消费,避免整表重建风暴)。
|
|
|
|
|
|
class UploadProgressHub {
|
|
|
|
|
|
final StreamController<UploadTaskProgress> _controller =
|
|
|
|
|
|
StreamController<UploadTaskProgress>.broadcast();
|
|
|
|
|
|
|
|
|
|
|
|
/// 进度事件流(广播;行级按 taskId 过滤)。
|
|
|
|
|
|
Stream<UploadTaskProgress> get stream => _controller.stream;
|
|
|
|
|
|
|
|
|
|
|
|
/// 引擎回调挂接点(uploadEngineProvider 内接线)。
|
|
|
|
|
|
void emit(String taskId, int sent, int total) =>
|
|
|
|
|
|
_controller.add(UploadTaskProgress(taskId: taskId, sent: sent, total: total));
|
|
|
|
|
|
|
|
|
|
|
|
void dispose() => _controller.close();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 进度总线 Provider。
|
|
|
|
|
|
final uploadProgressHubProvider = Provider<UploadProgressHub>((ref) {
|
|
|
|
|
|
final hub = UploadProgressHub();
|
|
|
|
|
|
ref.onDispose(hub.dispose);
|
|
|
|
|
|
return hub;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-09-19 02:50:40 +08:00
|
|
|
|
/// 传输引擎(裸 Dio 直传 presign;并行/保留期配置见 upload_engine.dart
|
2026-09-19 04:47:31 +08:00
|
|
|
|
/// 的 dart-define 常量;进度经 [UploadProgressHub] 广播)。
|
2026-09-19 02:50:40 +08:00
|
|
|
|
final uploadEngineProvider = Provider<UploadEngine>((ref) {
|
2026-09-19 04:47:31 +08:00
|
|
|
|
final hub = ref.watch(uploadProgressHubProvider);
|
2026-09-19 02:50:40 +08:00
|
|
|
|
final engine = UploadEngine(
|
|
|
|
|
|
ref.watch(taskStoreProvider),
|
|
|
|
|
|
ref.watch(apiClientProvider),
|
|
|
|
|
|
ref.watch(deviceIdProvider),
|
2026-09-19 04:47:31 +08:00
|
|
|
|
onProgress: (taskId, sent, total) => hub.emit(taskId, sent, total),
|
2026-09-19 02:50:40 +08:00
|
|
|
|
);
|
|
|
|
|
|
ref.onDispose(engine.dispose);
|
|
|
|
|
|
return engine;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/// 准备器(入队回调唤醒引擎立即出队)。
|
|
|
|
|
|
final uploadPreparerProvider = Provider<UploadPreparer>((ref) {
|
|
|
|
|
|
final engine = ref.watch(uploadEngineProvider);
|
|
|
|
|
|
return UploadPreparer(
|
|
|
|
|
|
ref.watch(taskStoreProvider),
|
|
|
|
|
|
onEnqueued: engine.notifyWorkAvailable,
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|