feat(telemetry): 将userId改为userSerialNum字符串格式并完善遥测追踪
Backend (presence-service): - 将EventLog.userId从BigInt改为String类型,存储userSerialNum(如D25121400005) - 更新Prisma schema,userId字段改为VarChar(20)并添加索引 - 更新心跳相关命令和事件,统一使用userSerialNum字符串 - 添加数据库迁移文件 - 更新相关单元测试和集成测试 Frontend (mobile-app): - TelemetryEvent新增toServerJson()方法,格式化为后端API期望的格式 - AccountService登录/恢复时设置TelemetryService的userId - MultiAccountService切换账号时同步更新TelemetryService的userId - 退出登录时清除TelemetryService的userId - AuthProvider初始化时设置userId 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
3942cb405a
commit
cc06638e0e
|
|
@ -192,7 +192,8 @@
|
|||
"Bash(git commit -m \"$(cat <<''EOF''\nfeat(profile): 添加我的伞下功能 - 展示下级用户树形结构\n\n- 后端新增 GET /referral/user/:accountSequence/direct-referrals API\n- 前端新增伞下树组件,支持懒加载、缓存、展开/收起\n- 使用 CustomPaint 绘制父子节点连接线\n- 超出屏幕宽度时显示省略号,点击弹出底部列表\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n)\")",
|
||||
"Bash(git tag -a v1.0.0-beta1 -m \"$(cat <<''EOF''\nv1.0.0-beta1: 用户首次测试通过\n\n主要修复:\n- fix(reward): 修复 accountSequence 转 userId 时字母前缀导致的 BigInt 转换失败\n- fix(authorization): 修复下级团队认种数重复减去自己认种数的 BUG\n- fix(frontend): 修正权益金额显示与后端实际配置一致\n\n功能完善:\n- 社区权益激活正常\n- 市团队/省团队/市区域/省区域权益考核显示\n- 我的伞下功能 - 展示下级用户树形结构\n\n此版本可作为回滚基准点\nEOF\n)\")",
|
||||
"Bash(ls -la \"c:\\Users\\dong\\Desktop\\rwadurian\\frontend\\mobile-app\\assets\\images\\splash_frames\"\" 2>/dev/null || dir \"c:UsersdongDesktoprwadurianfrontendmobile-appassetsimagessplash_frames\" 2>nul || echo \"目录不存在 \")",
|
||||
"Bash(ls -la \"c:\\Users\\dong\\Desktop\\rwadurian\\frontend\\mobile-app\\lib\\features\"\" | grep -E \"^d \")"
|
||||
"Bash(ls -la \"c:\\Users\\dong\\Desktop\\rwadurian\\frontend\\mobile-app\\lib\\features\"\" | grep -E \"^d \")",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\nfeat(telemetry): 将userId改为userSerialNum字符串格式并完善遥测追踪\n\nBackend (presence-service):\n- 将EventLog.userId从BigInt改为String类型,存储userSerialNum(如D25121400005)\n- 更新Prisma schema,userId字段改为VarChar(20)并添加索引\n- 更新心跳相关命令和事件,统一使用userSerialNum字符串\n- 添加数据库迁移文件\n- 更新相关单元测试和集成测试\n\nFrontend (mobile-app):\n- TelemetryEvent新增toServerJson()方法,格式化为后端API期望的格式\n- AccountService登录/恢复时设置TelemetryService的userId\n- MultiAccountService切换账号时同步更新TelemetryService的userId\n- 退出登录时清除TelemetryService的userId\n- AuthProvider初始化时设置userId\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n)\")"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
-- Migration: Change user_id from BIGINT to VARCHAR(20)
|
||||
-- Reason: user_id stores userSerialNum (e.g., "D25121400005") which is a string, not a number
|
||||
|
||||
-- Alter the user_id column type from BIGINT to VARCHAR(20)
|
||||
ALTER TABLE "analytics_event_log"
|
||||
ALTER COLUMN "user_id" TYPE VARCHAR(20);
|
||||
|
||||
-- Add index for user_id for better query performance
|
||||
CREATE INDEX IF NOT EXISTS "idx_event_log_user_id" ON "analytics_event_log"("user_id");
|
||||
|
|
@ -14,7 +14,7 @@ datasource db {
|
|||
// 事件日志表 (append-only)
|
||||
model EventLog {
|
||||
id BigInt @id @default(autoincrement())
|
||||
userId BigInt? @map("user_id")
|
||||
userId String? @map("user_id") @db.VarChar(20) // userSerialNum, e.g. "D25121400005"
|
||||
installId String @map("install_id") @db.VarChar(64)
|
||||
eventName String @map("event_name") @db.VarChar(64)
|
||||
eventTime DateTime @map("event_time") @db.Timestamptz()
|
||||
|
|
@ -24,6 +24,7 @@ model EventLog {
|
|||
@@index([eventTime], name: "idx_event_log_event_time")
|
||||
@@index([eventName], name: "idx_event_log_event_name")
|
||||
@@index([eventName, eventTime], name: "idx_event_log_event_name_time")
|
||||
@@index([userId], name: "idx_event_log_user_id")
|
||||
@@map("analytics_event_log")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export class PresenceController {
|
|||
@ApiBearerAuth()
|
||||
@ApiOperation({ summary: '心跳上报' })
|
||||
async heartbeat(
|
||||
@CurrentUser('userId') userId: bigint,
|
||||
@CurrentUser('userId') userId: string, // userSerialNum, e.g. "D25121400005"
|
||||
@Body() dto: HeartbeatDto,
|
||||
) {
|
||||
return this.commandBus.execute(
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ export class RecordEventsHandler implements ICommandHandler<RecordEventsCommand>
|
|||
|
||||
private toEventLog(dto: EventItemDto): EventLog {
|
||||
return EventLog.create({
|
||||
userId: dto.userId ? BigInt(dto.userId) : null,
|
||||
userId: dto.userId ?? null, // userSerialNum string, e.g. "D25121400005"
|
||||
installId: InstallId.fromString(dto.installId),
|
||||
eventName: EventName.fromString(dto.eventName),
|
||||
eventTime: new Date(dto.clientTs * 1000),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export class RecordHeartbeatCommand {
|
||||
constructor(
|
||||
public readonly userId: bigint,
|
||||
public readonly userId: string, // userSerialNum, e.g. "D25121400005"
|
||||
public readonly installId: string,
|
||||
public readonly appVersion: string,
|
||||
public readonly clientTs: number,
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@ export class RecordHeartbeatHandler implements ICommandHandler<RecordHeartbeatCo
|
|||
const { userId, installId, appVersion, clientTs } = command;
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
// 1. 更新Redis在线状态
|
||||
await this.presenceRedisRepository.updateUserPresence(userId.toString(), now);
|
||||
// 1. 更新Redis在线状态 (userId is userSerialNum string, e.g. "D25121400005")
|
||||
await this.presenceRedisRepository.updateUserPresence(userId, now);
|
||||
|
||||
// 2. 发布领域事件
|
||||
await this.eventPublisher.publish(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { EventProperties } from '../value-objects/event-properties.vo';
|
|||
|
||||
export class EventLog {
|
||||
private _id: bigint | null;
|
||||
private _userId: bigint | null;
|
||||
private _userId: string | null; // userSerialNum, e.g. "D25121400005"
|
||||
private _installId: InstallId;
|
||||
private _eventName: EventName;
|
||||
private _eventTime: Date;
|
||||
|
|
@ -18,7 +18,7 @@ export class EventLog {
|
|||
return this._id;
|
||||
}
|
||||
|
||||
get userId(): bigint | null {
|
||||
get userId(): string | null {
|
||||
return this._userId;
|
||||
}
|
||||
|
||||
|
|
@ -44,15 +44,15 @@ export class EventLog {
|
|||
|
||||
/**
|
||||
* 获取用于DAU去重的唯一标识
|
||||
* 优先使用 userId,否则使用 installId
|
||||
* 优先使用 userId (userSerialNum),否则使用 installId
|
||||
*/
|
||||
get dauIdentifier(): string {
|
||||
return this._userId?.toString() ?? this._installId.value;
|
||||
return this._userId ?? this._installId.value;
|
||||
}
|
||||
|
||||
// 工厂方法
|
||||
static create(props: {
|
||||
userId?: bigint | null;
|
||||
userId?: string | null; // userSerialNum, e.g. "D25121400005"
|
||||
installId: InstallId;
|
||||
eventName: EventName;
|
||||
eventTime: Date;
|
||||
|
|
@ -72,7 +72,7 @@ export class EventLog {
|
|||
// 从持久化恢复
|
||||
static reconstitute(props: {
|
||||
id: bigint;
|
||||
userId: bigint | null;
|
||||
userId: string | null; // userSerialNum, e.g. "D25121400005"
|
||||
installId: InstallId;
|
||||
eventName: EventName;
|
||||
eventTime: Date;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export class HeartbeatReceivedEvent {
|
|||
static readonly EVENT_NAME = 'presence.heartbeat.received';
|
||||
|
||||
constructor(
|
||||
public readonly userId: bigint,
|
||||
public readonly userId: string, // userSerialNum, e.g. "D25121400005"
|
||||
public readonly installId: string,
|
||||
public readonly occurredAt: Date,
|
||||
) {}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export class SessionStartedEvent {
|
|||
static readonly EVENT_NAME = 'analytics.session.started';
|
||||
|
||||
constructor(
|
||||
public readonly userId: bigint | null,
|
||||
public readonly userId: string | null, // userSerialNum, e.g. "D25121400005"
|
||||
public readonly installId: string,
|
||||
public readonly occurredAt: Date,
|
||||
public readonly metadata: {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { RecordHeartbeatHandler } from '../../../../src/application/commands/rec
|
|||
import { RecordHeartbeatCommand } from '../../../../src/application/commands/record-heartbeat/record-heartbeat.command';
|
||||
import { PresenceRedisRepository } from '../../../../src/infrastructure/redis/presence-redis.repository';
|
||||
import { EventPublisherService } from '../../../../src/infrastructure/kafka/event-publisher.service';
|
||||
import { MetricsService } from '../../../../src/infrastructure/metrics/metrics.service';
|
||||
import { HeartbeatReceivedEvent } from '../../../../src/domain/events/heartbeat-received.event';
|
||||
|
||||
describe('RecordHeartbeatHandler', () => {
|
||||
|
|
@ -22,6 +23,10 @@ describe('RecordHeartbeatHandler', () => {
|
|||
publish: jest.fn(),
|
||||
};
|
||||
|
||||
const mockMetricsService = {
|
||||
recordHeartbeat: jest.fn(),
|
||||
};
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
RecordHeartbeatHandler,
|
||||
|
|
@ -33,6 +38,10 @@ describe('RecordHeartbeatHandler', () => {
|
|||
provide: EventPublisherService,
|
||||
useValue: mockEventPublisher,
|
||||
},
|
||||
{
|
||||
provide: MetricsService,
|
||||
useValue: mockMetricsService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
|
|
@ -48,7 +57,7 @@ describe('RecordHeartbeatHandler', () => {
|
|||
describe('execute', () => {
|
||||
it('should record heartbeat and return success', async () => {
|
||||
const command = new RecordHeartbeatCommand(
|
||||
BigInt(12345),
|
||||
'D25121400005', // userSerialNum
|
||||
'install-id-123',
|
||||
'1.0.0',
|
||||
Date.now(),
|
||||
|
|
@ -64,8 +73,8 @@ describe('RecordHeartbeatHandler', () => {
|
|||
expect(typeof result.serverTs).toBe('number');
|
||||
});
|
||||
|
||||
it('should update Redis presence with correct userId', async () => {
|
||||
const userId = BigInt(99999);
|
||||
it('should update Redis presence with correct userId (userSerialNum)', async () => {
|
||||
const userId = 'D25121499999'; // userSerialNum
|
||||
const command = new RecordHeartbeatCommand(
|
||||
userId,
|
||||
'install-id-456',
|
||||
|
|
@ -79,14 +88,14 @@ describe('RecordHeartbeatHandler', () => {
|
|||
await handler.execute(command);
|
||||
|
||||
expect(presenceRedisRepository.updateUserPresence).toHaveBeenCalledWith(
|
||||
userId.toString(),
|
||||
userId, // userSerialNum is passed directly as string
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should publish HeartbeatReceivedEvent', async () => {
|
||||
const command = new RecordHeartbeatCommand(
|
||||
BigInt(12345),
|
||||
'D25121400005',
|
||||
'install-id-789',
|
||||
'1.0.0',
|
||||
Date.now(),
|
||||
|
|
@ -105,7 +114,7 @@ describe('RecordHeartbeatHandler', () => {
|
|||
|
||||
it('should return server timestamp close to current time', async () => {
|
||||
const command = new RecordHeartbeatCommand(
|
||||
BigInt(12345),
|
||||
'D25121400005',
|
||||
'install-id',
|
||||
'1.0.0',
|
||||
Date.now(),
|
||||
|
|
@ -124,7 +133,7 @@ describe('RecordHeartbeatHandler', () => {
|
|||
|
||||
it('should throw error when Redis update fails', async () => {
|
||||
const command = new RecordHeartbeatCommand(
|
||||
BigInt(12345),
|
||||
'D25121400005',
|
||||
'install-id',
|
||||
'1.0.0',
|
||||
Date.now(),
|
||||
|
|
@ -139,7 +148,7 @@ describe('RecordHeartbeatHandler', () => {
|
|||
|
||||
it('should throw error when event publish fails', async () => {
|
||||
const command = new RecordHeartbeatCommand(
|
||||
BigInt(12345),
|
||||
'D25121400005',
|
||||
'install-id',
|
||||
'1.0.0',
|
||||
Date.now(),
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ describe('EventLog Entity', () => {
|
|||
expect(eventLog.createdAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('should create EventLog with userId', () => {
|
||||
const userId = BigInt(12345);
|
||||
it('should create EventLog with userId (userSerialNum)', () => {
|
||||
const userId = 'D25121400005'; // userSerialNum format
|
||||
const eventLog = EventLog.create({
|
||||
userId,
|
||||
installId: createInstallId(),
|
||||
|
|
@ -90,7 +90,7 @@ describe('EventLog Entity', () => {
|
|||
describe('reconstitute', () => {
|
||||
it('should reconstitute EventLog from persistence data', () => {
|
||||
const id = BigInt(999);
|
||||
const userId = BigInt(123);
|
||||
const userId = 'D25121400123'; // userSerialNum format
|
||||
const installId = createInstallId();
|
||||
const eventName = createEventName();
|
||||
const eventTime = createEventTime();
|
||||
|
|
@ -132,8 +132,8 @@ describe('EventLog Entity', () => {
|
|||
});
|
||||
|
||||
describe('dauIdentifier', () => {
|
||||
it('should return userId as string when userId exists', () => {
|
||||
const userId = BigInt(12345);
|
||||
it('should return userId (userSerialNum) when userId exists', () => {
|
||||
const userId = 'D25121400005';
|
||||
const eventLog = EventLog.create({
|
||||
userId,
|
||||
installId: createInstallId(),
|
||||
|
|
@ -141,7 +141,7 @@ describe('EventLog Entity', () => {
|
|||
eventTime: createEventTime(),
|
||||
});
|
||||
|
||||
expect(eventLog.dauIdentifier).toBe('12345');
|
||||
expect(eventLog.dauIdentifier).toBe('D25121400005');
|
||||
});
|
||||
|
||||
it('should return installId value when userId is null', () => {
|
||||
|
|
@ -156,7 +156,7 @@ describe('EventLog Entity', () => {
|
|||
});
|
||||
|
||||
it('should prefer userId over installId', () => {
|
||||
const userId = BigInt(999);
|
||||
const userId = 'D25121400999';
|
||||
const installId = InstallId.fromString('should-not-use-this');
|
||||
const eventLog = EventLog.create({
|
||||
userId,
|
||||
|
|
@ -165,7 +165,7 @@ describe('EventLog Entity', () => {
|
|||
eventTime: createEventTime(),
|
||||
});
|
||||
|
||||
expect(eventLog.dauIdentifier).toBe('999');
|
||||
expect(eventLog.dauIdentifier).toBe('D25121400999');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -175,7 +175,7 @@ describe('EventLog Entity', () => {
|
|||
beforeEach(() => {
|
||||
eventLog = EventLog.reconstitute({
|
||||
id: BigInt(100),
|
||||
userId: BigInt(200),
|
||||
userId: 'D25121400200',
|
||||
installId: createInstallId(),
|
||||
eventName: createEventName(),
|
||||
eventTime: createEventTime(),
|
||||
|
|
@ -189,7 +189,7 @@ describe('EventLog Entity', () => {
|
|||
});
|
||||
|
||||
it('should return correct userId', () => {
|
||||
expect(eventLog.userId).toBe(BigInt(200));
|
||||
expect(eventLog.userId).toBe('D25121400200');
|
||||
});
|
||||
|
||||
it('should return correct installId', () => {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import '../network/api_client.dart';
|
|||
import '../storage/secure_storage.dart';
|
||||
import '../storage/storage_keys.dart';
|
||||
import '../errors/exceptions.dart';
|
||||
import '../telemetry/telemetry_service.dart';
|
||||
|
||||
/// 设备硬件信息 (存储在 deviceName 字段中)
|
||||
class DeviceHardwareInfo {
|
||||
|
|
@ -591,6 +592,12 @@ class AccountService {
|
|||
value: 'true',
|
||||
);
|
||||
|
||||
// 设置遥测服务的用户ID(使用userSerialNum,如D25121400005)
|
||||
if (TelemetryService().isInitialized) {
|
||||
TelemetryService().setUserId(response.userSerialNum);
|
||||
debugPrint('$_tag _saveAccountData() - 设置TelemetryService userId: ${response.userSerialNum}');
|
||||
}
|
||||
|
||||
debugPrint('$_tag _saveAccountData() - 账号数据保存完成');
|
||||
}
|
||||
|
||||
|
|
@ -932,6 +939,12 @@ class AccountService {
|
|||
value: 'true',
|
||||
);
|
||||
|
||||
// 设置遥测服务的用户ID(使用userSerialNum,如D25121400005)
|
||||
if (TelemetryService().isInitialized) {
|
||||
TelemetryService().setUserId(response.userSerialNum);
|
||||
debugPrint('$_tag _saveRecoverAccountData() - 设置TelemetryService userId: ${response.userSerialNum}');
|
||||
}
|
||||
|
||||
debugPrint('$_tag _saveRecoverAccountData() - 恢复账号数据保存完成');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import '../storage/secure_storage.dart';
|
||||
import '../storage/storage_keys.dart';
|
||||
import '../telemetry/telemetry_service.dart';
|
||||
|
||||
/// 账号信息摘要(用于账号列表显示)
|
||||
class AccountSummary {
|
||||
|
|
@ -144,6 +145,12 @@ class MultiAccountService {
|
|||
// 设置当前账号
|
||||
await setCurrentAccountId(userSerialNum);
|
||||
|
||||
// 设置遥测服务的用户ID(使用userSerialNum,如D25121400005)
|
||||
if (TelemetryService().isInitialized) {
|
||||
TelemetryService().setUserId(userSerialNum);
|
||||
debugPrint('$_tag switchToAccount() - 设置TelemetryService userId: $userSerialNum');
|
||||
}
|
||||
|
||||
debugPrint('$_tag switchToAccount() - 切换成功');
|
||||
return true;
|
||||
}
|
||||
|
|
@ -263,6 +270,12 @@ class MultiAccountService {
|
|||
await _secureStorage.delete(key: key);
|
||||
}
|
||||
|
||||
// 清除遥测服务的用户ID
|
||||
if (TelemetryService().isInitialized) {
|
||||
TelemetryService().clearUserId();
|
||||
debugPrint('$_tag logoutCurrentAccount() - 清除TelemetryService userId');
|
||||
}
|
||||
|
||||
debugPrint('$_tag logoutCurrentAccount() - 退出完成,已清除 ${keysToClear.length} 个状态');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ class TelemetryEvent extends Equatable {
|
|||
);
|
||||
}
|
||||
|
||||
/// 转换为本地存储 JSON 格式
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'eventId': eventId,
|
||||
|
|
@ -117,6 +118,30 @@ class TelemetryEvent extends Equatable {
|
|||
};
|
||||
}
|
||||
|
||||
/// 转换为服务端 API 格式
|
||||
/// 后端 presence-service 期望的格式:
|
||||
/// - eventName: string (required)
|
||||
/// - userId: string (optional) - userSerialNum, e.g. "D25121400005"
|
||||
/// - installId: string (required)
|
||||
/// - clientTs: number (required) - Unix timestamp in seconds
|
||||
/// - properties: object (optional)
|
||||
Map<String, dynamic> toServerJson() {
|
||||
return {
|
||||
'eventName': name,
|
||||
'userId': userId,
|
||||
'installId': installId,
|
||||
'clientTs': timestamp.millisecondsSinceEpoch ~/ 1000,
|
||||
'properties': {
|
||||
...?properties,
|
||||
'eventId': eventId,
|
||||
'type': type.name,
|
||||
'level': level.name,
|
||||
'sessionId': sessionId,
|
||||
'deviceContextId': deviceContextId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
TelemetryEvent copyWith({
|
||||
String? eventId,
|
||||
EventType? type,
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ class TelemetryUploader {
|
|||
final events = storage.dequeueEvents(batchSize);
|
||||
if (events.isEmpty) return true;
|
||||
|
||||
// 调用后端API
|
||||
// 调用后端API (使用 toServerJson 格式化为服务端期望的格式)
|
||||
final response = await _dio.post(
|
||||
'/api/v1/analytics/events',
|
||||
data: {
|
||||
'events': events.map((e) => e.toJson()).toList(),
|
||||
'events': events.map((e) => e.toServerJson()).toList(),
|
||||
},
|
||||
options: Options(
|
||||
headers: getAuthHeaders?.call(),
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import '../../../../core/storage/secure_storage.dart';
|
||||
import '../../../../core/storage/storage_keys.dart';
|
||||
import '../../../../core/di/injection_container.dart';
|
||||
import '../../../../core/telemetry/telemetry_service.dart';
|
||||
|
||||
enum AuthStatus {
|
||||
initial,
|
||||
|
|
@ -117,6 +118,10 @@ class AuthNotifier extends StateNotifier<AuthState> {
|
|||
userSerialNum: userSerialNum,
|
||||
referralCode: referralCode,
|
||||
);
|
||||
// 设置遥测服务的用户ID(使用userSerialNum,如D25121400005)
|
||||
if (userSerialNum != null && TelemetryService().isInitialized) {
|
||||
TelemetryService().setUserId(userSerialNum);
|
||||
}
|
||||
} else if (isAccountCreated) {
|
||||
// 账号已创建但钱包未完成(可能正在生成或未备份)
|
||||
state = state.copyWith(
|
||||
|
|
|
|||
Loading…
Reference in New Issue