diff --git a/.claude/settings.local.json b/.claude/settings.local.json index dd43080b..871480f3 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -353,7 +353,55 @@ "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" add backend/services/identity-service/src/api/controllers/internal.controller.ts)", "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" commit -m \"$\\(cat <<''EOF''\nfix\\(identity\\): 使用Prisma直接查询用户详情\n\ngetUserDetailBySequence 方法改用 Prisma 直接查询数据库,\n以获取 email 和 realName 等领域模型中未暴露的字段。\n\n之前的实现通过领域模型 UserAccount 访问这些字段会导致编译错误,\n因为领域模型没有直接暴露这些属性。\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" add backend/api-gateway/kong.yml frontend/mobile-app/lib/core/services/notification_service.dart)", - "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" commit -m \"$\\(cat <<''EOF''\nfix\\(notification\\): 修复通知中心API路径\n\n问题: 前端调用 /admin-service/mobile/notifications 路径不存在于Kong网关\n\n修复:\n1. Kong网关添加 /api/v1/mobile/notifications 路由到 admin-service\n2. 前端 NotificationService 修正 API 路径:\n - /admin-service/mobile/notifications -> /mobile/notifications\n - /admin-service/mobile/notifications/unread-count -> /mobile/notifications/unread-count\n - /admin-service/mobile/notifications/mark-read -> /mobile/notifications/mark-read\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")" + "Bash(git -C \"c:/Users/dong/Desktop/rwadurian\" commit -m \"$\\(cat <<''EOF''\nfix\\(notification\\): 修复通知中心API路径\n\n问题: 前端调用 /admin-service/mobile/notifications 路径不存在于Kong网关\n\n修复:\n1. Kong网关添加 /api/v1/mobile/notifications 路由到 admin-service\n2. 前端 NotificationService 修正 API 路径:\n - /admin-service/mobile/notifications -> /mobile/notifications\n - /admin-service/mobile/notifications/unread-count -> /mobile/notifications/unread-count\n - /admin-service/mobile/notifications/mark-read -> /mobile/notifications/mark-read\n\n🤖 Generated with [Claude Code]\\(https://claude.com/claude-code\\)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n\\)\")", + "Bash(set DATABASE_URL=postgresql://postgres:postgres@localhost:5432/rwadurian_planting?schema=public:*)", + "Bash($env:DATABASE_URL=\"postgresql://postgres:postgres@localhost:5432/rwadurian_planting?schema=public\")", + "Bash(python -c:*)", + "Bash(pip install:*)", + "Bash(backend/services/planting-service/package.json )", + "Bash(backend/services/planting-service/prisma/schema.prisma )", + "Bash(backend/services/planting-service/prisma/migrations/20241224000000_add_contract_signing/ )", + "Bash(backend/services/planting-service/prisma/seed.ts )", + "Bash(backend/services/planting-service/src/api/api.module.ts )", + "Bash(backend/services/planting-service/src/api/controllers/index.ts )", + "Bash(backend/services/planting-service/src/api/controllers/contract-signing.controller.ts )", + "Bash(backend/services/planting-service/src/application/application.module.ts )", + "Bash(backend/services/planting-service/src/application/services/index.ts )", + "Bash(backend/services/planting-service/src/application/services/planting-application.service.ts )", + "Bash(backend/services/planting-service/src/application/services/contract-signing.service.ts )", + "Bash(backend/services/planting-service/src/application/jobs/ )", + "Bash(backend/services/planting-service/src/domain/aggregates/index.ts )", + "Bash(backend/services/planting-service/src/domain/aggregates/contract-signing-task.aggregate.ts )", + "Bash(backend/services/planting-service/src/domain/aggregates/contract-template.aggregate.ts )", + "Bash(backend/services/planting-service/src/domain/repositories/index.ts )", + "Bash(backend/services/planting-service/src/domain/repositories/contract-signing-task.repository.interface.ts )", + "Bash(backend/services/planting-service/src/domain/repositories/contract-template.repository.interface.ts )", + "Bash(backend/services/planting-service/src/domain/value-objects/index.ts )", + "Bash(backend/services/planting-service/src/domain/value-objects/contract-signing-status.enum.ts )", + "Bash(backend/services/planting-service/src/infrastructure/infrastructure.module.ts )", + "Bash(backend/services/planting-service/src/infrastructure/kafka/event-publisher.service.ts )", + "Bash(backend/services/planting-service/src/infrastructure/kafka/index.ts )", + "Bash(backend/services/planting-service/src/infrastructure/kafka/contract-signing-event.consumer.ts )", + "Bash(backend/services/planting-service/src/infrastructure/persistence/repositories/index.ts )", + "Bash(backend/services/planting-service/src/infrastructure/persistence/repositories/contract-signing-task.repository.impl.ts )", + "Bash(backend/services/planting-service/src/infrastructure/persistence/repositories/contract-template.repository.impl.ts )", + "Bash(backend/services/planting-service/src/main.ts )", + "Bash(backend/services/referral-service/src/application/event-handlers/index.ts )", + "Bash(backend/services/referral-service/src/application/event-handlers/planting-created.handler.ts )", + "Bash(backend/services/referral-service/src/application/event-handlers/contract-signing.handler.ts )", + "Bash(backend/services/referral-service/src/infrastructure/external/index.ts )", + "Bash(backend/services/referral-service/src/infrastructure/external/wallet-service.client.ts )", + "Bash(backend/services/referral-service/src/modules/application.module.ts )", + "Bash(backend/services/referral-service/src/modules/infrastructure.module.ts )", + "Bash(backend/services/reward-service/src/application/services/reward-application.service.ts )", + "Bash(backend/services/reward-service/src/domain/services/reward-calculation.service.ts )", + "Bash(backend/services/reward-service/src/infrastructure/external/wallet-service/wallet-service.client.ts )", + "Bash(backend/services/reward-service/src/infrastructure/kafka/event-consumer.controller.ts)", + "Bash(frontend/mobile-app/lib/core/di/injection_container.dart )", + "Bash(frontend/mobile-app/lib/core/services/contract_check_service.dart )", + "Bash(frontend/mobile-app/lib/core/services/contract_signing_service.dart )", + "Bash(frontend/mobile-app/lib/features/contract_signing/ )", + "Bash(frontend/mobile-app/lib/features/home/presentation/pages/home_shell_page.dart )" ], "deny": [], "ask": [] diff --git a/contract.docx b/contract.docx new file mode 100644 index 00000000..3c85bbc0 Binary files /dev/null and b/contract.docx differ diff --git a/frontend/mobile-app/lib/features/kyc/presentation/pages/kyc_entry_page.dart b/frontend/mobile-app/lib/features/kyc/presentation/pages/kyc_entry_page.dart index 16457dfd..2a13a3f4 100644 --- a/frontend/mobile-app/lib/features/kyc/presentation/pages/kyc_entry_page.dart +++ b/frontend/mobile-app/lib/features/kyc/presentation/pages/kyc_entry_page.dart @@ -226,27 +226,105 @@ class KycEntryPage extends ConsumerWidget { Widget _buildLevel1Card(BuildContext context, WidgetRef ref, KycStatusResponse status) { final level1 = status.level1; final isCompleted = level1.verified; + // 必须先验证手机号才能进行实名认证 + final needsPhoneVerification = !status.phoneVerified; final isEnabled = !isCompleted; + String description; + if (isCompleted) { + description = '${level1.realName ?? ''} (${_maskIdCard(level1.idCardNumber)})'; + } else if (needsPhoneVerification) { + description = '请先验证手机号'; + } else { + description = '验证姓名和身份证号'; + } + return _buildStepCard( context: context, ref: ref, stepNumber: 1, title: '实名认证', subtitle: '二要素验证', - description: isCompleted - ? '${level1.realName ?? ''} (${_maskIdCard(level1.idCardNumber)})' - : '验证姓名和身份证号', + description: description, isCompleted: isCompleted, isEnabled: isEnabled, + isLocked: needsPhoneVerification && !isCompleted, onTap: () { if (isEnabled) { - context.push(RoutePaths.kycId); + if (needsPhoneVerification) { + // 手机号未验证,跳转到验证手机号页面 + _showPhoneVerificationRequiredDialog(context); + } else { + context.push(RoutePaths.kycId); + } } }, ); } + /// 显示需要先验证手机号的弹窗 + void _showPhoneVerificationRequiredDialog(BuildContext context) { + showDialog( + context: context, + builder: (ctx) => AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.r), + ), + title: Row( + children: [ + Icon(Icons.phone_android, color: const Color(0xFF2E7D32), size: 24.sp), + SizedBox(width: 8.w), + Text( + '请先验证手机号', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + content: Text( + '进行实名认证前,需要先验证您的手机号码。', + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF666666), + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(ctx).pop(), + child: Text( + '稍后验证', + style: TextStyle( + fontSize: 14.sp, + color: const Color(0xFF999999), + ), + ), + ), + ElevatedButton( + onPressed: () { + Navigator.of(ctx).pop(); + context.push(RoutePaths.changePhone); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFF2E7D32), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + ), + ), + child: Text( + '去验证', + style: TextStyle( + fontSize: 14.sp, + color: Colors.white, + ), + ), + ), + ], + ), + ); + } + /// 层级2: 实人认证卡片 Widget _buildLevel2Card(BuildContext context, WidgetRef ref, KycStatusResponse status) { final level2 = status.level2;