使用人工智能写一个websocket聊天页面

发布于:2025-08-28 ⋅ 阅读:(19) ⋅ 点赞:(0)

最近在学习springboot 里面的定时任务和websocket模块,然后为了偷懒使用人工智能写了个页面,的确强大,直接看图片
在这里插入图片描述

在这里插入图片描述
网页配置chat.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket消息收发演示</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }

        body {
            background: linear-gradient(135deg, #6e8efb, #a777e3);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        .container {
            width: 100%;
            max-width: 600px;
            background-color: white;
            border-radius: 12px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
            overflow: hidden;
        }

        .header {
            background-color: #4e73df;
            color: white;
            padding: 20px;
            text-align: center;
        }

        .status-bar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px 20px;
            background-color: #f8f9fc;
            border-bottom: 1px solid #e3e6f0;
        }

        .status {
            display: flex;
            align-items: center;
            font-size: 14px;
        }

        .status-indicator {
            width: 10px;
            height: 10px;
            border-radius: 50%;
            margin-right: 8px;
        }

        .connected {
            background-color: #1cc88a;
        }

        .disconnected {
            background-color: #e74a3b;
        }

        .connecting {
            background-color: #f6c23e;
        }

        .control-panel {
            padding: 15px 20px;
            display: flex;
            gap: 10px;
            border-bottom: 1px solid #e3e6f0;
        }

        button {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-weight: 500;
            transition: all 0.3s;
        }

        .connect-btn {
            background-color: #1cc88a;
            color: white;
        }

        .connect-btn:hover {
            background-color: #17a673;
        }

        .disconnect-btn {
            background-color: #e74a3b;
            color: white;
        }

        .disconnect-btn:hover {
            background-color: #c0352d;
        }

        .message-input {
            padding: 15px 20px;
            border-bottom: 1px solid #e3e6f0;
        }

        #message {
            width: 100%;
            padding: 12px 15px;
            border: 1px solid #d1d3e2;
            border-radius: 4px;
            font-size: 14px;
        }

        #send-btn {
            width: 100%;
            margin-top: 10px;
            padding: 10px;
            background-color: #4e73df;
            color: white;
            font-weight: 600;
        }

        #send-btn:hover {
            background-color: #2e59d9;
        }

        .messages-container {
            padding: 20px;
            height: 300px;
            overflow-y: auto;
            background-color: #f8f9fc;
        }

        .message {
            margin-bottom: 15px;
            padding: 12px 15px;
            border-radius: 6px;
            font-size: 14px;
            line-height: 1.5;
        }

        .received {
            background-color: #eaecf4;
            color: #333;
        }

        .sent {
            background-color: #4e73df;
            color: white;
            text-align: right;
        }

        .system {
            background-color: #f8f9fc;
            color: #858796;
            font-style: italic;
            text-align: center;
            border: 1px dashed #d1d3e2;
        }

        .error {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }

        .timestamp {
            font-size: 12px;
            color: #858796;
            margin-top: 5px;
        }

        .notification {
            position: fixed;
            top: 20px;
            right: 20px;
            padding: 15px 20px;
            border-radius: 6px;
            color: white;
            opacity: 0;
            transform: translateX(100%);
            transition: all 0.3s ease;
            z-index: 1000;
        }

        .notification.show {
            opacity: 1;
            transform: translateX(0);
        }

        .notification.success {
            background-color: #1cc88a;
        }

        .notification.error {
            background-color: #e74a3b;
        }

        .notification.info {
            background-color: #4e73df;
        }
    </style>
</head>
<body>
<div class="container">
    <div class="header">
        <h1>WebSocket消息收发演示</h1>
    </div>

    <div class="status-bar">
        <div class="status">
            <div class="status-indicator disconnected" id="status-indicator"></div>
            <span id="status-text">未连接</span>
        </div>
        <div id="connection-info"></div>
    </div>

    <div class="control-panel">
        <button class="connect-btn" id="connect-btn">连接</button>
        <button class="disconnect-btn" id="disconnect-btn" disabled>断开</button>
    </div>

    <div class="message-input">
        <input type="text" id="message" placeholder="输入要发送的消息..." disabled>
        <button id="send-btn" disabled>发送消息</button>
    </div>

    <div class="messages-container" id="messages-container">
        <div class="message system">
            欢迎使用WebSocket演示。请先点击"连接"按钮建立连接。
        </div>
    </div>
</div>

