# 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