1. 新增工作台三栏布局模式,适配宽屏设备 2. 添加跨平台系统托盘支持,新增托盘图标资源 3. 修复工作台模式下导航返回异常问题 4. 统一JSON类型安全解析,替换硬类型转换 5. 增加macOS深度链接支持,统一渠道分发信息 6. 优化部分页面生命周期和状态加载逻辑 7. 移除废弃的nearby_connections依赖
262 lines
7.5 KiB
Dart
262 lines
7.5 KiB
Dart
/// @name 每日任务核心模块
|
||
/// @date 2026-06-12
|
||
/// @desc 合并自 task_service.dart + task_provider.dart,包含API服务与状态管理
|
||
/// @update 2026-06-19 类型安全修复(int vs num): DailyTask/TaskSummary.fromJson 使用 SafeJson.parseInt
|
||
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:xianyan/core/utils/safe_json.dart';
|
||
import '../../core/network/api_client.dart';
|
||
|
||
// ============================================================
|
||
// API 服务层
|
||
// ============================================================
|
||
|
||
/// 每日任务API服务提供者
|
||
final taskServiceProvider = Provider<TaskService>((ref) => TaskService());
|
||
|
||
/// 每日任务API服务
|
||
class TaskService {
|
||
static final ApiClient _api = ApiClient.instance;
|
||
|
||
/// 获取今日任务列表
|
||
Future<Map<String, dynamic>> getTodayTasks() async {
|
||
final response = await _api.get<Map<String, dynamic>>('/api/task/today');
|
||
return response.data!;
|
||
}
|
||
|
||
/// 上报任务进度
|
||
Future<Map<String, dynamic>> reportProgress(int taskId, {int increment = 1}) async {
|
||
final response = await _api.post<Map<String, dynamic>>('/api/task/reportProgress', data: {
|
||
'task_id': taskId,
|
||
'increment': increment,
|
||
});
|
||
return response.data!;
|
||
}
|
||
|
||
/// 领取任务奖励
|
||
Future<Map<String, dynamic>> claimReward(int taskId) async {
|
||
final response = await _api.post<Map<String, dynamic>>('/api/task/claim', data: {
|
||
'task_id': taskId,
|
||
});
|
||
return response.data!;
|
||
}
|
||
|
||
/// 领取完美日奖励
|
||
Future<Map<String, dynamic>> claimPerfectDay() async {
|
||
final response = await _api.post<Map<String, dynamic>>('/api/task/claimPerfect', data: <String, dynamic>{});
|
||
return response.data!;
|
||
}
|
||
|
||
/// 注册自定义任务
|
||
Future<Map<String, dynamic>> registerCustomTask({
|
||
required String name,
|
||
String icon = '🎯',
|
||
String? customUrl,
|
||
String? customPage,
|
||
int target = 1,
|
||
int expReward = 5,
|
||
int scoreReward = 2,
|
||
}) async {
|
||
final data = <String, dynamic>{
|
||
'name': name,
|
||
'icon': icon,
|
||
'target': target,
|
||
'exp_reward': expReward,
|
||
'score_reward': scoreReward,
|
||
};
|
||
if (customUrl != null) data['custom_url'] = customUrl;
|
||
if (customPage != null) data['custom_page'] = customPage;
|
||
final response = await _api.post<Map<String, dynamic>>('/api/task/registerCustom', data: data);
|
||
return response.data!;
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 数据模型层
|
||
// ============================================================
|
||
|
||
/// 每日任务数据模型
|
||
class DailyTask {
|
||
final int id;
|
||
final String name;
|
||
final String icon;
|
||
final String type;
|
||
final int target;
|
||
final String action;
|
||
final String customUrl;
|
||
final String customPage;
|
||
final int expReward;
|
||
final int scoreReward;
|
||
final int isRandom;
|
||
int progress;
|
||
bool completed;
|
||
bool claimed;
|
||
int percent;
|
||
|
||
DailyTask({
|
||
required this.id,
|
||
required this.name,
|
||
required this.icon,
|
||
required this.type,
|
||
required this.target,
|
||
required this.action,
|
||
required this.customUrl,
|
||
required this.customPage,
|
||
required this.expReward,
|
||
required this.scoreReward,
|
||
required this.isRandom,
|
||
required this.progress,
|
||
required this.completed,
|
||
required this.claimed,
|
||
required this.percent,
|
||
});
|
||
|
||
factory DailyTask.fromJson(Map<String, dynamic> json) {
|
||
return DailyTask(
|
||
id: SafeJson.parseInt(json['id']),
|
||
name: (json['name'] ?? '') as String,
|
||
icon: (json['icon'] ?? '📋') as String,
|
||
type: (json['type'] ?? '') as String,
|
||
target: SafeJson.parseInt(json['target'], 1),
|
||
action: (json['action'] ?? '') as String,
|
||
customUrl: (json['custom_url'] ?? '') as String,
|
||
customPage: (json['custom_page'] ?? '') as String,
|
||
expReward: SafeJson.parseInt(json['exp_reward']),
|
||
scoreReward: SafeJson.parseInt(json['score_reward']),
|
||
isRandom: SafeJson.parseInt(json['is_random']),
|
||
progress: SafeJson.parseInt(json['progress']),
|
||
completed: json['completed'] == 1 || json['completed'] == true,
|
||
claimed: json['claimed'] == 1 || json['claimed'] == true,
|
||
percent: SafeJson.parseInt(json['percent']),
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 任务汇总数据模型
|
||
class TaskSummary {
|
||
final int total;
|
||
final int completed;
|
||
final int claimed;
|
||
final bool isPerfectDay;
|
||
final bool perfectClaimed;
|
||
final String date;
|
||
|
||
TaskSummary({
|
||
required this.total,
|
||
required this.completed,
|
||
required this.claimed,
|
||
required this.isPerfectDay,
|
||
required this.perfectClaimed,
|
||
required this.date,
|
||
});
|
||
|
||
factory TaskSummary.fromJson(Map<String, dynamic> json) {
|
||
return TaskSummary(
|
||
total: SafeJson.parseInt(json['total']),
|
||
completed: SafeJson.parseInt(json['completed']),
|
||
claimed: SafeJson.parseInt(json['claimed']),
|
||
isPerfectDay:
|
||
json['is_perfect_day'] == true || json['is_perfect_day'] == 1,
|
||
perfectClaimed:
|
||
json['perfect_claimed'] == true || json['perfect_claimed'] == 1,
|
||
date: (json['date'] ?? '') as String,
|
||
);
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 状态管理层
|
||
// ============================================================
|
||
|
||
/// 任务状态
|
||
class TaskState {
|
||
final List<DailyTask> tasks;
|
||
final TaskSummary? summary;
|
||
final bool isLoading;
|
||
final String? error;
|
||
|
||
const TaskState({
|
||
this.tasks = const [],
|
||
this.summary,
|
||
this.isLoading = false,
|
||
this.error,
|
||
});
|
||
|
||
TaskState copyWith({
|
||
List<DailyTask>? tasks,
|
||
TaskSummary? summary,
|
||
bool? isLoading,
|
||
String? error,
|
||
}) {
|
||
return TaskState(
|
||
tasks: tasks ?? this.tasks,
|
||
summary: summary ?? this.summary,
|
||
isLoading: isLoading ?? this.isLoading,
|
||
error: error,
|
||
);
|
||
}
|
||
}
|
||
|
||
/// 任务状态管理器
|
||
class TaskNotifier extends Notifier<TaskState> {
|
||
@override
|
||
TaskState build() => const TaskState();
|
||
|
||
TaskService get _taskService => ref.read(taskServiceProvider);
|
||
|
||
/// 加载今日任务
|
||
Future<void> loadTodayTasks() async {
|
||
state = state.copyWith(isLoading: true);
|
||
try {
|
||
final response = await _taskService.getTodayTasks();
|
||
final data = (response['data'] ?? response) as Map<String, dynamic>;
|
||
final List<dynamic> taskList = data['tasks'] as List<dynamic>? ?? [];
|
||
final tasks = taskList
|
||
.map((e) => DailyTask.fromJson(e as Map<String, dynamic>))
|
||
.toList();
|
||
final summary = TaskSummary.fromJson(data);
|
||
state = state.copyWith(tasks: tasks, summary: summary, isLoading: false);
|
||
} catch (e) {
|
||
state = state.copyWith(isLoading: false, error: e.toString());
|
||
}
|
||
}
|
||
|
||
/// 上报任务进度
|
||
Future<bool> reportProgress(int taskId, {int increment = 1}) async {
|
||
try {
|
||
await _taskService.reportProgress(taskId, increment: increment);
|
||
await loadTodayTasks();
|
||
return true;
|
||
} catch (e) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// 领取任务奖励
|
||
Future<Map<String, dynamic>?> claimReward(int taskId) async {
|
||
try {
|
||
final response = await _taskService.claimReward(taskId);
|
||
await loadTodayTasks();
|
||
return (response['data'] ?? response) as Map<String, dynamic>?;
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// 领取完美日奖励
|
||
Future<Map<String, dynamic>?> claimPerfectDay() async {
|
||
try {
|
||
final response = await _taskService.claimPerfectDay();
|
||
await loadTodayTasks();
|
||
return (response['data'] ?? response) as Map<String, dynamic>?;
|
||
} catch (e) {
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// 任务状态提供者
|
||
final taskProvider = NotifierProvider<TaskNotifier, TaskState>(
|
||
TaskNotifier.new,
|
||
);
|