diff --git a/.claude/settings.local.json b/.claude/settings.local.json index f0d07d4b..1a95c93b 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -195,7 +195,9 @@ "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 \nEOF\n)\")", "Bash(mkdir:*)", - "Bash(git commit -m \"$(cat <<''EOF''\nfeat(sentry): 集成 Sentry 自建崩溃收集与错误追踪系统\n\nBackend (infrastructure/sentry):\n- 添加 Sentry 自建部署 Docker Compose 配置\n- 包含 PostgreSQL, Redis, Kafka, ClickHouse, Snuba 等组件\n- 添加 Relay (事件网关) 和 Symbolicator (符号化服务) 配置\n- 添加部署脚本 deploy.sh 和配置文件\n- 更新 infrastructure README 文档\n\nFrontend (mobile-app):\n- 添加 sentry_flutter SDK 依赖\n- 创建 SentryService 封装类,统一管理崩溃收集\n- 创建 SentryConfig 配置类,支持开发/生产环境配置\n- 创建 SentryNavigationObserver 自动追踪页面导航\n- 创建 SentryDioInterceptor 自动追踪 HTTP 请求\n- 在 bootstrap.dart 中集成 Sentry 初始化\n- 支持 Flutter 错误和异步错误捕获\n- 自动过滤敏感信息 (密码、助记词、私钥等)\n- 在登录/登出/切换账号时同步 Sentry 用户信息\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n)\")" + "Bash(git commit -m \"$(cat <<''EOF''\nfeat(sentry): 集成 Sentry 自建崩溃收集与错误追踪系统\n\nBackend (infrastructure/sentry):\n- 添加 Sentry 自建部署 Docker Compose 配置\n- 包含 PostgreSQL, Redis, Kafka, ClickHouse, Snuba 等组件\n- 添加 Relay (事件网关) 和 Symbolicator (符号化服务) 配置\n- 添加部署脚本 deploy.sh 和配置文件\n- 更新 infrastructure README 文档\n\nFrontend (mobile-app):\n- 添加 sentry_flutter SDK 依赖\n- 创建 SentryService 封装类,统一管理崩溃收集\n- 创建 SentryConfig 配置类,支持开发/生产环境配置\n- 创建 SentryNavigationObserver 自动追踪页面导航\n- 创建 SentryDioInterceptor 自动追踪 HTTP 请求\n- 在 bootstrap.dart 中集成 Sentry 初始化\n- 支持 Flutter 错误和异步错误捕获\n- 自动过滤敏感信息 (密码、助记词、私钥等)\n- 在登录/登出/切换账号时同步 Sentry 用户信息\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n)\")", + "Bash(flutter pub get:*)", + "Bash(git commit -m \"$(cat <<''EOF''\nfix(sentry): 修复 Flutter 代码分析错误\n\n- 修复 bootstrap.dart 中 deviceModel 属性访问错误 (使用 brand + model)\n- 移除 sentry_navigation_observer.dart 中未使用的 _previousRouteName 字段\n- 更新 sentry_service.dart 使用新版 Sentry API (captureFeedback)\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 \nEOF\n)\")" ], "deny": [], "ask": [] diff --git a/frontend/mobile-app/lib/bootstrap.dart b/frontend/mobile-app/lib/bootstrap.dart index 775c803c..25c7c7d7 100644 --- a/frontend/mobile-app/lib/bootstrap.dart +++ b/frontend/mobile-app/lib/bootstrap.dart @@ -150,7 +150,7 @@ Future initializeTelemetry(BuildContext context, {String? userId}) async { if (deviceContext != null) { SentryService().setDeviceContext( deviceId: TelemetryService().installId, - deviceModel: deviceContext.deviceModel, + deviceModel: '${deviceContext.brand} ${deviceContext.model}', osVersion: deviceContext.osVersion, appVersion: deviceContext.appVersion, ); diff --git a/frontend/mobile-app/lib/core/sentry/sentry_navigation_observer.dart b/frontend/mobile-app/lib/core/sentry/sentry_navigation_observer.dart index 65e07fa1..7e06a06f 100644 --- a/frontend/mobile-app/lib/core/sentry/sentry_navigation_observer.dart +++ b/frontend/mobile-app/lib/core/sentry/sentry_navigation_observer.dart @@ -11,8 +11,6 @@ class SentryNavigationObserver extends NavigatorObserver { this.enablePerformanceTracing = true, }); - String? _previousRouteName; - @override void didPush(Route route, Route? previousRoute) { super.didPush(route, previousRoute); @@ -78,8 +76,6 @@ class SentryNavigationObserver extends NavigatorObserver { }); }); } - - _previousRouteName = toName; } String? _extractRouteName(Route? route) { diff --git a/frontend/mobile-app/lib/core/sentry/sentry_service.dart b/frontend/mobile-app/lib/core/sentry/sentry_service.dart index c9ee5a20..55f8f91b 100644 --- a/frontend/mobile-app/lib/core/sentry/sentry_service.dart +++ b/frontend/mobile-app/lib/core/sentry/sentry_service.dart @@ -410,11 +410,11 @@ class SentryService { }) async { if (!_isInitialized || _config?.enableUserFeedback != true) return; - await Sentry.captureUserFeedback(SentryUserFeedback( - eventId: eventId, - comments: comments, - email: email, + await Sentry.captureFeedback(SentryFeedback( + message: comments, + contactEmail: email, name: name, + associatedEventId: eventId, )); } diff --git a/frontend/mobile-app/linux/flutter/generated_plugin_registrant.cc b/frontend/mobile-app/linux/flutter/generated_plugin_registrant.cc index 3ccd5513..fe09dcd3 100644 --- a/frontend/mobile-app/linux/flutter/generated_plugin_registrant.cc +++ b/frontend/mobile-app/linux/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { @@ -17,6 +18,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); + g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); + sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/frontend/mobile-app/linux/flutter/generated_plugins.cmake b/frontend/mobile-app/linux/flutter/generated_plugins.cmake index 9ce94c49..514c3aff 100644 --- a/frontend/mobile-app/linux/flutter/generated_plugins.cmake +++ b/frontend/mobile-app/linux/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST file_selector_linux flutter_secure_storage_linux + sentry_flutter url_launcher_linux ) diff --git a/frontend/mobile-app/macos/Flutter/GeneratedPluginRegistrant.swift b/frontend/mobile-app/macos/Flutter/GeneratedPluginRegistrant.swift index 87cdbc2b..8111a6a0 100644 --- a/frontend/mobile-app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/frontend/mobile-app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -13,6 +13,7 @@ import local_auth_darwin import mobile_scanner import package_info_plus import path_provider_foundation +import sentry_flutter import share_plus import shared_preferences_foundation import sqflite_darwin @@ -27,6 +28,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/frontend/mobile-app/pubspec.lock b/frontend/mobile-app/pubspec.lock index c2d73878..f3f93d46 100644 --- a/frontend/mobile-app/pubspec.lock +++ b/frontend/mobile-app/pubspec.lock @@ -1221,6 +1221,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: "direct main" + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" share_plus: dependency: "direct main" description: diff --git a/frontend/mobile-app/windows/flutter/generated_plugin_registrant.cc b/frontend/mobile-app/windows/flutter/generated_plugin_registrant.cc index 4124c5b9..6ea4aa7b 100644 --- a/frontend/mobile-app/windows/flutter/generated_plugin_registrant.cc +++ b/frontend/mobile-app/windows/flutter/generated_plugin_registrant.cc @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("LocalAuthPlugin")); PermissionHandlerWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); + SentryFlutterPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SentryFlutterPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/frontend/mobile-app/windows/flutter/generated_plugins.cmake b/frontend/mobile-app/windows/flutter/generated_plugins.cmake index 4aa2827f..434e0737 100644 --- a/frontend/mobile-app/windows/flutter/generated_plugins.cmake +++ b/frontend/mobile-app/windows/flutter/generated_plugins.cmake @@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST flutter_secure_storage_windows local_auth_windows permission_handler_windows + sentry_flutter share_plus url_launcher_windows )