import 'package:flutter/foundation.dart'; import '../../../core/network/api_client.dart'; import '../../../core/errors/exceptions.dart'; /// KYC 状态枚举 (综合状态) enum KycStatusType { notStarted, // 未开始 realNameVerified, // 层级1完成: 实名认证通过 faceVerified, // 层级2完成: 实人认证通过 kycVerified, // 层级3完成: KYC认证通过 completed, // 所有层级完成 rejected, // 被拒绝 } /// KYC 配置响应 class KycConfigResponse { final bool level1Enabled; // 实名认证开关 final bool level2Enabled; // 实人认证开关 final bool level3Enabled; // KYC证件照开关 KycConfigResponse({ required this.level1Enabled, required this.level2Enabled, required this.level3Enabled, }); factory KycConfigResponse.fromJson(Map json) { return KycConfigResponse( level1Enabled: json['level1Enabled'] as bool? ?? true, level2Enabled: json['level2Enabled'] as bool? ?? true, level3Enabled: json['level3Enabled'] as bool? ?? true, ); } } /// 层级1状态 class KycLevel1Status { final bool enabled; final bool verified; final DateTime? verifiedAt; final String? realName; final String? idCardNumber; KycLevel1Status({ required this.enabled, required this.verified, this.verifiedAt, this.realName, this.idCardNumber, }); factory KycLevel1Status.fromJson(Map json) { return KycLevel1Status( enabled: json['enabled'] as bool? ?? true, verified: json['verified'] as bool? ?? false, verifiedAt: json['verifiedAt'] != null ? DateTime.parse(json['verifiedAt'] as String) : null, realName: json['realName'] as String?, idCardNumber: json['idCardNumber'] as String?, ); } } /// 层级2状态 class KycLevel2Status { final bool enabled; final bool verified; final DateTime? verifiedAt; final bool canStart; KycLevel2Status({ required this.enabled, required this.verified, this.verifiedAt, required this.canStart, }); factory KycLevel2Status.fromJson(Map json) { return KycLevel2Status( enabled: json['enabled'] as bool? ?? true, verified: json['verified'] as bool? ?? false, verifiedAt: json['verifiedAt'] != null ? DateTime.parse(json['verifiedAt'] as String) : null, canStart: json['canStart'] as bool? ?? false, ); } } /// 层级3状态 class KycLevel3Status { final bool enabled; final bool verified; final DateTime? verifiedAt; final bool hasIdCardFront; final bool hasIdCardBack; final bool canStart; KycLevel3Status({ required this.enabled, required this.verified, this.verifiedAt, required this.hasIdCardFront, required this.hasIdCardBack, required this.canStart, }); factory KycLevel3Status.fromJson(Map json) { return KycLevel3Status( enabled: json['enabled'] as bool? ?? true, verified: json['verified'] as bool? ?? false, verifiedAt: json['verifiedAt'] != null ? DateTime.parse(json['verifiedAt'] as String) : null, hasIdCardFront: json['hasIdCardFront'] as bool? ?? false, hasIdCardBack: json['hasIdCardBack'] as bool? ?? false, canStart: json['canStart'] as bool? ?? false, ); } } /// KYC 完整状态响应 (支持三层认证) class KycStatusResponse { final KycConfigResponse config; final List requiredSteps; // 层级1: 实名认证 final KycLevel1Status level1; // 层级2: 实人认证 final KycLevel2Status level2; // 层级3: KYC final KycLevel3Status level3; // 综合状态 final String kycStatus; final bool isCompleted; final String? rejectedReason; final String? phoneNumber; final bool phoneVerified; KycStatusResponse({ required this.config, required this.requiredSteps, required this.level1, required this.level2, required this.level3, required this.kycStatus, required this.isCompleted, this.rejectedReason, this.phoneNumber, required this.phoneVerified, }); factory KycStatusResponse.fromJson(Map json) { return KycStatusResponse( config: KycConfigResponse.fromJson( json['config'] as Map? ?? {}, ), requiredSteps: (json['requiredSteps'] as List?)?.cast() ?? [], level1: KycLevel1Status.fromJson( json['level1'] as Map? ?? {}, ), level2: KycLevel2Status.fromJson( json['level2'] as Map? ?? {}, ), level3: KycLevel3Status.fromJson( json['level3'] as Map? ?? {}, ), kycStatus: json['kycStatus'] as String? ?? 'NOT_STARTED', isCompleted: json['isCompleted'] as bool? ?? false, rejectedReason: json['rejectedReason'] as String?, phoneNumber: json['phoneNumber'] as String?, phoneVerified: json['phoneVerified'] as bool? ?? false, ); } KycStatusType get statusType { switch (kycStatus) { case 'REAL_NAME_VERIFIED': return KycStatusType.realNameVerified; case 'FACE_VERIFIED': return KycStatusType.faceVerified; case 'KYC_VERIFIED': return KycStatusType.kycVerified; case 'COMPLETED': return KycStatusType.completed; case 'REJECTED': return KycStatusType.rejected; default: return KycStatusType.notStarted; } } /// 兼容旧代码 String? get realName => level1.realName; String? get idCardNumber => level1.idCardNumber; DateTime? get kycVerifiedAt => level3.verifiedAt ?? level2.verifiedAt ?? level1.verifiedAt; bool get needsIdVerification => !level1.verified && level1.enabled; } /// 实名认证响应 class RealNameVerifyResponse { final bool success; final int level; final String? status; final String? message; final String? errorMessage; RealNameVerifyResponse({ required this.success, required this.level, this.status, this.message, this.errorMessage, }); factory RealNameVerifyResponse.fromJson(Map json) { return RealNameVerifyResponse( success: json['success'] as bool? ?? false, level: json['level'] as int? ?? 1, status: json['status'] as String?, message: json['message'] as String?, errorMessage: json['errorMessage'] as String?, ); } } /// 人脸认证初始化响应 class FaceVerifyInitResponse { final bool success; final String? certifyId; final String? certifyUrl; FaceVerifyInitResponse({ required this.success, this.certifyId, this.certifyUrl, }); factory FaceVerifyInitResponse.fromJson(Map json) { return FaceVerifyInitResponse( success: json['success'] as bool? ?? false, certifyId: json['certifyId'] as String?, certifyUrl: json['certifyUrl'] as String?, ); } } /// 人脸认证查询响应 class FaceVerifyQueryResponse { final bool success; final bool passed; final String status; final String? errorMessage; FaceVerifyQueryResponse({ required this.success, required this.passed, required this.status, this.errorMessage, }); factory FaceVerifyQueryResponse.fromJson(Map json) { return FaceVerifyQueryResponse( success: json['success'] as bool? ?? false, passed: json['passed'] as bool? ?? false, status: json['status'] as String? ?? 'PENDING', errorMessage: json['errorMessage'] as String?, ); } } /// 证件照上传响应 class IdCardUploadResponse { final bool success; final String side; final String? imageUrl; final IdCardOcrResult? ocrResult; IdCardUploadResponse({ required this.success, required this.side, this.imageUrl, this.ocrResult, }); factory IdCardUploadResponse.fromJson(Map json) { return IdCardUploadResponse( success: json['success'] as bool? ?? false, side: json['side'] as String? ?? '', imageUrl: json['imageUrl'] as String?, ocrResult: json['ocrResult'] != null ? IdCardOcrResult.fromJson(json['ocrResult'] as Map) : null, ); } } /// OCR 识别结果 class IdCardOcrResult { final String? name; final String? idNumber; final String? address; final String? issueAuthority; final String? validPeriod; IdCardOcrResult({ this.name, this.idNumber, this.address, this.issueAuthority, this.validPeriod, }); factory IdCardOcrResult.fromJson(Map json) { return IdCardOcrResult( name: json['name'] as String?, idNumber: json['idNumber'] as String?, address: json['address'] as String?, issueAuthority: json['issueAuthority'] as String?, validPeriod: json['validPeriod'] as String?, ); } } /// KYC 确认响应 class KycConfirmResponse { final bool success; final int level; final String status; final String message; KycConfirmResponse({ required this.success, required this.level, required this.status, required this.message, }); factory KycConfirmResponse.fromJson(Map json) { return KycConfirmResponse( success: json['success'] as bool? ?? false, level: json['level'] as int? ?? 3, status: json['status'] as String? ?? '', message: json['message'] as String? ?? '', ); } } /// 手机号状态响应 class PhoneStatusResponse { final bool isBound; final bool isVerified; final String? phoneNumber; final DateTime? verifiedAt; PhoneStatusResponse({ required this.isBound, required this.isVerified, this.phoneNumber, this.verifiedAt, }); factory PhoneStatusResponse.fromJson(Map json) { return PhoneStatusResponse( isBound: json['isBound'] as bool? ?? false, isVerified: json['isVerified'] as bool? ?? false, phoneNumber: json['phoneNumber'] as String?, verifiedAt: json['verifiedAt'] != null ? DateTime.parse(json['verifiedAt'] as String) : null, ); } } /// 验证旧手机响应 class VerifyOldPhoneResponse { final String changePhoneToken; final bool wasAlreadyVerified; VerifyOldPhoneResponse({ required this.changePhoneToken, required this.wasAlreadyVerified, }); factory VerifyOldPhoneResponse.fromJson(Map json) { return VerifyOldPhoneResponse( changePhoneToken: json['changePhoneToken'] as String, wasAlreadyVerified: json['wasAlreadyVerified'] as bool? ?? true, ); } } /// KYC 服务 - 支持三层认证 /// 层级1: 实名认证 (二要素: 姓名+身份证号) /// 层级2: 实人认证 (人脸活体检测) /// 层级3: KYC (证件照上传验证) class KycService { static const String _tag = '[KycService]'; final ApiClient _apiClient; KycService(this._apiClient); // ============ 获取状态和配置 ============ /// 获取 KYC 配置(三层认证开关) Future getKycConfig() async { debugPrint('$_tag getKycConfig() - 获取 KYC 配置'); try { final response = await _apiClient.get('/user/kyc/config'); debugPrint('$_tag getKycConfig() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('获取 KYC 配置失败: 空响应'); } final responseData = response.data as Map; final data = responseData['data'] as Map; return KycConfigResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag getKycConfig() - 异常: $e'); throw ApiException('获取 KYC 配置失败: $e'); } } /// 获取 KYC 完整状态(包含三层认证详情) Future getKycStatus() async { debugPrint('$_tag getKycStatus() - 获取 KYC 状态'); try { final response = await _apiClient.get('/user/kyc/status'); debugPrint('$_tag getKycStatus() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('获取 KYC 状态失败: 空响应'); } final responseData = response.data as Map; // 后端返回格式: { success, data: { code, message, data: {...实际数据...} } } final outerData = responseData['data'] as Map; final data = outerData['data'] as Map; return KycStatusResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag getKycStatus() - 异常: $e'); throw ApiException('获取 KYC 状态失败: $e'); } } // ============ 层级1: 实名认证 ============ /// 提交实名认证(二要素验证) Future submitRealNameVerification({ required String realName, required String idCardNumber, }) async { debugPrint('$_tag submitRealNameVerification() - 提交实名认证'); try { final response = await _apiClient.post( '/user/kyc/level1/submit', data: { 'realName': realName, 'idCardNumber': idCardNumber, }, ); debugPrint('$_tag submitRealNameVerification() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('提交失败: 空响应'); } final responseData = response.data as Map; // 后端返回格式: { success, data: { code, message, data: {...实际数据...} } } final outerData = responseData['data'] as Map; final data = outerData['data'] as Map; return RealNameVerifyResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag submitRealNameVerification() - 异常: $e'); throw ApiException('实名认证失败: $e'); } } // ============ 层级2: 实人认证 ============ /// 初始化人脸活体检测 Future initFaceVerification({String? metaInfo}) async { debugPrint('$_tag initFaceVerification() - 初始化人脸认证'); try { final response = await _apiClient.post( '/user/kyc/level2/init', data: { if (metaInfo != null) 'metaInfo': metaInfo, }, ); debugPrint('$_tag initFaceVerification() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('初始化失败: 空响应'); } final responseData = response.data as Map; final data = responseData['data'] as Map; return FaceVerifyInitResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag initFaceVerification() - 异常: $e'); throw ApiException('初始化人脸认证失败: $e'); } } /// 查询人脸认证结果 Future queryFaceVerification(String certifyId) async { debugPrint('$_tag queryFaceVerification() - 查询人脸认证结果'); try { final response = await _apiClient.get( '/user/kyc/level2/query', queryParameters: {'certifyId': certifyId}, ); debugPrint('$_tag queryFaceVerification() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('查询失败: 空响应'); } final responseData = response.data as Map; final data = responseData['data'] as Map; return FaceVerifyQueryResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag queryFaceVerification() - 异常: $e'); throw ApiException('查询人脸认证结果失败: $e'); } } // ============ 层级3: KYC 证件照 ============ /// 上传身份证照片 Future uploadIdCardPhoto({ required String side, // 'front' 或 'back' required List imageBytes, required String fileName, }) async { debugPrint('$_tag uploadIdCardPhoto() - 上传证件照 side=$side'); try { final response = await _apiClient.uploadFile( '/user/kyc/level3/upload/$side', imageBytes, fileName, fieldName: 'file', ); debugPrint('$_tag uploadIdCardPhoto() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('上传失败: 空响应'); } final responseData = response.data as Map; final data = responseData['data'] as Map; return IdCardUploadResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag uploadIdCardPhoto() - 异常: $e'); throw ApiException('上传证件照失败: $e'); } } /// 确认提交 KYC Future confirmKycSubmission() async { debugPrint('$_tag confirmKycSubmission() - 确认提交 KYC'); try { final response = await _apiClient.post('/user/kyc/level3/confirm'); debugPrint('$_tag confirmKycSubmission() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('提交失败: 空响应'); } final responseData = response.data as Map; final data = responseData['data'] as Map; return KycConfirmResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag confirmKycSubmission() - 异常: $e'); throw ApiException('提交 KYC 失败: $e'); } } // ============ 手机号验证相关 (用于跳过验证的用户补充验证) ============ /// 发送手机号验证短信 Future sendKycVerifySms() async { debugPrint('$_tag sendKycVerifySms() - 发送 KYC 手机验证短信'); try { final response = await _apiClient.post('/user/kyc/send-phone-sms'); debugPrint('$_tag sendKycVerifySms() - 响应: ${response.statusCode}'); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag sendKycVerifySms() - 异常: $e'); throw ApiException('发送验证码失败: $e'); } } /// 验证手机号验证码 Future verifyPhoneForKyc(String smsCode) async { debugPrint('$_tag verifyPhoneForKyc() - 验证手机号'); try { final response = await _apiClient.post( '/user/kyc/verify-phone', data: {'smsCode': smsCode}, ); debugPrint('$_tag verifyPhoneForKyc() - 响应: ${response.statusCode}'); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag verifyPhoneForKyc() - 异常: $e'); throw ApiException('验证失败: $e'); } } // ============ 更换手机号相关 ============ /// 获取手机号状态 Future getPhoneStatus() async { debugPrint('$_tag getPhoneStatus() - 获取手机号状态'); try { final response = await _apiClient.get('/user/phone-status'); debugPrint('$_tag getPhoneStatus() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('获取手机号状态失败: 空响应'); } final responseData = response.data as Map; final data = responseData['data'] as Map; return PhoneStatusResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag getPhoneStatus() - 异常: $e'); throw ApiException('获取手机号状态失败: $e'); } } /// 发送旧手机验证码 Future sendOldPhoneCode() async { debugPrint('$_tag sendOldPhoneCode() - 发送旧手机验证码'); try { final response = await _apiClient.post('/user/change-phone/send-old-code'); debugPrint('$_tag sendOldPhoneCode() - 响应: ${response.statusCode}'); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag sendOldPhoneCode() - 异常: $e'); throw ApiException('发送验证码失败: $e'); } } /// 验证旧手机验证码 /// 返回包含 changePhoneToken 和 wasAlreadyVerified 的响应 Future verifyOldPhoneCode(String smsCode) async { debugPrint('$_tag verifyOldPhoneCode() - 验证旧手机验证码'); try { final response = await _apiClient.post( '/user/change-phone/verify-old', data: {'smsCode': smsCode}, ); debugPrint('$_tag verifyOldPhoneCode() - 响应: ${response.statusCode}'); if (response.data == null) { throw const ApiException('验证失败: 空响应'); } final responseData = response.data as Map; final data = responseData['data'] as Map; return VerifyOldPhoneResponse.fromJson(data); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag verifyOldPhoneCode() - 异常: $e'); throw ApiException('验证失败: $e'); } } /// 发送新手机验证码 Future sendNewPhoneCode({ required String newPhoneNumber, required String changePhoneToken, }) async { debugPrint('$_tag sendNewPhoneCode() - 发送新手机验证码'); try { final response = await _apiClient.post( '/user/change-phone/send-new-code', data: { 'newPhoneNumber': newPhoneNumber, 'changePhoneToken': changePhoneToken, }, ); debugPrint('$_tag sendNewPhoneCode() - 响应: ${response.statusCode}'); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag sendNewPhoneCode() - 异常: $e'); throw ApiException('发送验证码失败: $e'); } } /// 确认更换手机号 Future confirmChangePhone({ required String newPhoneNumber, required String smsCode, required String changePhoneToken, }) async { debugPrint('$_tag confirmChangePhone() - 确认更换手机号'); try { final response = await _apiClient.post( '/user/change-phone/confirm', data: { 'newPhoneNumber': newPhoneNumber, 'smsCode': smsCode, 'changePhoneToken': changePhoneToken, }, ); debugPrint('$_tag confirmChangePhone() - 响应: ${response.statusCode}'); } on ApiException { rethrow; } catch (e) { debugPrint('$_tag confirmChangePhone() - 异常: $e'); throw ApiException('更换手机号失败: $e'); } } }