diff --git a/backend/services/planting-service/src/pre-planting/application/services/pre-planting-application.service.ts b/backend/services/planting-service/src/pre-planting/application/services/pre-planting-application.service.ts index 3bdc035b..feaf5773 100644 --- a/backend/services/planting-service/src/pre-planting/application/services/pre-planting-application.service.ts +++ b/backend/services/planting-service/src/pre-planting/application/services/pre-planting-application.service.ts @@ -406,10 +406,19 @@ export class PrePlantingApplicationService { return m; }); - // 获取用户 KYC 信息 - const kycInfo = await this.identityServiceClient.getUserKycInfo( - merge.accountSequence, - ); + // 获取用户 KYC 信息 - 未完成实名认证则不允许查看合同 + const kycInfo = await this.identityServiceClient.getUserKycInfo(merge.accountSequence); + if (!kycInfo || !kycInfo.realName) { + throw new BadRequestException('请先完成实名认证方可查看和签署合同'); + } + + // 从源订单汇总实际支付的绿积分金额 + const sourceOrders = await this.prisma.prePlantingOrder.findMany({ + where: { orderNo: { in: merge.sourceOrderNos } }, + select: { totalAmount: true }, + }); + const greenPointsAmount = sourceOrders.reduce((sum, o) => sum + o.totalAmount, 0); + const totalAmount = Math.round(greenPointsAmount * 1.1 * 100) / 100; // 含税 CNY // 生成北京时间日期 const now = new Date(Date.now() + 8 * 60 * 60 * 1000); @@ -417,10 +426,12 @@ export class PrePlantingApplicationService { return this.pdfGeneratorService.generateContractPdf({ contractNo: mergeNo, - userRealName: kycInfo?.realName || '未认证', - userIdCard: kycInfo?.idCardNumber || '', - userPhone: kycInfo?.phoneNumber || '', + userRealName: kycInfo.realName, + userIdCard: kycInfo.idCardNumber || '', + userPhone: kycInfo.phoneNumber || '', treeCount: merge.treeCount, + totalAmount, + greenPointsAmount, signingDate, }); } diff --git a/frontend/mobile-app/lib/features/pre_planting/presentation/pages/pre_planting_merge_signing_page.dart b/frontend/mobile-app/lib/features/pre_planting/presentation/pages/pre_planting_merge_signing_page.dart index 936d5c4e..44ef19df 100644 --- a/frontend/mobile-app/lib/features/pre_planting/presentation/pages/pre_planting_merge_signing_page.dart +++ b/frontend/mobile-app/lib/features/pre_planting/presentation/pages/pre_planting_merge_signing_page.dart @@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart'; import 'package:path_provider/path_provider.dart'; import '../../../../core/di/injection_container.dart'; import '../../../../core/services/pre_planting_service.dart'; +import '../../../../routes/route_paths.dart'; import '../../../contract_signing/presentation/widgets/signature_pad.dart'; /// 预种合并合同签署页面 @@ -38,6 +39,7 @@ class _PrePlantingMergeSigningPageState bool _isSubmitting = false; bool _showSignaturePad = false; String? _errorMessage; + bool _isKycRequired = false; int _downloadProgress = 0; int _downloadRetryCount = 0; @@ -90,10 +92,13 @@ class _PrePlantingMergeSigningPageState } catch (e) { debugPrint('[MergeSigningPage] PDF加载失败: $e'); if (mounted) { + final msg = e.toString(); + final isKyc = msg.contains('实名认证') || msg.contains('400'); setState(() { _isPdfLoading = false; - _downloadRetryCount++; - _errorMessage = '加载合同失败,请点击重试'; + _isKycRequired = isKyc; + _errorMessage = isKyc ? '请先完成实名认证方可查看和签署合同' : '加载合同失败,请点击重试'; + if (!isKyc) _downloadRetryCount++; }); } } @@ -306,8 +311,11 @@ class _PrePlantingMergeSigningPageState child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon(Icons.cloud_download_outlined, - color: Color(0xFFD4AF37), size: 56), + Icon( + _isKycRequired ? Icons.person_off_outlined : Icons.cloud_download_outlined, + color: _isKycRequired ? Colors.red : const Color(0xFFD4AF37), + size: 56, + ), const SizedBox(height: 16), Text( _errorMessage!, @@ -315,17 +323,29 @@ class _PrePlantingMergeSigningPageState textAlign: TextAlign.center, ), const SizedBox(height: 24), - ElevatedButton.icon( - onPressed: _loadPdf, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFD4AF37), - padding: const EdgeInsets.symmetric( - horizontal: 32, vertical: 12)), - icon: const Icon(Icons.refresh, color: Colors.white), - label: const Text('重试', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - if (_downloadRetryCount > 0) + if (_isKycRequired) + ElevatedButton.icon( + onPressed: () => context.push(RoutePaths.kycEntry), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + padding: const EdgeInsets.symmetric( + horizontal: 32, vertical: 12)), + icon: const Icon(Icons.verified_user, color: Colors.white), + label: const Text('去完成实名认证', + style: TextStyle(color: Colors.white, fontSize: 16)), + ) + else + ElevatedButton.icon( + onPressed: _loadPdf, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFD4AF37), + padding: const EdgeInsets.symmetric( + horizontal: 32, vertical: 12)), + icon: const Icon(Icons.refresh, color: Colors.white), + label: const Text('重试', + style: TextStyle(color: Colors.white, fontSize: 16)), + ), + if (!_isKycRequired && _downloadRetryCount > 0) Padding( padding: const EdgeInsets.only(top: 12), child: Text(