fix(guide): use fitWidth to prevent image stretching + add debug logs

- Change BoxFit.cover to BoxFit.fitWidth for guide page images
- Add screen info logging (resolution, pixel ratio, aspect ratio)
- Add detailed avatar loading logs in frontend and backend
- Log avatarUrl from DB and API response during recovery

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2025-12-09 18:36:43 -08:00
parent a3c0d3948d
commit 243105f97f
3 changed files with 121 additions and 62 deletions

View File

@ -43,6 +43,10 @@ export class RecoverByMnemonicHandler {
// 如果头像为空,重新生成一个
let avatarUrl = account.avatarUrl;
this.logger.log(`Account ${command.accountSequence} avatarUrl from DB: ${avatarUrl ? `长度=${avatarUrl.length}` : 'null'}`);
if (avatarUrl) {
this.logger.log(`Account ${command.accountSequence} avatarUrl前50字符: ${avatarUrl.substring(0, 50)}`);
}
if (!avatarUrl) {
this.logger.log(`Account ${command.accountSequence} has no avatar, generating new one`);
avatarUrl = generateRandomAvatarSvg();
@ -62,7 +66,7 @@ export class RecoverByMnemonicHandler {
await this.eventPublisher.publishAll(account.domainEvents);
account.clearDomainEvents();
return {
const result = {
userId: account.userId.toString(),
accountSequence: account.accountSequence.value,
nickname: account.nickname,
@ -71,5 +75,10 @@ export class RecoverByMnemonicHandler {
accessToken: tokens.accessToken,
refreshToken: tokens.refreshToken,
};
this.logger.log(`RecoverByMnemonic result - accountSequence: ${result.accountSequence}, nickname: ${result.nickname}`);
this.logger.log(`RecoverByMnemonic result - avatarUrl: ${result.avatarUrl ? `长度=${result.avatarUrl.length}` : 'null'}`);
return result;
}
}

View File

@ -199,11 +199,16 @@ class RecoverAccountResponse {
factory RecoverAccountResponse.fromJson(Map<String, dynamic> json) {
debugPrint('[AccountService] 解析 RecoverAccountResponse: ${json.keys.toList()}');
final avatarUrl = json['avatarUrl'] as String?;
debugPrint('[AccountService] RecoverAccountResponse.avatarUrl: ${avatarUrl != null ? "长度=${avatarUrl.length}" : "null"}');
if (avatarUrl != null && avatarUrl.isNotEmpty) {
debugPrint('[AccountService] RecoverAccountResponse.avatarUrl前50字符: ${avatarUrl.substring(0, avatarUrl.length > 50 ? 50 : avatarUrl.length)}');
}
return RecoverAccountResponse(
userId: json['userId'] as String,
userSerialNum: json['accountSequence'] as int,
username: json['nickname'] as String,
avatarSvg: json['avatarUrl'] as String?,
avatarSvg: avatarUrl,
referralCode: json['referralCode'] as String,
accessToken: json['accessToken'] as String,
refreshToken: json['refreshToken'] as String,
@ -884,12 +889,15 @@ class AccountService {
value: response.username,
);
if (response.avatarSvg != null) {
if (response.avatarSvg != null && response.avatarSvg!.isNotEmpty) {
debugPrint('$_tag _saveRecoverAccountData() - 保存 avatarSvg (长度: ${response.avatarSvg!.length})');
debugPrint('$_tag _saveRecoverAccountData() - avatarSvg前50字符: ${response.avatarSvg!.substring(0, response.avatarSvg!.length > 50 ? 50 : response.avatarSvg!.length)}');
await _secureStorage.write(
key: StorageKeys.avatarSvg,
value: response.avatarSvg!,
);
} else {
debugPrint('$_tag _saveRecoverAccountData() - ⚠️ avatarSvg 为空! response.avatarSvg=${response.avatarSvg}');
}
// Token

View File

@ -38,6 +38,31 @@ class _GuidePageState extends ConsumerState<GuidePage> {
final PageController _pageController = PageController();
int _currentPage = 0;
@override
void initState() {
super.initState();
// build
WidgetsBinding.instance.addPostFrameCallback((_) {
_logScreenInfo();
});
}
///
void _logScreenInfo() {
final mediaQuery = MediaQuery.of(context);
final screenSize = mediaQuery.size;
final devicePixelRatio = mediaQuery.devicePixelRatio;
final physicalSize = screenSize * devicePixelRatio;
debugPrint('[GuidePage] ========== 屏幕信息 ==========');
debugPrint('[GuidePage] 逻辑分辨率: ${screenSize.width.toStringAsFixed(1)} x ${screenSize.height.toStringAsFixed(1)}');
debugPrint('[GuidePage] 设备像素比: $devicePixelRatio');
debugPrint('[GuidePage] 物理分辨率: ${physicalSize.width.toStringAsFixed(0)} x ${physicalSize.height.toStringAsFixed(0)}');
debugPrint('[GuidePage] 屏幕宽高比: ${(screenSize.width / screenSize.height).toStringAsFixed(3)} (${screenSize.width.toStringAsFixed(0)}:${screenSize.height.toStringAsFixed(0)})');
debugPrint('[GuidePage] 图片设计比例: 0.5625 (1080:1920 = 9:16)');
debugPrint('[GuidePage] ================================');
}
// 1-5 (5)
// pngjpgwebp
final List<GuidePageData> _guidePages = const [
@ -121,37 +146,50 @@ class _GuidePageState extends ConsumerState<GuidePage> {
}
/// (1-4) -
/// 使 BoxFit.fitWidth
Widget _buildGuidePage(GuidePageData data, int index) {
return Stack(
fit: StackFit.expand,
children: [
//
if (data.imagePath != null)
Image.asset(
data.imagePath!,
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
errorBuilder: (context, error, stackTrace) {
return Container(
color: const Color(0xFFFFF8E7),
child: _buildPlaceholderImage(index),
);
},
)
else
Container(
color: const Color(0xFFFFF8E7),
child: _buildPlaceholderImage(index),
debugPrint('[GuidePage] _buildGuidePage() - 页面 ${index + 1}, 图片: ${data.imagePath}');
return Container(
color: Colors.black, //
child: Stack(
fit: StackFit.expand,
children: [
// - 使 fitWidth
if (data.imagePath != null)
LayoutBuilder(
builder: (context, constraints) {
debugPrint('[GuidePage] 页面 ${index + 1} 容器尺寸: ${constraints.maxWidth.toStringAsFixed(1)} x ${constraints.maxHeight.toStringAsFixed(1)}');
debugPrint('[GuidePage] 页面 ${index + 1} 使用 BoxFit.fitWidth');
return Center(
child: Image.asset(
data.imagePath!,
fit: BoxFit.fitWidth,
width: double.infinity,
errorBuilder: (context, error, stackTrace) {
debugPrint('[GuidePage] 页面 ${index + 1} 图片加载失败: $error');
return Container(
color: const Color(0xFFFFF8E7),
child: _buildPlaceholderImage(index),
);
},
),
);
},
)
else
Container(
color: const Color(0xFFFFF8E7),
child: _buildPlaceholderImage(index),
),
//
Positioned(
left: 0,
right: 0,
bottom: 80.h,
child: _buildPageIndicator(),
),
//
Positioned(
left: 0,
right: 0,
bottom: 80.h,
child: _buildPageIndicator(),
),
],
],
),
);
}
@ -385,39 +423,42 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Stack(
fit: StackFit.expand,
children: [
//
if (widget.backgroundImage != null)
Image.asset(
widget.backgroundImage!,
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
errorBuilder: (context, error, stackTrace) {
return Container(
color: const Color(0xFFFFF8E7),
);
},
)
else
child: Container(
color: Colors.black, //
child: Stack(
fit: StackFit.expand,
children: [
// - 使 fitWidth
if (widget.backgroundImage != null)
Center(
child: Image.asset(
widget.backgroundImage!,
fit: BoxFit.fitWidth,
width: double.infinity,
errorBuilder: (context, error, stackTrace) {
return Container(
color: const Color(0xFFFFF8E7),
);
},
),
)
else
Container(
color: const Color(0xFFFFF8E7),
),
//
Container(
color: const Color(0xFFFFF8E7),
),
//
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withValues(alpha: 0.3),
Colors.black.withValues(alpha: 0.6),
],
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withValues(alpha: 0.3),
Colors.black.withValues(alpha: 0.6),
],
),
),
),
),
//
SafeArea(
child: LayoutBuilder(
@ -526,6 +567,7 @@ class _WelcomePageContentState extends ConsumerState<_WelcomePageContent> {
),
),
],
),
),
);
}