1062 lines
22 KiB
Markdown
1062 lines
22 KiB
Markdown
# Admin Service 开发指南
|
||
|
||
## 目录
|
||
|
||
- [1. 开发环境设置](#1-开发环境设置)
|
||
- [2. 项目初始化](#2-项目初始化)
|
||
- [3. 开发流程](#3-开发流程)
|
||
- [4. 代码规范](#4-代码规范)
|
||
- [5. 调试技巧](#5-调试技巧)
|
||
- [6. 常见开发任务](#6-常见开发任务)
|
||
|
||
---
|
||
|
||
## 1. 开发环境设置
|
||
|
||
### 1.1 系统要求
|
||
|
||
| 工具 | 版本要求 | 说明 |
|
||
|-----|---------|------|
|
||
| **Node.js** | >= 20.x | 推荐使用 LTS 版本 |
|
||
| **npm** | >= 10.x | 或使用 yarn/pnpm |
|
||
| **PostgreSQL** | >= 16.x | 本地开发或 Docker |
|
||
| **Docker** | >= 24.x | (可选) 容器化开发 |
|
||
| **Git** | >= 2.x | 版本控制 |
|
||
| **VSCode** | 最新版 | 推荐 IDE |
|
||
|
||
### 1.2 VSCode 推荐插件
|
||
|
||
```json
|
||
{
|
||
"recommendations": [
|
||
"dbaeumer.vscode-eslint", // ESLint
|
||
"esbenp.prettier-vscode", // Prettier
|
||
"prisma.prisma", // Prisma
|
||
"firsttris.vscode-jest-runner", // Jest Runner
|
||
"orta.vscode-jest", // Jest
|
||
"ms-vscode.vscode-typescript-next", // TypeScript
|
||
"usernamehw.errorlens", // Error Lens
|
||
"eamodio.gitlens" // GitLens
|
||
]
|
||
}
|
||
```
|
||
|
||
保存到 `.vscode/extensions.json`
|
||
|
||
### 1.3 VSCode 工作区设置
|
||
|
||
```json
|
||
{
|
||
"editor.formatOnSave": true,
|
||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||
"editor.codeActionsOnSave": {
|
||
"source.fixAll.eslint": true
|
||
},
|
||
"typescript.preferences.importModuleSpecifier": "relative",
|
||
"jest.autoRun": "off",
|
||
"[prisma]": {
|
||
"editor.defaultFormatter": "Prisma.prisma"
|
||
}
|
||
}
|
||
```
|
||
|
||
保存到 `.vscode/settings.json`
|
||
|
||
---
|
||
|
||
## 2. 项目初始化
|
||
|
||
### 2.1 克隆项目
|
||
|
||
```bash
|
||
git clone https://github.com/your-org/rwa-durian.git
|
||
cd rwa-durian/backend/services/admin-service
|
||
```
|
||
|
||
### 2.2 安装依赖
|
||
|
||
```bash
|
||
# 使用 npm
|
||
npm install
|
||
|
||
# 或使用 yarn
|
||
yarn install
|
||
|
||
# 或使用 pnpm
|
||
pnpm install
|
||
```
|
||
|
||
### 2.3 环境配置
|
||
|
||
创建 `.env.development` 文件:
|
||
|
||
```env
|
||
# 应用配置
|
||
NODE_ENV=development
|
||
APP_PORT=3005
|
||
API_PREFIX=api/v1
|
||
|
||
# 数据库配置
|
||
DATABASE_URL=postgresql://postgres:password@localhost:5432/admin_service_dev?schema=public
|
||
|
||
# 日志配置
|
||
LOG_LEVEL=debug
|
||
|
||
# CORS 配置
|
||
CORS_ORIGIN=http://localhost:3000,http://localhost:3001
|
||
```
|
||
|
||
**注意**: 不要提交 `.env.*` 文件到 Git!已添加到 `.gitignore`。
|
||
|
||
### 2.4 数据库初始化
|
||
|
||
#### 方案 1: 本地 PostgreSQL
|
||
|
||
```bash
|
||
# 1. 创建数据库
|
||
psql -U postgres -c "CREATE DATABASE admin_service_dev;"
|
||
|
||
# 2. 运行迁移
|
||
npm run prisma:migrate:dev
|
||
|
||
# 3. 生成 Prisma Client
|
||
npm run prisma:generate
|
||
```
|
||
|
||
#### 方案 2: Docker PostgreSQL
|
||
|
||
```bash
|
||
# 1. 启动数据库容器
|
||
docker run -d \
|
||
--name admin-dev-db \
|
||
-e POSTGRES_USER=postgres \
|
||
-e POSTGRES_PASSWORD=password \
|
||
-e POSTGRES_DB=admin_service_dev \
|
||
-p 5432:5432 \
|
||
postgres:16-alpine
|
||
|
||
# 2. 运行迁移
|
||
npm run prisma:migrate:dev
|
||
|
||
# 3. 生成 Prisma Client
|
||
npm run prisma:generate
|
||
```
|
||
|
||
### 2.5 验证环境
|
||
|
||
```bash
|
||
# 检查数据库连接
|
||
npm run prisma:studio
|
||
|
||
# 运行测试
|
||
npm run test:unit
|
||
|
||
# 启动开发服务器
|
||
npm run start:dev
|
||
```
|
||
|
||
访问 `http://localhost:3005/api/v1/health` 应返回:
|
||
```json
|
||
{"status": "ok"}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 开发流程
|
||
|
||
### 3.1 Git 工作流
|
||
|
||
#### 分支策略
|
||
|
||
```
|
||
main (生产)
|
||
↑
|
||
develop (开发)
|
||
↑
|
||
feature/xxx (功能分支)
|
||
hotfix/xxx (紧急修复)
|
||
```
|
||
|
||
#### 创建功能分支
|
||
|
||
```bash
|
||
# 从 develop 创建功能分支
|
||
git checkout develop
|
||
git pull origin develop
|
||
git checkout -b feature/add-version-delete
|
||
|
||
# 开发...
|
||
|
||
# 提交
|
||
git add .
|
||
git commit -m "feat(version): add delete version functionality"
|
||
|
||
# 推送
|
||
git push origin feature/add-version-delete
|
||
|
||
# 创建 Pull Request
|
||
```
|
||
|
||
#### Commit Message 规范
|
||
|
||
遵循 [Conventional Commits](https://www.conventionalcommits.org/):
|
||
|
||
```
|
||
<type>(<scope>): <subject>
|
||
|
||
<body>
|
||
|
||
<footer>
|
||
```
|
||
|
||
**Type 类型**:
|
||
- `feat`: 新功能
|
||
- `fix`: Bug 修复
|
||
- `docs`: 文档更新
|
||
- `style`: 代码格式 (不影响功能)
|
||
- `refactor`: 重构
|
||
- `test`: 测试相关
|
||
- `chore`: 构建/工具配置
|
||
|
||
**示例**:
|
||
```bash
|
||
git commit -m "feat(version): add soft delete for versions"
|
||
git commit -m "fix(repository): handle duplicate version code error"
|
||
git commit -m "docs(api): update version check endpoint documentation"
|
||
git commit -m "test(handler): add unit tests for EnableVersionHandler"
|
||
```
|
||
|
||
### 3.2 开发迭代流程
|
||
|
||
#### 1. 需求分析
|
||
|
||
**示例**: 添加版本删除功能
|
||
|
||
- 功能需求: 支持软删除版本记录
|
||
- 业务规则:
|
||
- 只能删除未启用的版本
|
||
- 删除后不可恢复
|
||
- 记录删除人和删除时间
|
||
|
||
#### 2. 设计方案
|
||
|
||
**领域层修改**:
|
||
```typescript
|
||
// domain/entities/app-version.entity.ts
|
||
class AppVersion {
|
||
delete(deletedBy: string): void {
|
||
if (this._isEnabled) {
|
||
throw new DomainException('Cannot delete an enabled version');
|
||
}
|
||
this._deletedBy = deletedBy;
|
||
this._deletedAt = new Date();
|
||
}
|
||
|
||
get isDeleted(): boolean {
|
||
return this._deletedAt !== null;
|
||
}
|
||
}
|
||
```
|
||
|
||
**Prisma Schema 修改**:
|
||
```prisma
|
||
model AppVersion {
|
||
// ... 现有字段
|
||
deletedBy String?
|
||
deletedAt DateTime?
|
||
}
|
||
```
|
||
|
||
#### 3. 实现功能
|
||
|
||
**步骤 1**: 更新 Prisma Schema
|
||
```bash
|
||
# prisma/schema.prisma
|
||
# 添加 deletedBy 和 deletedAt 字段
|
||
|
||
# 创建迁移
|
||
npm run prisma:migrate:dev --name add_soft_delete
|
||
```
|
||
|
||
**步骤 2**: 更新领域实体
|
||
```typescript
|
||
// src/domain/entities/app-version.entity.ts
|
||
export class AppVersion {
|
||
private _deletedBy: string | null = null;
|
||
private _deletedAt: Date | null = null;
|
||
|
||
delete(deletedBy: string): void {
|
||
if (this._isEnabled) {
|
||
throw new DomainException('Cannot delete an enabled version');
|
||
}
|
||
this._deletedBy = deletedBy;
|
||
this._deletedAt = new Date();
|
||
}
|
||
|
||
get isDeleted(): boolean {
|
||
return this._deletedAt !== null;
|
||
}
|
||
}
|
||
```
|
||
|
||
**步骤 3**: 创建命令和处理器
|
||
```typescript
|
||
// src/application/commands/delete-version.command.ts
|
||
export class DeleteVersionCommand {
|
||
constructor(
|
||
public readonly versionId: string,
|
||
public readonly deletedBy: string,
|
||
) {}
|
||
}
|
||
|
||
// src/application/handlers/delete-version.handler.ts
|
||
@Injectable()
|
||
export class DeleteVersionHandler {
|
||
constructor(
|
||
@Inject(APP_VERSION_REPOSITORY)
|
||
private readonly repository: AppVersionRepository,
|
||
) {}
|
||
|
||
async execute(command: DeleteVersionCommand): Promise<void> {
|
||
const version = await this.repository.findById(command.versionId);
|
||
if (!version) {
|
||
throw new ApplicationException('Version not found');
|
||
}
|
||
|
||
version.delete(command.deletedBy);
|
||
await this.repository.save(version);
|
||
}
|
||
}
|
||
```
|
||
|
||
**步骤 4**: 添加控制器端点
|
||
```typescript
|
||
// src/api/controllers/version.controller.ts
|
||
@Delete(':id')
|
||
async deleteVersion(
|
||
@Param('id') id: string,
|
||
@Body() dto: DeleteVersionDto,
|
||
): Promise<void> {
|
||
const command = new DeleteVersionCommand(id, dto.deletedBy);
|
||
await this.deleteVersionHandler.execute(command);
|
||
}
|
||
```
|
||
|
||
**步骤 5**: 添加 DTO
|
||
```typescript
|
||
// src/api/dtos/delete-version.dto.ts
|
||
export class DeleteVersionDto {
|
||
@IsNotEmpty()
|
||
@IsString()
|
||
deletedBy: string;
|
||
}
|
||
```
|
||
|
||
#### 4. 编写测试
|
||
|
||
**单元测试**:
|
||
```typescript
|
||
// test/unit/domain/entities/app-version.entity.spec.ts
|
||
describe('delete', () => {
|
||
it('should delete version when disabled', () => {
|
||
const version = AppVersion.create({...});
|
||
version.disable('admin');
|
||
|
||
version.delete('admin');
|
||
|
||
expect(version.isDeleted).toBe(true);
|
||
});
|
||
|
||
it('should throw error when deleting enabled version', () => {
|
||
const version = AppVersion.create({...});
|
||
|
||
expect(() => version.delete('admin')).toThrow(DomainException);
|
||
});
|
||
});
|
||
```
|
||
|
||
**集成测试**:
|
||
```typescript
|
||
// test/integration/handlers/delete-version.handler.spec.ts
|
||
describe('DeleteVersionHandler', () => {
|
||
it('should delete version successfully', async () => {
|
||
const version = await createTestVersion({ isEnabled: false });
|
||
const command = new DeleteVersionCommand(version.id, 'admin');
|
||
|
||
await handler.execute(command);
|
||
|
||
const deleted = await repository.findById(version.id);
|
||
expect(deleted.isDeleted).toBe(true);
|
||
});
|
||
});
|
||
```
|
||
|
||
**E2E 测试**:
|
||
```typescript
|
||
// test/e2e/version.controller.spec.ts
|
||
describe('/version/:id (DELETE)', () => {
|
||
it('should delete version successfully', () => {
|
||
return request(app.getHttpServer())
|
||
.delete(`/api/v1/version/${versionId}`)
|
||
.send({ deletedBy: 'admin' })
|
||
.expect(204);
|
||
});
|
||
});
|
||
```
|
||
|
||
#### 5. 运行测试
|
||
|
||
```bash
|
||
# 单元测试
|
||
npm run test:unit
|
||
|
||
# 集成测试 (需要数据库)
|
||
DATABASE_URL="postgresql://postgres:password@localhost:5432/admin_service_test" \
|
||
npm run test:integration
|
||
|
||
# E2E 测试 (需要数据库)
|
||
DATABASE_URL="postgresql://postgres:password@localhost:5432/admin_service_test" \
|
||
npm run test:e2e
|
||
|
||
# 全部测试 + 覆盖率
|
||
npm run test:cov
|
||
```
|
||
|
||
#### 6. 本地验证
|
||
|
||
```bash
|
||
# 启动开发服务器
|
||
npm run start:dev
|
||
|
||
# 测试 API
|
||
curl -X DELETE http://localhost:3005/api/v1/version/550e8400-e29b-41d4-a716-446655440000 \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"deletedBy": "admin"}'
|
||
```
|
||
|
||
#### 7. 代码审查
|
||
|
||
```bash
|
||
# 格式化代码
|
||
npm run format
|
||
|
||
# 检查代码规范
|
||
npm run lint
|
||
|
||
# 修复 lint 问题
|
||
npm run lint:fix
|
||
```
|
||
|
||
#### 8. 提交代码
|
||
|
||
```bash
|
||
git add .
|
||
git commit -m "feat(version): add soft delete functionality
|
||
|
||
- Add deletedBy and deletedAt fields to AppVersion entity
|
||
- Implement delete business logic with validation
|
||
- Add DELETE /api/v1/version/:id endpoint
|
||
- Add comprehensive unit, integration, and E2E tests
|
||
- Update Prisma schema with migration
|
||
|
||
Closes #123"
|
||
|
||
git push origin feature/add-version-delete
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 代码规范
|
||
|
||
### 4.1 TypeScript 规范
|
||
|
||
#### 类型定义
|
||
|
||
```typescript
|
||
// ✅ 推荐: 显式类型注解
|
||
function calculateFileSize(bytes: bigint): string {
|
||
return `${bytes} bytes`;
|
||
}
|
||
|
||
// ❌ 避免: 使用 any
|
||
function process(data: any) { // 不推荐
|
||
return data.value;
|
||
}
|
||
|
||
// ✅ 推荐: 使用具体类型
|
||
interface ProcessData {
|
||
value: string;
|
||
}
|
||
function process(data: ProcessData) {
|
||
return data.value;
|
||
}
|
||
```
|
||
|
||
#### 命名规范
|
||
|
||
```typescript
|
||
// ✅ 类名: PascalCase
|
||
class AppVersion {}
|
||
class VersionCheckService {}
|
||
|
||
// ✅ 接口名: PascalCase, 以 I 开头 (可选)
|
||
interface AppVersionRepository {}
|
||
interface IAppVersionRepository {} // 也可以
|
||
|
||
// ✅ 方法/变量: camelCase
|
||
const versionCode = 100;
|
||
function findLatestVersion() {}
|
||
|
||
// ✅ 常量: SCREAMING_SNAKE_CASE
|
||
const MAX_FILE_SIZE = 1024 * 1024 * 100; // 100MB
|
||
const DEFAULT_PAGE_SIZE = 10;
|
||
|
||
// ✅ 私有属性: 下划线前缀
|
||
class AppVersion {
|
||
private _versionCode: VersionCode;
|
||
private _isEnabled: boolean;
|
||
}
|
||
```
|
||
|
||
#### 导入顺序
|
||
|
||
```typescript
|
||
// 1. Node.js 内置模块
|
||
import * as path from 'path';
|
||
import * as fs from 'fs';
|
||
|
||
// 2. 外部依赖
|
||
import { Injectable } from '@nestjs/common';
|
||
import { PrismaService } from '@/infrastructure/prisma/prisma.service';
|
||
|
||
// 3. 内部模块 (绝对路径)
|
||
import { AppVersion } from '@/domain/entities/app-version.entity';
|
||
import { VersionCode } from '@/domain/value-objects/version-code.vo';
|
||
|
||
// 4. 相对路径导入
|
||
import { CreateVersionDto } from './dtos/create-version.dto';
|
||
import { VersionController } from './version.controller';
|
||
```
|
||
|
||
### 4.2 DDD 代码规范
|
||
|
||
#### 值对象 (Value Object)
|
||
|
||
```typescript
|
||
// ✅ 推荐: 不可变、验证逻辑封装
|
||
export class VersionCode {
|
||
private readonly value: number;
|
||
|
||
constructor(value: number) {
|
||
if (!Number.isInteger(value) || value < 1) {
|
||
throw new DomainException('Version code must be a positive integer');
|
||
}
|
||
this.value = value;
|
||
}
|
||
|
||
static create(value: number): VersionCode {
|
||
return new VersionCode(value);
|
||
}
|
||
|
||
getValue(): number {
|
||
return this.value;
|
||
}
|
||
|
||
equals(other: VersionCode): boolean {
|
||
return this.value === other.value;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 实体 (Entity)
|
||
|
||
```typescript
|
||
// ✅ 推荐: 封装业务逻辑、私有属性、工厂方法
|
||
export class AppVersion {
|
||
private readonly _id: string;
|
||
private _isEnabled: boolean;
|
||
|
||
private constructor(props: AppVersionProps) {
|
||
this._id = props.id;
|
||
this._isEnabled = props.isEnabled;
|
||
}
|
||
|
||
// 工厂方法
|
||
static create(props: CreateAppVersionProps): AppVersion {
|
||
// 验证逻辑
|
||
return new AppVersion({...});
|
||
}
|
||
|
||
// 业务方法
|
||
enable(updatedBy: string): void {
|
||
this._isEnabled = true;
|
||
this._updatedBy = updatedBy;
|
||
this._updatedAt = new Date();
|
||
}
|
||
|
||
// 查询方法
|
||
isEnabledVersion(): boolean {
|
||
return this._isEnabled;
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Repository 接口
|
||
|
||
```typescript
|
||
// ✅ 推荐: 领域层定义接口,基础设施层实现
|
||
// domain/repositories/app-version.repository.ts
|
||
export interface AppVersionRepository {
|
||
save(version: AppVersion): Promise<AppVersion>;
|
||
findById(id: string): Promise<AppVersion | null>;
|
||
findLatestEnabledVersion(platform: Platform): Promise<AppVersion | null>;
|
||
}
|
||
|
||
// infrastructure/persistence/repositories/app-version.repository.impl.ts
|
||
@Injectable()
|
||
export class AppVersionRepositoryImpl implements AppVersionRepository {
|
||
constructor(private readonly prisma: PrismaService) {}
|
||
|
||
async save(version: AppVersion): Promise<AppVersion> {
|
||
// 实现逻辑
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4.3 NestJS 规范
|
||
|
||
#### 依赖注入
|
||
|
||
```typescript
|
||
// ✅ 推荐: 构造函数注入
|
||
@Injectable()
|
||
export class VersionController {
|
||
constructor(
|
||
private readonly createVersionHandler: CreateVersionHandler,
|
||
private readonly enableVersionHandler: EnableVersionHandler,
|
||
) {}
|
||
}
|
||
|
||
// ✅ 推荐: 使用 @Inject 注入接口
|
||
@Injectable()
|
||
export class CreateVersionHandler {
|
||
constructor(
|
||
@Inject(APP_VERSION_REPOSITORY)
|
||
private readonly repository: AppVersionRepository,
|
||
) {}
|
||
}
|
||
```
|
||
|
||
#### 模块组织
|
||
|
||
```typescript
|
||
// ✅ 推荐: 清晰的 Provider 定义
|
||
@Module({
|
||
imports: [PrismaModule],
|
||
controllers: [VersionController],
|
||
providers: [
|
||
// Handlers
|
||
CreateVersionHandler,
|
||
EnableVersionHandler,
|
||
DisableVersionHandler,
|
||
|
||
// Services
|
||
VersionCheckService,
|
||
|
||
// Repositories
|
||
{
|
||
provide: APP_VERSION_REPOSITORY,
|
||
useClass: AppVersionRepositoryImpl,
|
||
},
|
||
],
|
||
exports: [APP_VERSION_REPOSITORY],
|
||
})
|
||
export class VersionModule {}
|
||
```
|
||
|
||
### 4.4 Prisma 规范
|
||
|
||
#### Schema 定义
|
||
|
||
```prisma
|
||
// ✅ 推荐: 清晰的模型定义和注释
|
||
/// 应用版本表
|
||
model AppVersion {
|
||
/// 主键 (UUID)
|
||
id String @id @default(uuid())
|
||
|
||
/// 平台 (android/ios)
|
||
platform String
|
||
|
||
/// 版本号 (整数, 递增)
|
||
versionCode Int
|
||
|
||
/// 版本名称 (语义化版本)
|
||
versionName String
|
||
|
||
/// 创建时间
|
||
createdAt DateTime @default(now())
|
||
|
||
/// 更新时间
|
||
updatedAt DateTime @updatedAt
|
||
|
||
/// 平台 + 版本号唯一索引
|
||
@@unique([platform, versionCode], name: "platform_versionCode")
|
||
|
||
/// 表名
|
||
@@map("AppVersion")
|
||
}
|
||
```
|
||
|
||
#### 查询优化
|
||
|
||
```typescript
|
||
// ✅ 推荐: 使用索引字段查询
|
||
await prisma.appVersion.findFirst({
|
||
where: {
|
||
platform: 'android',
|
||
isEnabled: true,
|
||
},
|
||
orderBy: {
|
||
versionCode: 'desc',
|
||
},
|
||
});
|
||
|
||
// ✅ 推荐: 选择必要字段
|
||
await prisma.appVersion.findMany({
|
||
select: {
|
||
id: true,
|
||
versionCode: true,
|
||
versionName: true,
|
||
},
|
||
});
|
||
|
||
// ❌ 避免: N+1 查询问题
|
||
// 如果需要关联查询,使用 include
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 调试技巧
|
||
|
||
### 5.1 VSCode 调试配置
|
||
|
||
创建 `.vscode/launch.json`:
|
||
|
||
```json
|
||
{
|
||
"version": "0.2.0",
|
||
"configurations": [
|
||
{
|
||
"type": "node",
|
||
"request": "launch",
|
||
"name": "Debug NestJS",
|
||
"runtimeArgs": [
|
||
"-r",
|
||
"ts-node/register",
|
||
"-r",
|
||
"tsconfig-paths/register"
|
||
],
|
||
"args": ["${workspaceFolder}/src/main.ts"],
|
||
"envFile": "${workspaceFolder}/.env.development",
|
||
"console": "integratedTerminal",
|
||
"internalConsoleOptions": "neverOpen",
|
||
"skipFiles": ["<node_internals>/**"]
|
||
},
|
||
{
|
||
"type": "node",
|
||
"request": "launch",
|
||
"name": "Jest Current File",
|
||
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
||
"args": [
|
||
"${fileBasenameNoExtension}",
|
||
"--runInBand",
|
||
"--no-cache",
|
||
"--watchAll=false"
|
||
],
|
||
"console": "integratedTerminal",
|
||
"internalConsoleOptions": "neverOpen",
|
||
"windows": {
|
||
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
|
||
}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### 5.2 日志调试
|
||
|
||
```typescript
|
||
// 使用 NestJS Logger
|
||
import { Logger } from '@nestjs/common';
|
||
|
||
@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}`);
|
||
this.logger.debug(`Command details: ${JSON.stringify(command)}`);
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 5.3 数据库调试
|
||
|
||
```bash
|
||
# Prisma Studio - 可视化数据库工具
|
||
npm run prisma:studio
|
||
|
||
# 查看生成的 SQL
|
||
DATABASE_URL="..." npx prisma migrate dev --create-only
|
||
|
||
# 直接连接数据库
|
||
psql -U postgres -d admin_service_dev
|
||
|
||
# 查看迁移状态
|
||
npx prisma migrate status
|
||
```
|
||
|
||
### 5.4 HTTP 请求调试
|
||
|
||
#### 使用 REST Client (VSCode 插件)
|
||
|
||
创建 `test.http`:
|
||
|
||
```http
|
||
### 创建版本
|
||
POST http://localhost:3005/api/v1/version
|
||
Content-Type: application/json
|
||
|
||
{
|
||
"platform": "android",
|
||
"versionCode": 100,
|
||
"versionName": "1.0.0",
|
||
"buildNumber": "1",
|
||
"downloadUrl": "https://cdn.example.com/app-v1.0.0.apk",
|
||
"fileSize": 52428800,
|
||
"fileSha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||
"changelog": "Initial release",
|
||
"isEnabled": true,
|
||
"isForceUpdate": false,
|
||
"createdBy": "admin"
|
||
}
|
||
|
||
### 检查更新
|
||
GET http://localhost:3005/api/v1/version/check?platform=android&versionCode=99
|
||
```
|
||
|
||
---
|
||
|
||
## 6. 常见开发任务
|
||
|
||
### 6.1 添加新的值对象
|
||
|
||
```bash
|
||
# 1. 创建值对象文件
|
||
touch src/domain/value-objects/download-url.vo.ts
|
||
|
||
# 2. 实现值对象
|
||
# 3. 添加单元测试
|
||
touch test/unit/domain/value-objects/download-url.vo.spec.ts
|
||
|
||
# 4. 运行测试
|
||
npm run test:unit -- download-url.vo.spec
|
||
```
|
||
|
||
### 6.2 添加新的 API 端点
|
||
|
||
```bash
|
||
# 1. 创建 DTO
|
||
# src/api/dtos/update-version.dto.ts
|
||
|
||
# 2. 创建命令
|
||
# src/application/commands/update-version.command.ts
|
||
|
||
# 3. 创建处理器
|
||
# src/application/handlers/update-version.handler.ts
|
||
|
||
# 4. 添加控制器方法
|
||
# src/api/controllers/version.controller.ts
|
||
|
||
# 5. 添加测试
|
||
# test/e2e/version.controller.spec.ts
|
||
|
||
# 6. 验证
|
||
npm run start:dev
|
||
curl -X PATCH http://localhost:3005/api/v1/version/{id} -d '{...}'
|
||
```
|
||
|
||
### 6.3 修改数据库 Schema
|
||
|
||
```bash
|
||
# 1. 修改 prisma/schema.prisma
|
||
# 2. 创建迁移
|
||
npm run prisma:migrate:dev --name add_new_field
|
||
|
||
# 3. 生成 Prisma Client
|
||
npm run prisma:generate
|
||
|
||
# 4. 更新领域实体
|
||
# src/domain/entities/app-version.entity.ts
|
||
|
||
# 5. 更新 Mapper
|
||
# src/infrastructure/persistence/mappers/app-version.mapper.ts
|
||
|
||
# 6. 运行测试验证
|
||
npm run test
|
||
```
|
||
|
||
### 6.4 性能优化
|
||
|
||
```bash
|
||
# 1. 分析慢查询
|
||
# 启用 Prisma 查询日志
|
||
# prisma/schema.prisma
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
}
|
||
|
||
generator client {
|
||
provider = "prisma-client-js"
|
||
log = ["query", "info", "warn", "error"]
|
||
}
|
||
|
||
# 2. 添加数据库索引
|
||
# prisma/schema.prisma
|
||
model AppVersion {
|
||
// ...
|
||
@@index([platform, isEnabled, versionCode(sort: Desc)])
|
||
}
|
||
|
||
# 3. 创建迁移
|
||
npm run prisma:migrate:dev --name add_performance_index
|
||
```
|
||
|
||
### 6.5 添加缓存层
|
||
|
||
```typescript
|
||
// 1. 安装依赖
|
||
npm install @nestjs/cache-manager cache-manager
|
||
|
||
// 2. 配置缓存模块
|
||
// src/app.module.ts
|
||
import { CacheModule } from '@nestjs/cache-manager';
|
||
|
||
@Module({
|
||
imports: [
|
||
CacheModule.register({
|
||
ttl: 300, // 5 分钟
|
||
max: 100, // 最多缓存 100 项
|
||
}),
|
||
],
|
||
})
|
||
export class AppModule {}
|
||
|
||
// 3. 使用缓存
|
||
@Injectable()
|
||
export class VersionCheckService {
|
||
constructor(
|
||
@Inject(CACHE_MANAGER) private 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.performCheck(platform, versionCode);
|
||
await this.cacheManager.set(cacheKey, result);
|
||
|
||
return result;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 故障排查
|
||
|
||
### 7.1 常见问题
|
||
|
||
#### 问题 1: Prisma Client 未生成
|
||
|
||
**错误**:
|
||
```
|
||
Cannot find module '@prisma/client'
|
||
```
|
||
|
||
**解决方案**:
|
||
```bash
|
||
npm run prisma:generate
|
||
```
|
||
|
||
#### 问题 2: 数据库连接失败
|
||
|
||
**错误**:
|
||
```
|
||
Error: P1001: Can't reach database server
|
||
```
|
||
|
||
**解决方案**:
|
||
```bash
|
||
# 检查数据库是否运行
|
||
docker ps | grep postgres
|
||
|
||
# 检查 DATABASE_URL 配置
|
||
echo $DATABASE_URL
|
||
|
||
# 测试连接
|
||
psql -U postgres -h localhost -p 5432 -d admin_service_dev
|
||
```
|
||
|
||
#### 问题 3: 迁移冲突
|
||
|
||
**错误**:
|
||
```
|
||
Error: Migration failed
|
||
```
|
||
|
||
**解决方案**:
|
||
```bash
|
||
# 重置数据库 (开发环境)
|
||
npm run prisma:migrate:reset
|
||
|
||
# 或手动解决冲突
|
||
npx prisma migrate resolve --applied <migration_name>
|
||
```
|
||
|
||
### 7.2 性能分析
|
||
|
||
```bash
|
||
# 使用 Prisma Studio 查看数据
|
||
npm run prisma:studio
|
||
|
||
# 分析查询性能
|
||
DATABASE_URL="..." npx prisma db execute \
|
||
--file analyze_queries.sql
|
||
|
||
# 使用 Node.js profiler
|
||
node --inspect-brk dist/main.js
|
||
# 访问 chrome://inspect
|
||
```
|
||
|
||
---
|
||
|
||
**最后更新**: 2025-12-03
|
||
**版本**: 1.0.0
|
||
**维护者**: RWA Durian Team
|