# 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<br>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<VersionCheckResult> {
    // 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** (单一职责) | 每个值对象只负责一个验证逻辑<br>每个 Handler 只处理一个命令/查询 |
| **O** (开闭原则) | 新增平台类型无需修改现有代码<br>通过 enum 扩展实现 |
| **L** (里氏替换) | Repository 接口可被不同实现替换<br>（Prisma, TypeORM, InMemory） |
| **I** (接口隔离) | Repository 接口仅定义必要方法<br>不强制实现不需要的功能 |
| **D** (依赖倒置) | Domain Layer 定义 Repository 接口<br>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<boolean>
}
```

2. **修改 VersionCheckService**:
```typescript
async checkForUpdate(
  platform: Platform,
  currentVersionCode: number,
  userId?: string, // 新增参数
): Promise<VersionCheckResult>
```

---

## 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<AppVersion> {
    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
