From 28bea2f0015bdd8623786858fc8ee42eb124b602 Mon Sep 17 00:00:00 2001 From: moyin <244344649@qq.com> Date: Sat, 10 Jan 2026 21:54:17 +0800 Subject: [PATCH] =?UTF-8?q?websocket=EF=BC=9A=E9=9B=86=E6=88=90=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E6=B5=8B=E8=AF=95=E5=8A=9F=E8=83=BD=E5=88=B0WebSocket?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加通知模式切换功能,支持聊天和通知两种测试模式 - 实现通知WebSocket连接和用户认证 - 添加通知发送界面,支持API和WebSocket两种发送方式 - 集成通知管理功能,支持列表查看和已读标记 - 修复HTML结构,确保通知模式与聊天模式平级显示 - 更新页面标题和功能描述 --- .../zulip/websocket_test.controller.ts | 555 +++++++++++++++++- 1 file changed, 546 insertions(+), 9 deletions(-) diff --git a/src/business/zulip/websocket_test.controller.ts b/src/business/zulip/websocket_test.controller.ts index ea4f89e..86d900a 100644 --- a/src/business/zulip/websocket_test.controller.ts +++ b/src/business/zulip/websocket_test.controller.ts @@ -19,9 +19,9 @@ export class WebSocketTestController { @Get() @ApiOperation({ - summary: '🔌 WebSocket 测试页面 - 一键测试工具 + API监控', + summary: '🔌 WebSocket 测试页面 + 通知系统 - 一键测试工具 + API监控', description: ` -**🚀 功能强大的WebSocket测试工具** +**🚀 功能强大的WebSocket测试工具 + 通知系统** 提供完整的WebSocket测试功能: - ✅ 自动获取JWT Token @@ -30,7 +30,16 @@ export class WebSocketTestController { - ✅ 聊天消息发送测试 - ✅ 位置更新测试 - ✅ 实时消息日志 -- 📡 **新增:API调用监控** - 实时显示所有HTTP请求 +- 📡 **API调用监控** - 实时显示所有HTTP请求 +- 🔔 **通知系统测试** - 完整的通知功能测试 + +**新增通知系统功能**: +- 🔔 实时通知WebSocket连接 +- 📢 通知发送和接收测试 +- 📋 通知列表管理 +- 🔢 未读通知统计 +- 🎯 支持系统、用户、广播通知 +- ⏰ 定时通知功能 **使用方法**: 1. 点击下方"Execute"按钮 @@ -58,7 +67,7 @@ export class WebSocketTestController { - WebSocket 测试工具 + API监控 - Pixel Game Server + WebSocket 测试工具 + 通知系统 - Pixel Game Server -

🎮 Pixel Game Server - WebSocket 测试工具 + API监控

+

🎮 Pixel Game Server - WebSocket 测试工具 + 通知系统

📋 使用说明

@@ -286,7 +314,8 @@ export class WebSocketTestController { • 💾 本地存储:自动保存Token,下次访问无需重新获取
• 📧 真实邮箱支持:检测到真实邮箱时使用真实验证码发送
• 📡 **API调用监控**:实时显示所有HTTP请求和响应,方便调试
- • 🔍 详细日志:支持显示请求体、响应体和调用统计 + • 🔍 详细日志:支持显示请求体、响应体和调用统计
+ • 🔔 **通知系统测试**:完整的通知功能测试,支持实时推送和API调用
@@ -319,6 +348,17 @@ export class WebSocketTestController {
+ +
+

🎛️ 功能选择

+
+ + +
+
+ + +

🔌 连接控制

@@ -458,18 +498,110 @@ export class WebSocketTestController {
+ + + +
-

📋 消息日志

+

📋 消息日志

-

📡 API 调用日志

+

📡 API 调用日志

- 实时监控前端API调用 + 实时监控前端API调用
@@ -502,6 +634,11 @@ export class WebSocketTestController { let apiSuccessCount = 0; let apiErrorCount = 0; let showApiDetails = false; + let currentMode = 'chat'; // 当前模式:chat 或 notice + + // 通知系统相关变量 + let noticeWs = null; + let noticeAuthenticated = false; // API监控相关变量 const originalFetch = window.fetch; @@ -1869,6 +2006,16 @@ export class WebSocketTestController { addMessage('system', '🎲 一键测试会自动生成随机测试账号,方便多用户测试'); addMessage('system', '📡 新功能: API调用监控已启用,可实时查看所有HTTP请求'); addMessage('system', '🔍 API日志支持显示请求体、响应体和详细统计信息'); + addMessage('system', '🔔 新增: 通知系统测试功能,可切换到通知模式进行测试'); + + // 请求通知权限 + if (Notification.permission === 'default') { + Notification.requestPermission().then(permission => { + if (permission === 'granted') { + addMessage('system', '✅ 浏览器通知权限已获取'); + } + }); + } // 检查URL参数,看是否从API文档跳转过来 const urlParams = new URLSearchParams(window.location.search); @@ -1951,6 +2098,396 @@ export class WebSocketTestController { window.addEventListener('error', function(event) { addMessage('error', '❌ 页面错误: ' + event.error.message); }); + + // ==================== 通知系统功能 ==================== + + // 切换功能模式 + function switchMode(mode) { + console.log('switchMode called with:', mode); + currentMode = mode; + const chatMode = document.getElementById('chatMode'); + const noticeMode = document.getElementById('noticeMode'); + const chatBtn = document.getElementById('chatModeBtn'); + const noticeBtn = document.getElementById('noticeModeBtn'); + const logTitle = document.getElementById('logTitle'); + const apiLogTitle = document.getElementById('apiLogTitle'); + const apiLogSubtitle = document.getElementById('apiLogSubtitle'); + + console.log('Elements found:', { + chatMode: !!chatMode, + noticeMode: !!noticeMode, + chatBtn: !!chatBtn, + noticeBtn: !!noticeBtn + }); + + if (mode === 'chat') { + if (chatMode) chatMode.style.display = 'block'; + if (noticeMode) noticeMode.style.display = 'none'; + if (chatBtn) chatBtn.style.backgroundColor = '#1976d2'; + if (noticeBtn) noticeBtn.style.backgroundColor = '#666'; + if (logTitle) logTitle.textContent = '📋 消息日志'; + if (apiLogTitle) apiLogTitle.textContent = '📡 API 调用日志'; + if (apiLogSubtitle) apiLogSubtitle.textContent = '实时监控前端API调用'; + addMessage('system', '🔄 已切换到聊天测试模式'); + console.log('Switched to chat mode'); + } else { + console.log('Switching to notice mode...'); + if (chatMode) { + chatMode.style.display = 'none'; + console.log('Chat mode hidden'); + } + if (noticeMode) { + noticeMode.style.display = 'block'; + console.log('Notice mode shown, display style:', noticeMode.style.display); + console.log('Notice mode computed style:', window.getComputedStyle(noticeMode).display); + } + if (chatBtn) chatBtn.style.backgroundColor = '#666'; + if (noticeBtn) noticeBtn.style.backgroundColor = '#1976d2'; + if (logTitle) logTitle.textContent = '🔔 通知日志'; + if (apiLogTitle) apiLogTitle.textContent = '📡 通知API日志'; + if (apiLogSubtitle) apiLogSubtitle.textContent = '实时监控通知API调用'; + addMessage('system', '🔄 已切换到通知测试模式'); + console.log('Notice mode switch completed'); + + // 自动复制Token + copyTokenFromChat(); + } + } + + // 从聊天模式复制Token + function copyTokenFromChat() { + const chatToken = document.getElementById('jwtToken').value.trim(); + if (chatToken) { + document.getElementById('noticeJwtToken').value = chatToken; + addMessage('system', '✅ 已从聊天模式复制Token'); + } else { + addMessage('system', '💡 聊天模式中没有Token,请先在聊天模式获取Token'); + } + } + + // 切换通知WebSocket连接 + function toggleNoticeConnection() { + if (noticeWs && noticeWs.readyState === WebSocket.OPEN) { + noticeWs.close(); + } else { + connectNotice(); + } + } + + // 连接通知WebSocket + function connectNotice() { + const url = document.getElementById('noticeWsUrl').value; + updateNoticeStatus('connecting', '连接中...'); + + try { + noticeWs = new WebSocket(url); + + noticeWs.onopen = function() { + updateNoticeStatus('connected', '已连接'); + document.getElementById('noticeConnectBtn').textContent = '断开连接'; + addMessage('system', '✅ 通知WebSocket连接成功'); + }; + + noticeWs.onmessage = function(event) { + try { + const data = JSON.parse(event.data); + addMessage('received', '🔔 收到通知: ' + JSON.stringify(data, null, 2)); + + if (data.type === 'notice') { + showNotificationPopup(data.data); + } else if (data.type === 'authenticated') { + noticeAuthenticated = true; + addMessage('system', '✅ 通知系统认证成功'); + } else if (data.type === 'pong') { + addMessage('system', '🏓 收到pong响应'); + } + } catch (e) { + addMessage('received', '🔔 ' + event.data); + } + }; + + noticeWs.onclose = function() { + updateNoticeStatus('disconnected', '未连接'); + document.getElementById('noticeConnectBtn').textContent = '连接通知系统'; + noticeAuthenticated = false; + addMessage('system', '🔌 通知WebSocket连接已关闭'); + }; + + noticeWs.onerror = function(error) { + addMessage('error', '❌ 通知连接错误: ' + error); + }; + + } catch (error) { + updateNoticeStatus('disconnected', '连接失败'); + addMessage('error', '❌ 通知连接失败: ' + error.message); + } + } + + // 更新通知连接状态 + function updateNoticeStatus(status, message) { + const statusEl = document.getElementById('noticeConnectionStatus'); + statusEl.className = 'status ' + status; + statusEl.textContent = message; + } + + // 认证通知连接 + function authenticateNotice() { + if (!noticeWs || noticeWs.readyState !== WebSocket.OPEN) { + addMessage('error', '❌ 请先建立通知WebSocket连接'); + return; + } + + const userId = document.getElementById('noticeUserId').value.trim(); + if (!userId) { + addMessage('error', '❌ 请输入用户ID'); + return; + } + + const message = { + event: 'authenticate', + data: { userId: parseInt(userId) } + }; + + noticeWs.send(JSON.stringify(message)); + addMessage('sent', '📤 发送认证: ' + JSON.stringify(message, null, 2)); + } + + // 通过API发送通知 + async function sendNoticeViaAPI() { + const title = document.getElementById('noticeTitle').value.trim(); + const content = document.getElementById('noticeContent').value.trim(); + const type = document.getElementById('noticeType').value; + const targetUserId = document.getElementById('targetUserId').value.trim(); + const scheduledTime = document.getElementById('scheduledTime').value; + const token = document.getElementById('noticeJwtToken').value.trim(); + + if (!title || !content) { + addMessage('error', '❌ 请输入通知标题和内容'); + return; + } + + if (!token) { + addMessage('error', '❌ 请输入JWT Token'); + return; + } + + const payload = { + title, + content, + type, + userId: targetUserId ? parseInt(targetUserId) : undefined, + scheduledAt: scheduledTime || undefined + }; + + try { + let endpoint = '/api/notices'; + if (type === 'system') { + endpoint = '/api/notices/system'; + } else if (type === 'broadcast') { + endpoint = '/api/notices/broadcast'; + } + + const response = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + token + }, + body: JSON.stringify(payload) + }); + + const result = await response.json(); + + if (response.ok) { + addMessage('system', '✅ 通知发送成功: ' + JSON.stringify(result, null, 2)); + clearNoticeForm(); + } else { + addMessage('error', '❌ 通知发送失败: ' + (result.message || '未知错误')); + } + } catch (error) { + addMessage('error', '❌ 请求失败: ' + error.message); + } + } + + // 通过WebSocket发送通知 (这里只是演示,实际通知应该通过API发送) + function sendNoticeViaWS() { + if (!noticeWs || noticeWs.readyState !== WebSocket.OPEN) { + addMessage('error', '❌ 请先建立通知WebSocket连接'); + return; + } + + if (!noticeAuthenticated) { + addMessage('error', '❌ 请先认证通知连接'); + return; + } + + const message = { + event: 'ping' + }; + + noticeWs.send(JSON.stringify(message)); + addMessage('sent', '📤 发送ping: ' + JSON.stringify(message, null, 2)); + } + + // 发送快速通知 + function sendQuickNotice(title, content) { + document.getElementById('noticeTitle').value = title; + document.getElementById('noticeContent').value = content; + document.getElementById('noticeType').value = 'system'; + sendNoticeViaAPI(); + } + + // 加载通知列表 + async function loadNoticeList() { + const token = document.getElementById('noticeJwtToken').value.trim(); + + if (!token) { + addMessage('error', '❌ 请输入JWT Token'); + return; + } + + try { + const response = await fetch('/api/notices', { + headers: { + 'Authorization': 'Bearer ' + token + } + }); + + const notices = await response.json(); + + if (response.ok) { + displayNoticeList(notices); + addMessage('system', '✅ 通知列表加载成功,共 ' + notices.length + ' 条'); + } else { + addMessage('error', '❌ 加载通知列表失败: ' + (notices.message || '未知错误')); + } + } catch (error) { + addMessage('error', '❌ 请求失败: ' + error.message); + } + } + + // 获取未读通知数量 + async function getUnreadCount() { + const token = document.getElementById('noticeJwtToken').value.trim(); + + if (!token) { + addMessage('error', '❌ 请输入JWT Token'); + return; + } + + try { + const response = await fetch('/api/notices/unread-count', { + headers: { + 'Authorization': 'Bearer ' + token + } + }); + + const result = await response.json(); + + if (response.ok) { + addMessage('system', '📊 未读通知数量: ' + result.count); + } else { + addMessage('error', '❌ 获取未读数量失败: ' + (result.message || '未知错误')); + } + } catch (error) { + addMessage('error', '❌ 请求失败: ' + error.message); + } + } + + // 显示通知列表 + function displayNoticeList(notices) { + const listEl = document.getElementById('noticeList'); + listEl.innerHTML = ''; + + if (notices.length === 0) { + listEl.innerHTML = '
暂无通知
'; + return; + } + + notices.forEach(notice => { + const noticeEl = document.createElement('div'); + noticeEl.style.cssText = 'border: 1px solid #ddd; padding: 8px; margin-bottom: 8px; border-radius: 4px; background: white;'; + + const statusColor = notice.status === 'read' ? '#666' : '#1976d2'; + const statusText = notice.status === 'read' ? '已读' : '未读'; + + noticeEl.innerHTML = \` +
\${notice.title}
+
\${notice.content}
+
+ 类型: \${notice.type} | + 状态: \${statusText} | + 时间: \${new Date(notice.createdAt).toLocaleString()} + \${notice.status !== 'read' ? + \`\` : + '' + } +
+ \`; + + listEl.appendChild(noticeEl); + }); + } + + // 标记通知为已读 + async function markNoticeAsRead(noticeId) { + const token = document.getElementById('noticeJwtToken').value.trim(); + + if (!token) { + addMessage('error', '❌ 请输入JWT Token'); + return; + } + + try { + const response = await fetch(\`/api/notices/\${noticeId}/read\`, { + method: 'PATCH', + headers: { + 'Authorization': 'Bearer ' + token + } + }); + + const result = await response.json(); + + if (response.ok) { + addMessage('system', '✅ 通知已标记为已读'); + loadNoticeList(); // 刷新列表 + } else { + addMessage('error', '❌ 标记失败: ' + (result.message || '未知错误')); + } + } catch (error) { + addMessage('error', '❌ 请求失败: ' + error.message); + } + } + + // 显示通知弹窗 + function showNotificationPopup(notice) { + // 使用浏览器通知API + if (Notification.permission === 'granted') { + new Notification(notice.title, { + body: notice.content, + icon: '/favicon.ico' + }); + } + + // 在页面上显示 + addMessage('system', \`🔔 新通知: \${notice.title} - \${notice.content}\`); + } + + // 清空通知表单 + function clearNoticeForm() { + document.getElementById('noticeTitle').value = ''; + document.getElementById('noticeContent').value = ''; + document.getElementById('targetUserId').value = ''; + document.getElementById('scheduledTime').value = ''; + } + + // 清空通知日志 + function clearNoticeLog() { + if (currentMode === 'notice') { + clearLog(); + addMessage('system', '🗑️ 通知日志已清空'); + } + }