2 php8.0 中开发一个websocket 聊天 表设计

表设计:

sql 复制代码
-- 聊天记录表
CREATE TABLE chat (
    id INT PRIMARY KEY AUTO_INCREMENT, -- 聊天记录ID,自增主键
    from_userid INT NOT NULL, -- 发送消息的用户ID
    to_user_id INT NOT NULL, -- 接收消息的用户ID
    from_companyId INT, -- 发送消息的公司ID
    to_companyId INT, -- 接收消息的公司ID
    from_company_name VARCHAR(255), -- 发送消息公司的名称
    to_company_name VARCHAR(255), -- 接收消息公司的名称
    from_role VARCHAR(50) NOT NULL, -- 发送者角色(字符串类型)
    to_role VARCHAR(50) NOT NULL, -- 接收者角色(字符串类型)
    create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间
    type ENUM('text', 'image', 'file', 'audio', 'video') NOT NULL, -- 消息类型,文本、图片、文件、语音、视频
    text TEXT, -- 消息内容(适用于文本消息)
    withdraw BOOLEAN DEFAULT FALSE, -- 是否撤回消息
    withdraw_at TIMESTAMP NULL, -- 消息撤回时间
    view_flag BOOLEAN DEFAULT FALSE, -- 消息是否已读
    from_user_avatar VARCHAR(255), -- 发送消息用户的头像URL
    to_user_avatar VARCHAR(255), -- 接收消息用户的头像URL
    from_company_avatar VARCHAR(255), -- 发送消息公司头像URL
    to_company_avatar VARCHAR(255) -- 接收消息公司头像URL
);

-- 订单关联表
CREATE TABLE chat_order (
    id INT PRIMARY KEY AUTO_INCREMENT, -- 订单记录ID,自增主键
    order_id VARCHAR(50) NOT NULL, -- 订单编号
    chat_id INT NOT NULL, -- 关联的聊天记录ID
    type ENUM('product', 'service') NOT NULL, -- 订单类型,商品或服务
    price DECIMAL(10, 2) NOT NULL, -- 订单价格,最多10位数,两位小数
    customer_phone VARCHAR(20), -- 客户的联系电话
    status ENUM('pending', 'completed', 'cancelled', 'failed') NOT NULL, -- 订单状态,待处理、已完成、已取消、失败
    create_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间
    FOREIGN KEY (chat_id) REFERENCES chat(id) -- 外键,关联聊天记录表的ID
);

