# 开发指南 ## 1. 环境要求 ### 1.1 必需软件 | 软件 | 版本 | 用途 | |------|------|------| | Node.js | 20.x LTS | 运行时环境 | | npm | 10.x | 包管理器 | | Docker | 24.x+ | 容器化 | | Docker Compose | 2.x | 容器编排 | | PostgreSQL | 15.x | 数据库 | | Redis | 7.x | 缓存 | ### 1.2 推荐 IDE - **VS Code** (推荐) - 插件: ESLint, Prettier, Prisma, TypeScript - **WebStorm** - **Cursor** ## 2. 项目设置 ### 2.1 克隆项目 ```bash cd backend/services git clone reporting-service cd reporting-service ``` ### 2.2 安装依赖 ```bash npm install ``` ### 2.3 环境配置 创建 `.env.development` 文件: ```bash cp .env.example .env.development ``` 编辑 `.env.development`: ```env # Application NODE_ENV=development PORT=3000 # Database DATABASE_URL=postgresql://postgres:postgres@localhost:5432/rwadurian_reporting?schema=public # Redis REDIS_HOST=localhost REDIS_PORT=6379 # JWT JWT_SECRET=your-development-secret-key # External Services (可选) LEADERBOARD_SERVICE_URL=http://localhost:3001 PLANTING_SERVICE_URL=http://localhost:3002 ``` ### 2.4 数据库设置 启动 PostgreSQL (使用 Docker): ```bash docker run -d \ --name reporting-postgres \ -e POSTGRES_USER=postgres \ -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=rwadurian_reporting \ -p 5432:5432 \ postgres:15-alpine ``` 生成 Prisma Client: ```bash npx prisma generate ``` 同步数据库 Schema: ```bash npx prisma db push ``` (可选) 填充种子数据: ```bash npm run prisma:seed ``` ### 2.5 启动 Redis ```bash docker run -d \ --name reporting-redis \ -p 6379:6379 \ redis:7-alpine ``` ## 3. 开发工作流 ### 3.1 启动开发服务器 ```bash # 热重载模式 npm run start:dev # 调试模式 npm run start:debug ``` 服务启动后访问: - API: http://localhost:3000/api/v1 - Swagger: http://localhost:3000/api (如已启用) ### 3.2 常用命令 ```bash # 构建项目 npm run build # 运行 Lint npm run lint # 格式化代码 npm run format # 运行测试 npm test # 运行测试 (watch 模式) npm run test:watch # 生成测试覆盖率 npm run test:cov # Prisma Studio (数据库可视化) npm run prisma:studio ``` ### 3.3 使用 Makefile ```bash # 查看所有可用命令 make help # 安装依赖 make install # 构建项目 make build # 运行所有测试 make test # 运行单元测试 make test-unit # 运行集成测试 make test-integration # 运行 E2E 测试 make test-e2e # Docker 测试 make test-docker-all ``` ## 4. 代码规范 ### 4.1 目录结构规范 ``` src/ ├── api/ # API层 │ ├── controllers/ # 每个资源一个控制器文件 │ └── dto/ │ ├── request/ # 请求 DTO │ └── response/ # 响应 DTO ├── application/ # 应用层 │ ├── commands/ # 每个命令独立目录 │ │ └── xxx/ │ │ ├── xxx.command.ts │ │ └── xxx.handler.ts │ └── queries/ # 每个查询独立目录 ├── domain/ # 领域层 │ ├── aggregates/ # 每个聚合独立目录 │ │ └── xxx/ │ │ ├── index.ts │ │ ├── xxx.aggregate.ts │ │ └── xxx.spec.ts # 单元测试 │ ├── value-objects/ # 值对象 │ │ ├── xxx.vo.ts │ │ └── xxx.spec.ts │ └── repositories/ # 仅接口定义 └── infrastructure/ # 基础设施层 ``` ### 4.2 命名规范 **文件命名**: - 使用 kebab-case: `report-definition.aggregate.ts` - 后缀约定: - `.aggregate.ts` - 聚合根 - `.entity.ts` - 实体 - `.vo.ts` - 值对象 - `.service.ts` - 服务 - `.controller.ts` - 控制器 - `.dto.ts` - DTO - `.spec.ts` - 单元测试 - `.e2e-spec.ts` - E2E 测试 - `.integration.spec.ts` - 集成测试 **类命名**: - 使用 PascalCase - 聚合根: `ReportSnapshot` - 值对象: `DateRange` - 服务: `ReportingApplicationService` - 控制器: `ReportController` **接口命名**: - 以 `I` 开头: `IReportSnapshotRepository` ### 4.3 代码风格 **TypeScript 配置** (`tsconfig.json`): ```json { "compilerOptions": { "strict": true, "strictNullChecks": true, "noImplicitAny": true, "noUnusedLocals": true, "noUnusedParameters": true } } ``` **ESLint 规则** (关键规则): ```javascript { rules: { '@typescript-eslint/explicit-function-return-type': 'warn', '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/no-unused-vars': 'error', 'no-console': 'warn' } } ``` ### 4.4 领域驱动设计规范 **聚合根规则**: ```typescript // 1. 使用私有属性 + getter export class ReportSnapshot { private readonly _id: bigint; private _snapshotData: SnapshotData; get id(): bigint { return this._id; } get snapshotData(): SnapshotData { return this._snapshotData; } // 2. 使用工厂方法创建 public static create(props: CreateProps): ReportSnapshot { const snapshot = new ReportSnapshot(props); snapshot.addDomainEvent(new SnapshotCreatedEvent(snapshot)); return snapshot; } // 3. 使用 reconstitute 重建 (不触发事件) public static reconstitute(props: ReconstitutionProps): ReportSnapshot { return new ReportSnapshot(props); } // 4. 业务方法封装状态变更 public updateData(newData: SnapshotData): void { this.validateDataUpdate(newData); this._snapshotData = newData; this._updatedAt = new Date(); } // 5. 私有构造函数 private constructor(props: Props) { ... } } ``` **值对象规则**: ```typescript // 1. 不可变 export class DateRange { private readonly _startDate: Date; private readonly _endDate: Date; private constructor(startDate: Date, endDate: Date) { this._startDate = startDate; this._endDate = endDate; Object.freeze(this); // 冻结对象 } // 2. 工厂方法 + 验证 public static create(startDate: Date, endDate: Date): DateRange { if (startDate > endDate) { throw new DomainException('Invalid date range'); } return new DateRange(startDate, endDate); } // 3. 实现 equals 方法 public equals(other: DateRange): boolean { return this._startDate.getTime() === other._startDate.getTime() && this._endDate.getTime() === other._endDate.getTime(); } } ``` ## 5. Git 工作流 ### 5.1 分支策略 ``` main # 生产分支 ├── develop # 开发分支 │ ├── feature/xxx # 功能分支 │ ├── bugfix/xxx # 修复分支 │ └── refactor/xxx # 重构分支 └── release/x.x.x # 发布分支 ``` ### 5.2 提交规范 使用 Conventional Commits: ```bash # 格式 (): # 示例 feat(report): add daily report generation fix(export): resolve Excel formatting issue refactor(domain): extract value object for date range test(e2e): add report generation tests docs(api): update API documentation ``` **Type 类型**: - `feat`: 新功能 - `fix`: Bug 修复 - `refactor`: 代码重构 - `test`: 测试相关 - `docs`: 文档更新 - `chore`: 构建/配置变更 - `perf`: 性能优化 ### 5.3 PR 规范 PR 标题遵循提交规范,PR 描述需包含: ```markdown ## Summary 简要说明本次变更内容 ## Changes - 变更点 1 - 变更点 2 ## Test Plan - [ ] 单元测试通过 - [ ] 集成测试通过 - [ ] 手动测试场景 ``` ## 6. 调试技巧 ### 6.1 VS Code 调试配置 `.vscode/launch.json`: ```json { "version": "0.2.0", "configurations": [ { "type": "node", "request": "launch", "name": "Debug NestJS", "runtimeArgs": ["--nolazy", "-r", "ts-node/register"], "args": ["${workspaceFolder}/src/main.ts"], "sourceMaps": true, "envFile": "${workspaceFolder}/.env.development" }, { "type": "node", "request": "launch", "name": "Debug Jest Tests", "program": "${workspaceFolder}/node_modules/.bin/jest", "args": ["--runInBand", "--watchAll=false"], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" } ] } ``` ### 6.2 日志调试 ```typescript import { Logger } from '@nestjs/common'; @Injectable() export class ReportingApplicationService { private readonly logger = new Logger(ReportingApplicationService.name); async generateReport(dto: GenerateReportDto) { this.logger.debug(`Generating report: ${dto.reportCode}`); this.logger.log(`Report generated successfully`); this.logger.warn(`Performance warning: slow query`); this.logger.error(`Failed to generate report`, error.stack); } } ``` ### 6.3 Prisma 调试 启用查询日志: ```typescript // prisma.service.ts const prisma = new PrismaClient({ log: [ { level: 'query', emit: 'event' }, { level: 'error', emit: 'stdout' }, { level: 'warn', emit: 'stdout' }, ], }); prisma.$on('query', (e) => { console.log('Query: ' + e.query); console.log('Params: ' + e.params); console.log('Duration: ' + e.duration + 'ms'); }); ``` ## 7. 常见问题 ### Q: Prisma Client 未生成 ```bash npx prisma generate ``` ### Q: 数据库连接失败 1. 检查 PostgreSQL 是否运行: `docker ps` 2. 检查 DATABASE_URL 环境变量 3. 检查网络连接和端口 ### Q: 测试失败 - 表不存在 ```bash # 推送 schema 到测试数据库 DATABASE_URL="postgresql://..." npx prisma db push ``` ### Q: TypeScript 编译错误 ```bash # 清理并重新构建 rm -rf dist npm run build ``` ### Q: 端口被占用 ```bash # 查找占用端口的进程 lsof -i :3000 # 或在 Windows netstat -ano | findstr :3000 ```