<div class="notification" id="notification"></div>

<script>
    // WebSocket连接
    let socket = null;
    var client_id=Math.random().toString(36).substring(2);
    const connectBtn = document.getElementById('connect-btn');
    const disconnectBtn = document.getElementById('disconnect-btn');
    const sendBtn = document.getElementById('send-btn');
    const messageInput = document.getElementById('message');
    const messagesContainer = document.getElementById('messages-container');
    const statusIndicator = document.getElementById('status-indicator');
    const statusText = document.getElementById('status-text');
    const connectionInfo = document.getElementById('connection-info');
    const notification = document.getElementById('notification');

    // 初始化
    function init() {
        // 添加事件监听器
        connectBtn.addEventListener('click', connectWebSocket);
        disconnectBtn.addEventListener('click', disconnectWebSocket);
        sendBtn.addEventListener('click', sendMessage);
        messageInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter') sendMessage();
        });
    }

    // 连接WebSocket
    function connectWebSocket() {
        // 使用公共的WebSocket测试服务
        // 注意:实际使用时可能需要替换为您的WebSocket服务器地址
        try {
            const baseUrl = window.location.origin;
            const contextPath = window.location.pathname.split('/')[1];
            const wsUrl = `${baseUrl.replace('http', 'ws')}/ws/${client_id}`;

            socket = new WebSocket(wsUrl);
            // socket = new WebSocket("ws://localhost:8080/ws/"+client_id);
            // 更新UI状态
            updateConnectionStatus('connecting', '连接中...');
            connectBtn.disabled = true;
            disconnectBtn.disabled = false;

            // WebSocket事件处理
            socket.onopen = function(event) {
                showNotification('连接成功!', 'success');
                updateConnectionStatus('connected', '已连接');
                messageInput.disabled = false;
                sendBtn.disabled = false;
                addSystemMessage('WebSocket连接已建立');
            };

            socket.onmessage = function(event) {
                addMessage(event.data, 'received');
            };

            socket.onerror = function(event) {
                showNotification('连接错误!', 'error');
                addSystemMessage('WebSocket连接错误', 'error');
                console.error('WebSocket错误:', event);
            };

            socket.onclose = function(event) {
                showNotification('连接已关闭', 'info');
                updateConnectionStatus('disconnected', '未连接');
                connectBtn.disabled = false;
                disconnectBtn.disabled = true;
                messageInput.disabled = true;
                sendBtn.disabled = true;
                addSystemMessage('WebSocket连接已关闭');
            };

        } catch (error) {
            showNotification('连接失败: ' + error.message, 'error');
            console.error('WebSocket连接失败:', error);
            updateConnectionStatus('disconnected', '连接失败');
            connectBtn.disabled = false;
        }
    }

    // 断开WebSocket连接
    function disconnectWebSocket() {
        if (socket && socket.readyState === WebSocket.OPEN) {
            socket.close();
        }
    }

    // 发送消息
    function sendMessage() {
        const message = messageInput.value.trim();
        if (!message) return;

        if (socket && socket.readyState === WebSocket.OPEN) {
            socket.send(message);
            addMessage(message, 'sent');
            messageInput.value = '';
        } else {
            showNotification('无法发送消息: 连接未建立', 'error');
        }
    }

    // 添加消息到消息容器
    function addMessage(text, type) {
        const messageElement = document.createElement('div');
        messageElement.classList.add('message', type);

        const messageContent = document.createElement('div');
        messageContent.textContent = text;

        const timestamp = document.createElement('div');
        timestamp.classList.add('timestamp');
        timestamp.textContent = new Date().toLocaleTimeString();

        messageElement.appendChild(messageContent);
        messageElement.appendChild(timestamp);

        messagesContainer.appendChild(messageElement);
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    }

    // 添加系统消息
    function addSystemMessage(text, type = 'system') {
        const messageElement = document.createElement('div');
        messageElement.classList.add('message', type);
        messageElement.textContent = text;

        messagesContainer.appendChild(messageElement);
        messagesContainer.scrollTop = messagesContainer.scrollHeight;
    }

    // 更新连接状态
    function updateConnectionStatus(status, text) {
        statusIndicator.className = 'status-indicator ' + status;
        statusText.textContent = text;

        // 更新连接信息
        if (status === 'connected') {
            const baseUrl = window.location.origin;
            const contextPath = window.location.pathname.split('/')[1];
            const wsUrl = `${baseUrl.replace('http', 'ws')}/ws/${client_id}`;

            // socket = new WebSocket(wsUrl);
            connectionInfo.textContent = '使用'+wsUrl;
        } else {
            connectionInfo.textContent = '';
        }
    }

    // 显示通知
    function showNotification(message, type) {
        notification.textContent = message;
        notification.className = 'notification ' + type;
        notification.classList.add('show');

        setTimeout(() => {
            notification.classList.remove('show');
        }, 3000);
    }

    // 页面加载完成后初始化
    window.onload = init;
