refactor: 钱包状态轮询自动重试,移除手动重试按钮
设计原则:只要钱包不是 ready 状态,就持续轮询并自动重试生成 - 移除 failed 状态和重试按钮(用户无需手动操作) - 非 ready 时自动调用 retryWalletGeneration API(幂等) - 轮询间隔改为60秒(API 1-10分钟幂等) - 只有 ready 时停止轮询 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
ffd4fae0b6
commit
3e352dbcfe
|
|
@ -11,32 +11,27 @@ enum WalletGenerationStatus {
|
||||||
unknown, // 未知状态
|
unknown, // 未知状态
|
||||||
generating, // 生成中
|
generating, // 生成中
|
||||||
ready, // 已就绪
|
ready, // 已就绪
|
||||||
failed, // 生成失败
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 钱包状态信息
|
/// 钱包状态信息
|
||||||
class WalletStatusState {
|
class WalletStatusState {
|
||||||
final WalletGenerationStatus status;
|
final WalletGenerationStatus status;
|
||||||
final String? errorMessage;
|
|
||||||
final DateTime? lastChecked;
|
final DateTime? lastChecked;
|
||||||
final bool isPolling;
|
final bool isPolling;
|
||||||
|
|
||||||
const WalletStatusState({
|
const WalletStatusState({
|
||||||
this.status = WalletGenerationStatus.unknown,
|
this.status = WalletGenerationStatus.unknown,
|
||||||
this.errorMessage,
|
|
||||||
this.lastChecked,
|
this.lastChecked,
|
||||||
this.isPolling = false,
|
this.isPolling = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
WalletStatusState copyWith({
|
WalletStatusState copyWith({
|
||||||
WalletGenerationStatus? status,
|
WalletGenerationStatus? status,
|
||||||
String? errorMessage,
|
|
||||||
DateTime? lastChecked,
|
DateTime? lastChecked,
|
||||||
bool? isPolling,
|
bool? isPolling,
|
||||||
}) {
|
}) {
|
||||||
return WalletStatusState(
|
return WalletStatusState(
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
errorMessage: errorMessage,
|
|
||||||
lastChecked: lastChecked ?? this.lastChecked,
|
lastChecked: lastChecked ?? this.lastChecked,
|
||||||
isPolling: isPolling ?? this.isPolling,
|
isPolling: isPolling ?? this.isPolling,
|
||||||
);
|
);
|
||||||
|
|
@ -44,10 +39,14 @@ class WalletStatusState {
|
||||||
|
|
||||||
bool get isGenerating => status == WalletGenerationStatus.generating;
|
bool get isGenerating => status == WalletGenerationStatus.generating;
|
||||||
bool get isReady => status == WalletGenerationStatus.ready;
|
bool get isReady => status == WalletGenerationStatus.ready;
|
||||||
bool get isFailed => status == WalletGenerationStatus.failed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 钱包状态管理器
|
/// 钱包状态管理器
|
||||||
|
///
|
||||||
|
/// 设计原则:只要钱包不是 ready 状态,就持续轮询并自动重试生成
|
||||||
|
/// - 轮询间隔:1-10分钟(幂等)
|
||||||
|
/// - 只有 ready 时停止轮询
|
||||||
|
/// - 非 ready 时自动调用 retryWalletGeneration API 触发新的生成过程
|
||||||
class WalletStatusNotifier extends StateNotifier<WalletStatusState> {
|
class WalletStatusNotifier extends StateNotifier<WalletStatusState> {
|
||||||
final AccountService _accountService;
|
final AccountService _accountService;
|
||||||
final SecureStorage _secureStorage;
|
final SecureStorage _secureStorage;
|
||||||
|
|
@ -58,7 +57,7 @@ class WalletStatusNotifier extends StateNotifier<WalletStatusState> {
|
||||||
|
|
||||||
/// 开始轮询钱包状态
|
/// 开始轮询钱包状态
|
||||||
///
|
///
|
||||||
/// 每5秒检查一次,直到钱包就绪或失败
|
/// 每60秒检查一次,直到钱包就绪
|
||||||
Future<void> startPolling() async {
|
Future<void> startPolling() async {
|
||||||
if (state.isPolling) {
|
if (state.isPolling) {
|
||||||
debugPrint('[WalletStatusProvider] Already polling, skipping');
|
debugPrint('[WalletStatusProvider] Already polling, skipping');
|
||||||
|
|
@ -69,17 +68,12 @@ class WalletStatusNotifier extends StateNotifier<WalletStatusState> {
|
||||||
state = state.copyWith(isPolling: true);
|
state = state.copyWith(isPolling: true);
|
||||||
|
|
||||||
// 立即检查一次
|
// 立即检查一次
|
||||||
await _checkWalletStatus();
|
await _checkAndRetryIfNeeded();
|
||||||
|
|
||||||
// 设置定时器,每5秒检查一次
|
// 设置定时器,每60秒检查一次(幂等,1-10分钟间隔)
|
||||||
_pollingTimer?.cancel();
|
_pollingTimer?.cancel();
|
||||||
_pollingTimer = Timer.periodic(const Duration(seconds: 5), (_) async {
|
_pollingTimer = Timer.periodic(const Duration(seconds: 60), (_) async {
|
||||||
await _checkWalletStatus();
|
await _checkAndRetryIfNeeded();
|
||||||
|
|
||||||
// 如果钱包已就绪或失败,停止轮询
|
|
||||||
if (state.isReady || state.isFailed) {
|
|
||||||
stopPolling();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,8 +85,8 @@ class WalletStatusNotifier extends StateNotifier<WalletStatusState> {
|
||||||
state = state.copyWith(isPolling: false);
|
state = state.copyWith(isPolling: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 检查钱包状态
|
/// 检查钱包状态,非 ready 时自动重试
|
||||||
Future<void> _checkWalletStatus() async {
|
Future<void> _checkAndRetryIfNeeded() async {
|
||||||
try {
|
try {
|
||||||
debugPrint('[WalletStatusProvider] Checking wallet status...');
|
debugPrint('[WalletStatusProvider] Checking wallet status...');
|
||||||
|
|
||||||
|
|
@ -112,62 +106,46 @@ class WalletStatusNotifier extends StateNotifier<WalletStatusState> {
|
||||||
debugPrint(
|
debugPrint(
|
||||||
'[WalletStatusProvider] Wallet status: ${walletInfo.status}');
|
'[WalletStatusProvider] Wallet status: ${walletInfo.status}');
|
||||||
|
|
||||||
// 更新状态
|
|
||||||
WalletGenerationStatus newStatus;
|
|
||||||
if (walletInfo.isReady) {
|
if (walletInfo.isReady) {
|
||||||
newStatus = WalletGenerationStatus.ready;
|
// 钱包已就绪,保存标记并停止轮询
|
||||||
|
|
||||||
// 钱包已就绪,保存标记
|
|
||||||
await _secureStorage.write(
|
await _secureStorage.write(
|
||||||
key: StorageKeys.isWalletReady,
|
key: StorageKeys.isWalletReady,
|
||||||
value: 'true',
|
value: 'true',
|
||||||
);
|
);
|
||||||
debugPrint('[WalletStatusProvider] Wallet is ready, marked in storage');
|
debugPrint('[WalletStatusProvider] Wallet is ready, stopping polling');
|
||||||
} else if (walletInfo.isGenerating) {
|
|
||||||
newStatus = WalletGenerationStatus.generating;
|
|
||||||
} else if (walletInfo.isFailed) {
|
|
||||||
newStatus = WalletGenerationStatus.failed;
|
|
||||||
} else {
|
|
||||||
newStatus = WalletGenerationStatus.unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
status: newStatus,
|
status: WalletGenerationStatus.ready,
|
||||||
lastChecked: DateTime.now(),
|
lastChecked: DateTime.now(),
|
||||||
errorMessage: null,
|
);
|
||||||
);
|
|
||||||
|
stopPolling();
|
||||||
|
} else {
|
||||||
|
// 非 ready 状态,自动触发重试生成(API 是幂等的)
|
||||||
|
debugPrint('[WalletStatusProvider] Wallet not ready, triggering retry...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _accountService.retryWalletGeneration();
|
||||||
|
debugPrint('[WalletStatusProvider] Retry triggered successfully');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[WalletStatusProvider] Retry API call failed: $e');
|
||||||
|
// 继续轮询,下次再试
|
||||||
|
}
|
||||||
|
|
||||||
|
state = state.copyWith(
|
||||||
|
status: WalletGenerationStatus.generating,
|
||||||
|
lastChecked: DateTime.now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('[WalletStatusProvider] Error checking wallet status: $e');
|
debugPrint('[WalletStatusProvider] Error checking wallet status: $e');
|
||||||
|
// 出错不停止轮询,继续尝试
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
errorMessage: e.toString(),
|
|
||||||
lastChecked: DateTime.now(),
|
lastChecked: DateTime.now(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 手动触发重试
|
|
||||||
Future<void> retryWalletGeneration() async {
|
|
||||||
try {
|
|
||||||
debugPrint('[WalletStatusProvider] Manually retrying wallet generation');
|
|
||||||
|
|
||||||
// 调用重试 API
|
|
||||||
await _accountService.retryWalletGeneration();
|
|
||||||
|
|
||||||
// 重新开始轮询
|
|
||||||
state = state.copyWith(
|
|
||||||
status: WalletGenerationStatus.generating,
|
|
||||||
errorMessage: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!state.isPolling) {
|
|
||||||
await startPolling();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('[WalletStatusProvider] Error retrying wallet generation: $e');
|
|
||||||
state = state.copyWith(errorMessage: e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_pollingTimer?.cancel();
|
_pollingTimer?.cancel();
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,9 @@ enum MonitorStatus {
|
||||||
|
|
||||||
/// 钱包生成状态枚举
|
/// 钱包生成状态枚举
|
||||||
enum WalletCreationStatus {
|
enum WalletCreationStatus {
|
||||||
unknown, // 未知
|
unknown, // 未知(初始状态)
|
||||||
creating, // 创建中(显示"创建账号审核中...")
|
creating, // 创建中(显示"创建账号审核中...")
|
||||||
ready, // 已就绪
|
ready, // 已就绪
|
||||||
failed, // 失败
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 监控页面 - 显示监控状态和控制
|
/// 监控页面 - 显示监控状态和控制
|
||||||
|
|
@ -45,7 +44,6 @@ class _MiningPageState extends ConsumerState<MiningPage> {
|
||||||
|
|
||||||
// 钱包生成状态
|
// 钱包生成状态
|
||||||
WalletCreationStatus _walletStatus = WalletCreationStatus.unknown;
|
WalletCreationStatus _walletStatus = WalletCreationStatus.unknown;
|
||||||
String? _walletError;
|
|
||||||
Timer? _walletPollingTimer;
|
Timer? _walletPollingTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -170,19 +168,13 @@ class _MiningPageState extends ConsumerState<MiningPage> {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (walletInfo.isReady) {
|
if (walletInfo.isReady) {
|
||||||
_walletStatus = WalletCreationStatus.ready;
|
_walletStatus = WalletCreationStatus.ready;
|
||||||
_walletError = null;
|
|
||||||
// 钱包已就绪,停止轮询
|
// 钱包已就绪,停止轮询
|
||||||
_walletPollingTimer?.cancel();
|
_walletPollingTimer?.cancel();
|
||||||
debugPrint('[MiningPage] 钱包已就绪,停止轮询');
|
debugPrint('[MiningPage] 钱包已就绪,停止轮询');
|
||||||
} else if (walletInfo.isFailed) {
|
|
||||||
_walletStatus = WalletCreationStatus.failed;
|
|
||||||
_walletError = '账号创建失败';
|
|
||||||
_walletPollingTimer?.cancel();
|
|
||||||
debugPrint('[MiningPage] 钱包创建失败');
|
|
||||||
} else {
|
} else {
|
||||||
|
// 非 ready 状态(包括 failed、creating)都继续轮询
|
||||||
_walletStatus = WalletCreationStatus.creating;
|
_walletStatus = WalletCreationStatus.creating;
|
||||||
_walletError = null;
|
debugPrint('[MiningPage] 钱包创建中,继续轮询...');
|
||||||
debugPrint('[MiningPage] 钱包创建中...');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -191,34 +183,6 @@ class _MiningPageState extends ConsumerState<MiningPage> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 手动重试钱包生成
|
|
||||||
Future<void> _retryWalletGeneration() async {
|
|
||||||
try {
|
|
||||||
debugPrint('[MiningPage] 手动重试钱包生成...');
|
|
||||||
final accountService = ref.read(accountServiceProvider);
|
|
||||||
await accountService.retryWalletGeneration();
|
|
||||||
|
|
||||||
if (!mounted) return;
|
|
||||||
|
|
||||||
// 重置状态并重新开始轮询
|
|
||||||
setState(() {
|
|
||||||
_walletStatus = WalletCreationStatus.creating;
|
|
||||||
_walletError = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 重新开始轮询
|
|
||||||
_walletPollingTimer?.cancel();
|
|
||||||
_startWalletPolling();
|
|
||||||
} catch (e) {
|
|
||||||
debugPrint('[MiningPage] 重试钱包生成失败: $e');
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
_walletError = '重试失败: $e';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 显示帮助信息
|
/// 显示帮助信息
|
||||||
void _showHelpInfo() {
|
void _showHelpInfo() {
|
||||||
showDialog(
|
showDialog(
|
||||||
|
|
@ -433,43 +397,6 @@ class _MiningPageState extends ConsumerState<MiningPage> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
else if (_walletStatus == WalletCreationStatus.failed)
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
_walletError ?? '账号创建失败',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontFamily: 'Inter',
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: _retryWalletGeneration,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 12,
|
|
||||||
vertical: 6,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFF8B5A2B),
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'重试',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
else if (_walletStatus == WalletCreationStatus.ready)
|
else if (_walletStatus == WalletCreationStatus.ready)
|
||||||
Text(
|
Text(
|
||||||
'序列号$_serialNumber',
|
'序列号$_serialNumber',
|
||||||
|
|
|
||||||
|
|
@ -4079,52 +4079,7 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果钱包生成失败,显示失败状态和重试按钮
|
// 默认情况(unknown/generating状态)显示"创建账号审核中..."
|
||||||
if (walletStatus.isFailed) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.error_outline,
|
|
||||||
size: 16,
|
|
||||||
color: Color(0xFFD32F2F),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Text(
|
|
||||||
'钱包生成失败',
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontFamily: 'Inter',
|
|
||||||
height: 1.5,
|
|
||||||
color: Color(0xFFD32F2F),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
await ref.read(walletStatusProvider.notifier).retryWalletGeneration();
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: const Color(0xFFD4AF37),
|
|
||||||
borderRadius: BorderRadius.circular(4),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'重试',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 12,
|
|
||||||
fontFamily: 'Inter',
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认情况(unknown状态)显示"创建账号审核中..."
|
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue