feat(wechat-game): add level list API endpoints
Add CRUD endpoints for game levels with level field in response: - GET /v1/wechat-game/levels - list all levels ordered by sort_order - GET /v1/wechat-game/levels/:id - get single level by ID New files: - Level entity mapping to levels table - LevelRepository with ordered query support - LevelResponseDto with level field (1-based index from sort_order)
This commit is contained in:
41
src/modules/wechat-game/dto/level-response.dto.ts
Normal file
41
src/modules/wechat-game/dto/level-response.dto.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class LevelResponseDto {
|
||||
@ApiProperty({ description: '关卡编号' })
|
||||
level: number;
|
||||
|
||||
@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;
|
||||
}
|
||||
37
src/modules/wechat-game/entities/level.entity.ts
Normal file
37
src/modules/wechat-game/entities/level.entity.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity('levels')
|
||||
export class Level {
|
||||
@PrimaryColumn({ type: 'varchar', length: 191 })
|
||||
id: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 191, name: 'image_url' })
|
||||
imageUrl: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 191 })
|
||||
answer: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 191, nullable: true })
|
||||
hint1: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 191, nullable: true })
|
||||
hint2: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 191, nullable: true })
|
||||
hint3: string | null;
|
||||
|
||||
@Column({ type: 'int', name: 'sort_order', default: 0 })
|
||||
sortOrder: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Level } from '../entities/level.entity';
|
||||
|
||||
export interface ILevelRepository {
|
||||
findAll(): Promise<Level[]>;
|
||||
findById(id: string): Promise<Level | null>;
|
||||
findAllOrdered(): Promise<Level[]>;
|
||||
}
|
||||
27
src/modules/wechat-game/repositories/level.repository.ts
Normal file
27
src/modules/wechat-game/repositories/level.repository.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Level } from '../entities/level.entity';
|
||||
import { ILevelRepository } from './level.repository.interface';
|
||||
|
||||
@Injectable()
|
||||
export class LevelRepository implements ILevelRepository {
|
||||
constructor(
|
||||
@InjectRepository(Level)
|
||||
private readonly repository: Repository<Level>,
|
||||
) {}
|
||||
|
||||
async findAll(): Promise<Level[]> {
|
||||
return this.repository.find();
|
||||
}
|
||||
|
||||
async findById(id: string): Promise<Level | null> {
|
||||
return this.repository.findOne({ where: { id } });
|
||||
}
|
||||
|
||||
async findAllOrdered(): Promise<Level[]> {
|
||||
return this.repository.find({
|
||||
order: { sortOrder: 'ASC' },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,10 @@ import {
|
||||
GameConfigResponseDto,
|
||||
GameConfigListResponseDto,
|
||||
} from './dto/game-config-response.dto';
|
||||
import {
|
||||
LevelResponseDto,
|
||||
LevelListResponseDto,
|
||||
} from './dto/level-response.dto';
|
||||
import { ApiResponseDto } from '../../common/dto/api-response.dto';
|
||||
|
||||
@ApiTags('微信小游戏')
|
||||
@@ -13,7 +17,10 @@ export class WechatGameController {
|
||||
constructor(private readonly wechatGameService: WechatGameService) {}
|
||||
|
||||
@Get('configs')
|
||||
@ApiOperation({ summary: '获取所有游戏配置', description: '获取所有激活的游戏配置列表' })
|
||||
@ApiOperation({
|
||||
summary: '获取所有游戏配置',
|
||||
description: '获取所有激活的游戏配置列表',
|
||||
})
|
||||
@ApiResponse({ status: 200, description: '成功获取配置列表' })
|
||||
async getAllConfigs(): Promise<ApiResponseDto<GameConfigListResponseDto>> {
|
||||
const data = await this.wechatGameService.getAllConfigs();
|
||||
@@ -21,7 +28,10 @@ export class WechatGameController {
|
||||
}
|
||||
|
||||
@Get('configs/:key')
|
||||
@ApiOperation({ summary: '根据key获取配置', description: '根据配置键名获取单个游戏配置' })
|
||||
@ApiOperation({
|
||||
summary: '根据key获取配置',
|
||||
description: '根据配置键名获取单个游戏配置',
|
||||
})
|
||||
@ApiResponse({ status: 200, description: '成功获取配置' })
|
||||
@ApiResponse({ status: 404, description: '配置不存在' })
|
||||
async getConfigByKey(
|
||||
@@ -30,4 +40,29 @@ export class WechatGameController {
|
||||
const data = await this.wechatGameService.getConfigByKey(key);
|
||||
return ApiResponseDto.success(data);
|
||||
}
|
||||
|
||||
@Get('levels')
|
||||
@ApiOperation({
|
||||
summary: '获取所有关卡',
|
||||
description: '获取所有关卡列表,按sort_order排序',
|
||||
})
|
||||
@ApiResponse({ status: 200, description: '成功获取关卡列表' })
|
||||
async getAllLevels(): Promise<ApiResponseDto<LevelListResponseDto>> {
|
||||
const data = await this.wechatGameService.getAllLevels();
|
||||
return ApiResponseDto.success(data);
|
||||
}
|
||||
|
||||
@Get('levels/:id')
|
||||
@ApiOperation({
|
||||
summary: '根据ID获取关卡',
|
||||
description: '根据关卡ID获取单个关卡信息',
|
||||
})
|
||||
@ApiResponse({ status: 200, description: '成功获取关卡' })
|
||||
@ApiResponse({ status: 404, description: '关卡不存在' })
|
||||
async getLevelById(
|
||||
@Param('id') id: string,
|
||||
): Promise<ApiResponseDto<LevelResponseDto>> {
|
||||
const data = await this.wechatGameService.getLevelById(id);
|
||||
return ApiResponseDto.success(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { WechatGameController } from './wechat-game.controller';
|
||||
import { WechatGameService } from './wechat-game.service';
|
||||
import { GameConfig } from './entities/game-config.entity';
|
||||
import { Level } from './entities/level.entity';
|
||||
import { GameConfigRepository } from './repositories/game-config.repository';
|
||||
import { LevelRepository } from './repositories/level.repository';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([GameConfig])],
|
||||
imports: [TypeOrmModule.forFeature([GameConfig, Level])],
|
||||
controllers: [WechatGameController],
|
||||
providers: [WechatGameService, GameConfigRepository],
|
||||
providers: [WechatGameService, GameConfigRepository, LevelRepository],
|
||||
exports: [WechatGameService],
|
||||
})
|
||||
export class WechatGameModule {}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { GameConfigRepository } from './repositories/game-config.repository';
|
||||
import { LevelRepository } from './repositories/level.repository';
|
||||
import {
|
||||
GameConfigResponseDto,
|
||||
GameConfigListResponseDto,
|
||||
} from './dto/game-config-response.dto';
|
||||
import {
|
||||
LevelResponseDto,
|
||||
LevelListResponseDto,
|
||||
} from './dto/level-response.dto';
|
||||
|
||||
@Injectable()
|
||||
export class WechatGameService {
|
||||
constructor(private readonly gameConfigRepository: GameConfigRepository) {}
|
||||
constructor(
|
||||
private readonly gameConfigRepository: GameConfigRepository,
|
||||
private readonly levelRepository: LevelRepository,
|
||||
) {}
|
||||
|
||||
async getAllConfigs(): Promise<GameConfigListResponseDto> {
|
||||
const configs = await this.gameConfigRepository.findActiveConfigs();
|
||||
@@ -28,7 +36,31 @@ export class WechatGameService {
|
||||
return this.toResponseDto(config);
|
||||
}
|
||||
|
||||
private toResponseDto(config: import('./entities/game-config.entity').GameConfig): GameConfigResponseDto {
|
||||
async getAllLevels(): Promise<LevelListResponseDto> {
|
||||
const levels = await this.levelRepository.findAllOrdered();
|
||||
|
||||
return {
|
||||
levels: levels.map((level, index) =>
|
||||
this.toLevelResponseDto(level, index + 1),
|
||||
),
|
||||
total: levels.length,
|
||||
};
|
||||
}
|
||||
|
||||
async getLevelById(id: string): Promise<LevelResponseDto> {
|
||||
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);
|
||||
}
|
||||
|
||||
private toResponseDto(
|
||||
config: import('./entities/game-config.entity').GameConfig,
|
||||
): GameConfigResponseDto {
|
||||
return {
|
||||
id: config.id,
|
||||
configKey: config.configKey,
|
||||
@@ -39,4 +71,22 @@ export class WechatGameService {
|
||||
updatedAt: config.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
private toLevelResponseDto(
|
||||
level: import('./entities/level.entity').Level,
|
||||
levelNumber: number,
|
||||
): LevelResponseDto {
|
||||
return {
|
||||
level: levelNumber,
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user