feat(planting): 认种成功后检查实名认证状态
当CONTRACT_SIGNING_ENABLED=true时,认种成功后检查用户是否已完成实名认证: - 如果未完成实名认证,显示提示弹窗引导用户去认证 - 如果已完成或功能未启用,按原有流程返回个人中心 新增: - KycRequiredDialog 实名认证提示弹窗组件 - ContractSigningConfig 配置类和getConfig()方法 - kycServiceProvider 依赖注入 🤖 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
bc34907a84
commit
59e9cddf5b
|
|
@ -14,6 +14,7 @@ import '../services/notification_service.dart';
|
||||||
import '../services/system_config_service.dart';
|
import '../services/system_config_service.dart';
|
||||||
import '../services/contract_signing_service.dart';
|
import '../services/contract_signing_service.dart';
|
||||||
import '../services/contract_check_service.dart';
|
import '../services/contract_check_service.dart';
|
||||||
|
import '../../features/kyc/data/kyc_service.dart';
|
||||||
|
|
||||||
// Storage Providers
|
// Storage Providers
|
||||||
final secureStorageProvider = Provider<SecureStorage>((ref) {
|
final secureStorageProvider = Provider<SecureStorage>((ref) {
|
||||||
|
|
@ -107,6 +108,12 @@ final contractCheckServiceProvider = Provider<ContractCheckService>((ref) {
|
||||||
return ContractCheckService(contractSigningService: contractSigningService);
|
return ContractCheckService(contractSigningService: contractSigningService);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// KYC Service Provider (调用 identity-service)
|
||||||
|
final kycServiceProvider = Provider<KycService>((ref) {
|
||||||
|
final apiClient = ref.watch(apiClientProvider);
|
||||||
|
return KycService(apiClient: apiClient);
|
||||||
|
});
|
||||||
|
|
||||||
// Override provider with initialized instance
|
// Override provider with initialized instance
|
||||||
ProviderContainer createProviderContainer(LocalStorage localStorage) {
|
ProviderContainer createProviderContainer(LocalStorage localStorage) {
|
||||||
return ProviderContainer(
|
return ProviderContainer(
|
||||||
|
|
|
||||||
|
|
@ -153,6 +153,30 @@ class ContractSigningService {
|
||||||
|
|
||||||
ContractSigningService({required ApiClient apiClient}) : _apiClient = apiClient;
|
ContractSigningService({required ApiClient apiClient}) : _apiClient = apiClient;
|
||||||
|
|
||||||
|
/// 获取合同签署配置(公开接口,不需要认证)
|
||||||
|
/// 用于判断是否需要在认种前检查实名认证
|
||||||
|
Future<ContractSigningConfig> getConfig() async {
|
||||||
|
try {
|
||||||
|
debugPrint('[ContractSigningService] 获取合同签署配置');
|
||||||
|
|
||||||
|
final response = await _apiClient.get('/planting/contract-signing/config');
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final data = response.data as Map<String, dynamic>;
|
||||||
|
if (data['success'] == true && data['data'] != null) {
|
||||||
|
return ContractSigningConfig.fromJson(data['data']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认启用合同签署
|
||||||
|
return ContractSigningConfig(contractSigningEnabled: true);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[ContractSigningService] 获取配置失败: $e');
|
||||||
|
// 获取失败时默认启用合同签署
|
||||||
|
return ContractSigningConfig(contractSigningEnabled: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 获取待签署任务列表
|
/// 获取待签署任务列表
|
||||||
Future<List<ContractSigningTask>> getPendingTasks() async {
|
Future<List<ContractSigningTask>> getPendingTasks() async {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:city_pickers/city_pickers.dart';
|
import 'package:city_pickers/city_pickers.dart';
|
||||||
import '../widgets/planting_confirm_dialog.dart';
|
import '../widgets/planting_confirm_dialog.dart';
|
||||||
|
import '../widgets/kyc_required_dialog.dart';
|
||||||
import '../../../../core/di/injection_container.dart';
|
import '../../../../core/di/injection_container.dart';
|
||||||
|
import '../../../../routes/route_paths.dart';
|
||||||
|
|
||||||
/// 认种省市选择页面参数
|
/// 认种省市选择页面参数
|
||||||
class PlantingLocationParams {
|
class PlantingLocationParams {
|
||||||
|
|
@ -208,6 +210,37 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查是否需要实名认证
|
||||||
|
/// 仅在合同签署功能启用时才需要检查
|
||||||
|
Future<bool> _checkKycRequirement() async {
|
||||||
|
try {
|
||||||
|
// 1. 获取合同签署配置
|
||||||
|
final contractSigningService = ref.read(contractSigningServiceProvider);
|
||||||
|
final config = await contractSigningService.getConfig();
|
||||||
|
|
||||||
|
// 如果合同签署功能未启用,不需要检查实名认证
|
||||||
|
if (!config.contractSigningEnabled) {
|
||||||
|
debugPrint('[PlantingLocationPage] 合同签署功能未启用,跳过实名认证检查');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 合同签署功能启用时,检查实名认证状态
|
||||||
|
final kycService = ref.read(kycServiceProvider);
|
||||||
|
final kycStatus = await kycService.getKycStatus();
|
||||||
|
|
||||||
|
// 检查层级1(实名认证)是否完成
|
||||||
|
final isVerified = kycStatus.level1.verified;
|
||||||
|
debugPrint('[PlantingLocationPage] 合同签署功能启用,实名认证状态: $isVerified');
|
||||||
|
|
||||||
|
// 返回是否需要去做实名认证(未完成实名认证则需要)
|
||||||
|
return !isVerified;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('[PlantingLocationPage] 检查实名认证状态失败: $e');
|
||||||
|
// 检查失败时默认不阻止用户,让后端处理
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 提交认种请求
|
/// 提交认种请求
|
||||||
Future<void> _submitPlanting() async {
|
Future<void> _submitPlanting() async {
|
||||||
setState(() => _isSubmitting = true);
|
setState(() => _isSubmitting = true);
|
||||||
|
|
@ -243,8 +276,22 @@ class _PlantingLocationPageState extends ConsumerState<PlantingLocationPage> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// 5. 认种成功后,检查是否需要实名认证(仅在合同签署功能启用时)
|
||||||
|
final needsKyc = await _checkKycRequirement();
|
||||||
|
if (needsKyc && mounted) {
|
||||||
|
// 显示实名认证提示弹窗
|
||||||
|
final goToKyc = await KycRequiredDialog.show(context: context);
|
||||||
|
if (goToKyc == true && mounted) {
|
||||||
|
// 跳转到实名认证页面
|
||||||
|
context.push(RoutePaths.kycEntry);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 返回到个人中心
|
// 返回到个人中心
|
||||||
context.go('/profile');
|
if (mounted) {
|
||||||
|
context.go('/profile');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('认种失败: $e');
|
debugPrint('认种失败: $e');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,200 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// 未实名认证提示弹窗
|
||||||
|
/// 当用户未完成实名认证但尝试确认认种时显示
|
||||||
|
class KycRequiredDialog extends StatelessWidget {
|
||||||
|
const KycRequiredDialog({super.key});
|
||||||
|
|
||||||
|
/// 显示提示弹窗
|
||||||
|
/// 返回 true 表示用户选择去实名认证,false 表示取消
|
||||||
|
static Future<bool?> show({required BuildContext context}) {
|
||||||
|
return showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: true,
|
||||||
|
barrierColor: const Color(0x80000000),
|
||||||
|
builder: (context) => const KycRequiredDialog(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Dialog(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
insetPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: Container(
|
||||||
|
width: double.infinity,
|
||||||
|
constraints: const BoxConstraints(maxWidth: 360),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
boxShadow: const [
|
||||||
|
BoxShadow(
|
||||||
|
color: Color(0x40000000),
|
||||||
|
blurRadius: 50,
|
||||||
|
offset: Offset(0, 25),
|
||||||
|
spreadRadius: -12,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
// 主内容
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.fromLTRB(24, 32, 24, 24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// 图标
|
||||||
|
_buildIcon(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
// 标题
|
||||||
|
_buildTitle(),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
// 说明文字
|
||||||
|
_buildDescription(),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
// 按钮
|
||||||
|
_buildButtons(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 右上角关闭按钮
|
||||||
|
Positioned(
|
||||||
|
top: 8,
|
||||||
|
right: 8,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => Navigator.pop(context, false),
|
||||||
|
child: Container(
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0x0D000000),
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
child: const Icon(
|
||||||
|
Icons.close,
|
||||||
|
size: 18,
|
||||||
|
color: Color(0xFF8B5A2B),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 构建图标
|
||||||
|
Widget _buildIcon() {
|
||||||
|
return Container(
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFFFF7E6),
|
||||||
|
borderRadius: BorderRadius.circular(32),
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.verified_user_outlined,
|
||||||
|
color: Color(0xFFD4AF37),
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 构建标题
|
||||||
|
Widget _buildTitle() {
|
||||||
|
return const Text(
|
||||||
|
'需要先完成实名认证',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
height: 1.3,
|
||||||
|
color: Color(0xFF5D4037),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 构建说明文字
|
||||||
|
Widget _buildDescription() {
|
||||||
|
return const Text(
|
||||||
|
'为了保障您的权益,认种前需要完成实名认证。\n完成认证后即可继续认种。',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
height: 1.5,
|
||||||
|
color: Color(0xFF745D43),
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 构建按钮
|
||||||
|
Widget _buildButtons(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
// 去实名认证按钮
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => Navigator.pop(context, true),
|
||||||
|
child: Container(
|
||||||
|
height: 48,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFD4AF37),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Text(
|
||||||
|
'去实名认证',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
height: 1.5,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
// 稍后再说按钮
|
||||||
|
SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => Navigator.pop(context, false),
|
||||||
|
child: Container(
|
||||||
|
height: 48,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.transparent,
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xFFD4AF37),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Center(
|
||||||
|
child: Text(
|
||||||
|
'稍后再说',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
height: 1.5,
|
||||||
|
color: Color(0xFFD4AF37),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue