From 181d11d65647d413dd263305c4fc5d8a7638343c Mon Sep 17 00:00:00 2001 From: hailin Date: Wed, 24 Dec 2025 23:54:13 -0800 Subject: [PATCH] =?UTF-8?q?feat(kyc):=20=E5=8D=87=E7=BA=A7=E5=AE=9E?= =?UTF-8?q?=E5=90=8D=E8=AE=A4=E8=AF=81=E4=B8=BA=E4=B8=89=E8=A6=81=E7=B4=A0?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=EF=BC=88=E5=A7=93=E5=90=8D+=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E8=AF=81=E5=8F=B7+=E6=89=8B=E6=9C=BA=E5=8F=B7?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 aliyun-kyc.provider.ts: 改用 ID_CARD_THREE 类型,添加 PhoneNumber 参数 - 后端 kyc-application.service.ts: 从用户账户获取手机号传递给 KYC provider - 前端 kyc_id_page.dart: 更新文案为"三要素验证" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../services/kyc-application.service.ts | 16 +++++++--- .../external/kyc/aliyun-kyc.provider.ts | 32 +++++++++++++------ .../kyc/presentation/pages/kyc_id_page.dart | 8 ++--- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/backend/services/identity-service/src/application/services/kyc-application.service.ts b/backend/services/identity-service/src/application/services/kyc-application.service.ts index c1aa2847..0714bfd6 100644 --- a/backend/services/identity-service/src/application/services/kyc-application.service.ts +++ b/backend/services/identity-service/src/application/services/kyc-application.service.ts @@ -211,15 +211,16 @@ export class KycApplicationService { /** * ======================================== - * 层级1: 实名认证 - 提交二要素验证 + * 层级1: 实名认证 - 提交三要素验证 * ======================================== + * 验证姓名+身份证号+手机号(手机号从用户账户获取) */ async submitRealNameVerification( userId: string, realName: string, idCardNumber: string, ) { - this.logger.log(`[KYC] [Level1] Submitting real name verification for user: ${userId}`); + this.logger.log(`[KYC] [Level1] Submitting real name verification (3-factor) for user: ${userId}`); const config = await this.getKycConfig(); if (!config.level1Enabled) { @@ -231,6 +232,7 @@ export class KycApplicationService { select: { realNameVerified: true, kycStatus: true, + phoneNumber: true, }, }); @@ -242,6 +244,10 @@ export class KycApplicationService { throw new ApplicationError('实名认证已完成,无需重复提交'); } + if (!user.phoneNumber) { + throw new ApplicationError('未绑定手机号,无法进行实名认证'); + } + // 生成请求 ID const requestId = `REAL_NAME_${userId}_${Date.now()}`; @@ -249,22 +255,24 @@ export class KycApplicationService { const attempt = await this.prisma.kycVerificationAttempt.create({ data: { userId: BigInt(userId), - verificationType: 'ID_CARD', + verificationType: 'ID_CARD_THREE', provider: 'ALIYUN', requestId, inputData: { realName: this.maskName(realName), idCardNumber: this.maskIdCard(idCardNumber), + phoneNumber: this.maskPhoneNumber(user.phoneNumber), }, status: 'PENDING', }, }); try { - // 调用阿里云二要素验证 + // 调用阿里云三要素验证 const result = await this.aliyunKycProvider.verifyIdCard( realName, idCardNumber, + user.phoneNumber, requestId, ); diff --git a/backend/services/identity-service/src/infrastructure/external/kyc/aliyun-kyc.provider.ts b/backend/services/identity-service/src/infrastructure/external/kyc/aliyun-kyc.provider.ts index 843f0175..fffa180c 100644 --- a/backend/services/identity-service/src/infrastructure/external/kyc/aliyun-kyc.provider.ts +++ b/backend/services/identity-service/src/infrastructure/external/kyc/aliyun-kyc.provider.ts @@ -53,7 +53,7 @@ export interface IdCardOcrResult { /** * 阿里云实人认证服务 - 支持三层认证 * - * 层级1: 实名认证 - 二要素验证(姓名+身份证号) + * 层级1: 实名认证 - 三要素验证(姓名+身份证号+手机号) * 层级2: 实人认证 - 人脸活体检测 * 层级3: KYC - 证件照OCR识别验证 * @@ -83,32 +83,34 @@ export class AliyunKycProvider { /** * ======================================== - * 层级1: 实名认证 - 二要素验证 + * 层级1: 实名认证 - 三要素验证 * ======================================== - * 验证姓名和身份证号是否匹配 + * 验证姓名、身份证号和手机号是否匹配 */ async verifyIdCard( realName: string, idCardNumber: string, + phoneNumber: string, requestId: string, ): Promise { - this.logger.log(`[AliyunKYC] [Level1] Starting ID card verification, requestId: ${requestId}`); + this.logger.log(`[AliyunKYC] [Level1] Starting ID card verification (3-factor), requestId: ${requestId}`); // 开发/测试环境:模拟验证 if (!this.enabled) { this.logger.warn('[AliyunKYC] KYC is disabled, using mock verification'); - return this.mockIdCardVerification(realName, idCardNumber); + return this.mockIdCardVerification(realName, idCardNumber, phoneNumber); } try { - // 调用阿里云身份二要素核验 API + // 调用阿里云身份三要素核验 API const params = { Action: 'VerifyMaterial', Version: '2019-03-07', Format: 'JSON', - BizType: 'ID_CARD_TWO', + BizType: 'ID_CARD_THREE', Name: realName, IdCardNumber: idCardNumber, + PhoneNumber: phoneNumber, }; const response = await this.callAliyunApi(params); @@ -410,8 +412,8 @@ export class AliyunKycProvider { // ============ Mock 方法 (开发/测试环境) ============ - private mockIdCardVerification(realName: string, idCardNumber: string): IdCardVerificationResult { - this.logger.log('[AliyunKYC] Using mock ID card verification'); + private mockIdCardVerification(realName: string, idCardNumber: string, phoneNumber: string): IdCardVerificationResult { + this.logger.log('[AliyunKYC] Using mock ID card verification (3-factor)'); // 基本格式验证 if (!realName || realName.length < 2) { @@ -441,9 +443,19 @@ export class AliyunKycProvider { }; } + // 手机号格式验证 + const phoneRegex = /^1[3-9]\d{9}$/; + if (!phoneNumber || !phoneRegex.test(phoneNumber)) { + return { + success: false, + errorMessage: '手机号格式不正确', + rawResponse: { mock: true, reason: 'invalid_phone' }, + }; + } + return { success: true, - rawResponse: { mock: true, verifyTime: new Date().toISOString() }, + rawResponse: { mock: true, verifyTime: new Date().toISOString(), verifyType: '3-factor' }, }; } diff --git a/frontend/mobile-app/lib/features/kyc/presentation/pages/kyc_id_page.dart b/frontend/mobile-app/lib/features/kyc/presentation/pages/kyc_id_page.dart index 1be32d4f..589421ec 100644 --- a/frontend/mobile-app/lib/features/kyc/presentation/pages/kyc_id_page.dart +++ b/frontend/mobile-app/lib/features/kyc/presentation/pages/kyc_id_page.dart @@ -4,7 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:go_router/go_router.dart'; import 'kyc_entry_page.dart'; -/// KYC 层级1: 实名认证页面 (二要素验证) +/// KYC 层级1: 实名认证页面 (三要素验证: 姓名+身份证号+手机号) class KycIdPage extends ConsumerStatefulWidget { const KycIdPage({super.key}); @@ -143,7 +143,7 @@ class _KycIdPageState extends ConsumerState { SizedBox(height: 24.h), Text( - '二要素验证', + '三要素验证', style: TextStyle( fontSize: 24.sp, fontWeight: FontWeight.w700, @@ -152,7 +152,7 @@ class _KycIdPageState extends ConsumerState { ), SizedBox(height: 8.h), Text( - '请填写与身份证一致的信息', + '请填写与身份证一致的信息,系统将自动使用您的注册手机号进行验证', style: TextStyle( fontSize: 14.sp, color: const Color(0xFF999999), @@ -224,7 +224,7 @@ class _KycIdPageState extends ConsumerState { SizedBox(width: 8.w), Expanded( child: Text( - '您的身份信息将通过权威数据源进行验证,信息将被加密存储,不会泄露给任何第三方。', + '您的身份信息(姓名+身份证号+手机号)将通过权威数据源进行三要素验证,信息将被加密存储,不会泄露给任何第三方。', style: TextStyle( fontSize: 12.sp, color: const Color(0xFFE65100),