hailin
b9d2393aa1
fix(admin-service): 将文件存储从本地磁盘改为 MinIO
...
原实现将 APK/IPA 写入容器内 ./uploads 目录,导致 EACCES 权限错误。
改为通过 MinIO SDK 上传到 oss.gogenex.com / app-releases bucket,
与 docker-compose 中已有的 MINIO_* 环境变量保持一致。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 08:15:42 -08:00
hailin
7c8b79161a
feat(upload): parse=upload+save+metadata, register=JSON only — no double upload
...
Previously the flow uploaded the 53MB file twice:
1. POST /parse → parse metadata (file discarded)
2. POST /upload → parse again + save (file sent again)
New flow — file sent exactly once:
1. POST /parse → upload file, save to disk, parse metadata
returns {versionName, versionCode, minSdkVersion, storageKey, fileSize, fileSha256}
2. POST /register → JSON only (no file), creates DB record using storageKey
Frontend:
- handleFileChange: async, immediately uploads to /parse with progress bar (0-100%)
- handleSubmit: calls /register with storageKey + form metadata (instant)
- Upload modal: real-time progress bar, "confirm" button disabled until parse complete
- Console logs at every step for debugging
Backend:
- POST /parse: saves file after parsing, returns storageKey in response
- POST /register: new endpoint, accepts JSON + storageKey, creates version record
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 07:19:28 -08:00
hailin
eca1490c72
debug(admin-service): add timing logs to parse and upload endpoints
...
Logs file size on receipt, parse duration, file save duration, and
total request time — to pinpoint where upload latency comes from.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 06:33:18 -08:00
hailin
a6cb7add60
fix(admin-service): align APK/IPA parser with RWADurian implementation
...
Replace fragile internal ManifestParser approach with RWADurian's proven pattern:
- APK: write buffer to temp file → ApkReader.open(path) → readManifest() → cleanup
- IPA: unzipper.Open.buffer() → Info.plist → bplist-parser with XML fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 06:08:47 -08:00
hailin
7933f3fe4a
perf(upload): replace MinIO presigned-URL flow with local-disk storage
...
Previously the APK/IPA upload required two full public-internet transfers:
1. POST /parse → browser → gateway → admin-service (full file, for metadata)
2. PUT presigned → browser → oss.gogenex.com / MinIO (full file, to store)
Now follows the same pattern as RWADurian admin-service:
- Single multipart POST /admin/versions/upload
- admin-service parses buffer in-memory (yauzl / unzipper)
- Saves to local disk (UPLOAD_DIR env, default ./uploads)
- Download served via existing GET /app/version/download/:id (streams local file)
Changes:
- file-storage.service.ts: drop minio dep, use fs/promises + crypto
- admin-version.controller.ts: POST upload now accepts multipart file,
removes GET presigned-url endpoint (no longer needed)
- version.repository.ts (frontend): single FormData POST, removes
three-step presigned-URL flow
Result: file crosses public internet once instead of twice.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 05:49:36 -08:00
hailin
b710d416d1
fix(admin-service): restore FileInterceptor imports for parse endpoint
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 04:44:04 -08:00
hailin
0f611cf8cd
feat(upload): presigned URL — browser uploads directly to MinIO
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 04:41:38 -08:00
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