refactor: 拆分核心玩法模块并优化代码质量

将 WechatGame 单体模块拆分为独立的 User、Level、GameConfig 模块,
新增体力值系统、关卡闯关流程,并修复多项代码质量问题:
- 体力不足错误码从 401 修正为 400
- enterLevel 改用 findById 替代全表扫描
- consumeStamina 增加原子更新防止并发竞态
- 并行化独立数据库查询 (Promise.all)
- 移除 WechatGameService/Controller 死代码
This commit is contained in:
richarjiang
2026-04-10 09:07:50 +08:00
parent c775d5c6b0
commit fe2c13258e
33 changed files with 1681 additions and 978 deletions

View File

@@ -0,0 +1,39 @@
import { Controller, Get, Param } from '@nestjs/common';
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { GameConfigService } from './game-config.service';
import {
GameConfigResponseDto,
GameConfigListResponseDto,
} from '../wechat-game/dto/game-config-response.dto';
import { ApiResponseDto } from '../../common/dto/api-response.dto';
@ApiTags('游戏配置')
@Controller('v1/game-configs')
export class GameConfigController {
constructor(private readonly gameConfigService: GameConfigService) {}
@Get()
@ApiOperation({
summary: '获取所有游戏配置',
description: '获取所有激活的游戏配置列表',
})
@ApiResponse({ status: 200, description: '成功获取配置列表' })
async getAllConfigs(): Promise<ApiResponseDto<GameConfigListResponseDto>> {
const data = await this.gameConfigService.getAllConfigs();
return ApiResponseDto.success(data);
}
@Get(':key')
@ApiOperation({
summary: '根据 key 获取配置',
description: '根据配置键名获取单个游戏配置',
})
@ApiResponse({ status: 200, description: '成功获取配置' })
@ApiResponse({ status: 404, description: '配置不存在' })
async getConfigByKey(
@Param('key') key: string,
): Promise<ApiResponseDto<GameConfigResponseDto>> {
const data = await this.gameConfigService.getConfigByKey(key);
return ApiResponseDto.success(data);
}
}

View File

@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { GameConfigController } from './game-config.controller';
import { GameConfigService } from './game-config.service';
import { WechatGameModule } from '../wechat-game/wechat-game.module';
@Module({
imports: [WechatGameModule],
controllers: [GameConfigController],
providers: [GameConfigService],
})
export class GameConfigModule {}

View File

@@ -0,0 +1,42 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { GameConfigRepository } from '../wechat-game/repositories/game-config.repository';
import {
GameConfigResponseDto,
GameConfigListResponseDto,
} from '../wechat-game/dto/game-config-response.dto';
import { GameConfig } from '../wechat-game/entities/game-config.entity';
@Injectable()
export class GameConfigService {
constructor(
private readonly gameConfigRepository: GameConfigRepository,
) {}
async getAllConfigs(): Promise<GameConfigListResponseDto> {
const configs = await this.gameConfigRepository.findActiveConfigs();
return {
configs: configs.map((config) => this.toResponseDto(config)),
total: configs.length,
};
}
async getConfigByKey(key: string): Promise<GameConfigResponseDto> {
const config = await this.gameConfigRepository.findByKey(key);
if (!config) {
throw new NotFoundException(`Game config with key "${key}" not found`);
}
return this.toResponseDto(config);
}
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,
};
}
}