Commit Graph

10 Commits

Author SHA1 Message Date
hailin 9bcb1f864d feat(mobile): implement invite friends share page with APK QR code & referral marketing
- SharePage: QR code now encodes actual APK download URL from admin-service
  (GET /api/v1/app/version/download/{id} via Kong), referral code shown
  separately below QR for manual entry on registration
- Add referral reward marketing section: direct bonus / team levels (50
  layers) / new user welcome package — drives viral growth
- Show referrer info card when current user was invited via referral code
- Share text updated to marketing copy with APK link + referral code
- VersionChecker.getLatestApkUrl(): fetches latest APK URL regardless of
  needUpdate (passes version=0.0.0 to force server response)
- UpdateService.getLatestApkUrl(): exposes APK URL fetch for UI layer
- i18n (zh-CN/zh-TW/en/ja): add 14 new keys for marketing content,
  APK download hints, reward program descriptions, and referrer info

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 10:17:59 -08:00
hailin 192bc476c4 fix(mobile): move _startDownload to didChangeDependencies to avoid initState context crash
context.t() (Localizations) cannot be called during initState() — inherited
widgets are not yet mounted. When download fails instantly (e.g. no network),
setState with context.t() inside _startDownload fired synchronously within
initState, causing:
  dependOnInheritedWidgetOfExactType called before initState() completed

Fix: add _downloadStarted guard flag; call _startDownload() from
didChangeDependencies() instead of initState(), where context is fully ready.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 09:57:38 -08:00
hailin d79b7cf500 fix(mobile): fix telemetry import path in self_hosted_updater
../../../ → ../../ (channels/ → updater/ → core/, not lib/)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 09:45:20 -08:00
hailin 9b760c56ee feat(mobile/telemetry): add comprehensive event tracking for DAU, real-time presence & funnels
## 架构设计(亿级用户规模)
- 客户端:本地队列(500条) + 批量上传(20条/30s) + 10% 采样率
- 心跳:前台每60s发送,后端3分钟滑动窗口 → 实时在线率
- DAU:app_session_start 事件 + installId 去重
- 后端接入:Kafka → ClickHouse 消费落盘

## 新增 TelemetryRouteObserver (NEW FILE)
- 继承 RouteObserver<PageRoute>,注册到 MaterialApp.navigatorObservers
- 自动记录所有页面 page_view(push/pop/replace),零侵入业务页面
- 属性:page_name, previous_page, nav_action
- 覆盖全部 37 个已注册路由,无需逐页埋点

## main.dart
- 接入 TelemetryRouteObserver.instance
- 新增 FlutterError.onError 全局钩子 → logError 上报 Widget build 异常

## auth_service.dart — 认证漏斗
| 事件 | 触发时机 | method 属性 |
|------|---------|------------|
| register_success | 注册成功 | phone_sms / email_code |
| login_success    | 登录成功 | password / phone_sms / email_code / wechat / alipay / google / apple |
| user_logout      | 主动登出 | — |

## self_hosted_updater.dart — 升级漏斗
update_prompted → update_accepted / update_dismissed
→ update_download_started → update_download_completed / update_download_failed / update_download_cancelled
→ update_install_triggered / update_install_failed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 09:39:48 -08:00
hailin d174b74764 debug(mobile): 下载失败诊断日志
添加详细 debugPrint,覆盖:
- URL 检查失败原因
- 实际下载 URL 和保存路径
- HTTP 响应状态码和 content-length/content-range
- DioException 类型、消息、HTTP 状态码
- SHA-256 校验结果(预期值 vs 实际值)
- 文件大小

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 08:53:05 -08:00
hailin 0c3ef3b598 fix(mobile): fix version check API and platform detection in both apps
genex-mobile/lib/core/updater/version_checker.dart
- platform 从写死 'android' 改为运行时检测 Platform.isIOS ? 'IOS' : 'ANDROID'

admin-app/lib/core/updater/version_checker.dart
- API 路径修正: /api/app/version/check → /api/v1/app/version/check
- 新增 app_type: 'ADMIN_APP'(原缺失,后端默认返回 GENEX_MOBILE 版本)
- platform 从写死 'android' 改为运行时检测
- 响应解析修正: 原逻辑把外层 {code,data} 当版本数据用,
  改为正确取 response.data['data'] 内层对象
- 新增 downloadUrl 相对路径修复(向下兼容旧记录)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 00:50:42 -08:00
hailin abb358100d fix(genex-mobile): 还原 api.gogenex.com + 增加更新检测日志
.cn 域名因未 ICP 备案被 ISP 拦截,继续使用 api.gogenex.com (HK IP)。
增加 [UpdateService] 和 [VersionChecker] 详细日志便于调试。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 23:17:12 -08:00
hailin 286e4d8886 fix(admin-service+mobile): 修复 OTA 下载三处 Bug
1. MinIO 内网 hostname 导致 redirect 失效
   → 改为 admin-service 直接流式代理传输文件(streamFile)
   → MinIO 仅绑 127.0.0.1:49000,不对外暴露

