@@ -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', '🗑️ 通知日志已清空');
+ }
+ }