</script>
</body>
</html>

java springboot后端项目
WebSocketConfig.java文件

package com.yestea.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

WebConfig.java文件

package com.yestea.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.yestea.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.text.SimpleDateFormat;
import java.util.List;
import java.util.TimeZone;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/template/**").addResourceLocations("classpath:/template/");
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor).addPathPatterns("/**")  // 拦截所有请求
                .excludePathPatterns("/login", "/error", "/regisger", "/doc.html", "/webjars/**", "/swagger-resources/**", "/v2/api-docs", "/v2/api-docs-ext", "/csrf","/template/**"); // 排除登录和测试接口
    }


    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 创建消息转换器
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();

        // 获取ObjectMapper
        ObjectMapper objectMapper = converter.getObjectMapper();

        // 设置日期格式
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        objectMapper.setDateFormat(dateFormat);

        // 设置时区
        objectMapper.setTimeZone(TimeZone.getDefault());

        // 设置序列化规则(可选)
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // 将配置好的转换器添加到转换器列表的第一个位置,确保优先使用
        converters.add(0, converter);
    }
}

定时任务MyTask.java

package com.yestea.task;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.yestea.websocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
@EnableScheduling
@Slf4j
public class MyTask {
    @Scheduled(cron="0 * * * * ?")
    public void executeTask(){
        Date date=new Date();
        String format = DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
        log.info("定时任务开始执行:{}",format);
    }

    @Scheduled(cron="0 * * * * ?")
    public void sendAllMessage(){
        Date date=new Date();
        String format = DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
        log.info("websocket定时任务开始广播:{}",format);
        WebSocketServer.sendMessage("定时任务开始广播:"+format);
    }
}

WebSocketServer.java

package com.yestea.websocket;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
    private static Map<String,Session> sessionMap=new HashMap<>();
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        log.info("连接成功"+ sid);
        System.out.println("连接成功");
        sessionMap.put(sid,session);
    }
    @OnMessage
    public void onMessage(String message, @PathParam("sid") String sid) {
        log.info("收到消息"+ message);
        System.out.println("收到消息");
        for (String key : sessionMap.keySet()) {
            Session session = sessionMap.get(key);
        }
    }
    @OnClose
    public void onClose(@PathParam("sid") String sid) {
        log.info("连接关闭"+ sid);
        System.out.println("连接关闭");
        sessionMap.remove(sid);
    }
    public static void sendMessage(String message) {
        Collection<Session> values = sessionMap.values();
        for (Session session : values) {
            try {
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }

    }
}

springboot启动日志

2025-08-27 13:44:00.003  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : websocket定时任务开始广播:2025-08-27 13:44:00
2025-08-27 13:44:00.004  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : 定时任务开始执行:2025-08-27 13:44:00
2025-08-27 13:45:00.013  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : 定时任务开始执行:2025-08-27 13:45:00
2025-08-27 13:45:00.013  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : websocket定时任务开始广播:2025-08-27 13:45:00
2025-08-27 13:46:00.010  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : websocket定时任务开始广播:2025-08-27 13:46:00
2025-08-27 13:46:00.010  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : 定时任务开始执行:2025-08-27 13:46:00
2025-08-27 13:47:00.008  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : websocket定时任务开始广播:2025-08-27 13:47:00
2025-08-27 13:47:00.008  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : 定时任务开始执行:2025-08-27 13:47:00
2025-08-27 13:48:00.004  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : 定时任务开始执行:2025-08-27 13:48:00
2025-08-27 13:48:00.005  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : websocket定时任务开始广播:2025-08-27 13:48:00
2025-08-27 13:49:00.015  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : websocket定时任务开始广播:2025-08-27 13:49:00
2025-08-27 13:49:00.015  INFO 16268 --- [   scheduling-1] com.yestea.task.MyTask                   : 定时任务开始执行:2025-08-27 13:49:00