# MemeMind-Server Backend Analysis **Date**: 2026-04-05 **Project**: MemeMind-Server (NestJS + TypeORM) **Location**: `/Users/richard/Documents/code/xieyingeng/MemeMind-Server` ## Executive Summary MemeMind-Server is a NestJS backend for a WeChat mini-game with a clean layered architecture. It provides two main modules: **WechatGame** (game level and config management) and **AppConfig** (environment setup). The server uses TypeORM with MySQL as the database and follows enterprise patterns including repositories, DTOs, and global error handling. --- ## 1. Project Structure ``` MemeMind-Server/ ├── src/ │ ├── main.ts # App entry point │ ├── app.module.ts # Root module (imports config & wechat-game) │ ├── common/ │ │ ├── dto/ │ │ │ └── api-response.dto.ts # Generic API response wrapper │ │ └── filters/ │ │ └── http-exception.filter.ts # Global exception handler │ ├── config/ │ │ ├── config.module.ts # Environment config setup │ │ ├── database.config.ts # TypeORM & MySQL config │ │ └── env.validation.ts # Environment variable validation │ └── modules/ │ └── wechat-game/ │ ├── wechat-game.module.ts # Module definition │ ├── wechat-game.controller.ts # 4 REST endpoints │ ├── wechat-game.service.ts # Business logic │ ├── entities/ │ │ ├── level.entity.ts # Level data model │ │ └── game-config.entity.ts # Configuration model │ ├── dto/ │ │ ├── level-response.dto.ts # Level API response │ │ └── game-config-response.dto.ts # Config API response │ └── repositories/ │ ├── level.repository.ts # Level DB access │ ├── level.repository.interface.ts │ ├── game-config.repository.ts # Config DB access │ └── game-config.repository.interface.ts ├── test/ │ ├── app.e2e-spec.ts │ └── jest-e2e.json ├── package.json ├── tsconfig.json ├── tsconfig.build.json └── nest-cli.json ``` --- ## 2. Package Dependencies ### Core Framework - **@nestjs/core** ^11.0.1 - NestJS core - **@nestjs/common** ^11.0.1 - Common utilities - **@nestjs/platform-express** ^11.0.1 - Express integration - **@nestjs/config** ^4.0.3 - Environment management - **@nestjs/typeorm** ^11.0.0 - TypeORM integration - **@nestjs/swagger** ^11.2.6 - API documentation ### Database - **typeorm** ^0.3.28 - ORM framework - **mysql2** ^3.19.1 - MySQL driver ### Data Validation & Transformation - **class-validator** ^0.15.1 - DTO validation - **class-transformer** ^0.5.1 - DTO transformation - **reflect-metadata** ^0.2.2 - Reflection for decorators ### Utilities - **rxjs** ^7.8.1 - Reactive programming ### Development Tools - **@nestjs/cli** ^11.0.0 - **@nestjs/testing** ^11.0.1 - **typescript** ^5.7.3 - **ts-jest** ^29.2.5 - **jest** ^30.0.0 - **prettier** ^3.4.2 - **eslint** ^9.18.0 --- ## 3. Application Setup (main.ts) **File**: `src/main.ts` (46 lines) ```typescript async function bootstrap() { const app = await NestFactory.create(AppModule); // Global prefix for all routes: /api app.setGlobalPrefix('api'); // CORS enabled (supports WeChat mini-game cross-origin requests) app.enableCors({ origin: true, credentials: true, }); // Global validation pipe app.useGlobalPipes( new ValidationPipe({ whitelist: true, // Strip unknown properties forbidNonWhitelisted: true, // Throw on unknown properties transform: true, // Auto-transform DTOs }), ); // Global exception filter app.useGlobalFilters(new HttpExceptionFilter()); // Swagger documentation at /api/docs const config = new DocumentBuilder() .setTitle('MemeMind Server API') .setDescription('微信小游戏 MemeMind 服务端 API 文档') .setVersion('1.0') .build(); const port = process.env.PORT ?? 3000; await app.listen(port); } ``` ### Key Setup Details - **Global prefix**: All routes start with `/api` - **CORS**: Enabled for WeChat mini-game frontend - **Validation**: Auto-validates all DTO inputs, strips unknown fields - **Exception handling**: Global filter wraps all errors in `ApiResponseDto` - **Swagger**: Auto-generated API docs at `/api/docs` - **Default port**: 3000 (configurable via `PORT` env var) --- ## 4. Root Module Configuration (app.module.ts) **File**: `src/app.module.ts` (30 lines) ```typescript @Module({ imports: [ AppConfigModule, // Environment setup TypeOrmModule.forRootAsync({ // Dynamic DB config imports: [ConfigModule], inject: [ConfigService], useFactory: (configService: ConfigService) => ({ type: 'mysql', host: configService.get('database.host'), port: configService.get('database.port'), username: configService.get('database.username'), password: configService.get('database.password'), database: configService.get('database.database'), entities: [__dirname + '/**/*.entity{.ts,.js}'], synchronize: NODE_ENV !== 'production', // Auto-create tables in dev logging: NODE_ENV !== 'production', // Log queries in dev autoLoadEntities: true, // Auto-discover entities }), }), WechatGameModule, // Game module ], }) export class AppModule {} ``` ### Key Configuration - **Async TypeORM config**: Reads database settings from environment - **Auto-synchronization**: In development, automatically creates/updates tables - **Auto-load entities**: Discovers all `.entity.ts` files - **Logging**: Database queries logged in development mode --- ## 5. Environment Configuration ### Files - **config.module.ts**: Global config setup - **database.config.ts**: Database connection parameters - **env.validation.ts**: Type-safe environment variables ### Configuration Structure (env.validation.ts) ```typescript class EnvironmentVariables { @IsEnum(Environment) NODE_ENV: Environment = 'development'; // 'development' | 'production' | 'test' @IsNumber() PORT: number = 3000; @IsString() DB_HOST: string = 'localhost'; @IsNumber() DB_PORT: number = 3306; @IsString() DB_USERNAME: string = 'meme_user'; @IsString() DB_PASSWORD: string = ''; @IsString() DB_DATABASE: string = 'meme_mind'; } ``` ### Environment Files The app looks for environment files in this order: 1. `.env.local` (local override) 2. `.env.production` (production override) 3. `.env` (default) ### Database Configuration (database.config.ts) - **Default MySQL connection**: localhost:3306 - **Fallback credentials**: meme_user / (no password) - **Default database**: meme_mind - **Auto-synchronize**: ✅ Enabled in development - **Query logging**: ✅ Enabled in development --- ## 6. Global Response Format ### ApiResponseDto (src/common/dto/api-response.dto.ts) All API responses follow this generic wrapper: ```typescript interface ApiResponseDto { success: boolean; // Request successful? data: T | null; // Response data (null on error) message: string | null; // Error message (null on success) timestamp: Date; // Response timestamp // Static factory methods static success(data: T): ApiResponseDto static error(message: string): ApiResponseDto } ``` ### Example Success Response ```json { "success": true, "data": { "levels": [...], "total": 50 }, "message": null, "timestamp": "2026-04-05T10:30:00.000Z" } ``` ### Example Error Response ```json { "success": false, "data": null, "message": "Level with id 'xyz' not found", "timestamp": "2026-04-05T10:30:00.000Z", "path": "/api/v1/wechat-game/levels/xyz" } ``` --- ## 7. Global Exception Handler ### HttpExceptionFilter (src/common/filters/http-exception.filter.ts) ```typescript @Catch() export class HttpExceptionFilter implements ExceptionFilter { catch(exception: unknown, host: ArgumentsHost) { // Extracts HTTP status code from exception // Wraps error in ApiResponseDto format // Includes request path for debugging // Logs unexpected errors } } ``` **Features**: - ✅ Catches all exceptions (HTTP and runtime errors) - ✅ Wraps all errors in standard `ApiResponseDto` format - ✅ Includes request URL in error response - ✅ Logs errors to console - ✅ Proper HTTP status codes (404, 400, 500, etc.) --- ## 8. WechatGame Module Architecture ### Module Structure (wechat-game.module.ts) ```typescript @Module({ imports: [TypeOrmModule.forFeature([GameConfig, Level])], controllers: [WechatGameController], providers: [WechatGameService, GameConfigRepository, LevelRepository], exports: [WechatGameService], }) export class WechatGameModule {} ``` ### Layer Architecture ``` ┌─────────────────────────────────────┐ │ HTTP Requests (REST Controller) │ │ Base: /api/v1/wechat-game │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ Business Logic (Service) │ │ • Query logic │ │ • DTO mapping │ │ • Error handling │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ Data Access (Repositories) │ │ • Level Repository │ │ • GameConfig Repository │ └──────────────┬──────────────────────┘ │ ┌──────────────▼──────────────────────┐ │ TypeORM (Database Layer) │ │ • MySQL queries │ │ • Transaction management │ └─────────────────────────────────────┘ ``` --- ## 9. REST API Endpoints ### Base URL: `/api/v1/wechat-game` **Endpoint 1: Get All Game Configs** ``` GET /api/v1/wechat-game/configs Response: ApiResponseDto Status: 200 ``` **Endpoint 2: Get Config by Key** ``` GET /api/v1/wechat-game/configs/:key Response: ApiResponseDto Status: 200 | 404 (not found) ``` **Endpoint 3: Get All Levels** ``` GET /api/v1/wechat-game/levels Response: ApiResponseDto Status: 200 Description: Returns levels sorted by sort_order ``` **Endpoint 4: Get Level by ID** ``` GET /api/v1/wechat-game/levels/:id Response: ApiResponseDto Status: 200 | 404 (not found) ``` --- ## 10. Data Models ### Entity 1: Level (src/modules/wechat-game/entities/level.entity.ts) ```typescript @Entity('levels') export class Level { @PrimaryColumn({ type: 'varchar', length: 191 }) id: string; // UUID or custom ID @Column({ type: 'varchar', length: 191, name: 'image_url' }) imageUrl: string; // Game image URL @Column({ type: 'varchar', length: 191 }) answer: string; // Correct answer (single string) @Column({ type: 'varchar', length: 191, nullable: true }) hint1: string | null; // First hint (free) @Column({ type: 'varchar', length: 191, nullable: true }) hint2: string | null; // Second hint (costs 1 life) @Column({ type: 'varchar', length: 191, nullable: true }) hint3: string | null; // Third hint (costs 1 life) @Column({ type: 'int', name: 'sort_order', default: 0 }) sortOrder: number; // Order of display @CreateDateColumn({ name: 'created_at' }) createdAt: Date; @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date; } ``` **Database Table**: `levels` ### Entity 2: GameConfig (src/modules/wechat-game/entities/game-config.entity.ts) ```typescript @Entity('game_configs') export class GameConfig { @PrimaryGeneratedColumn('uuid') id: string; // Auto-generated UUID @Column({ type: 'varchar', length: 255, name: 'config_key' }) configKey: string; // Unique config identifier @Column({ type: 'text', name: 'config_value' }) configValue: string; // Config value (stored as text) @Column({ type: 'varchar', length: 100, nullable: true, }) description: string | null; // Optional description @Column({ type: 'boolean', default: true, name: 'is_active' }) isActive: boolean; // Is this config active? @CreateDateColumn({ name: 'created_at' }) createdAt: Date; @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date; } ``` **Database Table**: `game_configs` --- ## 11. Service Layer (WechatGameService) **File**: `src/modules/wechat-game/wechat-game.service.ts` (93 lines) ### Methods #### 1. getAllConfigs() ```typescript async getAllConfigs(): Promise { const configs = await this.gameConfigRepository.findActiveConfigs(); return { configs: configs.map((config) => this.toResponseDto(config)), total: configs.length, }; } ``` - Returns only `isActive: true` configurations - Maps entities to DTOs - Returns list with total count #### 2. getConfigByKey(key: string) ```typescript async getConfigByKey(key: string): Promise { const config = await this.gameConfigRepository.findByKey(key); if (!config) { throw new NotFoundException(`Game config with key "${key}" not found`); } return this.toResponseDto(config); } ``` - Throws 404 if not found - Maps entity to DTO #### 3. getAllLevels() ```typescript async getAllLevels(): Promise { const levels = await this.levelRepository.findAllOrdered(); return { levels: levels.map((level, index) => this.toLevelResponseDto(level, index + 1), // 1-indexed level number ), total: levels.length, }; } ``` - Fetches levels in sort_order - Adds computed `level` number (1-indexed) - Returns list with total count #### 4. getLevelById(id: string) ```typescript async getLevelById(id: string): Promise { const levels = await this.levelRepository.findAllOrdered(); const levelIndex = levels.findIndex((l) => l.id === id); if (levelIndex === -1) { throw new NotFoundException(`Level with id "${id}" not found`); } return this.toLevelResponseDto(levels[levelIndex], levelIndex + 1); } ``` - Fetches all levels and finds by ID (not optimal - see improvements) - Throws 404 if not found - Adds computed `level` number #### 5. toResponseDto(config) ```typescript private toResponseDto(config: GameConfig): GameConfigResponseDto { return { id: config.id, configKey: config.configKey, configValue: config.configValue, description: config.description, isActive: config.isActive, createdAt: config.createdAt, updatedAt: config.updatedAt, }; } ``` #### 6. toLevelResponseDto(level, levelNumber) ```typescript private toLevelResponseDto(level: Level, levelNumber: number): LevelResponseDto { return { level: levelNumber, // Computed field id: level.id, imageUrl: level.imageUrl, answer: level.answer, hint1: level.hint1, hint2: level.hint2, hint3: level.hint3, sortOrder: level.sortOrder, createdAt: level.createdAt, updatedAt: level.updatedAt, }; } ``` --- ## 12. Repository Pattern ### Repository Interfaces **ILevelRepository**: ```typescript interface ILevelRepository { findAll(): Promise; findById(id: string): Promise; findAllOrdered(): Promise; // Ordered by sortOrder ASC } ``` **IGameConfigRepository**: ```typescript interface IGameConfigRepository { findAll(): Promise; findById(id: string): Promise; findByKey(key: string): Promise; findActiveConfigs(): Promise; // Where isActive = true } ``` ### Implementations **LevelRepository** (src/modules/wechat-game/repositories/level.repository.ts): ```typescript @Injectable() export class LevelRepository implements ILevelRepository { constructor( @InjectRepository(Level) private readonly repository: Repository, ) {} async findAll(): Promise { return this.repository.find(); } async findById(id: string): Promise { return this.repository.findOne({ where: { id } }); } async findAllOrdered(): Promise { return this.repository.find({ order: { sortOrder: 'ASC' }, }); } } ``` **GameConfigRepository** (src/modules/wechat-game/repositories/game-config.repository.ts): ```typescript @Injectable() export class GameConfigRepository implements IGameConfigRepository { constructor( @InjectRepository(GameConfig) private readonly repository: Repository, ) {} async findAll(): Promise { return this.repository.find(); } async findById(id: string): Promise { return this.repository.findOne({ where: { id } }); } async findByKey(key: string): Promise { return this.repository.findOne({ where: { configKey: key } }); } async findActiveConfigs(): Promise { return this.repository.find({ where: { isActive: true } }); } } ``` --- ## 13. Data Transfer Objects (DTOs) ### LevelResponseDto (src/modules/wechat-game/dto/level-response.dto.ts) ```typescript export class LevelResponseDto { @ApiProperty({ description: '关卡编号' }) level: number; // Computed level number (1-indexed) @ApiProperty({ description: '关卡ID' }) id: string; @ApiProperty({ description: '图片URL' }) imageUrl: string; @ApiProperty({ description: '答案' }) answer: string; @ApiProperty({ description: '提示1', nullable: true }) hint1: string | null; @ApiProperty({ description: '提示2', nullable: true }) hint2: string | null; @ApiProperty({ description: '提示3', nullable: true }) hint3: string | null; @ApiProperty({ description: '排序顺序' }) sortOrder: number; @ApiProperty({ description: '创建时间' }) createdAt: Date; @ApiProperty({ description: '更新时间' }) updatedAt: Date; } export class LevelListResponseDto { @ApiProperty({ type: [LevelResponseDto], description: '关卡列表' }) levels: LevelResponseDto[]; @ApiProperty({ description: '关卡总数' }) total: number; } ``` ### GameConfigResponseDto (src/modules/wechat-game/dto/game-config-response.dto.ts) ```typescript export class GameConfigResponseDto { @ApiProperty({ description: '配置ID' }) id: string; @ApiProperty({ description: '配置键名' }) configKey: string; @ApiProperty({ description: '配置值' }) configValue: string; @ApiProperty({ description: '配置描述', nullable: true }) description: string | null; @ApiProperty({ description: '是否激活' }) isActive: boolean; @ApiProperty({ description: '创建时间' }) createdAt: Date; @ApiProperty({ description: '更新时间' }) updatedAt: Date; } export class GameConfigListResponseDto { @ApiProperty({ type: [GameConfigResponseDto], description: '配置列表' }) configs: GameConfigResponseDto[]; @ApiProperty({ description: '配置总数' }) total: number; } ``` --- ## 14. Controller (WechatGameController) **File**: `src/modules/wechat-game/wechat-game.controller.ts` (69 lines) ```typescript @ApiTags('微信小游戏') @Controller('v1/wechat-game') export class WechatGameController { constructor(private readonly wechatGameService: WechatGameService) {} @Get('configs') @ApiOperation({ summary: '获取所有游戏配置', description: '获取所有激活的游戏配置列表', }) async getAllConfigs(): Promise> { const data = await this.wechatGameService.getAllConfigs(); return ApiResponseDto.success(data); } @Get('configs/:key') @ApiOperation({ summary: '根据key获取配置', }) async getConfigByKey( @Param('key') key: string, ): Promise> { const data = await this.wechatGameService.getConfigByKey(key); return ApiResponseDto.success(data); } @Get('levels') @ApiOperation({ summary: '获取所有关卡', description: '获取所有关卡列表,按sort_order排序', }) async getAllLevels(): Promise> { const data = await this.wechatGameService.getAllLevels(); return ApiResponseDto.success(data); } @Get('levels/:id') @ApiOperation({ summary: '根据ID获取关卡', }) async getLevelById( @Param('id') id: string, ): Promise> { const data = await this.wechatGameService.getLevelById(id); return ApiResponseDto.success(data); } } ``` ### Features - ✅ All endpoints wrapped in generic `ApiResponseDto` - ✅ Full Swagger documentation (`@ApiOperation`, `@ApiResponse`) - ✅ Proper HTTP methods and status codes - ✅ Route parameters validated by NestJS --- ## 15. Integration with Client ### Client API Call Pattern From the Cocos Creator client (`LevelDataManager.ts`), the server is called: ```typescript // Client calls const response = await HttpUtil.get('https://ilookai.cn/api/v1/wechat-game/levels'); // Server Response { "success": true, "data": { "levels": [ { "level": 1, "id": "level-001", "imageUrl": "https://example.com/image.jpg", "answer": "answer text", "hint1": "hint 1", "hint2": "hint 2", "hint3": "hint 3", "sortOrder": 0, "createdAt": "2026-04-05T00:00:00Z", "updatedAt": "2026-04-05T00:00:00Z" }, ... ], "total": 50 }, "message": null, "timestamp": "2026-04-05T10:30:00.000Z" } ``` ### Client Integration Points 1. **Level Loading**: Client calls `GET /api/v1/wechat-game/levels` 2. **Single Level Fetch**: Client calls `GET /api/v1/wechat-game/levels/:id` (though it doesn't currently) 3. **Config Management**: Client can call `GET /api/v1/wechat-game/configs` for game settings --- ## 16. Architecture Patterns ### 1. Dependency Injection - NestJS constructor injection for all dependencies - Repository interfaces decoupled from implementations - Testable design ### 2. Repository Pattern - Repositories abstract database access - Implements `IRepository` interfaces - Easy to mock for testing ### 3. Service Layer Pattern - Services contain business logic - Controllers delegate to services - Services delegate to repositories ### 4. DTO Pattern - DTOs separate API contracts from internal entities - Validation and transformation in DTOs - Swagger documentation on DTO fields ### 5. Global Exception Handling - Single exception filter wraps all errors - Consistent error response format - Centralized logging ### 6. Configuration Management - Environment-based configuration - Typed environment variables - Validation on startup --- ## 17. TypeORM Configuration Details ### Auto-Discovery - Entities auto-loaded from: `**/*.entity{.ts,.js}` - Repositories auto-created by `@InjectRepository()` ### Development Features - ✅ Database synchronization (auto-create tables) - ✅ Query logging to console - ✅ SQL visible in development ### Production Features - ✅ No auto-sync (safer) - ✅ No query logging (better performance) - ✅ Connection pooling via mysql2 ### Column Mapping - Snake_case in database: `image_url`, `sort_order`, `created_at` - Camel case in entities: `imageUrl`, `sortOrder`, `createdAt` - Auto-converted via TypeORM decorators --- ## 18. Security Patterns ### Global Validation Pipe ```typescript new ValidationPipe({ whitelist: true, // ✅ Remove unknown properties forbidNonWhitelisted: true, // ✅ Throw if unknown properties transform: true, // ✅ Auto-transform types }) ``` ### Considerations for Production - ⚠️ No authentication middleware currently - ⚠️ No rate limiting - ⚠️ No request logging middleware - ⚠️ CORS allows all origins (fine for public API) - ⚠️ No input sanitization beyond whitelist ### Recommendations 1. Add JWT authentication for admin endpoints 2. Add rate limiting (e.g., `@nestjs/throttler`) 3. Add request logging (e.g., Morgan middleware) 4. Add input validation for text fields (sanitize HTML) 5. Add API key authentication for WeChat client --- ## 19. Performance Considerations ### Current Implementation ``` GET /api/v1/wechat-game/levels/:id └─> getAllLevels() - Fetches ALL levels └─> findAllOrdered() - Database query for all levels └─> Memory search for :id - O(n) in application ``` **Problem**: `getLevelById()` fetches all levels, then searches in memory ### Optimization ```typescript // BETTER - Direct database query async getLevelById(id: string): Promise { const level = await this.levelRepository.findById(id); if (!level) throw new NotFoundException(); // Need to get level index for number calculation const index = await this.levelRepository.getLevelIndex(id); return this.toLevelResponseDto(level, index + 1); } ``` ### Database Indexing - Consider adding index on `levels.sort_order` for sorting - Consider adding index on `game_configs.config_key` for lookups - Consider adding composite index on `game_configs(config_key, is_active)` --- ## 20. API Response Flow Diagram ``` ┌─────────────────────────┐ │ HTTP Request │ │ GET /api/v1/... │ └────────────┬────────────┘ │ ┌────────────▼────────────┐ │ Global Validation Pipe │ ← Validates path params └────────────┬────────────┘ │ ┌────────────▼────────────────────────┐ │ Controller Method │ │ @Get('levels/:id') │ └────────────┬────────────────────────┘ │ ┌────────────▼────────────────────────┐ │ Service Method │ │ getLevelById(id) │ │ - Repository query │ │ - DTO mapping │ │ - Null check → NotFoundException │ └────────────┬────────────────────────┘ │ Success ───────────────┐ or Error │ │ │ ┌────────────▼────────────────────────┐ │ Global Exception Filter (if error) │ │ Wraps in ApiResponseDto │ └────────────┬────────────────────────┘ │ ┌────────────▼────────────────────────┐ │ ApiResponseDto.success() or error()│ │ Wraps in standard response format │ └────────────┬────────────────────────┘ │ ┌────────────▼────────────────────────┐ │ HTTP Response (200, 400, 404, 500) │ │ JSON: {success, data, message} │ └─────────────────────────────────────┘ ``` --- ## 21. Key Findings ### Strengths ✅ 1. **Clean architecture**: Proper separation of concerns (Controller → Service → Repository) 2. **Type safety**: Full TypeScript with DTOs and entity types 3. **Error handling**: Centralized exception filter with consistent format 4. **Documentation**: Swagger auto-documentation on all endpoints 5. **Configuration**: Type-safe environment variables with validation 6. **Database**: TypeORM with MySQL, auto-sync in dev 7. **Validation**: Global validation pipe with whitelist protection 8. **Modularity**: Feature-based module structure ### Potential Issues ⚠️ 1. **Performance**: `getLevelById()` fetches all levels instead of direct query 2. **Authentication**: No auth mechanism for sensitive endpoints 3. **Rate limiting**: No rate limiting implemented 4. **Caching**: No caching layer (clients could cache locally) 5. **Pagination**: No pagination on large datasets 6. **Middleware**: Limited middleware (no logging, no request IDs) 7. **Testing**: No E2E tests implemented yet ### Missing Features 🔧 1. Authentication & Authorization (guards, JWT tokens) 2. POST/PUT/DELETE endpoints (read-only currently) 3. User management module 4. Score/points tracking 5. Level submission endpoints 6. Statistics/leaderboard endpoints --- ## 22. Database Schema ### levels table ```sql CREATE TABLE levels ( id VARCHAR(191) PRIMARY KEY, image_url VARCHAR(191) NOT NULL, answer VARCHAR(191) NOT NULL, hint1 VARCHAR(191), hint2 VARCHAR(191), hint3 VARCHAR(191), sort_order INT DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_sort_order (sort_order) ); ``` ### game_configs table ```sql CREATE TABLE game_configs ( id CHAR(36) PRIMARY KEY, -- UUID config_key VARCHAR(255) NOT NULL UNIQUE, config_value TEXT NOT NULL, description VARCHAR(100), is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_config_key (config_key), KEY idx_active (is_active) ); ``` --- ## 23. Extension Points ### Adding User Authentication ```typescript // 1. Create users.entity.ts @Entity('users') export class User { @PrimaryGeneratedColumn('uuid') id: string; @Column({ unique: true }) wxOpenId: string; // WeChat OpenID @Column() nickname: string; @Column() lives: number; // Current lives @Column() currentLevel: number; // Progress } // 2. Create user.module.ts with UserController, UserService, UserRepository // 3. Add JWT guard to protected endpoints // 4. Add user context to requests ``` ### Adding Level Submission Endpoint ```typescript // POST /api/v1/wechat-game/levels/:id/submit @Post('levels/:id/submit') async submitAnswer( @Param('id') id: string, @Body() dto: SubmitAnswerDto, ): Promise> { // Validate answer // Update user progress // Award points/lives // Return result } ``` ### Adding Caching ```typescript // Import cache module import { CacheModule } from '@nestjs/cache-manager'; // In WechatGameModule @Module({ imports: [ CacheModule.register({ ttl: 3600, // 1 hour }), TypeOrmModule.forFeature([GameConfig, Level]), ], }) // In service constructor( private cacheManager: Cache, private levelRepository: LevelRepository, ) {} async getAllLevels() { const cached = await this.cacheManager.get('all_levels'); if (cached) return cached; const levels = await this.levelRepository.findAllOrdered(); await this.cacheManager.set('all_levels', levels, 3600); return levels; } ``` --- ## 24. Quick Reference ### Starting the Server ```bash npm install # Install dependencies npm run start:dev # Development with watch mode npm run start:prod # Production mode npm run build # Build TypeScript npm test # Run tests npm run test:e2e # End-to-end tests ``` ### Project Commands ```bash npm run format # Format code with Prettier npm run lint # Lint and fix with ESLint npm run test:watch # Watch tests npm run test:cov # Test coverage ``` ### Environment Setup ```bash # .env file NODE_ENV=development PORT=3000 DB_HOST=localhost DB_PORT=3306 DB_USERNAME=meme_user DB_PASSWORD=password DB_DATABASE=meme_mind ``` ### Swagger UI - **Location**: `http://localhost:3000/api/docs` - **API Endpoint**: `http://localhost:3000/api/docs-json` - Auto-updated from decorators ### Database Connection ``` Host: localhost Port: 3306 Username: meme_user Database: meme_mind ``` --- ## Summary MemeMind-Server is a well-structured NestJS backend with: - ✅ Clean layered architecture (Controller → Service → Repository) - ✅ Type-safe with full TypeScript - ✅ Comprehensive error handling and validation - ✅ Auto-generated Swagger documentation - ✅ MySQL persistence with TypeORM - ✅ Environment-based configuration - ✅ Ready for feature expansion (auth, scoring, leaderboards) The server currently provides **read-only access** to game levels and configurations, with endpoints designed to support the WeChat mini-game client's level loading and configuration management needs. --- *Generated: 2026-04-05 | Last Updated: 2026-04-05*