feat(mobile-app): 添加后台定时检测未签署合同和KYC需求
- 添加 60-180 秒随机间隔的后台定时器 - 检测已KYC但未签署合同的用户,强制跳转签署页面 - 检测已付款但未完成KYC的用户,强制跳转实名认证页面 - 使用 PopScope 替代已弃用的 WillPopScope - 防止重复弹窗的状态管理 🤖 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
f62a96f3f1
commit
b63aa0737c
|
|
@ -1,9 +1,9 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../../../core/theme/app_colors.dart';
|
||||
import '../../../../core/theme/app_dimensions.dart';
|
||||
import '../../../../core/di/injection_container.dart';
|
||||
import '../../../../core/services/contract_check_service.dart';
|
||||
import '../../../../routes/route_paths.dart';
|
||||
|
|
@ -27,6 +27,12 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
|||
/// 是否已检查过合同(防止重复检查)
|
||||
static bool _hasCheckedContracts = false;
|
||||
|
||||
/// 后台合同检查定时器
|
||||
Timer? _contractCheckTimer;
|
||||
|
||||
/// 是否正在显示弹窗(防止重复弹窗)
|
||||
bool _isShowingDialog = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
|
@ -35,12 +41,15 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
|||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_checkForUpdateIfNeeded();
|
||||
_checkContractsAndKyc();
|
||||
// 启动后台定时检查
|
||||
_startBackgroundContractCheck();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_contractCheckTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +58,11 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
|||
// 从后台恢复时检查更新
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
_checkForUpdateIfNeeded();
|
||||
// 从后台恢复时也触发一次检查
|
||||
_scheduleNextContractCheck();
|
||||
} else if (state == AppLifecycleState.paused) {
|
||||
// 进入后台时取消定时器
|
||||
_contractCheckTimer?.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +78,57 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
|||
}
|
||||
}
|
||||
|
||||
/// 启动后台合同检查定时器
|
||||
void _startBackgroundContractCheck() {
|
||||
_scheduleNextContractCheck();
|
||||
}
|
||||
|
||||
/// 安排下一次合同检查(随机 60-180 秒)
|
||||
void _scheduleNextContractCheck() {
|
||||
_contractCheckTimer?.cancel();
|
||||
|
||||
// 随机 60-180 秒(1-3分钟)
|
||||
final randomSeconds = 60 + Random().nextInt(121); // 60 + 0~120 = 60~180
|
||||
debugPrint('[HomeShellPage] 下次合同检查将在 $randomSeconds 秒后执行');
|
||||
|
||||
_contractCheckTimer = Timer(Duration(seconds: randomSeconds), () async {
|
||||
await _performBackgroundContractCheck();
|
||||
// 检查完成后安排下一次检查
|
||||
if (mounted) {
|
||||
_scheduleNextContractCheck();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// 执行后台综合检查(合同 + KYC)
|
||||
Future<void> _performBackgroundContractCheck() async {
|
||||
if (!mounted || _isShowingDialog) return;
|
||||
|
||||
try {
|
||||
debugPrint('[HomeShellPage] 执行后台综合检查...');
|
||||
|
||||
final contractCheckService = ref.read(contractCheckServiceProvider);
|
||||
final result = await contractCheckService.checkAll();
|
||||
|
||||
if (!mounted || _isShowingDialog) return;
|
||||
|
||||
// 1. 优先处理待签署合同
|
||||
if (result.hasPendingContracts) {
|
||||
debugPrint('[HomeShellPage] 后台检测到待签署合同,显示弹窗');
|
||||
await _showContractRequiredDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 处理需要 KYC 的情况
|
||||
if (result.requiresKyc) {
|
||||
debugPrint('[HomeShellPage] 后台检测到需要KYC,显示弹窗');
|
||||
await _showKycRequiredDialog(result);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('[HomeShellPage] 后台综合检查失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 综合检查:待签署合同和 KYC 需求
|
||||
Future<void> _checkContractsAndKyc() async {
|
||||
// 每次会话只检查一次
|
||||
|
|
@ -94,16 +159,67 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
|||
}
|
||||
}
|
||||
|
||||
/// 显示合同签署必需弹窗(后台检测触发)
|
||||
Future<void> _showContractRequiredDialog() async {
|
||||
if (_isShowingDialog) return;
|
||||
_isShowingDialog = true;
|
||||
|
||||
const message =
|
||||
'系统检测到您已认种了榴莲树,但尚未按国家法规签署合规的认种合同。请先完成合同签署,才能继续使用APP。';
|
||||
|
||||
await showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false, // 不允许点击外部关闭
|
||||
builder: (context) => PopScope(
|
||||
canPop: false, // 不允许返回键关闭
|
||||
child: AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
Icon(Icons.description_outlined, color: Colors.orange[700], size: 28),
|
||||
const SizedBox(width: 8),
|
||||
const Text('需要签署合同'),
|
||||
],
|
||||
),
|
||||
content: const Text(
|
||||
message,
|
||||
style: TextStyle(fontSize: 15, height: 1.5),
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_isShowingDialog = false;
|
||||
// 跳转到待签署合同列表页面
|
||||
context.push(RoutePaths.pendingContracts, extra: true);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
minimumSize: const Size(double.infinity, 48),
|
||||
),
|
||||
child: const Text('立即签署', style: TextStyle(fontSize: 16)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
_isShowingDialog = false;
|
||||
}
|
||||
|
||||
/// 显示 KYC 必需弹窗
|
||||
Future<void> _showKycRequiredDialog(ContractCheckResult result) async {
|
||||
if (_isShowingDialog) return;
|
||||
_isShowingDialog = true;
|
||||
|
||||
final message = result.kycMessage ??
|
||||
'系统检测到您已认种了榴莲树,但尚未按国家法规完成实名认证,无法签署合同。请先完成实名认证后才能继续使用APP。';
|
||||
|
||||
await showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false, // 不允许点击外部关闭
|
||||
builder: (context) => WillPopScope(
|
||||
onWillPop: () async => false, // 不允许返回键关闭
|
||||
builder: (context) => PopScope(
|
||||
canPop: false, // 不允许返回键关闭
|
||||
child: AlertDialog(
|
||||
title: Row(
|
||||
children: [
|
||||
|
|
@ -125,7 +241,7 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
|||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.orange.withOpacity(0.1),
|
||||
color: Colors.orange.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Row(
|
||||
|
|
@ -151,6 +267,7 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
|||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
_isShowingDialog = false;
|
||||
// 跳转到 KYC 入口页面
|
||||
context.push(RoutePaths.kycEntry);
|
||||
},
|
||||
|
|
@ -165,6 +282,8 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
|||
),
|
||||
),
|
||||
);
|
||||
|
||||
_isShowingDialog = false;
|
||||
}
|
||||
|
||||
/// 重置合同检查状态(用于用户切换账号时)
|
||||
|
|
|
|||
Loading…
Reference in New Issue