351 lines
9.5 KiB
TypeScript
351 lines
9.5 KiB
TypeScript
/**
|
||
* 位置广播HTTP API控制器
|
||
*
|
||
* 功能描述:
|
||
* - 提供位置广播系统的REST API接口
|
||
* - 处理HTTP请求和响应格式化
|
||
* - 集成JWT认证和权限验证
|
||
* - 提供完整的API文档和错误处理
|
||
*
|
||
* 职责分离:
|
||
* - HTTP处理:专注于HTTP请求和响应的处理
|
||
* - 数据转换:请求参数和响应数据的格式转换
|
||
* - 权限验证:API访问权限的验证和控制
|
||
* - 文档生成:Swagger API文档的自动生成
|
||
*
|
||
* 技术实现:
|
||
* - NestJS控制器:使用装饰器定义API端点
|
||
* - Swagger集成:自动生成API文档
|
||
* - 数据验证:使用DTO进行请求数据验证
|
||
* - 异常处理:统一的HTTP异常处理机制
|
||
*
|
||
* 最近修改:
|
||
* - 2026-01-08: 功能新增 - 创建位置广播HTTP API控制器
|
||
*
|
||
* @author moyin
|
||
* @version 1.0.0
|
||
* @since 2026-01-08
|
||
* @lastModified 2026-01-08
|
||
*/
|
||
|
||
import {
|
||
Controller,
|
||
Get,
|
||
Post,
|
||
Put,
|
||
Delete,
|
||
Body,
|
||
Param,
|
||
Query,
|
||
UseGuards,
|
||
HttpStatus,
|
||
HttpException,
|
||
Logger,
|
||
} from '@nestjs/common';
|
||
import {
|
||
ApiTags,
|
||
ApiOperation,
|
||
ApiResponse,
|
||
ApiParam,
|
||
ApiQuery,
|
||
ApiBearerAuth,
|
||
ApiBody,
|
||
} from '@nestjs/swagger';
|
||
import { JwtAuthGuard } from '../../auth/jwt_auth.guard';
|
||
import { CurrentUser } from '../../auth/current_user.decorator';
|
||
import { JwtPayload } from '../../../core/login_core/login_core.service';
|
||
|
||
// 导入业务服务
|
||
import {
|
||
LocationBroadcastService,
|
||
LocationSessionService,
|
||
LocationPositionService,
|
||
} from '../services';
|
||
|
||
// 导入DTO
|
||
import {
|
||
CreateSessionDto,
|
||
SessionQueryDto,
|
||
PositionQueryDto,
|
||
UpdateSessionConfigDto,
|
||
} from '../dto/api.dto';
|
||
|
||
/**
|
||
* 位置广播API控制器
|
||
*
|
||
* 提供以下API端点:
|
||
* - 会话管理:创建、查询、配置会话
|
||
* - 位置管理:查询位置、获取统计信息
|
||
* - 用户管理:获取用户状态、清理数据
|
||
*/
|
||
@ApiTags('位置广播')
|
||
@Controller('location-broadcast')
|
||
@ApiBearerAuth()
|
||
@UseGuards(JwtAuthGuard)
|
||
export class LocationBroadcastController {
|
||
private readonly logger = new Logger(LocationBroadcastController.name);
|
||
|
||
constructor(
|
||
private readonly locationBroadcastService: LocationBroadcastService,
|
||
private readonly locationSessionService: LocationSessionService,
|
||
private readonly locationPositionService: LocationPositionService,
|
||
) {}
|
||
|
||
/**
|
||
* 创建新会话
|
||
*/
|
||
@Post('sessions')
|
||
@ApiOperation({
|
||
summary: '创建新游戏会话',
|
||
description: '创建一个新的位置广播会话,支持自定义配置',
|
||
})
|
||
@ApiResponse({
|
||
status: 201,
|
||
description: '会话创建成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
success: { type: 'boolean', example: true },
|
||
sessionId: { type: 'string', example: 'session_12345' },
|
||
message: { type: 'string', example: '会话创建成功' },
|
||
},
|
||
},
|
||
})
|
||
@ApiResponse({ status: 400, description: '请求参数错误' })
|
||
@ApiResponse({ status: 409, description: '会话ID已存在' })
|
||
async createSession(
|
||
@Body() createSessionDto: CreateSessionDto,
|
||
@CurrentUser() user: JwtPayload,
|
||
) {
|
||
try {
|
||
const result = await this.locationSessionService.createSession({
|
||
...createSessionDto,
|
||
creatorId: user.sub,
|
||
});
|
||
|
||
return {
|
||
success: true,
|
||
session: result,
|
||
message: '会话创建成功',
|
||
};
|
||
} catch (error: any) {
|
||
this.logger.error('创建会话失败', error);
|
||
throw new HttpException(
|
||
error.message || '创建会话失败',
|
||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询会话列表
|
||
*/
|
||
@Get('sessions')
|
||
@ApiOperation({
|
||
summary: '查询会话列表',
|
||
description: '根据条件查询游戏会话列表,支持分页和过滤',
|
||
})
|
||
@ApiQuery({ name: 'status', required: false, description: '会话状态' })
|
||
@ApiQuery({ name: 'limit', required: false, description: '分页大小' })
|
||
@ApiQuery({ name: 'offset', required: false, description: '分页偏移' })
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '查询成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
success: { type: 'boolean', example: true },
|
||
sessions: { type: 'array', items: { type: 'object' } },
|
||
total: { type: 'number', example: 10 },
|
||
message: { type: 'string', example: '查询成功' },
|
||
},
|
||
},
|
||
})
|
||
async querySessions(
|
||
@Query() query: SessionQueryDto,
|
||
@CurrentUser() user: JwtPayload,
|
||
) {
|
||
try {
|
||
const result = await this.locationSessionService.querySessions(query as any);
|
||
return {
|
||
success: true,
|
||
...result,
|
||
message: '查询成功',
|
||
};
|
||
} catch (error: any) {
|
||
this.logger.error('查询会话失败', error);
|
||
throw new HttpException(
|
||
error.message || '查询会话失败',
|
||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取会话详情
|
||
*/
|
||
@Get('sessions/:sessionId')
|
||
@ApiOperation({
|
||
summary: '获取会话详情',
|
||
description: '获取指定会话的详细信息,包括用户列表和位置信息',
|
||
})
|
||
@ApiParam({ name: 'sessionId', description: '会话ID' })
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
success: { type: 'boolean', example: true },
|
||
session: { type: 'object' },
|
||
users: { type: 'array', items: { type: 'object' } },
|
||
message: { type: 'string', example: '获取成功' },
|
||
},
|
||
},
|
||
})
|
||
@ApiResponse({ status: 404, description: '会话不存在' })
|
||
async getSessionDetail(
|
||
@Param('sessionId') sessionId: string,
|
||
@CurrentUser() user: JwtPayload,
|
||
) {
|
||
try {
|
||
const result = await this.locationSessionService.getSessionDetail(sessionId);
|
||
return {
|
||
success: true,
|
||
...result,
|
||
message: '获取成功',
|
||
};
|
||
} catch (error: any) {
|
||
this.logger.error('获取会话详情失败', error);
|
||
throw new HttpException(
|
||
error.message || '获取会话详情失败',
|
||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 查询位置信息
|
||
*/
|
||
@Get('positions')
|
||
@ApiOperation({
|
||
summary: '查询位置信息',
|
||
description: '根据条件查询用户位置信息,支持范围查询和地图过滤',
|
||
})
|
||
@ApiQuery({ name: 'mapId', required: false, description: '地图ID' })
|
||
@ApiQuery({ name: 'sessionId', required: false, description: '会话ID' })
|
||
@ApiQuery({ name: 'limit', required: false, description: '分页大小' })
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '查询成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
success: { type: 'boolean', example: true },
|
||
positions: { type: 'array', items: { type: 'object' } },
|
||
total: { type: 'number', example: 5 },
|
||
message: { type: 'string', example: '查询成功' },
|
||
},
|
||
},
|
||
})
|
||
async queryPositions(
|
||
@Query() query: PositionQueryDto,
|
||
@CurrentUser() user: JwtPayload,
|
||
) {
|
||
try {
|
||
const result = await this.locationPositionService.queryPositions(query as any);
|
||
return {
|
||
success: true,
|
||
...result,
|
||
message: '查询成功',
|
||
};
|
||
} catch (error: any) {
|
||
this.logger.error('查询位置失败', error);
|
||
throw new HttpException(
|
||
error.message || '查询位置失败',
|
||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取位置统计信息
|
||
*/
|
||
@Get('positions/stats')
|
||
@ApiOperation({
|
||
summary: '获取位置统计信息',
|
||
description: '获取系统位置数据的统计信息,包括用户分布和活跃度',
|
||
})
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '获取成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
success: { type: 'boolean', example: true },
|
||
stats: { type: 'object' },
|
||
message: { type: 'string', example: '获取成功' },
|
||
},
|
||
},
|
||
})
|
||
async getPositionStats(@CurrentUser() user: JwtPayload) {
|
||
try {
|
||
const stats = await this.locationPositionService.getPositionStats({});
|
||
return {
|
||
success: true,
|
||
stats,
|
||
message: '获取成功',
|
||
};
|
||
} catch (error: any) {
|
||
this.logger.error('获取位置统计失败', error);
|
||
throw new HttpException(
|
||
error.message || '获取位置统计失败',
|
||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理用户数据
|
||
*/
|
||
@Delete('users/:userId/data')
|
||
@ApiOperation({
|
||
summary: '清理用户数据',
|
||
description: '清理指定用户的位置数据和会话信息',
|
||
})
|
||
@ApiParam({ name: 'userId', description: '用户ID' })
|
||
@ApiResponse({
|
||
status: 200,
|
||
description: '清理成功',
|
||
schema: {
|
||
type: 'object',
|
||
properties: {
|
||
success: { type: 'boolean', example: true },
|
||
message: { type: 'string', example: '清理成功' },
|
||
},
|
||
},
|
||
})
|
||
async cleanupUserData(
|
||
@Param('userId') userId: string,
|
||
@CurrentUser() user: JwtPayload,
|
||
) {
|
||
try {
|
||
// 只允许用户清理自己的数据,或管理员清理任意用户数据
|
||
if (user.sub !== userId && user.role !== 2) {
|
||
throw new HttpException('权限不足', HttpStatus.FORBIDDEN);
|
||
}
|
||
|
||
await this.locationBroadcastService.cleanupUserData(userId);
|
||
return {
|
||
success: true,
|
||
message: '清理成功',
|
||
};
|
||
} catch (error: any) {
|
||
this.logger.error('清理用户数据失败', error);
|
||
throw new HttpException(
|
||
error.message || '清理用户数据失败',
|
||
error.status || HttpStatus.INTERNAL_SERVER_ERROR,
|
||
);
|
||
}
|
||
}
|
||
} |