2. version_checker.dart 响应解析错误
   → API 返回 {"code":0,"data":{...}} 但代码误把外层 Map 当 VersionInfo
   → 现在正确提取 responseMap["data"] 内层对象

3. VersionInfo.fromJson 在 releaseDate=null 时崩溃
   → releaseDate 改为 nullable (DateTime?),null-safe 解析
   → 同步修复 version_checker.dart 拼接绝对 downloadUrl

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 22:19:10 -08:00
hailin c6c434a07a fix(update): 修复 app 版本更新检查路径 + 解决 MinIO presigned URL 24h 过期
Mobile 端:
- version_checker.dart: /api/app/... → /api/v1/app/... (与 Kong 路由匹配)

Backend (admin-service):
- AppVersion 实体新增 storage_key 字段(已执行 ALTER TABLE)
- FileStorageService: uploadFile 不再返回 presigned URL,只返回 objectName
- AdminVersionController: upload 后保存 storageKey,downloadUrl 设为
  /api/v1/app/version/download/{id}(稳定 API 地址,不过期)
- AppVersionController.downloadVersion: storageKey 存在时每次请求动态
  生成 presigned URL(1小时有效,只需够下载完成即可)
- AppVersionService.checkUpdate: 有 storageKey 的版本统一返回 API 下载地址

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 21:13:13 -08:00
hailin 184a7d16db feat: 三端集成 App升级 + 内部推送 + FCM外部推送框架 (genex-mobile/admin-app/mobile)
从 rwadurian/frontend/mobile-app 移植升级系统和通知系统到 Genex 三个 Flutter 客户端,
适配目标项目轻量架构(ValueNotifier 替代 Riverpod,Dio HTTP 客户端,移除 screenutil)。

## 新增核心模块 (每个 app 13 个 Dart 文件)

### 升级系统 (core/updater/)
- UpdateService: 统一升级服务单例,支持 Google Play + 自建服务器双渠道
- VersionChecker: 版本检测器,调用 GET /api/app/version/check
- DownloadManager: APK 下载管理,支持断点续传 + SHA256 校验
- ApkInstaller: APK 安装器 (Platform Channel)
- AppMarketDetector: 应用市场来源检测
- SelfHostedUpdater: 自建服务器渠道更新对话框 (i18n 化)
- GooglePlayUpdater: Google Play 应用内更新

### 通知系统 (core/services/ + core/providers/)
- NotificationService: 通知 + 公告 API 服务
  - GET /notifications, /notifications/unread-count
  - PUT /notifications/:id/read
  - GET /announcements, /announcements/unread-count
  - PUT /announcements/:id/read, /announcements/read-all
- NotificationBadgeManager: 未读徽章管理器
  - ValueNotifier<int> 驱动 UI
  - 30秒定时自动刷新 + 前后台切换刷新 (WidgetsBindingObserver)

### FCM 推送框架 (core/push/)
- PushService: Firebase 推送服务框架
  - Firebase 代码注释保护,无配置文件时静默跳过
  - 设备 token 注册 POST /device-tokens
  - 待 Firebase 配置文件就绪后取消注释即可启用

### HTTP 客户端 (core/network/)
- ApiClient: Dio 封装单例,baseUrl = https://api.gogenex.cn

## Android 原生配置 (每个 app)
- AndroidManifest.xml: 添加 REQUEST_INSTALL_PACKAGES 权限 + FileProvider
- res/xml/file_paths.xml: FileProvider 路径配置
- MainActivity.kt: APK 安装器 + 应用市场检测 MethodChannel

## UI 层改造 (每个 app)
- main.dart: 异步启动,初始化 UpdateService/PushService/NotificationBadgeManager
- MainShell: 消息 Tab 徽章改为 ValueListenableBuilder 动态未读数,进入后 3 秒检查更新
- SettingsPage: StatefulWidget 化,动态版本号 (PackageInfo),点击版本号手动检查更新
- MessagePage: 移除 mock 数据,接入 NotificationService API,4 Tab 分类 + 下拉刷新 + 标记已读

## i18n 新增 (~35 keys/语言)
- update.*: 25 个升级相关 keys
- notification.*: 9 个通知相关 keys
- genex-mobile: 4 语言 (zh_CN/zh_TW/en/ja) 分文件
- admin-app: 3 语言 (zh_CN/en_US/ja_JP) 内联单文件
- mobile: 4 语言 (zh_CN/zh_TW/en/ja) 分文件

## 三端差异化配置
| App | MethodChannel 前缀 | applicationId |
|-----|-------------------|---------------|
| genex-mobile | cn.gogenex.consumer | cn.gogenex.consumer |
| admin-app | cn.gogenex.issuer | cn.gogenex.issuer |
| mobile | cn.gogenex.mobile | cn.gogenex.mobile |

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:02:14 -08:00