import { Controller, Delete, Get, HttpCode, HttpStatus, Param } from '@nestjs/common'; import { ApiParam, ApiProperty, ApiTags } from '@nestjs/swagger'; import { FailResult, OkResult } from '../../common/dto/result.dto'; import { MeetingService } from './meeting.service'; import { NonEmptyStringPipe } from '@/common/pipes/non-empty-string.pipe'; import { Uint32Pipe } from '@/common/pipes/uint32.pipe'; import { ApiCustomOkResponse } from '@/common/decorators/swagger.decorator'; import { StudentListResponseDto, TokenResponseDto } from './meeting.dto'; import { MeetingRedisService } from '../websocket/meeting-redis.service'; /** * 黑名单用户 DTO */ class BlacklistUserDto { @ApiProperty({ description: '短 UID', example: 1001 }) shortUid?: number; @ApiProperty({ description: '用户名称', example: '张三' }) userName?: string; } /** * 会议控制器 * 处理会议相关的 API 请求 */ @ApiTags('Meeting') @Controller('meeting') export class MeetingController { public constructor( private readonly meetingService: MeetingService, private readonly redis: MeetingRedisService ) {} /** * 生成声网 RTC Token */ @Get('get-token/:channelName/:uid') @HttpCode(HttpStatus.OK) @ApiCustomOkResponse({ summary: '声网 RTC Token', model: TokenResponseDto, apiDescription: '声网 RTC Token 信息', resDescription: '声网 RTC Token 信息包含appid', }) @ApiParam({ name: 'channelName', description: '需要加入的频道名(格式: `n_课程名称`)', example: 'n_1234567890' }) @ApiParam({ name: 'uid', description: '用户的短 UID(0~4294967295)', example: '1001' }) public async getToken(@Param('channelName', new NonEmptyStringPipe('channelName')) channelName: string, @Param('uid', new Uint32Pipe('uid')) uid: number) { const result = this.meetingService.generateToken(channelName, uid); if (!result) { return new FailResult('生成 Token 失败'); } return new OkResult(result); } /** * 查询房间黑名单(包含短 UID 和名称的数组) */ @Get('blacklist/:roomId') @HttpCode(HttpStatus.OK) @ApiParam({ name: 'roomId', description: '服务端房间 ID(Socket.IO 房间名)' }) @ApiCustomOkResponse({ summary: '房间黑名单', model: [BlacklistUserDto], apiDescription: '房间黑名单(包含短 UID 和名称的数组)', resDescription: '房间黑名单(包含短 UID 和名称的数组)', }) public async getBlacklist(@Param('roomId', new NonEmptyStringPipe('roomId')) roomId: string) { const list = await this.redis.getBlacklist(roomId); return new OkResult(list); } /** * 从房间黑名单移除指定用户 (短 UID) */ @Delete('blacklist/:roomId/:shortUid') @HttpCode(HttpStatus.OK) @ApiParam({ name: 'roomId', description: '服务端房间 ID(Socket.IO 房间名)' }) @ApiParam({ name: 'shortUid', description: '用户短 UID', example: '1001' }) @ApiCustomOkResponse({ summary: '从房间黑名单移除用户', model: Boolean, apiDescription: '从房间黑名单移除指定用户(短 UID)', resDescription: '是否成功移除用户', }) public async removeFromBlacklist( @Param('roomId', new NonEmptyStringPipe('roomId')) roomId: string, @Param('shortUid', new Uint32Pipe('shortUid')) shortUid: number ) { await this.redis.removeFromBlacklist(roomId, shortUid); return new OkResult(true); } /** * 通过作业 ID 查询学生详细信息列表 */ @Get('students/:homeworkId') @HttpCode(HttpStatus.OK) @ApiParam({ name: 'homeworkId', description: '作业 ID', example: '1234567890' }) @ApiCustomOkResponse({ summary: '学生详细信息列表', model: StudentListResponseDto, apiDescription: '学生详细信息列表,包含长 ID、短 ID (可能为 null) 和姓名', resDescription: '学生详细信息列表', }) public async getStudentDetails(@Param('homeworkId', new Uint32Pipe('homeworkId')) homeworkId: number) { const students = await this.meetingService.getStudentDetailsByHomeworkId(homeworkId); return new OkResult({ total: students.length, students, }); } }