Files
whale-town-end/src/business/zulip/websocket_docs.controller.ts
moyin 0f37130832 refactor:重构业务层服务架构
- 重构共享模块,移除冗余DTO定义
- 优化Zulip服务模块,重新组织控制器结构
- 更新用户管理和认证服务
- 移除过时的登录服务测试文件
2026-01-08 23:05:13 +08:00

431 lines
11 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 API 文档控制器
*
* 功能描述:
* - 提供 WebSocket API 的详细文档
* - 展示消息格式和事件类型
* - 提供连接示例和测试工具
*
* 职责分离:
* - API文档提供完整的WebSocket API使用说明
* - 示例代码:提供各种编程语言的连接示例
* - 调试支持:提供消息格式验证和测试工具
* - 开发指导:提供最佳实践和故障排除指南
*
* 最近修改:
* - 2026-01-07: 代码规范优化 - 完善文件头注释和修改记录 (修改者: moyin)
*
* @author angjustinl
* @version 1.0.1
* @since 2025-01-07
* @lastModified 2026-01-07
*/
import { Controller, Get } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
@ApiTags('chat')
@Controller('websocket')
export class WebSocketDocsController {
/**
* 获取 WebSocket API 文档
*/
@Get('docs')
@ApiOperation({
summary: 'WebSocket API 文档',
description: '获取 WebSocket 连接和消息格式的详细文档'
})
@ApiResponse({
status: 200,
description: 'WebSocket API 文档',
schema: {
type: 'object',
properties: {
connection: {
type: 'object',
properties: {
url: {
type: 'string',
example: 'ws://localhost:3000/game',
description: 'WebSocket 连接地址'
},
namespace: {
type: 'string',
example: '/game',
description: 'Socket.IO 命名空间'
},
transports: {
type: 'array',
items: { type: 'string' },
example: ['websocket', 'polling'],
description: '支持的传输协议'
}
}
},
authentication: {
type: 'object',
properties: {
required: {
type: 'boolean',
example: true,
description: '是否需要认证'
},
method: {
type: 'string',
example: 'JWT Token',
description: '认证方式'
},
tokenFormat: {
type: 'object',
description: 'JWT Token 格式要求'
}
}
},
events: {
type: 'object',
description: '支持的事件和消息格式'
}
}
}
})
getWebSocketDocs() {
return {
connection: {
url: 'ws://localhost:3000/game',
namespace: '/game',
transports: ['websocket', 'polling'],
options: {
timeout: 20000,
forceNew: true,
reconnection: true,
reconnectionAttempts: 3,
reconnectionDelay: 1000
}
},
authentication: {
required: true,
method: 'JWT Token',
tokenFormat: {
issuer: 'whale-town',
audience: 'whale-town-users',
type: 'access',
requiredFields: ['sub', 'username', 'email', 'role'],
example: {
sub: 'user_123',
username: 'player_name',
email: 'user@example.com',
role: 'user',
type: 'access',
aud: 'whale-town-users',
iss: 'whale-town',
iat: 1767768599,
exp: 1768373399
}
}
},
events: {
clientToServer: {
login: {
description: '用户登录',
format: {
type: 'login',
token: 'JWT_TOKEN_HERE'
},
example: {
type: 'login',
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
},
responses: ['login_success', 'login_error']
},
chat: {
description: '发送聊天消息',
format: {
t: 'chat',
content: 'string',
scope: 'local | global'
},
example: {
t: 'chat',
content: '大家好!我刚进入游戏',
scope: 'local'
},
responses: ['chat_sent', 'chat_error']
},
position_update: {
description: '更新玩家位置',
format: {
t: 'position',
x: 'number',
y: 'number',
mapId: 'string'
},
example: {
t: 'position',
x: 150,
y: 400,
mapId: 'whale_port'
},
responses: []
}
},
serverToClient: {
login_success: {
description: '登录成功响应',
format: {
t: 'login_success',
sessionId: 'string',
userId: 'string',
username: 'string',
currentMap: 'string'
},
example: {
t: 'login_success',
sessionId: '89aff162-52d9-484e-9a35-036ba63a2280',
userId: 'user_123',
username: 'Player_123',
currentMap: 'whale_port'
}
},
login_error: {
description: '登录失败响应',
format: {
t: 'login_error',
message: 'string'
},
example: {
t: 'login_error',
message: 'Token验证失败'
}
},
chat_sent: {
description: '消息发送成功确认',
format: {
t: 'chat_sent',
messageId: 'number',
message: 'string'
},
example: {
t: 'chat_sent',
messageId: 137,
message: '消息发送成功'
}
},
chat_error: {
description: '消息发送失败',
format: {
t: 'chat_error',
message: 'string'
},
example: {
t: 'chat_error',
message: '消息内容不能为空'
}
},
chat_render: {
description: '接收到聊天消息',
format: {
t: 'chat_render',
from: 'string',
txt: 'string',
bubble: 'boolean'
},
example: {
t: 'chat_render',
from: 'Player_456',
txt: '欢迎新玩家!',
bubble: true
}
}
}
},
maps: {
whale_port: {
name: 'Whale Port',
displayName: '鲸鱼港',
zulipStream: 'Whale Port',
description: '游戏的主要港口区域'
},
pumpkin_valley: {
name: 'Pumpkin Valley',
displayName: '南瓜谷',
zulipStream: 'Pumpkin Valley',
description: '充满南瓜的神秘山谷'
},
novice_village: {
name: 'Novice Village',
displayName: '新手村',
zulipStream: 'Novice Village',
description: '新玩家的起始区域'
}
},
examples: {
javascript: {
connection: `
// 使用 Socket.IO 客户端连接
const io = require('socket.io-client');
const socket = io('ws://localhost:3000/game', {
transports: ['websocket', 'polling'],
timeout: 20000,
forceNew: true,
reconnection: true,
reconnectionAttempts: 3,
reconnectionDelay: 1000
});
// 连接成功
socket.on('connect', () => {
console.log('连接成功:', socket.id);
// 发送登录消息
socket.emit('login', {
type: 'login',
token: 'YOUR_JWT_TOKEN_HERE'
});
});
// 登录成功
socket.on('login_success', (data) => {
console.log('登录成功:', data);
// 发送聊天消息
socket.emit('chat', {
t: 'chat',
content: '大家好!',
scope: 'local'
});
});
// 接收聊天消息
socket.on('chat_render', (data) => {
console.log('收到消息:', data.from, '说:', data.txt);
});
`,
godot: `
# Godot WebSocket 客户端示例
extends Node
var socket = WebSocketClient.new()
var url = "ws://localhost:3000/game"
func _ready():
socket.connect("connection_closed", self, "_closed")
socket.connect("connection_error", self, "_error")
socket.connect("connection_established", self, "_connected")
socket.connect("data_received", self, "_on_data")
var err = socket.connect_to_url(url)
if err != OK:
print("连接失败")
func _connected(protocol):
print("WebSocket 连接成功")
# 发送登录消息
var login_msg = {
"type": "login",
"token": "YOUR_JWT_TOKEN_HERE"
}
socket.get_peer(1).put_packet(JSON.print(login_msg).to_utf8())
func _on_data():
var packet = socket.get_peer(1).get_packet()
var message = JSON.parse(packet.get_string_from_utf8())
print("收到消息: ", message.result)
`
}
},
troubleshooting: {
commonIssues: [
{
issue: 'Token验证失败',
solution: '确保JWT Token包含正确的issuer、audience和type字段'
},
{
issue: '连接超时',
solution: '检查服务器是否运行,防火墙设置是否正确'
},
{
issue: '消息发送失败',
solution: '确保已经成功登录,消息内容不为空'
}
],
testTools: [
{
name: 'WebSocket King',
url: 'https://websocketking.com/',
description: '在线WebSocket测试工具'
},
{
name: 'Postman',
description: 'Postman也支持WebSocket连接测试'
}
]
}
};
}
/**
* 获取消息格式示例
*/
@Get('message-examples')
@ApiOperation({
summary: '消息格式示例',
description: '获取各种 WebSocket 消息的格式示例'
})
@ApiResponse({
status: 200,
description: '消息格式示例',
})
getMessageExamples() {
return {
login: {
request: {
type: 'login',
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0X3VzZXJfMTIzIiwidXNlcm5hbWUiOiJ0ZXN0X3VzZXIiLCJlbWFpbCI6InRlc3RfdXNlckBleGFtcGxlLmNvbSIsInJvbGUiOiJ1c2VyIiwidHlwZSI6ImFjY2VzcyIsImF1ZCI6IndoYWxlLXRvd24tdXNlcnMiLCJpc3MiOiJ3aGFsZS10b3duIiwiaWF0IjoxNzY3NzY4NTk5LCJleHAiOjE3NjgzNzMzOTl9.Mq3YccSV_pMKxIAbeNRAUws1j7doqFqvlSv4Z9DhGjI'
},
successResponse: {
t: 'login_success',
sessionId: '89aff162-52d9-484e-9a35-036ba63a2280',
userId: 'test_user_123',
username: 'test_user',
currentMap: 'whale_port'
},
errorResponse: {
t: 'login_error',
message: 'Token验证失败'
}
},
chat: {
request: {
t: 'chat',
content: '大家好!我刚进入游戏',
scope: 'local'
},
successResponse: {
t: 'chat_sent',
messageId: 137,
message: '消息发送成功'
},
errorResponse: {
t: 'chat_error',
message: '消息内容不能为空'
},
incomingMessage: {
t: 'chat_render',
from: 'Player_456',
txt: '欢迎新玩家!',
bubble: true
}
},
position: {
request: {
t: 'position',
x: 150,
y: 400,
mapId: 'pumpkin_valley'
}
}
};
}
}