refactor: improve auto-create API semantics and use real device ID
Frontend (account_service.dart): - Use Android ID instead of random UUID for deviceId - Add DeviceHardwareInfo class with full hardware details - Remove provinceCode/cityCode from CreateAccountRequest - Simplify to: deviceId (required), deviceName (optional JSON), inviterReferralCode (optional) Backend (identity-service): - Rename validateDeviceId() to checkDeviceNotRegistered() for clarity - Rename generateNext() to generateNextUserSequence() for semantics - Update error message: "该设备已创建过账户" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bc82f58549
commit
17fd663fe3
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(cat:*)",
|
||||||
|
"Bash(git add:*)",
|
||||||
|
"Bash(git commit:*)",
|
||||||
|
"Bash(git push:*)"
|
||||||
|
],
|
||||||
|
"deny": [],
|
||||||
|
"ask": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,8 +26,8 @@ export class AutoCreateAccountHandler {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(command: AutoCreateAccountCommand): Promise<AutoCreateAccountResult> {
|
async execute(command: AutoCreateAccountCommand): Promise<AutoCreateAccountResult> {
|
||||||
const deviceValidation = await this.validatorService.validateDeviceId(command.deviceId);
|
const deviceCheck = await this.validatorService.checkDeviceNotRegistered(command.deviceId);
|
||||||
if (!deviceValidation.isValid) throw new ApplicationError(deviceValidation.errorMessage!);
|
if (!deviceCheck.isValid) throw new ApplicationError(deviceCheck.errorMessage!);
|
||||||
|
|
||||||
let inviterSequence: AccountSequence | null = null;
|
let inviterSequence: AccountSequence | null = null;
|
||||||
if (command.inviterReferralCode) {
|
if (command.inviterReferralCode) {
|
||||||
|
|
@ -38,7 +38,7 @@ export class AutoCreateAccountHandler {
|
||||||
inviterSequence = inviter!.accountSequence;
|
inviterSequence = inviter!.accountSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountSequence = await this.sequenceGenerator.generateNext();
|
const accountSequence = await this.sequenceGenerator.generateNextUserSequence();
|
||||||
|
|
||||||
const account = UserAccount.createAutomatic({
|
const account = UserAccount.createAutomatic({
|
||||||
accountSequence,
|
accountSequence,
|
||||||
|
|
|
||||||
|
|
@ -73,8 +73,8 @@ export class UserApplicationService {
|
||||||
inviterSequence = inviter!.accountSequence;
|
inviterSequence = inviter!.accountSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 生成账户序列号
|
// 3. 生成用户序列号
|
||||||
const accountSequence = await this.sequenceGenerator.generateNext();
|
const accountSequence = await this.sequenceGenerator.generateNextUserSequence();
|
||||||
|
|
||||||
// 4. 创建用户账户
|
// 4. 创建用户账户
|
||||||
const account = UserAccount.createAutomatic({
|
const account = UserAccount.createAutomatic({
|
||||||
|
|
@ -301,7 +301,7 @@ export class UserApplicationService {
|
||||||
inviterSequence = inviter!.accountSequence;
|
inviterSequence = inviter!.accountSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountSequence = await this.sequenceGenerator.generateNext();
|
const accountSequence = await this.sequenceGenerator.generateNextUserSequence();
|
||||||
|
|
||||||
const account = UserAccount.create({
|
const account = UserAccount.create({
|
||||||
accountSequence,
|
accountSequence,
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export class AccountSequenceGeneratorService {
|
||||||
private readonly repository: UserAccountRepository,
|
private readonly repository: UserAccountRepository,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async generateNext(): Promise<AccountSequence> {
|
async generateNextUserSequence(): Promise<AccountSequence> {
|
||||||
return this.repository.getNextAccountSequence();
|
return this.repository.getNextAccountSequence();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,9 +30,9 @@ export class UserValidatorService {
|
||||||
return ValidationResult.success();
|
return ValidationResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateDeviceId(deviceId: string): Promise<ValidationResult> {
|
async checkDeviceNotRegistered(deviceId: string): Promise<ValidationResult> {
|
||||||
const existing = await this.repository.findByDeviceId(deviceId);
|
const existing = await this.repository.findByDeviceId(deviceId);
|
||||||
if (existing) return ValidationResult.failure('该设备已创建账户');
|
if (existing) return ValidationResult.failure('该设备已创建过账户');
|
||||||
return ValidationResult.success();
|
return ValidationResult.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,29 +8,60 @@ import '../storage/storage_keys.dart';
|
||||||
import '../errors/exceptions.dart';
|
import '../errors/exceptions.dart';
|
||||||
import 'mpc_share_service.dart';
|
import 'mpc_share_service.dart';
|
||||||
|
|
||||||
|
/// 设备硬件信息 (存储在 deviceName 字段中)
|
||||||
|
class DeviceHardwareInfo {
|
||||||
|
final String? brand; // 品牌 (如 "Xiaomi")
|
||||||
|
final String? manufacturer; // 厂商 (如 "Xiaomi")
|
||||||
|
final String? model; // 型号 (如 "Mi 11")
|
||||||
|
final String? device; // 设备名 (如 "venus")
|
||||||
|
final String? product; // 产品名 (如 "venus_eea")
|
||||||
|
final String? hardware; // 硬件名 (如 "qcom")
|
||||||
|
final String? osVersion; // 系统版本 (如 "13")
|
||||||
|
final int? sdkInt; // SDK 版本 (如 33)
|
||||||
|
final bool? isPhysicalDevice; // 是否真机
|
||||||
|
|
||||||
|
DeviceHardwareInfo({
|
||||||
|
this.brand,
|
||||||
|
this.manufacturer,
|
||||||
|
this.model,
|
||||||
|
this.device,
|
||||||
|
this.product,
|
||||||
|
this.hardware,
|
||||||
|
this.osVersion,
|
||||||
|
this.sdkInt,
|
||||||
|
this.isPhysicalDevice,
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
if (brand != null) 'brand': brand,
|
||||||
|
if (manufacturer != null) 'manufacturer': manufacturer,
|
||||||
|
if (model != null) 'model': model,
|
||||||
|
if (device != null) 'device': device,
|
||||||
|
if (product != null) 'product': product,
|
||||||
|
if (hardware != null) 'hardware': hardware,
|
||||||
|
if (osVersion != null) 'osVersion': osVersion,
|
||||||
|
if (sdkInt != null) 'sdkInt': sdkInt,
|
||||||
|
if (isPhysicalDevice != null) 'isPhysicalDevice': isPhysicalDevice,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// 创建账号请求
|
/// 创建账号请求
|
||||||
class CreateAccountRequest {
|
class CreateAccountRequest {
|
||||||
final String deviceId;
|
final String deviceId; // 必填: Android ID 或 iOS identifierForVendor
|
||||||
final String? deviceName;
|
final DeviceHardwareInfo? deviceName; // 可选: 设备硬件信息 (JSON)
|
||||||
final String? inviterReferralCode;
|
final String? inviterReferralCode; // 可选: 邀请人推荐码
|
||||||
final String? provinceCode;
|
|
||||||
final String? cityCode;
|
|
||||||
|
|
||||||
CreateAccountRequest({
|
CreateAccountRequest({
|
||||||
required this.deviceId,
|
required this.deviceId,
|
||||||
this.deviceName,
|
this.deviceName,
|
||||||
this.inviterReferralCode,
|
this.inviterReferralCode,
|
||||||
this.provinceCode,
|
|
||||||
this.cityCode,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'deviceId': deviceId,
|
'deviceId': deviceId,
|
||||||
if (deviceName != null) 'deviceName': deviceName,
|
if (deviceName != null) 'deviceName': deviceName!.toJson(),
|
||||||
if (inviterReferralCode != null)
|
if (inviterReferralCode != null)
|
||||||
'inviterReferralCode': inviterReferralCode,
|
'inviterReferralCode': inviterReferralCode,
|
||||||
if (provinceCode != null) 'provinceCode': provinceCode,
|
|
||||||
if (cityCode != null) 'cityCode': cityCode,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,33 +139,59 @@ class AccountService {
|
||||||
}) : _apiClient = apiClient,
|
}) : _apiClient = apiClient,
|
||||||
_secureStorage = secureStorage;
|
_secureStorage = secureStorage;
|
||||||
|
|
||||||
/// 获取或生成设备ID
|
/// 获取设备唯一标识符
|
||||||
Future<String> getOrCreateDeviceId() async {
|
///
|
||||||
// 尝试从存储读取
|
/// Android: 使用 Android ID (64位十六进制字符串)
|
||||||
var deviceId = await _secureStorage.read(key: StorageKeys.deviceId);
|
/// iOS: 使用 identifierForVendor
|
||||||
if (deviceId != null && deviceId.isNotEmpty) {
|
Future<String> getDeviceId() async {
|
||||||
return deviceId;
|
final deviceInfoPlugin = DeviceInfoPlugin();
|
||||||
}
|
|
||||||
|
|
||||||
// 生成新的设备ID
|
|
||||||
deviceId = const Uuid().v4();
|
|
||||||
await _secureStorage.write(key: StorageKeys.deviceId, value: deviceId);
|
|
||||||
return deviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 获取设备名称
|
|
||||||
Future<String> getDeviceName() async {
|
|
||||||
final deviceInfo = DeviceInfoPlugin();
|
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
final info = await deviceInfo.androidInfo;
|
final info = await deviceInfoPlugin.androidInfo;
|
||||||
return '${info.brand} ${info.model}';
|
return info.id; // Android ID
|
||||||
} else if (Platform.isIOS) {
|
} else if (Platform.isIOS) {
|
||||||
final info = await deviceInfo.iosInfo;
|
final info = await deviceInfoPlugin.iosInfo;
|
||||||
return info.name;
|
return info.identifierForVendor ?? const Uuid().v4();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Unknown Device';
|
// 其他平台回退到 UUID
|
||||||
|
return const Uuid().v4();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取设备硬件信息
|
||||||
|
///
|
||||||
|
/// Android: 完整硬件信息
|
||||||
|
/// iOS: 基本信息
|
||||||
|
Future<DeviceHardwareInfo> getDeviceHardwareInfo() async {
|
||||||
|
final deviceInfoPlugin = DeviceInfoPlugin();
|
||||||
|
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
final info = await deviceInfoPlugin.androidInfo;
|
||||||
|
return DeviceHardwareInfo(
|
||||||
|
brand: info.brand,
|
||||||
|
manufacturer: info.manufacturer,
|
||||||
|
model: info.model,
|
||||||
|
device: info.device,
|
||||||
|
product: info.product,
|
||||||
|
hardware: info.hardware,
|
||||||
|
osVersion: info.version.release,
|
||||||
|
sdkInt: info.version.sdkInt,
|
||||||
|
isPhysicalDevice: info.isPhysicalDevice,
|
||||||
|
);
|
||||||
|
} else if (Platform.isIOS) {
|
||||||
|
final info = await deviceInfoPlugin.iosInfo;
|
||||||
|
return DeviceHardwareInfo(
|
||||||
|
brand: 'Apple',
|
||||||
|
manufacturer: 'Apple',
|
||||||
|
model: info.model,
|
||||||
|
device: info.name,
|
||||||
|
osVersion: info.systemVersion,
|
||||||
|
isPhysicalDevice: info.isPhysicalDevice,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他平台返回空信息
|
||||||
|
return DeviceHardwareInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 自动创建账号 (首次打开APP)
|
/// 自动创建账号 (首次打开APP)
|
||||||
|
|
@ -142,13 +199,12 @@ class AccountService {
|
||||||
/// 使用 MPC 2-of-3 协议生成钱包地址
|
/// 使用 MPC 2-of-3 协议生成钱包地址
|
||||||
Future<CreateAccountResponse> createAccount({
|
Future<CreateAccountResponse> createAccount({
|
||||||
String? inviterReferralCode,
|
String? inviterReferralCode,
|
||||||
String? provinceCode,
|
|
||||||
String? cityCode,
|
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
// 获取设备信息
|
// 获取设备ID (必填)
|
||||||
final deviceId = await getOrCreateDeviceId();
|
final deviceId = await getDeviceId();
|
||||||
final deviceName = await getDeviceName();
|
// 获取设备硬件信息 (可选)
|
||||||
|
final deviceName = await getDeviceHardwareInfo();
|
||||||
|
|
||||||
// 调用 API
|
// 调用 API
|
||||||
final response = await _apiClient.post(
|
final response = await _apiClient.post(
|
||||||
|
|
@ -157,8 +213,6 @@ class AccountService {
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
deviceName: deviceName,
|
deviceName: deviceName,
|
||||||
inviterReferralCode: inviterReferralCode,
|
inviterReferralCode: inviterReferralCode,
|
||||||
provinceCode: provinceCode,
|
|
||||||
cityCode: cityCode,
|
|
||||||
).toJson(),
|
).toJson(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue