340 lines
10 KiB
Dart
340 lines
10 KiB
Dart
import 'package:flutter/foundation.dart';
|
|
import '../../../core/network/api_client.dart';
|
|
import '../../../core/errors/exceptions.dart';
|
|
|
|
/// KYC 状态枚举
|
|
enum KycStatusType {
|
|
notStarted, // 未开始
|
|
phoneVerified, // 手机已验证
|
|
idPending, // 身份验证中
|
|
idVerified, // 身份已验证
|
|
completed, // 已完成
|
|
rejected, // 已拒绝
|
|
}
|
|
|
|
/// KYC 状态响应
|
|
class KycStatusResponse {
|
|
final bool phoneVerified;
|
|
final String kycStatus;
|
|
final String? realName;
|
|
final String? idCardNumber;
|
|
final DateTime? kycVerifiedAt;
|
|
final String? rejectedReason;
|
|
final String? phoneNumber;
|
|
|
|
KycStatusResponse({
|
|
required this.phoneVerified,
|
|
required this.kycStatus,
|
|
this.realName,
|
|
this.idCardNumber,
|
|
this.kycVerifiedAt,
|
|
this.rejectedReason,
|
|
this.phoneNumber,
|
|
});
|
|
|
|
factory KycStatusResponse.fromJson(Map<String, dynamic> json) {
|
|
return KycStatusResponse(
|
|
phoneVerified: json['phoneVerified'] as bool? ?? false,
|
|
kycStatus: json['kycStatus'] as String? ?? 'NOT_STARTED',
|
|
realName: json['realName'] as String?,
|
|
idCardNumber: json['idCardNumber'] as String?,
|
|
kycVerifiedAt: json['kycVerifiedAt'] != null
|
|
? DateTime.parse(json['kycVerifiedAt'] as String)
|
|
: null,
|
|
rejectedReason: json['rejectedReason'] as String?,
|
|
phoneNumber: json['phoneNumber'] as String?,
|
|
);
|
|
}
|
|
|
|
KycStatusType get statusType {
|
|
switch (kycStatus) {
|
|
case 'NOT_STARTED':
|
|
return KycStatusType.notStarted;
|
|
case 'PHONE_VERIFIED':
|
|
return KycStatusType.phoneVerified;
|
|
case 'ID_PENDING':
|
|
return KycStatusType.idPending;
|
|
case 'ID_VERIFIED':
|
|
return KycStatusType.idVerified;
|
|
case 'COMPLETED':
|
|
return KycStatusType.completed;
|
|
case 'REJECTED':
|
|
return KycStatusType.rejected;
|
|
default:
|
|
return KycStatusType.notStarted;
|
|
}
|
|
}
|
|
|
|
bool get isCompleted => statusType == KycStatusType.completed;
|
|
bool get needsPhoneVerification => !phoneVerified;
|
|
bool get needsIdVerification =>
|
|
statusType == KycStatusType.notStarted ||
|
|
statusType == KycStatusType.phoneVerified ||
|
|
statusType == KycStatusType.rejected;
|
|
}
|
|
|
|
/// 身份验证响应
|
|
class IdVerificationResponse {
|
|
final String requestId;
|
|
final String status;
|
|
final String? failureReason;
|
|
final String? kycStatus;
|
|
|
|
IdVerificationResponse({
|
|
required this.requestId,
|
|
required this.status,
|
|
this.failureReason,
|
|
this.kycStatus,
|
|
});
|
|
|
|
factory IdVerificationResponse.fromJson(Map<String, dynamic> json) {
|
|
return IdVerificationResponse(
|
|
requestId: json['requestId'] as String,
|
|
status: json['status'] as String,
|
|
failureReason: json['failureReason'] as String?,
|
|
kycStatus: json['kycStatus'] as String?,
|
|
);
|
|
}
|
|
|
|
bool get isSuccess => status == 'SUCCESS';
|
|
bool get isFailed => status == 'FAILED';
|
|
}
|
|
|
|
/// KYC 服务
|
|
class KycService {
|
|
static const String _tag = '[KycService]';
|
|
final ApiClient _apiClient;
|
|
|
|
KycService(this._apiClient);
|
|
|
|
/// 获取 KYC 状态
|
|
Future<KycStatusResponse> 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<String, dynamic>;
|
|
final data = responseData['data'] as Map<String, dynamic>;
|
|
return KycStatusResponse.fromJson(data);
|
|
} on ApiException {
|
|
rethrow;
|
|
} catch (e) {
|
|
debugPrint('$_tag getKycStatus() - 异常: $e');
|
|
throw ApiException('获取 KYC 状态失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 发送 KYC 手机验证码
|
|
Future<void> sendKycVerifySms() async {
|
|
debugPrint('$_tag sendKycVerifySms() - 发送验证码');
|
|
|
|
try {
|
|
final response = await _apiClient.post('/user/kyc/send-verify-sms');
|
|
debugPrint('$_tag sendKycVerifySms() - 响应: ${response.statusCode}');
|
|
} on ApiException {
|
|
rethrow;
|
|
} catch (e) {
|
|
debugPrint('$_tag sendKycVerifySms() - 异常: $e');
|
|
throw ApiException('发送验证码失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 验证手机号 (KYC 流程)
|
|
Future<void> 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<IdVerificationResponse> submitIdVerification({
|
|
required String realName,
|
|
required String idCardNumber,
|
|
}) async {
|
|
debugPrint('$_tag submitIdVerification() - 提交身份验证');
|
|
|
|
try {
|
|
final response = await _apiClient.post(
|
|
'/user/kyc/submit-id',
|
|
data: {
|
|
'realName': realName,
|
|
'idCardNumber': idCardNumber,
|
|
},
|
|
);
|
|
debugPrint('$_tag submitIdVerification() - 响应: ${response.statusCode}');
|
|
|
|
if (response.data == null) {
|
|
throw const ApiException('提交身份验证失败: 空响应');
|
|
}
|
|
|
|
final responseData = response.data as Map<String, dynamic>;
|
|
final data = responseData['data'] as Map<String, dynamic>;
|
|
return IdVerificationResponse.fromJson(data);
|
|
} on ApiException {
|
|
rethrow;
|
|
} catch (e) {
|
|
debugPrint('$_tag submitIdVerification() - 异常: $e');
|
|
throw ApiException('提交身份验证失败: $e');
|
|
}
|
|
}
|
|
|
|
// ============ 更换手机号相关 ============
|
|
|
|
/// 获取手机号状态
|
|
Future<PhoneStatusResponse> 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<String, dynamic>;
|
|
final data = responseData['data'] as Map<String, dynamic>;
|
|
return PhoneStatusResponse.fromJson(data);
|
|
} on ApiException {
|
|
rethrow;
|
|
} catch (e) {
|
|
debugPrint('$_tag getPhoneStatus() - 异常: $e');
|
|
throw ApiException('获取手机号状态失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 发送旧手机验证码(更换手机号第一步)
|
|
Future<void> 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');
|
|
}
|
|
}
|
|
|
|
/// 验证旧手机验证码(更换手机号第二步)
|
|
Future<String> 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<String, dynamic>;
|
|
final data = responseData['data'] as Map<String, dynamic>;
|
|
return data['changePhoneToken'] as String;
|
|
} on ApiException {
|
|
rethrow;
|
|
} catch (e) {
|
|
debugPrint('$_tag verifyOldPhoneCode() - 异常: $e');
|
|
throw ApiException('验证失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 发送新手机验证码(更换手机号第三步)
|
|
Future<void> 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<void> 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');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 手机号状态响应
|
|
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<String, dynamic> 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,
|
|
);
|
|
}
|
|
}
|