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>
相关推荐
爱吃青椒不爱吃西红柿‍️1 分钟前
华为ASP与CSP是什么?
服务器·前端·数据库
Yz987637 分钟前
hive的存储格式
大数据·数据库·数据仓库·hive·hadoop·数据库开发
苏-言1 小时前
Spring IOC实战指南:从零到一的构建过程
java·数据库·spring
Ljw...1 小时前
索引(MySQL)
数据库·mysql·索引
菠萝咕噜肉i1 小时前
超详细:Redis分布式锁
数据库·redis·分布式·缓存·分布式锁
长风清留扬1 小时前
一篇文章了解何为 “大数据治理“ 理论与实践
大数据·数据库·面试·数据治理
OpsEye1 小时前
MySQL 8.0.40版本自动升级异常的预警提示
数据库·mysql·数据库升级
Ljw...1 小时前
表的增删改查(MySQL)
数据库·后端·mysql·表的增删查改
刽子手发艺2 小时前
WebSocket详解、WebSocket入门案例
网络·websocket·网络协议
远歌已逝4 小时前
维护在线重做日志(二)
数据库·oracle