hailin
|
6bdd8d1e19
|
fix(admin-service): 增加 multer fileSize 限制 500MB,支持 APK/IPA 上传
FileInterceptor upload/parse 均未设置文件大小限制,可能导致 multer 默认
内存保护触发。与 Nginx client_max_body_size 500m 保持一致。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 21:57:50 -08:00 |
hailin
|
0f07698262
|
fix(issuer-service): 修复 LifecyclePipeline 接口字段与 Coupon.couponType 类型错误
- LifecyclePipeline 接口字段与实际返回值对齐 (minted/listed/sold/inCirculation/redeemed/recalled)
- coupon.type → coupon.couponType (Coupon entity 字段名修正)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 21:20:36 -08:00 |
hailin
|
4957b2ef85
|
feat(genex-mobile): 重构为 Clean Architecture + Riverpod
- 添加 flutter_riverpod ^2.5.1 依赖
- 搭建 core/error/failures.dart 和 core/usecases/usecase.dart 基础骨架
- auth feature 完整 3 层重构:
- domain: AuthUser + AuthSession 实体, IAuthRepository 接口, 全套 UseCases
- data: AuthRepositoryImpl(Strangler Fig,委托给 AuthService 保留 token 刷新逻辑)
- presentation: AuthNotifier + authProvider + currentUserProvider + isAuthenticatedProvider
- 4 个 auth 页面升级为 ConsumerStatefulWidget,使用 ref.read(authProvider.notifier)
- main.dart: ProviderScope 包裹 GenexConsumerApp,改为 ConsumerStatefulWidget
- 桥接 AuthService ValueNotifier → Riverpod authProvider(会话过期自动导航)
- 12 个 feature 全部创建 Riverpod providers(FutureProvider/NotifierProvider)
- 修复 my_coupons_page.dart 中 EmptyState/StatusTags 缺少 BuildContext 的预存在错误
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 20:12:50 -08:00 |
hailin
|
332a8dafe8
|
fix(admin-web): 补回 AdminLayout useState import
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 19:20:05 -08:00 |
hailin
|
4feea2667c
|
refactor(admin-web): 实现 Clean Architecture + Zustand + Redux Toolkit
按要求重构架构,从扁平的 React Context + useState 升级为大厂标准模式:
Clean Architecture 分层:
domain/entities/ — 业务实体 (AdminUser/User/Issuer/AppVersion)
domain/repositories/ — Repository 接口(契约层)
infrastructure/http/ — HttpClient(替代旧 api-client.ts)
infrastructure/repositories/ — Repository 实现(AuthRepository/UserRepository)
状态管理(大厂混合模式):
Zustand useAuthStore — 轻量客户端状态:登录会话 + localStorage 持久化
Zustand useUIStore — UI 偏好:sidebar 折叠状态持久化
Redux uiSlice — 全局通知队列、globalLoading
Redux usersSlice — 用户列表筛选/分页 client state
React Query — 服务端数据 fetching/缓存(保留)
更新:
providers.tsx — 加入 Redux Provider,移除旧 AuthProvider
auth-context.tsx — 向下兼容层,re-export Zustand store
api-client.ts — 向下兼容层,re-export httpClient
AdminLayout.tsx — 使用 Zustand auth/ui store
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 19:18:20 -08:00 |
hailin
|
96dad278ea
|
fix(admin-web): 修复 React #310 — useState 必须在条件 return 前调用
根因:expandedKeys 的 useState 调用位于 "if (!isAuthenticated) return null"
之后,违反 Rules of Hooks。认证状态变化时 hooks 调用数量不一致,
React 报 #310 "Cannot update a component while rendering a different component"。
修复:将 activeKey 计算和 expandedKeys useState 全部移至条件 return 之前,
确保每次渲染 hooks 调用顺序完全一致。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 19:05:34 -08:00 |
hailin
|
300d55ff14
|
fix(admin-web): 部署版本守卫,彻底防止 stale bundle 崩溃
方案:
- next.config.ts 构建时注入 NEXT_PUBLIC_BUILD_TIME 时间戳
- 新增 /api/version 路由,返回服务器当前 build 时间戳
- DeployGuard 组件每 3 分钟轮询 /api/version,
发现版本变化立即 window.location.reload(),用户完全无感知
- global-error / admin error 边界仅做最后兜底(静默 reload)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 18:57:12 -08:00 |
hailin
|
36b899d7ed
|
fix(admin-web): 简化错误边界为静默 reload,去掉多余 UI
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 18:55:19 -08:00 |
hailin
|
f642ef1d56
|
fix(admin-web): 添加 global-error.tsx 修复部署后 Server Action 崩溃
问题:每次重新部署容器后,浏览器持有旧 bundle,Next.js App Router
内部 Server Action ID("r"/"multi")与新服务器不匹配,导致客户端抛出
未捕获异常,触发全屏 "Application error"(周期性崩溃根因)。
修复:
- 添加 src/app/global-error.tsx(根级错误边界),检测到 stale bundle
相关错误时自动调用 window.location.reload(),无感知恢复
- 添加 src/app/(admin)/error.tsx(admin 路由段错误边界),同样自动刷新
- 两个边界均提供「立即刷新」「重试」按钮,防止极端情况下自动刷新失效
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 18:50:22 -08:00 |
hailin
|
4878449f8c
|
fix(genex-mobile): WXEntryActivity 完整转发微信回调 Intent
用 Intent(intent) 拷贝构造完整复制原始 Intent(action / data / extras),
再通过 setClass 重定向到 MainActivity,确保 fluwx 5.x 的
WXAPiHandler.handleIntent() 能读取到完整的微信授权回调内容。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 18:05:01 -08:00 |
hailin
|
5d72d9bd0b
|
fix(genex-mobile): 修复 WXEntryActivity fluwx 5.x 编译错误
fluwx 5.x 将包名从 com.jarvanmo.fluwx 改为 com.jarvan.fluwx,
并移除了 WXEntryActivity 基类,导致编译报错:
Unresolved reference 'jarvanmo'
Cycle in supertypes detected
改为「中继(relay)」模式:
- WXEntryActivity 继承 Activity(不再继承 fluwx 基类)
- onCreate 时将微信回调 Intent extras 转发给 MainActivity
- fluwx 5.x 的 FluwxPlugin 实现了 PluginRegistry.NewIntentListener,
MainActivity.onNewIntent() 会触发 FluwxPlugin 解析微信回调
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 18:03:16 -08:00 |
hailin
|
828770add8
|
fix(alipay): 适配 tobias 5.x 新 auth API,后端生成签名 authString
tobias 3.x+ 移除了顶层函数 aliPayAuth(appId, scope),
改为需要后端预签名的 Tobias().auth(authString)。
变更:
- alipay.provider.ts: 新增 generateMobileAuthString(scope) 方法,
用 RSA2 私钥生成符合 Alipay SDK 格式的签名授权字符串
- auth.controller.ts: 新增 GET /auth/alipay/auth-string 接口
- pubspec.yaml: tobias ^3.0.0 → ^5.0.0
- auth_service.dart: 新增 getAlipayAuthString() 方法
- welcome_page.dart: 更新支付宝登录流程,先获取 authString 再调用 tobias
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 09:26:30 -08:00 |
hailin
|
a27baa1181
|
docs: 完善 fluwx 5.x 迁移与 AdminLayout 跳转备注
- main.dart: 说明 fluwx 5.x 实例共享机制与迁移前后对比
- welcome_page.dart: 说明 addSubscriber/NormalAuth/WeChatAuthResponse 用法,
以及 fluwx 3.x → 5.x API 对照
- AdminLayout.tsx: 说明 useEffect 跳转的原因(React #310 错误根因)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 09:00:18 -08:00 |
hailin
|
2e66db08ef
|
fix(genex-mobile): 升级 fluwx 3.x → 5.x,适配新版 API
fluwx 3.x 不含 Android namespace,导致新版 AGP 构建失败。
升级至 5.x 并迁移 API:
- registerWxApi → Fluwx().registerApi()
- weChatResponseEventHandler → fluwx.addSubscriber()
- sendWeChatAuth → fluwx.authBy(NormalAuth)
- isWeChatInstalled → fluwx.isWeChatInstalled
- WXAuthResp → WeChatAuthResponse
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 08:42:56 -08:00 |
hailin
|
7aea49a0c9
|
fix(admin-web): 修复 AdminLayout 渲染期间调用 router.replace 导致 React #310 错误
将未登录跳转逻辑从 render 函数移至 useEffect,
避免在渲染子组件时触发父组件状态更新。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 05:43:58 -08:00 |
hailin
|
91f06890a5
|
docs(auth): 完善三方登录模块注释 — 含申请步骤与算法说明
AlipayProvider:
- callGateway: 补充完整请求参数结构说明
- sign: 详细说明 RSA2 签名5步骤(字典序→拼接→SHA256WithRSA→Base64)
- chunkBase64: 说明 PEM 格式每64字符换行要求
AppleProvider:
- verifyIdentityToken: 详细说明 claims 验证逻辑和签名验证原理
- getAppleJWKS: 说明 JWKS 缓存策略和 Apple 密钥轮换机制
- jwkToPublicKeyPem: 说明 JWK→SPKI PEM 转换过程
- base64urlDecode: 说明 Base64URL 与标准 Base64 的区别
GoogleProvider:
- verifyIdToken: 说明 tokeninfo vs JWKS 本地验证的权衡,补充响应字段说明
welcome_page.dart:
- 类头注释: 补充平台配置要求和各登录方式申请前提
- _onAlipayTap: 补充 tobias v3 API 结构、result 格式解析、scope 含义、iOS/Android 配置要求
- _parseParam: 说明 URI query 解析的原因
- _onGoogleTap: 补充 signIn 返回 null 的含义、idToken 说明、Android 配置
- _onAppleTap: 补充 Apple 3 项重要限制(姓名/邮箱只在首次授权时返回)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 04:56:11 -08:00 |
hailin
|
e8e2a44fef
|
fix(auth-service): 修复 AlipayProvider TypeScript 类型错误 (unknown 类型断言)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 04:47:01 -08:00 |
hailin
|
1fc0fcb95e
|
feat(auth): 支付宝 + Google + Apple 三方登录
后端 (auth-service):
- 新增 AlipayProvider (RSA2签名) + AlipayService + POST /auth/alipay
- 新增 GoogleProvider (tokeninfo验证) + GoogleService + POST /auth/google
- 新增 AppleProvider (JWKS验证ES256 JWT) + AppleService + POST /auth/apple
- SocialProvider 枚举新增 ALIPAY
- .env.example 补充三方登录申请步骤文档
Flutter (genex-mobile):
- pubspec.yaml: 新增 tobias / google_sign_in / sign_in_with_apple
- auth_service.dart: loginByAlipay / loginByGoogle / loginByApple
- welcome_page.dart: Android=微信+支付宝+Google, iOS=+Apple
- AndroidManifest: 添加支付宝包名查询
- Info.plist: 支付宝 URL Scheme + alipay/alipays queries
- i18n: 4 语言补充失败提示文案
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 04:40:31 -08:00 |
hailin
|
2790d4c226
|
fix(genex-mobile): Apple 登录按钮仅在 iOS 上显示
Apple Sign In 是 iOS/macOS 生态专属功能,Android 用户不存在
Apple ID 账号体系,不应在 Android 上展示该入口。
使用 defaultTargetPlatform == TargetPlatform.iOS 条件渲染:
- Android: WeChat + Google(2个按钮)
- iOS: WeChat + Google + Apple(3个按钮)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 04:21:34 -08:00 |
hailin
|
44de21a733
|
docs(auth): 完善微信登录模块注释 — 含申请移动应用完整步骤
wechat.provider.ts:
- 补充微信开放平台申请移动应用的完整流程(企业资质、创建应用、
获取 AppID/AppSecret、启用 unionid、获取 Android 签名 MD5)
- 说明常见 errcode 含义(40029/42003/40163)
- 安全注意事项(AppSecret 保密、服务端换 token、防重放建议)
wechat.service.ts:
- 完整业务流程注释(6步,含新老用户分支逻辑)
- unionid 优先策略原理(跨 App 唯一性,防重复注册)
- 每次登录同步微信信息说明
- 自动生成账号字段说明(nickname/email/phone/password/kycLevel)
.env.example:
- 微信开放平台申请步骤(注册/认证/创建/获取/启用 unionid)
- Android 签名 MD5 获取命令
- Flutter --dart-define 构建参数说明
- Universal Links 说明
WXEntryActivity.kt:
- 包名路径规范说明(applicationId vs namespace 区别)
- AndroidManifest 配置要求
- 回调未触发的排查步骤(签名/包名/debug包名)
- 获取 Android 签名 MD5 命令
main.dart:
- registerWxApi 传参说明(--dart-define 用法)
- 未配置 WECHAT_APP_ID 时的降级行为
- Universal Links 配置说明
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 04:04:26 -08:00 |
hailin
|
c9100d3262
|
fix(auth): 移除 wechat.service.ts 中对 bcrypt 的直接依赖
改用 Password VO 生成随机密码哈希,避免 TypeScript 找不到
bcrypt 类型声明的编译错误。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 03:52:17 -08:00 |
hailin
|
d68d48cb95
|
feat(auth): 微信登录 / 注册完整实现 — social_accounts + fluwx 全链路
后端:
- 新增 social_accounts 表 Entity/Repository/Migration (049)
- WechatProvider: code ↔ access_token / 获取用户信息 (native https)
- WechatService: unionid 优先查找 → 自动登录/注册 → 发布事件
- POST /auth/wechat 端点 (WechatLoginDto, referralCode 支持)
- auth.module.ts 注册 SocialAccount、WechatProvider、WechatService
Flutter (genex-mobile):
- pubspec.yaml: 添加 fluwx ^3.10.0
- main.dart: registerWxApi 初始化 (WECHAT_APP_ID via --dart-define)
- AuthService: loginByWechat(code, referralCode?, deviceInfo?)
- WelcomePage: 改为 StatefulWidget,监听 weChatResponseEventHandler
微信按钮触发 sendWeChatAuth,授权成功后自动登录 → /main
未安装微信 / 登录失败均有 SnackBar 提示
- 4语言 i18n: wechatNotInstalled / wechatLoginFailed
Android:
- AndroidManifest: WXEntryActivity + queries(com.tencent.mm)
- WXEntryActivity.kt: 继承 fluwx 提供的基类,无额外代码
- proguard-rules.pro: keep WeChat SDK 类
iOS:
- Info.plist: CFBundleURLTypes (wx${WECHAT_APP_ID}) + LSApplicationQueriesSchemes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 03:37:51 -08:00 |
hailin
|
ff558ab77f
|
docs(auth): 完善邮件注册模块注释与说明
为以下新增文件补充详细的文件头注释:
- EmailVerification entity: 验证流程、字段说明、清理策略
- EmailLog entity: 审计日志、状态枚举、provider 说明
- EmailCodeService: Redis 缓存层职责、Key 模式、降级机制
- IEmailProvider: 已实现 Provider 列表、切换逻辑、扩展说明
- IEmailVerificationRepository: 关键方法说明
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 03:20:42 -08:00 |
hailin
|
9473512530
|
feat(auth): 邮箱注册完整实现 — Gmail SMTP + 邮件验证码全链路
后端 (auth-service):
- 新增 EmailVerification / EmailLog 实体 + TypeORM 映射
- Email 值对象:格式校验、小写归一化、脱敏展示
- Gmail SMTP Provider (nodemailer) + ConsoleEmailProvider (dev)
- EmailCodeService:Redis 缓存快速路径,与 SmsCodeService 对称
- EmailService:sendCode/verifyCode + 日限额 + 业务规则校验
- 新增端点:POST /auth/email/send / register-email / login-email / reset-password-email
- EMAIL_ENABLED 环境变量切换真实/控制台发送
- 数据库迁移:048_create_email_verifications.sql
前端 (genex-mobile):
- AuthService 新增 sendEmailCode / registerByEmail / loginByEmail / resetPasswordByEmail
- RegisterPage 根据 isEmail 参数自动切换 SMS/Email API 调用
- WelcomePage 邮箱注册按钮传递 isEmail:true 参数
- i18n 新增 register.errorEmailRequired(4语种)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 02:27:43 -08:00 |
hailin
|
a893dbdb1b
|
feat(genex-mobile): Token 持久化 — 登录状态跨重启保持
## 核心变更
### 新增 SessionStorage (lib/core/storage/session_storage.dart)
- flutter_secure_storage 封装层
- Android: EncryptedSharedPreferences(AES-256,Android Keystore 托管密钥)
- iOS: Keychain(AccessibilityFirstUnlock)
- 存储键: genex_access_token / genex_refresh_token / genex_user_json
- API: save() / load() / updateTokens() / getRefreshToken() / clear()
### 升级 ApiClient (lib/core/network/api_client.dart)
- 新增 Dio 错误拦截器(_buildAuthInterceptor)
- 401 自动触发 Token 静默刷新,成功后无感重试原始请求
- Completer 并发锁:多个 401 同时发生只执行一次刷新,其余等待结果
- 跳过重试的端点:/auth/refresh、/auth/login、/auth/register 等
- configureAuthCallbacks():注册 onTokenRefreshed / onSessionExpired 反向回调
### 升级 AuthService (lib/core/services/auth_service.dart)
- _setAuth():登录/注册后 await 写入 SecureStorage
- _clearAuth():登出/Token 过期后 await 清除 SecureStorage
- restoreSession():App 冷启动时从 SecureStorage 恢复 Token + 注册 ApiClient 回调
- refreshToken():主动刷新(正常由拦截器自动触发,无需手动调用)
### 升级 main.dart
- await AuthService.instance.restoreSession() 在 runApp 前执行
- initialRoute 动态判断:isLoggedIn → '/main',否则 '/'
- 全局 NavigatorKey(_navigatorKey)挂载到 MaterialApp
- 监听 authState ValueNotifier:Token 过期后自动导航回 '/'(pushNamedAndRemoveUntil)
## 用户体验
- 登录后关闭 App 再打开:直接进主界面,无需重新登录
- Access Token 过期:ApiClient 自动静默刷新,用户无感知
- Refresh Token 过期:清除本地会话,跳回欢迎页,提示重新登录
- 主动登出:清除 SecureStorage,跳回欢迎页
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 02:01:27 -08:00 |
hailin
|
1c36c849e2
|
docs(genex-mobile): 完善邀请分享模块注释与说明
SharePage:
- 文件头注释:完整功能概述、支持的分享场景、数据来源、URL格式、依赖包说明
- 类注释:生命周期描述、Widget 树结构图(ASCII)
- 状态变量:详细说明 _info/_loading/_error/_baseInviteUrl
- _inviteLink:注释已加载/未加载两种输出示例
- _loadInfo:成功/失败两条路径说明
- _showCopied:SnackBar 样式描述
- _copyCode/_copyLink:示例复制内容
- _shareNative:iOS/Android 行为说明 + 文案模板示例
- _buildHeroCard:视觉层次注释 + QR 参数说明
- _buildStatsCard:布局描述 + 数据来源注释
- _buildShareActions:三项操作的点击行为说明
ReferralService:
- 文件头:完整端点一览、推荐码格式、推荐链规则
- ReferralInfo:字段含义 + 后端响应 JSON 示例
- getMyInfo:登录要求说明
- validateCode:用途说明 + 返回值降级策略
- getDirectReferrals:分页参数范围 + 响应 JSON 示例
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 01:42:47 -08:00 |
hailin
|
46d2404d19
|
feat(genex-mobile): 邀请好友 — 分享二维码页面全链路实现
- 新增 SharePage:推荐码 QR 码 + 邀请进度 + 一键复制/原生分享
- ProfilePage 添加「邀请好友」渐变横幅入口
- 新增 ReferralService(getMyInfo / getDirectReferrals)
- pubspec.yaml 引入 qr_flutter ^4.1.0、share_plus ^10.0.2
- 路由 /share 注册
- i18n:4 语言新增 share.* 共 20 个翻译键(zh-CN / zh-TW / en / ja)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 01:28:22 -08:00 |
hailin
|
3ae5e2f982
|
feat(kong): 添加 referral-service 路由 /api/v1/referral
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 01:03:32 -08:00 |
hailin
|
0ecf295c35
|
feat(referral): 推荐服务全链路实现
Backend:
- referral-service: 全新微服务 (NestJS + DDD + Clean Architecture)
- ReferralProfile 聚合根 (TypeORM entity)
- ReferralCode / ReferralChain 值对象
- 仓储接口 + 实现
- ReferralService 应用服务: 创建档案/验证码/查询直推
- UserRegisteredHandler: 订阅 genex.user.registered Kafka 事件
- REST: GET /referral/my, GET /referral/validate, GET /referral/direct
- 内网: POST /internal/referral/profiles
- 端口 3013 (外部 4013)
- auth-service: RegisterDto 增加 referralCode 字段
- auth-service: UserRegisteredEvent 携带 referralCode
- auth-service: EventPublisherService 实际接入 Kafka (之前是 stub)
- migrations: 047_create_referral_profiles.sql
- docker-compose: 新增 referral-service 服务块
Frontend (genex-mobile):
- 注册页添加推荐码输入框 (可选),输入时实时验证推荐码有效性
- AuthService.register() 增加 referralCode 参数
- AuthService.validateReferralCode() 新增方法
- i18n: zh_CN/zh_TW/en/ja 各新增 4 个推荐码相关 key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 00:58:28 -08:00 |
hailin
|
ec0af6c47e
|
fix(auth-service): 密码登录时对手机号做 E.164 归一化
注册时手机号存储为 +8618926762721,但密码登录用原始输入
18926762721 查库,导致找不到用户。在 login() 中先尝试
Phone.create() 归一化,如果不是合法手机号则作为邮箱使用。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-04 00:23:04 -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
|
12a665f2ba
|
fix(genex-mobile): 升级服务改用 api.gogenex.cn — 国内网络更稳定
.com 域名指向 HK IP 在中国大陆部分网络解析失败,
改用 .cn 国内域名确保升级检查能正常访问。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-03 22:55:04 -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
|
2cf90db0b1
|
fix(admin-service): 移除全局前缀排除规则 — 让 app/version 路由正确响应 /api/v1 前缀
Kong 以 strip_path=false 转发 /api/v1/app/version/check,但服务排除了该路径的
全局前缀,导致 404。去掉 exclude 后所有路由统一挂载在 /api/v1 下。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-03 21:39:32 -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
|
50d6c77dfd
|
fix(i18n): 补全 en.dart 注册页缺失的6个 key
register.hasAccount / loginNow / errorPhoneRequired /
errorCodeInvalid / errorPasswordWeak / errorTermsRequired
在英文环境下均有对应译文,不再回退显示中文
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-03 20:55:07 -08:00 |
hailin
|
04a55e3e12
|
fix(ui): 修复登录页中英混用 + 搜索栏溢出,更新品牌口号并支持后台配置
- admin-web 登录页底部 "Genex 券金融平台" → "Genex 管理后台",风格统一
- admin-web 顶部搜索栏改为弹性宽度(width:100%/maxWidth:320),修复窄屏溢出
- 全平台品牌口号统一更新为"让每一张券,自由流动"
覆盖:genex-mobile(4语言) / miniapp(3语言) / portal(zh-CN/en-US)
- backend admin-system.service: getConfig() 新增 brandConfig 字段(4语言口号)
- admin-web 系统配置页新增品牌配置卡片:支持4语言口号在线编辑并保存
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
2026-03-03 20:41:37 -08:00 |
hailin
|
e7c1e33355
|
fix(genex-mobile): 语言偏好持久化 — 用户选择不再因重启丢失
修复 LocaleManager 仅用内存 ValueNotifier 存储语言选择的问题,
重启 App 后用户选的语言会丢失,回退到系统语言。
改动:
- pubspec.yaml: 添加 shared_preferences 依赖
- locale_manager.dart: 新增 init() 启动恢复 + setLocale() 写入持久化
· null = 跟随系统语言(清除 SP 记录)
· 非 null = 持久化到 SharedPreferences,重启后自动恢复
- main.dart: 启动时调用 await LocaleManager.init()
- settings_page.dart: 语言选择改用 LocaleManager.setLocale()
行为:首次安装跟随系统语言 → 用户选择后持久化 → 重启保持不变
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-03 19:39:07 -08:00 |
hailin
|
0cd5c58ecb
|
feat(android): APK versionCode 自动递增 — 每次编译自增,各环境独立计数
参考 rwadurian mobile-app 的 version.properties 方案,为 genex-mobile
和 admin-app 添加 build 时 versionCode 自动 +1 逻辑:
- build.gradle.kts: calculateNextVersionCode() 读取 version.properties,
自增后写回;versionName 格式 "pubspec版本.buildNumber"(如 1.0.0.42)
- .gitignore: 排除 /android/app/version.properties,每个构建环境
(本机、CI、服务器)各自维护独立计数器,互不干扰
- 首次编译从 1 开始,后续跨日期持续递增,保证 APK 版本始终唯一
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-03 09:09:13 -08:00 |
hailin
|
fcf49b5257
|
fix(admin-service): 注册 JwtStrategy 解决 "Unknown authentication strategy" 错误
admin-service 使用了 @genex/common 的 JwtAuthGuard 保护管理端接口,
但缺少对应的 Passport JWT Strategy 注册。新增轻量级 JwtStrategy,
仅验证 token 签名和提取 payload,不依赖 UserRepository。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-03 08:16:38 -08:00 |
hailin
|
6f87be4454
|
fix(admin-service): 修复公开 API 路由前缀排除规则
setGlobalPrefix exclude 应匹配 controller 路径而非完整 URL。
修复前: exclude: ['api/app/version/(.*)']
→ 不生效,公开 API 被加上 /api/v1 前缀
→ 移动端需访问 /api/v1/app/version/check (错误)
修复后: exclude: ['app/version/(.*)']
→ 正确排除 AppVersionController 的路由
→ 移动端访问 /api/app/version/check (无 /api/v1 前缀)
→ 管理端继续使用 /api/v1/admin/versions (不受影响)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-03 07:37:44 -08:00 |
hailin
|
1d1e08cc8f
|
chore: 忽略本地构建中间文件 (package-lock.json, tsbuildinfo)
package-lock.json 和 *.tsbuildinfo 是本地 npm install / tsc 的产物,
生产环境在 Docker 容器内独立安装依赖,无需纳入版本管理。
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-03 06:21:31 -08:00 |
hailin
|
c1681085b8
|
feat(admin): App 版本管理 — 多应用支持 + 管理后台页面
为 admin-service 添加 appType 维度,支持管理 genex-mobile (用户端)
和 admin-app (发行方管理端) 两个 Flutter 应用的版本发布。
同时在 admin-web 新增完整的版本管理页面。
### 后端改动 (admin-service)
数据模型:
- 新增 AppType 枚举: GENEX_MOBILE | ADMIN_APP
- app_versions 表添加 app_type 列 (VARCHAR(20), 默认 GENEX_MOBILE)
- 重建唯一索引: (app_type, platform, version_code)
- Migration 046: ALTER TABLE + 索引重建
DDD 各层更新:
- Repository 接口/实现: 所有查询方法增加 appType 参数
- Service: checkUpdate/listVersions/createVersion 支持按 appType 过滤
重复检测范围: 同一 appType + platform 内的 versionCode 唯一
- AdminVersionController:
- GET /admin/versions 增加 ?appType= 查询参数
- POST /admin/versions body 增加 appType 字段
- POST /admin/versions/upload body 增加 appType 字段
- AppVersionController (移动端):
- GET /app/version/check 增加 ?app_type= 参数 (默认 GENEX_MOBILE)
### 前端改动 (admin-web)
新增页面 /app-versions:
- App 选择器 Tab: Genex 用户端 / 发行方管理端
- 平台过滤器: 全部 / Android / iOS
- 版本列表表格: 版本号、代码、平台、构建号、文件大小、强制更新、状态、发布日期
- 操作列: 编辑 / 启用|禁用 / 删除
- 上传对话框: 文件选择 → 自动解析包信息 → 填写表单 → 上传到 MinIO
- 编辑对话框: 更新日志、最低系统版本、强制更新、启用状态
- i18n: zh-CN / en-US / ja-JP 各 28 个新翻译键
- 侧边栏: 在「系统管理」前增加「📱 应用版本」菜单项
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-03 06:09:55 -08:00 |
hailin
|
41b8a8fcfb
|
feat(auth): SMS 模板按类型分发 + 阿里云 8 模板配置
重构 auth-service 短信发送逻辑,从单一模板改为按验证类型分发不同模板。
变更:
- SmsVerificationType 枚举新增 3 类型: IDENTITY_VERIFY, TRANSACTION_CONFIRM, PAYMENT_VERIFY
- AliyunSmsProvider.getTemplateCode() 通过 TEMPLATE_ENV_MAP 按类型查找环境变量
优先使用类型专属模板,fallback 到通用 ALIYUN_SMS_TEMPLATE_CODE
- 无模板配置时返回错误而非发送空模板
- 日志增加类型和模板代码,便于排查
阿里云已创建 8 个 Genex 短信模板:
- SMS_501745796 注册验证码 (已通过)
- SMS_501720822 登录验证码
- SMS_501735781 重置密码
- SMS_501925825 身份验证 (已通过)
- SMS_501820752 交易确认 (已通过)
- SMS_501855782 支付验证 (已通过)
- SMS_501780799 异常登录提醒 (已通过)
- SMS_501810819 账户变更通知
环境变量:
- .env.example / docker-compose.yml 新增 ALIYUN_SMS_TPL_* 共 7 项
- aliyun_sms_manager.py 迁移到 CreateSmsTemplate 新 API (旧 AddSmsTemplate 已下线)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-03 05:13:16 -08:00 |
hailin
|
bd6ecaa0fd
|
feat(portal): Genex 门户官网 — 20 页 Next.js SSG 站点
新增 frontend/portal/ 独立 Next.js 15 应用,作为 gogenex.com 品牌官网。
页面清单 (20 pages):
- 首页 (/) — Hero + 数据看板 + 三大优势 + 产品展示 + 信任背书 + CTA
- 关于我们 (/about) — 愿景 + 团队 + 里程碑时间线 (2025 Q1 起)
- 产品功能 (/features) — 购券流程 + 交易 + 清算 + 多端支持
- 解决方案 (/solutions) — 消费者/商户场景 + 用例故事
- 开发者 (/developers) — API 文档入口 + SDK + 区块链浏览器
- 联系我们 (/contact) — 联系表单 + 地址 + 社交
- 下载 (/download) — iOS/Android/Web/小程序 四端下载
- 职位招聘 (/careers) — 6 个岗位卡片
- 博客 (/blog) — 6 篇文章摘要
- 新闻稿 (/press) — 3 篇新闻发布
- 帮助中心 (/help) — 6 大帮助主题
- 社区 (/community) — 6 个社交渠道卡片
- 系统状态 (/status) — 6 项服务运行状态
- 服务条款 (/terms) — 完整法律条款
- 隐私政策 (/privacy) — 完整隐私声明
- Cookie 政策 (/cookie-policy)
- 风险披露 (/risk)
技术栈:
- Next.js 15 + React 18 + TypeScript + CSS Modules
- Framer Motion 滚动动画
- 复用 admin-web design-tokens.css 变量体系
- 完整 i18n (zh-CN / en-US, 800+ 翻译键)
- Docker 多阶段构建 + Nginx 反向代理配置
- SSG 静态生成,SEO 友好
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-03 05:03:53 -08:00 |
hailin
|
8af65a3a48
|
feat(scripts): 阿里云管理工具 (SMS/CloudAuth/Domain)
- aliyun_sms_manager.py: 签名/模板管理、发送记录查询、发送统计、发送测试
- aliyun_cloudauth_manager.py: 实人认证场景、人脸活体检测、身份二要素核验、套餐余额查询
- aliyun_domain_manager.py: 域名列表/详情/续费/转出/信息模板管理
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-02 19:56:18 -08:00 |
hailin
|
34b85f68ae
|
feat(admin-web): 登录页 + Auth Guard + API URL 切换域名
- 新建 /login 页面(邮箱/密码登录,对接 auth-context)
- AdminLayout 添加 auth guard:未登录自动跳转 /login
- api-client 默认 URL 从 localhost:8080 → https://api.gogenex.com
- Header 头像显示用户首字母,点击登出
- i18n 补充 header_logout (zh/en/ja)
API 联通验证(全部正常):
- POST /api/v1/auth/sms/send → 400 (手机号未注册)
- POST /api/v1/auth/login → 401 (密码错误)
- POST /api/v1/auth/register → 400 (验证码过期)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-02 02:57:45 -08:00 |
hailin
|
65d2904f1a
|
feat(deploy): 扩展Nginx配置覆盖全部11个.com子域名
Namecheap DNS 新增 8 条记录 (→ 154.84.135.121):
portal / console / auth / rpc / explorer / bridge / faucet / ipfs-gw
SSL 证书已扩展覆盖全部 11 个 .com 子域名 (Let's Encrypt)
临时方案: .cn 域名因 ICP 备案未完成被 ISP 拦截,
所有公网服务走 .com 域名绕行海外 IP
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-02 02:36:24 -08:00 |
hailin
|
535f53041f
|
feat(deploy): Nginx反向代理 + SSL + 前端切换域名
部署架构:
- Nginx (跳板机 14.215.128.96) → Kong (192.168.1.222:48080)
- SSL: Let's Encrypt 证书已为 api.gogenex.com 签发
- HTTP 自动 301 → HTTPS
前端 API 地址:
- genex-mobile: https://api.gogenex.com (ApiClient + UpdateService)
- miniapp: https://api.gogenex.com (development config)
- 备案完成后切回 https://api.gogenex.cn
Namecheap DNS 新增:
- admin.gogenex.com → 154.84.135.121
- ws.gogenex.com → 154.84.135.121
备注:
- gogenex.cn 的 80/443 端口被世纪互联 ISP 拦截,需完成 ICP 备案
- admin/ws 子域名的 SSL 证书待 DNS 传播后申请
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-02 00:00:58 -08:00 |
hailin
|
457ee8f4cb
|
feat(infra): 双域名DNS架构 + DNS管理脚本
## 域名规划
- gogenex.cn (阿里云DNS): 29条记录,国内公网指向 14.215.128.96
- gogenex.com (Namecheap DNS): 5条记录,海外公网指向 154.84.135.121
- 两个域名到达同一台物理服务器(双网卡),再反代到内网 192.168.1.222
## DNS 管理脚本
- scripts/alidns_manager.py: 阿里云DNS单条记录CRUD
- scripts/alidns_batch.py: 阿里云DNS批量配置
- scripts/namecheap_manager.py: Namecheap DNS安全读-合并-写操作
- scripts/namecheap_batch.py: Namecheap DNS批量配置
- 修复 namecheap_manager.py XML解析大小写问题 (Host vs host)
## 文档
- docs/gogenex-domain-planning.md: 双域名架构规划 v1.1
- docs/plan-schema.md: 批量DNS配置JSON格式定义
- docs/migration-guide.md: DNS迁移指南
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
2026-03-01 22:12:47 -08:00 |