-- 添加索引以优化查询性能
CREATE INDEX idx_from_userid ON chat(from_userid); -- 发送消息用户ID索引
CREATE INDEX idx_to_user_id ON chat(to_user_id); -- 接收消息用户ID索引
CREATE INDEX idx_create_at ON chat(create_at); -- 创建时间索引
CREATE INDEX idx_order_id ON chat_order(order_id); -- 订单编号索引
CREATE INDEX idx_chat_id ON chat_order(chat_id); -- 聊天记录ID索引
CREATE INDEX idx_type ON chat_order(type); -- 订单类型索引
CREATE INDEX idx_status ON chat_order(status); -- 订单状态索引
相关的界面代码:
html 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>WebSocket 聊天室</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      background-color: #f4f4f9;
      margin: 0;
      padding: 0;
    }

    .chat-container {
      width: 50%;
      margin: 50px auto;
      background-color: #fff;
      border: 1px solid #ddd;
      border-radius: 8px;
      box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
    }

    .chat-header {
      background-color: #007bff;
      color: white;
      padding: 15px;
      text-align: center;
      font-size: 18px;
      font-weight: bold;
    }

    .chat-messages {
      height: 400px;
      overflow-y: auto;
      padding: 20px;
      background-color: #f9f9f9;
      border-bottom: 1px solid #ddd;
    }

    .message {
      display: flex;
      margin-bottom: 10px;
      padding: 10px;
      border-radius: 8px;
      max-width: 70%;
    }

    .message.sent {
      background-color: #dcf8c6;
      margin-left: auto;
      text-align: right;
      flex-direction: row-reverse;
    }

    .message.received {
      background-color: #e1f5fe;
    }

    .avatar {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      margin-right: 10px;
    }

    .message-content {
      max-width: 100%;
      word-wrap: break-word;
    }

    .chat-footer {
      padding: 20px;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .chat-footer input[type="text"] {
      width: 70%;
      padding: 10px;
      border: 1px solid #ddd;
      border-radius: 4px;
    }

    .chat-footer button {
      padding: 10px 20px;
      background-color: #007bff;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }

    .chat-footer button:hover {
      background-color: #0056b3;
    }

    .file-input {
      display: none;
    }

    .extra-options {
      display: none;
      flex-direction: column;
    }

    .extra-options button {
      margin-top: 5px;
      background-color: #17a2b8;
    }
  </style>
</head>
<body>

<div class="chat-container">
  <div class="chat-header">聊天室</div>

  <div class="chat-messages" id="chatMessages"></div>

  <div class="chat-footer">
    <div>
      <button id="attachBtn">📎</button>
      <div class="extra-options" id="extraOptions">
        <button id="uploadImageBtn">发送图片</button>
        <button id="uploadFileBtn">发送文件</button>
        <button id="sendAudioBtn">发送语音</button>
        <button id="sendVideoBtn">发送视频</button>
      </div>
      <input type="file" id="imageInput" class="file-input" accept="image/*">
      <input type="file" id="fileInput" class="file-input" accept="*/*">
    </div>
    <input type="text" id="messageInput" placeholder="输入消息...">
    <button id="sendMessageBtn">发送</button>
  </div>
</div>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
  $(document).ready(function() {
    let ws = new WebSocket("ws://localhost:3000");

    let userName = prompt("请输入您的用户名:");
    let groupName = "defaultGroup";
    let userAvatar = "https://i.pravatar.cc/40"; // 使用随机头像

    ws.onopen = function() {
      // 加入群组
      ws.send(JSON.stringify({
        action: "joinGroup",
        groupName: groupName,
        userName: userName
      }));
    };

    ws.onmessage = function(event) {
      let data = JSON.parse(event.data);

      // 处理接收到的消息
      if (data.action === "sendMessage") {
        appendMessage(data.user_name, data.message, data.user_avatar, 'received');
      }

      if (data.type === "join" || data.type === "left") {
        appendSystemMessage(data.content);
      }
    };

    $('#sendMessageBtn').click(function() {
      let messageText = $('#messageInput').val().trim();
      if (messageText !== '') {
        let messageData = {
          action: "sendMessage",
          group_name: groupName,
          user_name: userName,
          message: messageText,
          user_avatar: userAvatar
        };

        ws.send(JSON.stringify(messageData));
        appendMessage(userName, messageText, userAvatar, 'sent');
        $('#messageInput').val('');
      }
    });

    // Toggle extra options for attachments
    $('#attachBtn').click(function() {
      $('#extraOptions').toggle();
    });

    // Handle image upload
    $('#uploadImageBtn').click(function() {
      $('#imageInput').click();
    });

    $('#imageInput').change(function() {
      let file = this.files[0];
      let reader = new FileReader();
      reader.onload = function(e) {
        ws.send(JSON.stringify({
          action: "sendMessage",
          group_name: groupName,
          user_name: userName,
          message: e.target.result,  // Send base64-encoded image
          user_avatar: userAvatar,
          fileType: 'image'
        }));
        appendMessage(userName, '<img src="' + e.target.result + '" width="100">', userAvatar, 'sent');
      };
      reader.readAsDataURL(file);
    });

    // Handle file upload
    $('#uploadFileBtn').click(function() {
      $('#fileInput').click();
    });

    $('#fileInput').change(function() {
      let file = this.files[0];
      let reader = new FileReader();
      reader.onload = function(e) {
        ws.send(JSON.stringify({
          action: "sendMessage",
          group_name: groupName,
          user_name: userName,
          message: file.name,  // Send file name
          fileData: e.target.result,  // Send base64-encoded file
          user_avatar: userAvatar,
          fileType: 'file'
        }));
        appendMessage(userName, '<a href="' + e.target.result + '" download>' + file.name + '</a>', userAvatar, 'sent');
      };
      reader.readAsDataURL(file);
    });

    // Scroll to bottom
    function appendMessage(name, message, avatar, messageType) {
      let messageHtml = `
                <div class="message ${messageType}">
                    <img src="${avatar}" class="avatar">
                    <div class="message-content">
                        <strong>${name}</strong><br>${message}
                    </div>
                </div>
            `;
      $('#chatMessages').append(messageHtml);
      scrollToBottom();
    }

    function appendSystemMessage(content) {
      let systemMessageHtml = `<div class="system-message">${content}</div>`;
      $('#chatMessages').append(systemMessageHtml);
      scrollToBottom();
    }

    function scrollToBottom() {
      $('#chatMessages').scrollTop($('#chatMessages')[0].scrollHeight);
    }

    $('#messageInput').keypress(function(e) {
      if (e.which == 13) {
        $('#sendMessageBtn').click();
      }
    });
  });
</script>

</body>
</html>
相关推荐
Tttian62217 分钟前
基于Pycharm与数据库的新闻管理系统(2)Redis
数据库·redis·pycharm
做梦敲代码1 小时前
达梦数据库-读写分离集群部署
数据库·达梦数据库
EasyDSS2 小时前
国标GB28181-2022平台EasyGBS:安防监控中P2P的穿透方法
网络协议·php·音视频·p2p
小蜗牛慢慢爬行2 小时前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
hanbarger2 小时前
nosql,Redis,minio,elasticsearch
数据库·redis·nosql
网安墨雨2 小时前
常用网络协议
网络·网络协议
微服务 spring cloud2 小时前
配置PostgreSQL用于集成测试的步骤
数据库·postgresql·集成测试
先睡2 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
弗罗里达老大爷2 小时前
Redis
数据库·redis·缓存