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 小时前
springboot-数据库事务支持
数据库·spring boot·后端
fFee-ops2 小时前
321. 拼接最大数
java·linux·数据库
溟洵2 小时前
查看TCP/UDP网络连接通信情况
网络协议·tcp/ip·udp
扬子3 小时前
golang中连接达梦数据库使用域名来代替IP时会出现解析问题
开发语言·数据库·golang
天冬忘忧3 小时前
SQL题目分析:打折日期交叉问题--计算品牌总优惠天数
数据库·sql
jamesdodo3 小时前
SQL创建索引加快查询速度的方法
数据库·sql·索引·加快sql查询
洛阳泰山3 小时前
Chainlit集成Langchain并使用通义千问实现和数据库交互的网页对话应用增强扩展(text2sql)
数据库·python·langchain·交互·通义千问·postgres·chainlit
2407-2 shw3 小时前
seafaring靶场漏洞测试攻略
数据库·sql·文件上传漏洞·命令执行漏洞·文件包含漏洞·xss漏洞
limengshi1383923 小时前
通信工程学习:什么是SNI业务节点接口
网络·网络协议·学习·信息与通信
sleP4o4 小时前
MySQL指令
数据库·mysql·oracle