Files
whale-town-end/src/business/location_broadcast/dto/websocket_message.dto.ts
moyin c31cbe559d feat:实现位置广播系统
- 添加位置广播核心控制器和服务
- 实现健康检查和位置同步功能
- 添加WebSocket实时位置更新支持
- 完善位置广播的测试覆盖
2026-01-08 23:05:52 +08:00

334 lines
7.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* WebSocket消息数据传输对象
*
* 功能描述:
* - 定义WebSocket通信的消息格式和验证规则
* - 提供客户端和服务端之间的数据交换标准
* - 支持位置广播系统的实时通信需求
* - 实现消息类型的统一管理和验证
*
* 职责分离:
* - 消息格式定义WebSocket消息的标准结构
* - 数据验证使用class-validator进行输入验证
* - 类型安全提供TypeScript类型约束
* - 接口规范:统一的消息交换格式
*
* 最近修改:
* - 2026-01-08: 功能新增 - 创建WebSocket消息DTO支持位置广播系统
*
* @author moyin
* @version 1.0.0
* @since 2026-01-08
* @lastModified 2026-01-08
*/
import { IsString, IsNumber, IsNotEmpty, IsOptional, IsObject, Length } from 'class-validator';
import { Type } from 'class-transformer';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
/**
* 加入会话消息DTO
*
* 职责:
* - 定义用户加入游戏会话的请求数据
* - 验证会话ID和认证token的格式
* - 支持可选的初始位置设置
*/
export class JoinSessionMessage {
/**
* 消息类型标识
*/
@ApiProperty({
description: '消息类型',
example: 'join_session',
enum: ['join_session']
})
@IsString({ message: '消息类型必须是字符串' })
@IsOptional()
type?: 'join_session' = 'join_session';
/**
* 游戏会话ID
*/
@ApiProperty({
description: '游戏会话ID',
example: 'session_12345',
minLength: 1,
maxLength: 100
})
@IsString({ message: '会话ID必须是字符串' })
@IsNotEmpty({ message: '会话ID不能为空' })
@Length(1, 100, { message: '会话ID长度必须在1-100个字符之间' })
sessionId: string;
/**
* JWT认证token
*/
@ApiProperty({
description: 'JWT认证token',
example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
})
@IsString({ message: 'Token必须是字符串' })
@IsNotEmpty({ message: 'Token不能为空' })
token: string;
/**
* 会话密码(可选)
*/
@ApiPropertyOptional({
description: '会话密码(如果会话需要密码)',
example: 'password123'
})
@IsOptional()
@IsString({ message: '会话密码必须是字符串' })
password?: string;
/**
* 初始位置(可选)
*/
@ApiPropertyOptional({
description: '用户初始位置',
example: {
mapId: 'plaza',
x: 100,
y: 200
}
})
@IsOptional()
@IsObject({ message: '初始位置必须是对象格式' })
initialPosition?: {
mapId: string;
x: number;
y: number;
};
}
/**
* 离开会话消息DTO
*
* 职责:
* - 定义用户离开游戏会话的请求数据
* - 支持主动离开和被动断开的区分
* - 提供离开原因的记录
*/
export class LeaveSessionMessage {
/**
* 消息类型标识
*/
@ApiProperty({
description: '消息类型',
example: 'leave_session',
enum: ['leave_session']
})
@IsString({ message: '消息类型必须是字符串' })
@IsOptional()
type?: 'leave_session' = 'leave_session';
/**
* 游戏会话ID
*/
@ApiProperty({
description: '游戏会话ID',
example: 'session_12345'
})
@IsString({ message: '会话ID必须是字符串' })
@IsNotEmpty({ message: '会话ID不能为空' })
sessionId: string;
/**
* 离开原因(可选)
*/
@ApiPropertyOptional({
description: '离开原因',
example: 'user_left',
enum: ['user_left', 'connection_lost', 'kicked', 'error']
})
@IsOptional()
@IsString({ message: '离开原因必须是字符串' })
reason?: string;
}
/**
* 位置更新消息DTO
*
* 职责:
* - 定义用户位置更新的请求数据
* - 验证位置坐标和地图ID的有效性
* - 支持位置元数据的扩展
*/
export class PositionUpdateMessage {
/**
* 消息类型标识
*/
@ApiProperty({
description: '消息类型',
example: 'position_update',
enum: ['position_update']
})
@IsString({ message: '消息类型必须是字符串' })
@IsOptional()
type?: 'position_update' = 'position_update';
/**
* 地图ID
*/
@ApiProperty({
description: '地图ID',
example: 'plaza',
minLength: 1,
maxLength: 50
})
@IsString({ message: '地图ID必须是字符串' })
@IsNotEmpty({ message: '地图ID不能为空' })
@Length(1, 50, { message: '地图ID长度必须在1-50个字符之间' })
mapId: string;
/**
* X轴坐标
*/
@ApiProperty({
description: 'X轴坐标',
example: 100.5,
type: 'number'
})
@IsNumber({}, { message: 'X坐标必须是数字' })
@Type(() => Number)
x: number;
/**
* Y轴坐标
*/
@ApiProperty({
description: 'Y轴坐标',
example: 200.3,
type: 'number'
})
@IsNumber({}, { message: 'Y坐标必须是数字' })
@Type(() => Number)
y: number;
/**
* 时间戳(可选,服务端会自动设置)
*/
@ApiPropertyOptional({
description: '位置更新时间戳',
example: 1641024000000
})
@IsOptional()
@IsNumber({}, { message: '时间戳必须是数字' })
@Type(() => Number)
timestamp?: number;
/**
* 扩展元数据(可选)
*/
@ApiPropertyOptional({
description: '位置扩展元数据',
example: {
speed: 5.2,
direction: 'north'
}
})
@IsOptional()
@IsObject({ message: '元数据必须是对象格式' })
metadata?: Record<string, any>;
}
/**
* 心跳消息DTO
*
* 职责:
* - 定义WebSocket连接的心跳检测消息
* - 维持连接活跃状态
* - 检测连接质量和延迟
*/
export class HeartbeatMessage {
/**
* 消息类型标识
*/
@ApiProperty({
description: '消息类型',
example: 'heartbeat',
enum: ['heartbeat']
})
@IsString({ message: '消息类型必须是字符串' })
@IsOptional()
type?: 'heartbeat' = 'heartbeat';
/**
* 客户端时间戳
*/
@ApiProperty({
description: '客户端发送时间戳',
example: 1641024000000
})
@IsNumber({}, { message: '时间戳必须是数字' })
@Type(() => Number)
timestamp: number;
/**
* 序列号(可选)
*/
@ApiPropertyOptional({
description: '心跳序列号',
example: 1
})
@IsOptional()
@IsNumber({}, { message: '序列号必须是数字' })
@Type(() => Number)
sequence?: number;
}
/**
* 通用WebSocket消息DTO
*
* 职责:
* - 定义所有WebSocket消息的基础结构
* - 提供消息类型的统一管理
* - 支持消息的路由和处理
*/
export class WebSocketMessage {
/**
* 消息类型
*/
@ApiProperty({
description: '消息类型',
example: 'join_session',
enum: ['join_session', 'leave_session', 'position_update', 'heartbeat']
})
@IsString({ message: '消息类型必须是字符串' })
@IsNotEmpty({ message: '消息类型不能为空' })
type: string;
/**
* 消息数据
*/
@ApiProperty({
description: '消息数据',
example: {}
})
@IsObject({ message: '消息数据必须是对象格式' })
data: any;
/**
* 消息ID可选
*/
@ApiPropertyOptional({
description: '消息唯一标识',
example: 'msg_12345'
})
@IsOptional()
@IsString({ message: '消息ID必须是字符串' })
messageId?: string;
/**
* 时间戳
*/
@ApiProperty({
description: '消息时间戳',
example: 1641024000000
})
@IsNumber({}, { message: '时间戳必须是数字' })
@Type(() => Number)
timestamp: number;
}