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>
相关推荐
数据库生产实战9 分钟前
Oracle 19C RAC下TRUNCATE TABLE的REUSE STORAGE选项作用和风险浅析!
数据库·oracle
wydaicls40 分钟前
C语言完成Socket通信
c语言·网络·websocket
小白银子42 分钟前
零基础从头教学Linux(Day 60)
linux·数据库·mysql·oracle
瀚高PG实验室1 小时前
数据库安全配置指导
服务器·数据库·瀚高数据库
憋问我,我也不会1 小时前
MYSQL 命令
数据库·mysql
24K老游2 小时前
postgres15 flink cdc同步测试
数据库
无泡汽水2 小时前
MySQL入门练习50题
数据库·mysql
JIngJaneIL3 小时前
助农惠农服务平台|助农服务系统|基于SprinBoot+vue的助农服务系统(源码+数据库+文档)
java·前端·数据库·vue.js·论文·毕设·助农惠农服务平台
云外天ノ☼3 小时前
待办事项全栈实现:Vue3 + Node.js (Koa) + MySQL深度整合,构建生产级任务管理系统的技术实践
前端·数据库·vue.js·mysql·vue3·koa·jwt认证
小光学长3 小时前
基于Vue的智慧楼宇报修平台设计与实现066z15wb(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js