# Admin Service 架构文档 ## 目录 - [1. 服务概述](#1-服务概述) - [2. 架构设计](#2-架构设计) - [3. 领域设计](#3-领域设计) - [4. 技术栈](#4-技术栈) - [5. 目录结构](#5-目录结构) - [6. 数据流](#6-数据流) --- ## 1. 服务概述 ### 1.1 服务职责 Admin Service 是 RWA Durian 项目的**应用版本管理服务**,负责: - 📱 **版本发布管理**: 管理 Android/iOS 应用版本的创建、更新、启用/禁用 - 🔄 **版本检查**: 为移动端提供版本检查 API,支持强制更新和普通更新 - 📊 **版本查询**: 支持按平台、版本号、启用状态等条件查询版本信息 - 🔐 **SHA256 校验**: 确保 APK/IPA 文件完整性和安全性 ### 1.2 核心功能 | 功能 | 说明 | API 端点 | |-----|------|---------| | 创建版本 | 发布新版本(Android/iOS) | POST /api/v1/version | | 检查更新 | 移动端检查是否有新版本 | GET /api/v1/version/check | | 查询版本 | 查询所有版本或特定版本 | GET /api/v1/version | | 启用/禁用版本 | 控制版本可用性 | PATCH /api/v1/version/:id/enable
PATCH /api/v1/version/:id/disable | --- ## 2. 架构设计 ### 2.1 架构模式 Admin Service 采用 **DDD (领域驱动设计) + Hexagonal Architecture (六边形架构)** 的混合架构模式。 ``` ┌─────────────────────────────────────────────────────────┐ │ API Layer (NestJS) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Controllers │ │ DTOs │ │ Guards │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Application Layer (Handlers) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Commands │ │ Queries │ │ Events │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Domain Layer │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Entities │ │ Value Objects│ │ Services │ │ │ │ │ │ │ │ │ │ │ │ AppVersion │ │VersionCode │ │VersionCheck │ │ │ │ │ │VersionName │ │ Service │ │ │ │ │ │ FileSize │ │ │ │ │ │ │ │ FileSha256 │ │ │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────┐ │ │ │ Repository Interfaces (Port) │ │ │ └──────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────┐ │ Infrastructure Layer (Adapters) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ Repositories │ │ Mappers │ │ Prisma │ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ └─────────────────────────────────────────────────────────┘ │ ▼ ┌──────────┐ │PostgreSQL│ └──────────┘ ``` ### 2.2 分层职责 #### API Layer (接口层) - **Controllers**: 处理 HTTP 请求,路由分发 - **DTOs**: 定义请求/响应数据传输对象 - **Guards**: 身份验证、权限控制 (暂未实现) - **依赖方向**: → Application Layer #### Application Layer (应用层) - **Command Handlers**: 处理写操作命令 (Create, Update, Delete) - **Query Handlers**: 处理读操作查询 (Get, List, Find) - **Event Handlers**: 处理领域事件 (暂未实现) - **依赖方向**: → Domain Layer #### Domain Layer (领域层) - **Entities**: 聚合根,包含业务逻辑 (`AppVersion`) - **Value Objects**: 不可变值对象 (`VersionCode`, `VersionName`, `FileSize`, `FileSha256`) - **Domain Services**: 跨实体的业务逻辑 (`VersionCheckService`) - **Repository Interfaces**: 持久化端口定义 - **依赖方向**: 无外部依赖 (核心层) #### Infrastructure Layer (基础设施层) - **Repositories**: Repository 接口的 Prisma 实现 - **Mappers**: 领域对象 ↔ 持久化对象转换 - **Prisma Client**: 数据库 ORM - **依赖方向**: → Domain Layer (依赖倒置) --- ## 3. 领域设计 ### 3.1 领域模型 #### 聚合根: AppVersion ```typescript class AppVersion { // 标识 private readonly _id: string; private readonly _platform: Platform; // 版本信息 private readonly _versionCode: VersionCode; private readonly _versionName: VersionName; private readonly _buildNumber: string; // 文件信息 private _downloadUrl: string; private readonly _fileSize: FileSize; private readonly _fileSha256: FileSha256; // 更新信息 private _changelog: string; private _isEnabled: boolean; private _isForceUpdate: boolean; // 审计信息 private readonly _createdBy: string; private readonly _createdAt: Date; private _updatedBy: string; private _updatedAt: Date; } ``` **业务不变式**: 1. `versionCode` 必须是正整数 2. `versionName` 必须符合语义化版本格式 (x.y.z) 3. `fileSize` 必须大于 0 4. `fileSha256` 必须是有效的 64 位十六进制字符串 5. 同一平台同一版本号的版本只能有一个启用 #### 值对象 **VersionCode (版本号)** ```typescript class VersionCode { constructor(private readonly value: number) { if (!Number.isInteger(value) || value < 1) { throw new DomainException('Version code must be a positive integer'); } } isGreaterThan(other: VersionCode): boolean isLessThan(other: VersionCode): boolean equals(other: VersionCode): boolean } ``` **VersionName (版本名称)** ```typescript class VersionName { private readonly SEMVER_REGEX = /^\d+\.\d+\.\d+$/; constructor(private readonly value: string) { if (!this.SEMVER_REGEX.test(value)) { throw new DomainException('Invalid semantic version format'); } } get major(): number get minor(): number get patch(): number } ``` **FileSize (文件大小)** ```typescript class FileSize { constructor(private readonly bytes: bigint) { if (bytes < 0n) { throw new DomainException('File size cannot be negative'); } } toHumanReadable(): string // "1.50 MB" toMegabytes(): string // "1.50" } ``` **FileSha256 (SHA256 哈希)** ```typescript class FileSha256 { private readonly SHA256_REGEX = /^[a-f0-9]{64}$/; constructor(private readonly hash: string) { if (!this.SHA256_REGEX.test(hash.toLowerCase())) { throw new DomainException('Invalid SHA256 hash format'); } } } ``` ### 3.2 领域服务 **VersionCheckService** ```typescript class VersionCheckService { async checkForUpdate( platform: Platform, currentVersionCode: number, ): Promise { // 1. 查找最新启用的版本 const latestVersion = await this.repository.findLatestEnabledVersion(platform); // 2. 比较版本号 if (!latestVersion || latestVersion.versionCode.value <= currentVersionCode) { return VersionCheckResult.noUpdate(); } // 3. 返回更新信息 return VersionCheckResult.hasUpdate({ latestVersion: latestVersion.versionName.value, downloadUrl: latestVersion.downloadUrl, isForceUpdate: latestVersion.isForceUpdate, changelog: latestVersion.changelog, }); } } ``` ### 3.3 业务规则 | 规则 | 实现位置 | 验证时机 | |-----|---------|---------| | 版本号必须唯一 | `AppVersionRepository` | 创建版本时 | | 禁用版本不能强制更新 | `AppVersion.disable()` | 禁用操作时 | | 文件大小必须 > 0 | `FileSize` VO | 值对象创建时 | | SHA256 必须 64 位十六进制 | `FileSha256` VO | 值对象创建时 | | 版本名称必须符合 semver | `VersionName` VO | 值对象创建时 | --- ## 4. 技术栈 ### 4.1 核心框架 | 技术 | 版本 | 用途 | |-----|------|------| | NestJS | 10.0.0 | Web 框架 | | TypeScript | 5.1.3 | 编程语言 | | Node.js | 20.x | 运行时 | | Prisma | 5.7.0 | ORM | | PostgreSQL | 16 | 数据库 | ### 4.2 开发工具 | 工具 | 版本 | 用途 | |-----|------|------| | Jest | 29.5.0 | 测试框架 | | ts-jest | 29.1.0 | TypeScript + Jest | | Supertest | 6.3.3 | HTTP 测试 | | ESLint | 8.42.0 | 代码检查 | | Prettier | 3.0.0 | 代码格式化 | ### 4.3 部署工具 | 工具 | 用途 | |-----|------| | Docker | 容器化 | | Docker Compose | 多容器编排 | | Makefile | 自动化脚本 | --- ## 5. 目录结构 ``` admin-service/ ├── src/ │ ├── api/ # API 层 │ │ ├── controllers/ # 控制器 │ │ │ └── version.controller.ts │ │ └── dtos/ # 数据传输对象 │ │ ├── create-version.dto.ts │ │ ├── update-version.dto.ts │ │ ├── check-version.dto.ts │ │ └── version-response.dto.ts │ │ │ ├── application/ # 应用层 │ │ ├── commands/ # 命令 │ │ │ ├── create-version.command.ts │ │ │ ├── enable-version.command.ts │ │ │ └── disable-version.command.ts │ │ ├── handlers/ # 处理器 │ │ │ ├── create-version.handler.ts │ │ │ ├── enable-version.handler.ts │ │ │ └── disable-version.handler.ts │ │ └── queries/ # 查询 │ │ ├── find-version-by-id.query.ts │ │ └── find-all-versions.query.ts │ │ │ ├── domain/ # 领域层 │ │ ├── entities/ # 实体 │ │ │ └── app-version.entity.ts │ │ ├── value-objects/ # 值对象 │ │ │ ├── version-code.vo.ts │ │ │ ├── version-name.vo.ts │ │ │ ├── file-size.vo.ts │ │ │ └── file-sha256.vo.ts │ │ ├── repositories/ # 仓储接口 │ │ │ └── app-version.repository.ts │ │ ├── services/ # 领域服务 │ │ │ └── version-check.service.ts │ │ └── enums/ # 枚举 │ │ └── platform.enum.ts │ │ │ ├── infrastructure/ # 基础设施层 │ │ ├── persistence/ # 持久化 │ │ │ ├── repositories/ # 仓储实现 │ │ │ │ └── app-version.repository.impl.ts │ │ │ └── mappers/ # 映射器 │ │ │ └── app-version.mapper.ts │ │ └── prisma/ # Prisma │ │ └── prisma.service.ts │ │ │ ├── shared/ # 共享模块 │ │ ├── exceptions/ # 异常 │ │ │ ├── domain.exception.ts │ │ │ └── application.exception.ts │ │ └── utils/ # 工具 │ │ │ ├── app.module.ts # 根模块 │ └── main.ts # 入口文件 │ ├── prisma/ │ ├── schema.prisma # Prisma Schema │ └── migrations/ # 数据库迁移 │ ├── test/ # 测试 │ ├── unit/ # 单元测试 │ ├── integration/ # 集成测试 │ └── e2e/ # E2E 测试 │ ├── database/ # 数据库初始化 │ ├── init.sql # 初始化脚本 │ └── README.md │ ├── docs/ # 文档 │ ├── ARCHITECTURE.md # 本文档 │ ├── API.md # API 文档 │ ├── DEVELOPMENT.md # 开发指南 │ ├── TESTING.md # 测试文档 │ └── DEPLOYMENT.md # 部署文档 │ ├── scripts/ # 脚本 │ ├── test-in-wsl.sh │ ├── run-wsl-tests.ps1 │ └── test-with-docker-db.sh │ ├── docker-compose.yml # Docker Compose ├── Dockerfile # Dockerfile ├── Makefile # Make 命令 ├── package.json # NPM 配置 ├── tsconfig.json # TypeScript 配置 └── README.md # 项目说明 ``` --- ## 6. 数据流 ### 6.1 创建版本流程 ``` Client Request (POST /api/v1/version) │ ▼ ┌─────────────────────────────────────┐ │ VersionController.createVersion() │ ← API Layer └─────────────────────────────────────┘ │ CreateVersionDto ▼ ┌─────────────────────────────────────┐ │ CreateVersionHandler.execute() │ ← Application Layer │ 1. Create Command │ │ 2. Validate Business Rules │ │ 3. Call Repository │ └─────────────────────────────────────┘ │ CreateVersionCommand ▼ ┌─────────────────────────────────────┐ │ AppVersion.create() │ ← Domain Layer │ 1. Create Value Objects │ │ - VersionCode │ │ - VersionName │ │ - FileSize │ │ - FileSha256 │ │ 2. Create Entity │ │ 3. Apply Business Rules │ └─────────────────────────────────────┘ │ AppVersion Entity ▼ ┌─────────────────────────────────────┐ │ AppVersionRepositoryImpl.save() │ ← Infrastructure Layer │ 1. Map Entity → Prisma Model │ │ 2. Save to Database │ │ 3. Return Persisted Entity │ └─────────────────────────────────────┘ │ ▼ PostgreSQL ``` ### 6.2 检查更新流程 ``` Mobile Client (GET /api/v1/version/check?platform=android&versionCode=100) │ ▼ ┌─────────────────────────────────────┐ │ VersionController.checkForUpdate() │ ← API Layer └─────────────────────────────────────┘ │ CheckVersionDto ▼ ┌─────────────────────────────────────┐ │ VersionCheckService.checkForUpdate()│ ← Domain Service │ 1. Query Latest Enabled Version │ │ 2. Compare Version Codes │ │ 3. Build Update Result │ └─────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────┐ │ AppVersionRepository │ ← Repository │ .findLatestEnabledVersion() │ └─────────────────────────────────────┘ │ ▼ PostgreSQL │ ▼ ┌─────────────────────────────────────┐ │ VersionCheckResult │ ← Response │ - hasUpdate: boolean │ │ - latestVersion: string │ │ - downloadUrl: string │ │ - isForceUpdate: boolean │ │ - changelog: string │ └─────────────────────────────────────┘ ``` ### 6.3 依赖方向 ``` API Layer ↓ depends on Application Layer ↓ depends on Domain Layer (Core) ↑ implemented by Infrastructure Layer ``` **核心原则**: - Domain Layer 不依赖任何外部层 - Infrastructure Layer 通过接口依赖 Domain Layer (依赖倒置原则) - Application Layer 协调 Domain 和 Infrastructure - API Layer 仅依赖 Application Layer --- ## 7. 设计原则 ### 7.1 SOLID 原则应用 | 原则 | 应用实例 | |-----|---------| | **S** (单一职责) | 每个值对象只负责一个验证逻辑
每个 Handler 只处理一个命令/查询 | | **O** (开闭原则) | 新增平台类型无需修改现有代码
通过 enum 扩展实现 | | **L** (里氏替换) | Repository 接口可被不同实现替换
(Prisma, TypeORM, InMemory) | | **I** (接口隔离) | Repository 接口仅定义必要方法
不强制实现不需要的功能 | | **D** (依赖倒置) | Domain Layer 定义 Repository 接口
Infrastructure Layer 实现接口 | ### 7.2 DDD 战术模式 | 模式 | 应用 | |-----|------| | **Entity** | `AppVersion` 聚合根 | | **Value Object** | `VersionCode`, `VersionName`, `FileSize`, `FileSha256` | | **Aggregate** | `AppVersion` 作为聚合边界 | | **Repository** | `AppVersionRepository` 接口及实现 | | **Domain Service** | `VersionCheckService` 处理跨实体逻辑 | | **Factory Method** | `AppVersion.create()` 静态工厂方法 | --- ## 8. 扩展性设计 ### 8.1 新增平台支持 当需要支持新平台(如 HarmonyOS)时: 1. **枚举扩展** (`domain/enums/platform.enum.ts`): ```typescript export enum Platform { ANDROID = 'android', IOS = 'ios', HARMONYOS = 'harmonyos', // 新增 } ``` 2. **无需修改**: - Entity 逻辑 - Repository 实现 - Controller/Handler ### 8.2 新增版本检查策略 当需要支持灰度发布、A/B 测试时: 1. **新增领域服务**: ```typescript class GrayReleaseService { async checkEligibility(userId: string, version: AppVersion): Promise } ``` 2. **修改 VersionCheckService**: ```typescript async checkForUpdate( platform: Platform, currentVersionCode: number, userId?: string, // 新增参数 ): Promise ``` --- ## 9. 性能考量 ### 9.1 数据库索引 ```sql -- 平台 + 版本号唯一索引 CREATE UNIQUE INDEX idx_platform_versioncode ON "AppVersion" (platform, "versionCode"); -- 启用状态 + 平台 + 版本号索引(查询最新版本) CREATE INDEX idx_enabled_platform_versioncode ON "AppVersion" ("isEnabled", platform, "versionCode" DESC); ``` ### 9.2 缓存策略 **建议实现** (当前未实现): ```typescript @Injectable() export class CachedVersionCheckService { constructor( private readonly versionCheckService: VersionCheckService, private readonly cacheManager: Cache, ) {} async checkForUpdate(platform: Platform, versionCode: number) { const cacheKey = `version:${platform}:${versionCode}`; const cached = await this.cacheManager.get(cacheKey); if (cached) return cached; const result = await this.versionCheckService.checkForUpdate(platform, versionCode); await this.cacheManager.set(cacheKey, result, { ttl: 300 }); // 5分钟 return result; } } ``` --- ## 10. 安全性 ### 10.1 文件校验 - **SHA256 验证**: 确保下载文件未被篡改 - **下载 URL**: 建议使用 HTTPS + CDN - **文件大小**: 防止异常大文件攻击 ### 10.2 API 安全 (待实现) ```typescript @Controller('api/v1/version') @UseGuards(JwtAuthGuard) // 管理端需要认证 export class VersionController { @Post() @UseGuards(RolesGuard) @Roles('admin', 'developer') // 仅管理员和开发者可创建版本 async createVersion(@Body() dto: CreateVersionDto) {} @Get('check') // 公开端点,无需认证 async checkForUpdate(@Query() dto: CheckVersionDto) {} } ``` --- ## 11. 监控和日志 ### 11.1 关键指标 | 指标 | 说明 | 监控方式 | |-----|------|---------| | 版本检查 QPS | 每秒查询次数 | Prometheus + Grafana | | 创建版本成功率 | 创建操作成功/失败比例 | Application Logs | | 数据库查询延迟 | 查询耗时 | Prisma Metrics | | 强制更新触发率 | 强制更新用户占比 | Business Metrics | ### 11.2 日志记录 ```typescript @Injectable() export class CreateVersionHandler { private readonly logger = new Logger(CreateVersionHandler.name); async execute(command: CreateVersionCommand): Promise { this.logger.log(`Creating version: ${command.platform} v${command.versionName}`); try { const version = await this.repository.save(appVersion); this.logger.log(`Version created successfully: ${version.id}`); return version; } catch (error) { this.logger.error(`Failed to create version: ${error.message}`, error.stack); throw error; } } } ``` --- ## 12. 未来改进 ### 12.1 短期 (1-3 个月) - [ ] 实现 JWT 认证和 RBAC 权限控制 - [ ] 添加版本删除功能(软删除) - [ ] 实现分页查询 - [ ] 添加 Redis 缓存层 ### 12.2 中期 (3-6 个月) - [ ] 实现灰度发布功能 - [ ] 添加版本回滚机制 - [ ] 实现版本发布审批流程 - [ ] 集成 CDN 文件上传 ### 12.3 长期 (6-12 个月) - [ ] 实现多渠道版本管理(Google Play, App Store, 自建服务器) - [ ] 添加 A/B 测试支持 - [ ] 实现版本使用统计和分析 - [ ] 集成 Sentry 错误监控 --- **最后更新**: 2025-12-03 **版本**: 1.0.0 **维护者**: RWA Durian Team