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 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../core/theme/app_colors.dart';
|
import '../../../../core/theme/app_colors.dart';
|
||||||
import '../../../../core/theme/app_dimensions.dart';
|
|
||||||
import '../../../../core/di/injection_container.dart';
|
import '../../../../core/di/injection_container.dart';
|
||||||
import '../../../../core/services/contract_check_service.dart';
|
import '../../../../core/services/contract_check_service.dart';
|
||||||
import '../../../../routes/route_paths.dart';
|
import '../../../../routes/route_paths.dart';
|
||||||
|
|
@ -27,6 +27,12 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
||||||
/// 是否已检查过合同(防止重复检查)
|
/// 是否已检查过合同(防止重复检查)
|
||||||
static bool _hasCheckedContracts = false;
|
static bool _hasCheckedContracts = false;
|
||||||
|
|
||||||
|
/// 后台合同检查定时器
|
||||||
|
Timer? _contractCheckTimer;
|
||||||
|
|
||||||
|
/// 是否正在显示弹窗(防止重复弹窗)
|
||||||
|
bool _isShowingDialog = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
@ -35,12 +41,15 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_checkForUpdateIfNeeded();
|
_checkForUpdateIfNeeded();
|
||||||
_checkContractsAndKyc();
|
_checkContractsAndKyc();
|
||||||
|
// 启动后台定时检查
|
||||||
|
_startBackgroundContractCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
_contractCheckTimer?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,6 +58,11 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
||||||
// 从后台恢复时检查更新
|
// 从后台恢复时检查更新
|
||||||
if (state == AppLifecycleState.resumed) {
|
if (state == AppLifecycleState.resumed) {
|
||||||
_checkForUpdateIfNeeded();
|
_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 需求
|
/// 综合检查:待签署合同和 KYC 需求
|
||||||
Future<void> _checkContractsAndKyc() async {
|
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 必需弹窗
|
/// 显示 KYC 必需弹窗
|
||||||
Future<void> _showKycRequiredDialog(ContractCheckResult result) async {
|
Future<void> _showKycRequiredDialog(ContractCheckResult result) async {
|
||||||
|
if (_isShowingDialog) return;
|
||||||
|
_isShowingDialog = true;
|
||||||
|
|
||||||
final message = result.kycMessage ??
|
final message = result.kycMessage ??
|
||||||
'系统检测到您已认种了榴莲树,但尚未按国家法规完成实名认证,无法签署合同。请先完成实名认证后才能继续使用APP。';
|
'系统检测到您已认种了榴莲树,但尚未按国家法规完成实名认证,无法签署合同。请先完成实名认证后才能继续使用APP。';
|
||||||
|
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false, // 不允许点击外部关闭
|
barrierDismissible: false, // 不允许点击外部关闭
|
||||||
builder: (context) => WillPopScope(
|
builder: (context) => PopScope(
|
||||||
onWillPop: () async => false, // 不允许返回键关闭
|
canPop: false, // 不允许返回键关闭
|
||||||
child: AlertDialog(
|
child: AlertDialog(
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -125,7 +241,7 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.orange.withOpacity(0.1),
|
color: Colors.orange.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -151,6 +267,7 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
_isShowingDialog = false;
|
||||||
// 跳转到 KYC 入口页面
|
// 跳转到 KYC 入口页面
|
||||||
context.push(RoutePaths.kycEntry);
|
context.push(RoutePaths.kycEntry);
|
||||||
},
|
},
|
||||||
|
|
@ -165,6 +282,8 @@ class _HomeShellPageState extends ConsumerState<HomeShellPage>
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_isShowingDialog = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 重置合同检查状态(用于用户切换账号时)
|
/// 重置合同检查状态(用于用户切换账号时)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue