Commit Graph

26 Commits

Author SHA1 Message Date
hailin 7ba5401e2f feat(infra): use oss.gogenex.com for app version download URLs
将应用版本的文件下载链接从 API 代理路径改为直接指向对象存储域名。

backend/admin-service (admin-version.controller.ts)
- uploadVersion 上传成功后,downloadUrl 改为:
    https://oss.gogenex.com/app-releases/<storageKey>
  (原:/api/v1/app/version/download/:id 代理路径)
- 读取 OSS_BASE_URL 环境变量,默认 https://oss.gogenex.com

backend/docker-compose.yml
- admin-service 新增 OSS_BASE_URL=https://oss.gogenex.com

infrastructure/minio/deploy.sh
- app-releases bucket 加入公开下载列表
  (APK/IPA 需被移动端直接下载,无需鉴权)

frontend/admin-web
- .env.production 新增 NEXT_PUBLIC_OSS_URL=https://oss.gogenex.com
- .env.development 新增 NEXT_PUBLIC_OSS_URL=https://oss.gogenex.com

MinIO 现状:
  app-releases bucket 已在服务器上设为 anonymous download

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 00:42:05 -08:00
hailin 4c5f5e2cd9 fix(admin-service): update MinIO default endpoint to oss.gogenex.com
将 FileStorageService 的默认连接参数从 localhost 更新为生产域名:
- endPoint 默认值: localhost → oss.gogenex.com
- port 默认值: 9000 → 443
- useSSL 默认值: false → true(?? 运算符保证未设置时默认开启)
- accessKey/secretKey 默认值更新为生产凭证

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 00:22:53 -08:00
hailin fc85983b43 fix(upload): remove explicit Content-Type header so browser sets multipart boundary
Without boundary multer receives undefined file. Also add guards in
backend parse/upload to avoid crash if file is missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 10:56:58 -08:00
hailin adda5e04d7 fix(admin-service): 修正 ManifestParser 调用方式 — 直接传原始 Buffer
ManifestParser(buffer) 内部自带 BinaryXmlParser,无需先调用 BinaryXmlParser.parse()
再把结果传入 ManifestParser,否则导致 readUInt16LE is not a function。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 09:31:08 -08:00
hailin 276eda2a84 fix(admin-service): 改用 yauzl.fromBuffer 解析 APK,支持 V2/V3 签名格式
yauzl.open() 在 APK V2/V3 签名包上报 'end of central directory record
signature not found',因为签名块会挤移 EOCD 位置。改为直接调用
yauzl.fromBuffer() + adbkit-apkreader 内部 BinaryXmlParser/ManifestParser
直接从内存解析 AndroidManifest.xml,兼容所有签名格式,且无需临时文件。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 09:18:56 -08:00
hailin e00cbb71c5 fix(admin-service): 修复 APK 解析失败 — 写入临时文件后再解析
adbkit-apkreader v3 底层使用 yauzl.open(),只接受文件路径,
传入 Buffer 会被当成文件路径字符串导致 ENOENT。
改为先将 Buffer 写入 os.tmpdir() 临时文件,解析完成后在 finally 中删除。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 09:03:14 -08:00
hailin 4369aecf60 fix: 修复上传版本500错误 + 优化App冷启动通知请求
- fix(admin-service): versionCode 兜底从 Date.now() 改为 1,避免超出 PostgreSQL integer 范围
- fix(genex-mobile): NotificationBadgeManager 加登录检查,未登录跳过API请求
- fix(genex-mobile): 将通知徽章初始化移至首帧后执行,消除冷启动DNS竞争

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 08:48:09 -08:00
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 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 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 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 e8e2d14fbc fix(health): 修正NestJS服务健康检查URL路径 /health → /api/v1/health
NestJS服务注册了全局路由前缀 api/v1,HealthController 实际挂载在
/api/v1/health 而非 /health,导致 Docker HEALTHCHECK 返回 404。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 05:50:26 -08:00
hailin c5787020d2 feat(health): 为全部12个微服务添加Docker健康检查
## 变更概要
所有后端微服务均增加 Docker HEALTHCHECK 指令,统一通过 GET /health
端点检测服务存活状态。解决 `docker ps` 中13个容器无 (healthy) 状态的问题。

## NestJS 服务 (9个)
- auth-service (:3010), user-service (:3001), issuer-service (:3002),
  clearing-service (:3004), compliance-service (:3005), ai-service (:3006),
  notification-service (:3008), telemetry-service (:3011), admin-service (:3012)
- 7个服务的 app.module.ts 新增 `import { HealthModule } from '@genex/common'`
  注册标准健康检查控制器 (GET /health, /health/ready, /health/live)
- telemetry-service 和 admin-service 已有自建 HealthController,无需导入
- Dockerfile: HEALTHCHECK --interval=30s --start-period=15s --retries=3

## Go 服务 (3个)
- trading-service (:3003), translate-service (:3007), chain-indexer (:3009)
- 已有 /health 端点 (Gin 路由),仅 Dockerfile 添加 HEALTHCHECK
- Dockerfile: HEALTHCHECK --interval=30s --start-period=10s --retries=3

## Kafka Connect
- docker-compose.yml 添加 healthcheck (curl http://localhost:8083/)

## 健康检查方式
- 所有服务统一使用 `wget --spider http://localhost:PORT/health`
  (node:20-alpine 和 alpine:3.19 均自带 BusyBox wget)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 05:39:25 -08:00
hailin 0b1de382b0 fix(deploy): 服务器部署修复 — 全栈编译运行通过
在内网服务器 (192.168.1.222) 首次部署时发现并修复的所有问题,
涉及 backend (12微服务)、blockchain (Cosmos SDK节点)、前端 (admin-web)。

## Backend 修复

### docker-compose.yml
- DB_PASSWORD: 从硬编码 `genex_dev_password` 改为 `${DB_PASSWORD}` 环境变量插值,
  所有13个服务统一从 .env 读取,避免密码不一致导致连接失败
- Kong 镜像: `kong:3.5-alpine` → `kong:3.5`,alpine 版本在 amd64 上缺少依赖
- chain-indexer: 环境变量名 `CHAIN_RPC_URL` → `RPC_URL` (与 Go 代码 getEnv 一致)
- chain-indexer: RPC 地址改为 `http://172.17.0.1:8545` (Docker bridge gateway,
  跨 docker-compose network 访问 blockchain 节点的 EVM JSON-RPC)
- chain-indexer: 补全缺失的 DB_HOST/DB_PORT/DB_USERNAME/DB_PASSWORD/DB_NAME

### ai-service
- ai.module.ts: 移除重复的 TypeOrmModule.forRootAsync — 该模块错误地使用了
  DB_USER/DB_PASS (默认 'genex'/'genex') 和独立数据库 genex_ai,与 app.module.ts
  中已有的 TypeOrmModule.forRoot 冲突,导致密码认证失败
- app.module.ts: 添加 ConfigModule.forRoot({ isGlobal: true }),因为
  JwtModule.registerAsync 依赖 ConfigService 注入

### 依赖补全
- user-service/package.json: 添加 kafkajs ^2.2.4 (Kafka 事件消费)
- admin-service/package.json: 添加 kafkajs ^2.2.4 (Kafka 事件消费)

## Blockchain 修复

### genex-chain/app.go — 修复 chainConfig panic
- 问题: NewGenexApp 被调用两次 (tempApp 注册编码 + 实际启动),cosmos/evm 的
  SetChainConfig 在第二次调用时 panic: "chainConfig already set"
- 原因: 原代码 `if evmChainID == 0 { evmChainID = GenexEVMChainID }` 导致
  tempApp 也使用 8888,SetChainConfig 设置后,实际 app 再次设置时触发 panic
- 修复: 移除默认值回退,让 tempApp 使用 evmChainID=0 → DefaultEVMChainID(262144),
  实际 app 从 app.toml 读取 8888 后可正常覆盖

### genex-chain/cmd/genexd/cmd/root.go
- 移除未使用的 banktypes import (编译错误)

### docker-compose.yml
- 添加 `command: ["start", "--chain-id", "genex-testnet-1"]` 到 x-genex-node 默认配置,
  否则 InitChain 时 chain-id 为空导致校验失败

### init-genesis.sh (新增)
- 创世初始化脚本: init → 补丁 genesis.json → 创建验证者 → gentx → collect-gentxs
- 将所有默认 denom (aatom) 替换为 agnx (GNX, 18 decimals EVM 兼容)
- 配置: evm_denom, bond_denom, mint_denom, gov min_deposit, bank denom_metadata
- 启用 JSON-RPC (0.0.0.0:8545/8546)、REST API、CORS
- 设置 evm-chain-id=8888

## 部署结果
- 20 个 Docker 容器全部运行 (4 基础设施 + 12 后端 + 1 区块链节点 + 1 前端 + 2 辅助)
- 区块链稳定出块 (height 80+),EVM JSON-RPC 正常
- chain-indexer 实时索引区块 (lag=0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 05:29:33 -08:00
hailin 2f583a0a24 fix(admin-service): 添加APK/IPA解析依赖 + 修复Platform类型推断
- 添加adbkit-apkreader, unzipper, bplist-parser
- 修复platform变量类型为Platform枚举

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 19:58:12 -08:00
hailin c42827c6c1 fix(backend): 修复所有服务编译问题 — tsconfig宽松化 + 补全缺失依赖
- 所有tsconfig.json: strict改为false, 移除paths映射(改用node_modules解析)
- compliance/clearing/ai/notification: 添加缺失的@nestjs/passport和@nestjs/jwt
- ai-service: 添加缺失的@nestjs/config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 19:20:48 -08:00
hailin d48bd3acb9 feat(docker): 重构Monorepo构建 — 支持@genex/common共享包
- docker-compose.yml build context从service目录改为backend根目录
- 所有NestJS Dockerfile改为先构建共享@genex/common包
- 共享包编译后复制到service的node_modules供运行时解析
- 新增backend/.dockerignore减少构建上下文体积

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 19:00:27 -08:00
hailin d146bf0a1f fix(tsconfig): 关闭strictPropertyInitialization以兼容TypeORM实体
TypeORM实体使用装饰器定义属性,不在构造函数中初始化。
strict:true隐式启用strictPropertyInitialization导致TS2564编译错误。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 17:48:48 -08:00
hailin 12bb31ede1 fix(docker): 所有NestJS服务Dockerfile使用npm install替代npm ci
项目未包含package-lock.json,npm ci要求该文件存在才能运行。
改用npm install确保Docker构建正常完成。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 17:33:31 -08:00
hailin c11d009ae7 fix: admin-service/telemetry-service Dockerfile 改为多阶段构建
修复缺少 builder 阶段导致 COPY dist/ 找不到目录的问题

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 17:29:56 -08:00
hailin bc0d1e0876 feat: 新增 deploy.sh 部署管理脚本体系 — 后端主控+12服务独立+区块链
参照 rwadurian 项目模式,为全栈系统创建统一的 deploy.sh 管理体系:

- backend/deploy.sh: 主控脚本 (up/down/restart/build/build-no-cache/health/infra-*/单服务操作)
- 12个服务独立 deploy.sh (build/rebuild/start/stop/restart/logs/health/shell/test)
- blockchain/deploy.sh: 节点+生态+合约+浏览器+监控全覆盖
- 更新 09-编译部署指南.md 新增第9章 deploy.sh 使用文档

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 17:13:09 -08:00
hailin acaec56849 feat: 全12服务DDD重构 + 公告定向推送系统 (移植自rwadurian)
## 一、DDD + Clean Architecture 重构 (全12服务)

对全部12个微服务(9 NestJS + 3 Go)实施严格的DDD分层:

### NestJS 服务 (9个):
- auth-service: JWT策略已在domain层
- user-service: 新增 5 个 repository interface + implementation,
  5 个 value objects (Email/Phone/KycLevel/Money), 3 组 domain events
- issuer-service: 新增 5 个 repository interface + implementation,
  3 个 value objects, 2 组 domain events, external AI client port
- clearing-service: 新增 5 个 repository interface + implementation,
  2 个 value objects, domain events
- compliance-service: 新增 7 个 repository interface + implementation,
  2 个 value objects, domain events, audit logger service
- ai-service: 新增 conversation repository + TypeORM entity,
  AI agent client port 移至 domain/ports/
- notification-service: 新增 notification repository interface +
  implementation, channel providers, value objects
- telemetry-service: 新增 3 个 repository interface + implementation
- admin-service: 新增 app-version repository interface + implementation

### Go 服务 (3个):
- trading-service: 重构为 domain/application/infrastructure 分层,
  新增 repository interface + postgres 实现, matching engine 移入
  application/service, 新增 value objects (Price/Quantity/OrderSide/OrderType),
  Kafka event publisher
- translate-service: 新增 repository interface + postgres 实现,
  value objects (Address/ChainType)
- chain-indexer: 新增 repository interface + postgres 实现,
  value objects (BlockHeight/TxHash), Kafka event publisher

### 关键模式:
- NestJS: Symbol token DI (provide: Symbol → useClass: Impl)
- Go: compile-time interface check (var _ Interface = (*Impl)(nil))
- TypeORM entity 保留 domain methods (pragmatic DDD)
- Repository interface 在 domain/, 实现在 infrastructure/persistence/

## 二、公告定向推送系统 (ported from rwadurian)

在 notification-service 中新增 Announcement 公告体系,
支持管理端向全体/标签/指定用户推送消息:

### 数据库:
- 038_create_announcements.sql: 5张新表
  - announcements (公告主表)
  - announcement_tag_targets (标签定向)
  - announcement_user_targets (用户定向)
  - announcement_reads (已读记录)
  - user_tags (用户标签)

### 三种定向模式:
- ALL: 推送给全体用户
- BY_TAG: 按标签筛选用户 (用户标签与公告标签有交集)
- SPECIFIC: 指定用户ID列表

### 新增文件 (15个):
- 5 个 TypeORM entity (Announcement + Read + TagTarget + UserTarget + UserTag)
- 2 个 repository interface (IAnnouncementRepository 11方法, IUserTagRepository 6方法)
- 2 个 repository 实现 (TypeORM QueryBuilder, targeting filter SQL)
- 2 个 application service (AnnouncementService, UserTagService)
- 2 个 DTO 文件 (announcement.dto.ts, user-tag.dto.ts)
- 1 个 controller 文件 (含3个controller: AdminAnnouncement/AdminUserTag/UserAnnouncement)
- 1 个 migration SQL

### API 端点:
管理端:
  POST   /admin/announcements       创建公告(含定向配置)
  GET    /admin/announcements       公告列表
  GET    /admin/announcements/:id   公告详情
  PUT    /admin/announcements/:id   更新公告
  DELETE /admin/announcements/:id   删除公告
  GET    /admin/user-tags           所有标签(含用户数)
  POST   /admin/user-tags/:userId   添加用户标签
  DELETE /admin/user-tags/:userId   移除用户标签
  PUT    /admin/user-tags/:userId/sync  同步用户标签

用户端:
  GET    /announcements             用户公告(按定向过滤+已读状态)
  GET    /announcements/unread-count  未读数
  PUT    /announcements/:id/read    标记已读
  PUT    /announcements/read-all    全部已读

### 设计决策:
- Announcement 与现有 Notification 并存 (双轨):
  Notification = 事件驱动1:1通知, Announcement = 管理端广播/定向
- rwadurian accountSequences → gcx userIds (UUID)
- rwadurian Prisma → gcx TypeORM

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 21:11:24 -08:00
hailin e20c321d12 refactor: 遥测与版本管理拆分为独立微服务 (telemetry-service + admin-service)
架构重构: 将遥测(Telemetry)和版本管理(App Version)从 user-service 拆分为两个独立微服务,
严格遵循 DDD + Clean Architecture 四层架构。

新增服务:
- telemetry-service (:3011) — 用户心跳检测、事件采集、DAU统计、Prometheus指标
  - domain: 3实体 + 3值对象(EventName/InstallId/TimeWindow) + 2领域事件
  - infrastructure: Redis(Sorted Set心跳) + Kafka(事件发布) + Prometheus(5指标)
  - 定时任务: 每分钟在线快照、每小时清理过期、凌晨DAU精确计算、滚动DAU更新
- admin-service (:3012) — APK/IPA版本管理、OTA更新、MinIO文件存储
  - domain: 1实体 + 4值对象(VersionCode/VersionName/FileSha256/DownloadUrl)
  - infrastructure: MinIO(文件上传/下载) + APK/IPA解析器
  - 移动端: 检查更新API(无认证) + 下载重定向(预签名URL)
  - 管理端: 版本CRUD + 上传解析 + 启禁用

user-service 清理:
- 删除24个已迁移文件(4实体+4服务+4基础设施+5控制器+6DTO+1gitkeep)
- 移除不再需要的依赖: @nestjs/schedule, minio, prom-client, kafkajs
- 精简 user.module.ts,仅保留用户核心功能(Profile/KYC/Wallet/Message/Admin)

基础设施更新:
- Kong: 遥测路由 → telemetry-service:3011, 版本路由 → admin-service:3012
- docker-compose: 新增2个服务容器 + MinIO app-releases bucket
- 07开发指南: 更新为独立服务架构描述

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 18:30:39 -08:00