Files
MemeMind-Server/CLAUDE.md
2026-04-19 13:27:10 +08:00

267 lines
11 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**MemeMind-Server** is a NestJS backend for a WeChat mini-game called MemeMind. The server handles user authentication via WeChat login, manages game levels and progress, supports social sharing/challenges, and maintains user profiles. All API responses use a standardized format and are exposed under `/api` with Swagger docs at `/api/docs`.
## Tech Stack
- **Framework**: NestJS 11 with TypeScript 5.7
- **Database**: MySQL (via TypeORM)
- **Authentication**: JWT (7-day expiration)
- **Package Manager**: pnpm (with pnpm-lock.yaml)
- **Testing**: Jest (unit tests alongside source, e2e tests in `test/`)
- **Code Quality**: ESLint + Prettier (single quotes, trailing commas)
- **Deployment**: PM2 with rsync to remote server
## Project Structure
```
src/
├── main.ts # App bootstrap (CORS, validation, Swagger)
├── app.module.ts # Root module importing all features
├── config/
│ ├── config.module.ts # Global config provider (env validation)
│ ├── env.validation.ts # Environment variable schema validation
│ └── database.config.ts # TypeORM configuration
├── database/
│ └── migrations/ # TypeORM migrations (not heavily used yet)
├── common/
│ ├── dto/api-response.dto.ts # Unified response wrapper (success/error)
│ ├── filters/http-exception.filter.ts # Global error handling
│ ├── guards/jwt-auth.guard.ts # JWT verification & payload extraction
│ └── decorators/current-user.decorator.ts # Param decorator for @CurrentUser()
└── modules/
├── auth/ # WeChat login, user creation, JWT issuance
├── user/ # User profile and stamina management
├── level/ # Game level progression tracking
├── share/ # Social challenge/share features
├── game-config/ # Game configuration endpoints
└── wechat-game/ # Shared game entities and repositories
docs/
├── api/ # API documentation (Markdown, auto-synced with code)
├── api/README.md # Index of all API modules
└── superpowers/ # Legacy or undocumented features
```
## Key Commands
### Development
```bash
pnpm install # Install dependencies
pnpm run start:dev # Run with file watching (http://localhost:3000/api)
pnpm run start:debug # Debug mode with Node inspector
pnpm run build # Compile TypeScript to dist/
pnpm run start:prod # Run compiled production build
```
### Code Quality
```bash
pnpm run lint # ESLint with auto-fix
pnpm run format # Prettier format (src/ and test/)
```
### Testing
```bash
pnpm run test # All unit tests (*.spec.ts in src/)
pnpm run test:watch # Watch mode
pnpm run test:cov # Coverage report (outputs to coverage/)
pnpm run test:debug # Debug unit tests
pnpm run test:e2e # E2E tests (test/*.e2e-spec.ts)
```
### Deployment
```bash
pnpm run deploy # Build, rsync to server, restart PM2 cluster
```
## Architecture Patterns
### Module Structure
Each feature module follows NestJS conventions:
```typescript
// module.ts: Declares imports (other modules, TypeORM entities, services)
@Module({
imports: [TypeOrmModule.forFeature([Entity]), AuthModule],
controllers: [FeatureController],
providers: [FeatureService, CustomRepository],
exports: [FeatureService], // For cross-module injection
})
export class FeatureModule {}
// controller.ts: HTTP routing, DTO validation, Swagger decorators
@Controller('v1/feature')
export class FeatureController {
@Post() async create(@Body() dto: CreateDto): Promise<ApiResponseDto<ResponseDto>>
@UseGuards(JwtAuthGuard) // Applied per-endpoint
@Get(':id') async getOne(@Param('id') id: string, @CurrentUser() user: JwtPayload)
}
// service.ts: Business logic, repository calls, external API calls
@Injectable()
export class FeatureService {
constructor(private readonly repo: CustomRepository) {}
async create(data: CreateDto): Promise<ResponseDto> { ... }
}
// repositories/: Custom data access logic (extend TypeORM repositories)
@Injectable()
export class CustomRepository extends Repository<Entity> {
async customQuery(): Promise<Entity[]> { ... }
}
// dto/: Request/response schemas with class-validator decorators
export class CreateDto {
@IsString() @MinLength(1) name: string;
}
```
### Authentication & Authorization
- **No global guard**: JWT validation is per-endpoint via `@UseGuards(JwtAuthGuard)`
- **User extraction**: Use `@CurrentUser()` to inject `JwtPayload { sub: userId, openid }`
- **WeChat integration**: `AuthService.wxLogin(code)` calls WeChat API, creates user on first login, issues JWT
### Request/Response Contract
All endpoints return `ApiResponseDto<T>`:
```typescript
{
success: boolean, // true on success, false on error
data: T | null, // Response payload or null
message: string | null, // Error message (null on success)
timestamp: Date // ISO 8601 timestamp
}
```
Errors are caught by `HttpExceptionFilter`, which formats exceptions as failed responses.
### Database
- **ORM**: TypeORM with MySQL
- **Entities**: Auto-loaded from `**/*.entity.ts`, sync mode in dev
- **Migrations**: Located in `src/database/migrations/` (minimal usage)
- **Connection**: Configured via `ConfigService` (env vars: DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD, DB_DATABASE)
## Configuration
Environment variables are validated via `env.validation.ts` using class-validator:
```
NODE_ENV # development|production|test (default: development)
PORT # Server port (default: 3000)
DB_HOST # MySQL host (default: localhost)
DB_PORT # MySQL port (default: 3306)
DB_USERNAME # MySQL user (default: meme_user)
DB_PASSWORD # MySQL password (default: '')
DB_DATABASE # MySQL database (default: meme_mind)
WX_APPID # WeChat mini-game app ID
WX_SECRET # WeChat mini-game secret
JWT_SECRET # JWT signing secret
```
Config files loaded in order: `.env.local``.env``.env.production`. Use `.env.local` for local overrides (never committed).
## Testing
- **Unit tests**: Colocated with source as `*.spec.ts` (e.g., `auth.service.spec.ts`)
- **E2E tests**: In `test/` directory (e.g., `test/app.e2e-spec.ts`)
- **Framework**: Jest with ts-jest compiler
- **Coverage**: Run `pnpm run test:cov` to generate coverage report in `coverage/`
- **Individual tests**: `pnpm run test -- --testNamePattern="test name"` or `pnpm run test:debug`
## API Documentation
API docs are maintained in `docs/api/` and **must be updated whenever controller/DTO changes occur**. The `api-doc-maintainer` skill automates this when editing `src/modules/*/`.
**File mapping**:
- `docs/api/auth-api.md``src/modules/auth/`
- `docs/api/game-api.md``src/modules/wechat-game/` & `src/modules/level/`
- `docs/api/share-challenge-api.md``src/modules/share/`
**When to update docs**:
- New endpoint added
- Request/response DTO fields changed
- Error codes or business logic modified
- Authentication requirements changed
Use the template in `AGENTS.md` (api-doc-maintainer skill) for consistent formatting.
## Deployment
**Local to production**:
1. Ensure `.env.production` has correct credentials (WX_APPID, WX_SECRET, JWT_SECRET, DB credentials)
2. Run `pnpm run deploy` (triggers `deploy.sh`)
3. Script: builds locally → rsyncs `dist/`, `package.json`, `pnpm-lock.yaml` to `/var/www/MemeMind-Server/` on server → installs deps → restarts PM2 cluster (2 instances)
**PM2 config** (`ecosystem.config.js`): Cluster mode (2 instances), auto-restart, 1GB memory limit, logs to `logs/*.log`
**Important**: Before deploying, verify deploy.sh credentials (SERVER_IP, SERVER_USER, REMOTE_DIR, APP_NAME) match your target environment.
## Code Style & Conventions
- **TypeScript**: Strict mode enabled, no implicit `any`, explicit types on public APIs
- **Naming**: Classes PascalCase, methods/variables camelCase, directories kebab-case
- **DTO files**: Semantic names (e.g., `wx-login.dto.ts`, `share-response.dto.ts`)
- **Quotes**: Single quotes (enforced by Prettier)
- **Indentation**: 2 spaces
- **Commits**: Use Conventional Commits (e.g., `feat(auth):`, `fix(level):`, `docs(api):`)
## Key Integration Points
### Auth Module
- **Controller**: `POST /api/v1/auth/wx-login`
- **Flow**: WeChat code → `AuthService.wxLogin()` → calls WeChat API (`jscode2session`) → creates/updates user → signs JWT
- **Exports**: `AuthService`, `UserRepository`, `UserLevelProgressRepository` (used by other modules)
### User Module
- **Depends on**: AuthModule (for JWT guard, UserRepository)
- **Usage**: Profile endpoints, stamina management
### Level Module
- **Depends on**: AuthModule, UserModule, WechatGameModule
- **Entities**: Reused from WechatGameModule (Level, UserLevelProgress)
### Share Module
- **Entities**: ShareConfig, ShareParticipant, ShareLevelProgress (independent tables)
- **Depends on**: WechatGameModule, AuthModule
- **Pattern**: Multi-table repository pattern for complex queries
## Common Development Tasks
- **Add a new API endpoint**: Create DTO in `modules/{feature}/dto/`, add method to controller, implement in service, call `pnpm run lint && pnpm run test` to verify
- **Add a new entity**: Create `*.entity.ts` in module folder, add to TypeOrmModule.forFeature in module, update relevant repository
- **Modify API response format**: Update DTO/entity, regenerate Swagger docs, update `docs/api/*.md` manually if not auto-synced
- **Debug a failing test**: Run `pnpm run test:debug -- --testNamePattern="specific test"` and use Node inspector
- **Run migrations**: TypeORM in-dev synchronize mode auto-creates tables; for production, use TypeORM CLI or manual SQL
## Troubleshooting
- **Port 3000 in use**: `lsof -i :3000` and `kill -9 <PID>`, or change PORT env var
- **Database connection fails**: Verify DB_HOST, DB_PORT, DB_USERNAME, DB_PASSWORD in `.env.local` match your MySQL setup
- **JWT verification fails**: Check JWT_SECRET is consistent across app instances and `.env` files
- **Swagger not loading**: Ensure app.listen() completes; check browser console for CORS errors
- **Tests hanging**: Check for open database connections; run `pnpm run test:debug` to inspect
- **Prettier/ESLint conflicts**: Run `pnpm run format && pnpm run lint` in sequence (format first, then lint)
## Additional Resources
- **NestJS docs**: https://docs.nestjs.com
- **TypeORM docs**: https://typeorm.io
- **Project-specific guidance**: See `AGENTS.md` for multi-agent collaboration patterns