首页 / 应用软件 / 手把手教学,为你的网站添加在线协同白板与团队创意协作功能

手把手教学,为你的网站添加在线协同白板与团队创意协作功能

手把手教学:为你的网站添加在线协同白板与团队创意协作功能

引言:为什么你的网站需要协同白板功能?

在当今数字化工作环境中,远程协作已成为常态。无论是产品设计、项目规划还是创意头脑风暴,团队成员往往分散在不同地点。传统的沟通方式如邮件、即时消息和视频会议虽然有效,但在创意协作方面存在明显局限——缺乏直观的视觉共享空间。

在线协同白板正是解决这一痛点的理想工具。它提供了一个虚拟的"画布",团队成员可以实时绘制图表、添加便签、上传图片、创建思维导图,并看到彼此的修改。这种视觉化协作方式能显著提高团队效率,激发创意灵感,并确保所有参与者对项目有统一的理解。

对于WordPress网站所有者来说,添加这样的功能不仅能提升用户体验,还能将你的网站从一个单向信息发布平台转变为互动协作空间。无论是企业内部协作、在线教育、咨询服务还是客户项目沟通,协同白板都能为你的网站增加巨大价值。

第一部分:准备工作与环境配置

1.1 理解WordPress二次开发基础

在开始之前,我们需要明确WordPress二次开发的基本概念。WordPress不仅是一个内容管理系统,更是一个强大的开发平台,通过其丰富的API和钩子系统,我们可以扩展其功能而不影响核心文件。

关键概念:

  • 主题与插件:功能扩展主要通过子主题或自定义插件实现
  • 动作钩子(Action Hooks):在特定时间点执行自定义代码
  • 过滤器钩子(Filter Hooks):修改WordPress处理的数据
  • 短代码(Shortcodes):在内容中嵌入动态功能
  • REST API:为前端应用提供数据接口

1.2 开发环境搭建

为了安全地进行开发,我们首先需要搭建本地测试环境:

  1. 本地服务器环境:推荐使用XAMPP、MAMP或Local by Flywheel
  2. 代码编辑器:VS Code、PHPStorm或Sublime Text
  3. 浏览器开发者工具:Chrome DevTools或Firefox Developer Tools
  4. 版本控制:Git用于代码管理

1.3 创建自定义插件框架

我们将创建一个独立的插件来管理所有协同白板功能,这样可以确保功能独立于主题,便于维护和迁移。

<?php
/**
 * Plugin Name: 协同白板与团队协作工具
 * Plugin URI: https://yourwebsite.com/
 * Description: 为WordPress网站添加在线协同白板与团队创意协作功能
 * Version: 1.0.0
 * Author: 你的名字
 * License: GPL v2 or later
 */

// 防止直接访问
if (!defined('ABSPATH')) {
    exit;
}

// 定义插件常量
define('COLLAB_WHITEBOARD_VERSION', '1.0.0');
define('COLLAB_WHITEBOARD_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('COLLAB_WHITEBOARD_PLUGIN_URL', plugin_dir_url(__FILE__));

// 初始化插件
function collab_whiteboard_init() {
    // 检查依赖项
    if (!class_exists('WP_List_Table')) {
        require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
    }
    
    // 加载必要文件
    require_once COLLAB_WHITEBOARD_PLUGIN_DIR . 'includes/class-whiteboard-manager.php';
    require_once COLLAB_WHITEBOARD_PLUGIN_DIR . 'includes/class-whiteboard-db.php';
    require_once COLLAB_WHITEBOARD_PLUGIN_DIR . 'includes/class-whiteboard-shortcodes.php';
    
    // 初始化组件
    new Whiteboard_Manager();
    new Whiteboard_Shortcodes();
}
add_action('plugins_loaded', 'collab_whiteboard_init');

// 激活插件时创建数据库表
function collab_whiteboard_activate() {
    require_once COLLAB_WHITEBOARD_PLUGIN_DIR . 'includes/class-whiteboard-db.php';
    Whiteboard_DB::create_tables();
}
register_activation_hook(__FILE__, 'collab_whiteboard_activate');

// 停用插件时的清理工作
function collab_whiteboard_deactivate() {
    // 可选的清理代码
}
register_deactivation_hook(__FILE__, 'collab_whiteboard_deactivate');

第二部分:数据库设计与用户权限管理

2.1 设计协同白板数据库结构

协同白板需要存储白板数据、用户权限和修改历史。我们创建以下数据库表:

// 在includes/class-whiteboard-db.php中
class Whiteboard_DB {
    public static function create_tables() {
        global $wpdb;
        
        $charset_collate = $wpdb->get_charset_collate();
        $table_prefix = $wpdb->prefix . 'collab_';
        
        // 白板主表
        $whiteboards_table = $table_prefix . 'whiteboards';
        $sql1 = "CREATE TABLE IF NOT EXISTS $whiteboards_table (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            title varchar(255) NOT NULL,
            description text,
            content longtext,
            created_by bigint(20) NOT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            settings text,
            is_public tinyint(1) DEFAULT 0,
            PRIMARY KEY (id)
        ) $charset_collate;";
        
        // 白板权限表
        $permissions_table = $table_prefix . 'permissions';
        $sql2 = "CREATE TABLE IF NOT EXISTS $permissions_table (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            whiteboard_id mediumint(9) NOT NULL,
            user_id bigint(20) NOT NULL,
            permission_level varchar(50) NOT NULL,
            added_by bigint(20) NOT NULL,
            added_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            UNIQUE KEY unique_whiteboard_user (whiteboard_id, user_id)
        ) $charset_collate;";
        
        // 白板历史记录表
        $history_table = $table_prefix . 'history';
        $sql3 = "CREATE TABLE IF NOT EXISTS $history_table (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            whiteboard_id mediumint(9) NOT NULL,
            user_id bigint(20) NOT NULL,
            action varchar(100) NOT NULL,
            changes text,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY whiteboard_id (whiteboard_id)
        ) $charset_collate;";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql1);
        dbDelta($sql2);
        dbDelta($sql3);
    }
}

2.2 实现用户权限系统

协同白板需要精细的权限控制,不同用户应有不同级别的访问和编辑权限:

class Whiteboard_Permissions {
    const PERMISSION_VIEW = 'view';
    const PERMISSION_COMMENT = 'comment';
    const PERMISSION_EDIT = 'edit';
    const PERMISSION_ADMIN = 'admin';
    
    /**
     * 检查用户对白板的权限
     */
    public static function check_permission($whiteboard_id, $user_id, $required_permission) {
        global $wpdb;
        
        // 获取白板信息
        $table_name = $wpdb->prefix . 'collab_whiteboards';
        $whiteboard = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table_name WHERE id = %d",
            $whiteboard_id
        ));
        
        if (!$whiteboard) {
            return false;
        }
        
        // 如果是公开白板且只需要查看权限
        if ($whiteboard->is_public && $required_permission === self::PERMISSION_VIEW) {
            return true;
        }
        
        // 创建者拥有所有权限
        if ($whiteboard->created_by == $user_id) {
            return true;
        }
        
        // 检查用户特定权限
        $permissions_table = $wpdb->prefix . 'collab_permissions';
        $user_permission = $wpdb->get_var($wpdb->prepare(
            "SELECT permission_level FROM $permissions_table 
             WHERE whiteboard_id = %d AND user_id = %d",
            $whiteboard_id, $user_id
        ));
        
        if (!$user_permission) {
            return false;
        }
        
        // 权限等级映射
        $permission_hierarchy = [
            self::PERMISSION_VIEW => 1,
            self::PERMISSION_COMMENT => 2,
            self::PERMISSION_EDIT => 3,
            self::PERMISSION_ADMIN => 4
        ];
        
        $required_level = $permission_hierarchy[$required_permission] ?? 0;
        $user_level = $permission_hierarchy[$user_permission] ?? 0;
        
        return $user_level >= $required_level;
    }
    
    /**
     * 为用户分配白板权限
     */
    public static function assign_permission($whiteboard_id, $user_id, $permission_level, $assigned_by) {
        global $wpdb;
        
        $table_name = $wpdb->prefix . 'collab_permissions';
        
        // 检查是否已存在权限记录
        $existing = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM $table_name 
             WHERE whiteboard_id = %d AND user_id = %d",
            $whiteboard_id, $user_id
        ));
        
        if ($existing) {
            // 更新现有权限
            return $wpdb->update(
                $table_name,
                [
                    'permission_level' => $permission_level,
                    'added_by' => $assigned_by
                ],
                [
                    'whiteboard_id' => $whiteboard_id,
                    'user_id' => $user_id
                ]
            );
        } else {
            // 插入新权限记录
            return $wpdb->insert(
                $table_name,
                [
                    'whiteboard_id' => $whiteboard_id,
                    'user_id' => $user_id,
                    'permission_level' => $permission_level,
                    'added_by' => $assigned_by
                ]
            );
        }
    }
}

第三部分:前端白板界面与实时协作实现

3.1 选择前端绘图库

对于协同白板,我们需要一个强大的前端绘图库。这里我们选择Fabric.js,它是一个功能强大的Canvas库,支持丰富的图形操作和事件处理。

首先,在插件中注册必要的脚本和样式:

class Whiteboard_Assets {
    public static function enqueue_frontend_assets() {
        // Fabric.js 绘图库
        wp_enqueue_script(
            'fabric-js',
            'https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.5.0/fabric.min.js',
            [],
            '4.5.0',
            true
        );
        
        // Socket.io 客户端 (用于实时通信)
        wp_enqueue_script(
            'socket-io',
            'https://cdn.socket.io/4.5.0/socket.io.min.js',
            [],
            '4.5.0',
            true
        );
        
        // 自定义白板脚本
        wp_enqueue_script(
            'collab-whiteboard',
            COLLAB_WHITEBOARD_PLUGIN_URL . 'assets/js/whiteboard.js',
            ['jquery', 'fabric-js', 'socket-io'],
            COLLAB_WHITEBOARD_VERSION,
            true
        );
        
        // 白板样式
        wp_enqueue_style(
            'collab-whiteboard-style',
            COLLAB_WHITEBOARD_PLUGIN_URL . 'assets/css/whiteboard.css',
            [],
            COLLAB_WHITEBOARD_VERSION
        );
        
        // 本地化脚本,传递必要数据
        wp_localize_script('collab-whiteboard', 'whiteboardConfig', [
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('collab_whiteboard_nonce'),
            'currentUserId' => get_current_user_id(),
            'socketServer' => self::get_socket_server_url()
        ]);
    }
    
    private static function get_socket_server_url() {
        // 这里返回你的WebSocket服务器地址
        // 可以是独立的Node.js服务器或通过WordPress REST API实现
        return home_url('/wp-json/collab-whiteboard/v1/socket');
    }
}
add_action('wp_enqueue_scripts', ['Whiteboard_Assets', 'enqueue_frontend_assets']);

3.2 创建白板画布界面

接下来,我们创建白板的主要HTML结构和JavaScript逻辑:

<!-- 在短代码输出的HTML中 -->
<div class="whiteboard-container" data-whiteboard-id="<?php echo $whiteboard_id; ?>">
    <div class="whiteboard-toolbar">
        <div class="tool-group">
            <button class="tool-btn" data-tool="select" title="选择工具">
                <i class="icon-cursor"></i>
            </button>
            <button class="tool-btn active" data-tool="pencil" title="铅笔">
                <i class="icon-pencil"></i>
            </button>
            <button class="tool-btn" data-tool="line" title="直线">
                <i class="icon-line"></i>
            </button>
            <button class="tool-btn" data-tool="rectangle" title="矩形">
                <i class="icon-square"></i>
            </button>
            <button class="tool-btn" data-tool="circle" title="圆形">
                <i class="icon-circle"></i>
            </button>
            <button class="tool-btn" data-tool="text" title="文本">
                <i class="icon-text"></i>
            </button>
        </div>
        
        <div class="tool-group">
            <input type="color" class="color-picker" value="#000000" title="颜色">
            <input type="range" class="brush-size" min="1" max="50" value="3" title="笔刷大小">
            <button class="tool-btn" data-action="undo" title="撤销">
                <i class="icon-undo"></i>
            </button>
            <button class="tool-btn" data-action="redo" title="重做">
                <i class="icon-redo"></i>
            </button>
            <button class="tool-btn" data-action="clear" title="清空白板">
                <i class="icon-trash"></i>
            </button>
        </div>
        
        <div class="tool-group user-list">
            <span class="online-users">在线用户: <span class="user-count">1</span></span>
        </div>
    </div>
    
    <div class="whiteboard-canvas-container">
        <canvas id="whiteboard-canvas"></canvas>
    </div>
    
    <div class="whiteboard-sidebar">
        <div class="sidebar-section">
            <h4>元素属性</h4>
            <div class="properties-panel">
                <!-- 动态属性控件将在这里显示 -->
            </div>
        </div>
        
        <div class="sidebar-section">
            <h4>聊天与评论</h4>
            <div class="chat-container">
                <div class="chat-messages"></div>
                <div class="chat-input">
                    <input type="text" placeholder="输入消息...">
                    <button class="send-btn">发送</button>
                </div>
            </div>
        </div>
    </div>
</div>

3.3 实现实时协作功能

实时协作是协同白板的核心功能。我们使用WebSocket实现实时数据同步:

// assets/js/whiteboard.js
class CollaborativeWhiteboard {
    constructor(containerElement) {
        this.container = containerElement;
        this.whiteboardId = containerElement.dataset.whiteboardId;
        this.canvas = null;
        this.socket = null;
        this.currentTool = 'pencil';
        this.isDrawing = false;
        this.lastPoint = null;
        this.history = [];
        this.historyIndex = -1;
        
        this.init();
    }
    
    init() {
        // 初始化画布
        this.canvas = new fabric.Canvas('whiteboard-canvas', {
            isDrawingMode: true,
            width: this.container.querySelector('.whiteboard-canvas-container').offsetWidth,
            height: 600,
            backgroundColor: '#ffffff'
        });
        
        // 连接WebSocket服务器
        this.connectSocket();
        
        // 绑定事件
        this.bindEvents();
        
        // 加载现有白板数据
        this.loadWhiteboardData();
    }
    
    connectSocket() {
        // 连接到WebSocket服务器
        this.socket = io(whiteboardConfig.socketServer, {
            query: {
                whiteboardId: this.whiteboardId,
                userId: whiteboardConfig.currentUserId
            }
        });
        
        // 监听服务器消息
        this.socket.on('connect', () => {
            console.log('已连接到白板服务器');
        });
        
        this.socket.on('drawing', (data) => {
            this.handleRemoteDrawing(data);
        });
        
        this.socket.on('object:modified', (data) => {
            this.handleRemoteObjectModification(data);
        });
        
        this.socket.on('user:joined', (data) => {
            this.updateOnlineUsers(data.users);
        });
        
        this.socket.on('user:left', (data) => {
            this.updateOnlineUsers(data.users);
        });
        
        this.socket.on('chat:message', (data) => {
            this.addChatMessage(data);
        });
    }
    
    bindEvents() {
        // 工具按钮点击事件
        this.container.querySelectorAll('.tool-btn[data-tool]').forEach(btn => {
            btn.addEventListener('click', (e) => {
                this.setTool(e.target.closest('.tool-btn').dataset.tool);
            });
        });
        
        // 动作按钮点击事件
        this.container.querySelectorAll('.tool-btn[data-action]').forEach(btn => {
            btn.addEventListener('click', (e) => {
                const action = e.target.closest('.tool-btn').dataset.action;
                this.handleAction(action);
            });
        });
        
        // 颜色选择器

EventListener('change', (e) => {

        this.setColor(e.target.value);
    });
    
    // 笔刷大小
    this.container.querySelector('.brush-size').addEventListener('input', (e) => {
        this.setBrushSize(parseInt(e.target.value));
    });
    
    // 画布事件
    this.canvas.on('mouse:down', (options) => {
        this.onMouseDown(options);
    });
    
    this.canvas.on('mouse:move', (options) => {
        this.onMouseMove(options);
    });
    
    this.canvas.on('mouse:up', (options) => {
        this.onMouseUp(options);
    });
    
    this.canvas.on('object:added', (options) => {
        this.onObjectAdded(options);
    });
    
    this.canvas.on('object:modified', (options) => {
        this.onObjectModified(options);
    });
    
    // 聊天功能
    const chatInput = this.container.querySelector('.chat-input input');
    const sendBtn = this.container.querySelector('.chat-input .send-btn');
    
    sendBtn.addEventListener('click', () => {
        this.sendChatMessage(chatInput.value);
        chatInput.value = '';
    });
    
    chatInput.addEventListener('keypress', (e) => {
        if (e.key === 'Enter') {
            this.sendChatMessage(chatInput.value);
            chatInput.value = '';
        }
    });
    
    // 窗口大小调整
    window.addEventListener('resize', () => {
        this.resizeCanvas();
    });
}

setTool(tool) {
    this.currentTool = tool;
    
    // 更新按钮状态
    this.container.querySelectorAll('.tool-btn[data-tool]').forEach(btn => {
        btn.classList.toggle('active', btn.dataset.tool === tool);
    });
    
    // 根据工具设置画布模式
    switch(tool) {
        case 'select':
            this.canvas.isDrawingMode = false;
            this.canvas.selection = true;
            break;
        case 'pencil':
            this.canvas.isDrawingMode = true;
            this.canvas.freeDrawingBrush = new fabric.PencilBrush(this.canvas);
            this.canvas.freeDrawingBrush.width = this.brushSize || 3;
            this.canvas.freeDrawingBrush.color = this.currentColor || '#000000';
            break;
        case 'line':
            this.canvas.isDrawingMode = false;
            this.canvas.selection = false;
            // 实现直线绘制逻辑
            break;
        // 其他工具的实现...
    }
}

setColor(color) {
    this.currentColor = color;
    if (this.canvas.isDrawingMode) {
        this.canvas.freeDrawingBrush.color = color;
    }
    
    // 如果选择了对象,则更改对象颜色
    const activeObject = this.canvas.getActiveObject();
    if (activeObject) {
        activeObject.set('fill', color);
        this.canvas.renderAll();
        this.sendObjectUpdate(activeObject);
    }
}

setBrushSize(size) {
    this.brushSize = size;
    if (this.canvas.isDrawingMode) {
        this.canvas.freeDrawingBrush.width = size;
    }
}

onMouseDown(options) {
    if (!options.target && this.currentTool === 'line') {
        this.isDrawing = true;
        this.lastPoint = options.pointer;
    }
}

onMouseMove(options) {
    if (this.isDrawing && this.currentTool === 'line') {
        // 绘制直线预览
    }
}

onMouseUp(options) {
    if (this.isDrawing && this.currentTool === 'line' && this.lastPoint) {
        const line = new fabric.Line([
            this.lastPoint.x, this.lastPoint.y,
            options.pointer.x, options.pointer.y
        ], {
            stroke: this.currentColor || '#000000',
            strokeWidth: this.brushSize || 3
        });
        
        this.canvas.add(line);
        this.isDrawing = false;
        this.lastPoint = null;
    }
}

onObjectAdded(options) {
    // 保存到历史记录
    this.saveToHistory();
    
    // 发送到服务器
    if (options.target) {
        this.sendDrawingData({
            type: 'object:added',
            object: options.target.toJSON(),
            userId: whiteboardConfig.currentUserId
        });
    }
}

onObjectModified(options) {
    // 发送对象更新到服务器
    if (options.target) {
        this.sendObjectUpdate(options.target);
    }
}

sendDrawingData(data) {
    if (this.socket && this.socket.connected) {
        this.socket.emit('drawing', {
            whiteboardId: this.whiteboardId,
            ...data
        });
    }
}

sendObjectUpdate(object) {
    this.sendDrawingData({
        type: 'object:modified',
        object: object.toJSON(),
        userId: whiteboardConfig.currentUserId
    });
}

handleRemoteDrawing(data) {
    // 忽略自己发送的数据
    if (data.userId === whiteboardConfig.currentUserId) return;
    
    switch(data.type) {
        case 'object:added':
            fabric.util.enlivenObjects([data.object], (objects) => {
                objects.forEach(obj => {
                    this.canvas.add(obj);
                });
            });
            break;
            
        case 'object:modified':
            const object = this.canvas.getObjects().find(obj => 
                obj.data && obj.data.id === data.object.data.id
            );
            if (object) {
                object.set(data.object);
                this.canvas.renderAll();
            }
            break;
    }
}

sendChatMessage(message) {
    if (!message.trim()) return;
    
    if (this.socket && this.socket.connected) {
        this.socket.emit('chat:message', {
            whiteboardId: this.whiteboardId,
            userId: whiteboardConfig.currentUserId,
            message: message,
            timestamp: new Date().toISOString()
        });
    }
}

addChatMessage(data) {
    const chatMessages = this.container.querySelector('.chat-messages');
    const messageElement = document.createElement('div');
    messageElement.className = 'chat-message';
    messageElement.innerHTML = `
        <div class="message-header">
            <span class="user-name">用户 ${data.userId}</span>
            <span class="message-time">${new Date(data.timestamp).toLocaleTimeString()}</span>
        </div>
        <div class="message-content">${this.escapeHtml(data.message)}</div>
    `;
    
    chatMessages.appendChild(messageElement);
    chatMessages.scrollTop = chatMessages.scrollHeight;
}

updateOnlineUsers(users) {
    const userCountElement = this.container.querySelector('.user-count');
    if (userCountElement) {
        userCountElement.textContent = users.length;
    }
}

saveToHistory() {
    // 保存当前画布状态到历史记录
    const state = JSON.stringify(this.canvas.toJSON());
    this.history = this.history.slice(0, this.historyIndex + 1);
    this.history.push(state);
    this.historyIndex++;
}

loadWhiteboardData() {
    // 通过AJAX加载白板数据
    jQuery.ajax({
        url: whiteboardConfig.ajaxUrl,
        method: 'POST',
        data: {
            action: 'load_whiteboard',
            whiteboard_id: this.whiteboardId,
            nonce: whiteboardConfig.nonce
        },
        success: (response) => {
            if (response.success && response.data) {
                this.canvas.loadFromJSON(response.data, () => {
                    this.canvas.renderAll();
                });
            }
        }
    });
}

resizeCanvas() {
    const container = this.container.querySelector('.whiteboard-canvas-container');
    this.canvas.setDimensions({
        width: container.offsetWidth,
        height: 600
    });
}

escapeHtml(text) {
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}

}

// 初始化白板
document.addEventListener('DOMContentLoaded', () => {

const whiteboardContainers = document.querySelectorAll('.whiteboard-container');
whiteboardContainers.forEach(container => {
    new CollaborativeWhiteboard(container);
});

});


## 第四部分:后端WebSocket服务器与数据同步

### 4.1 设置WebSocket服务器

对于实时协作,我们需要一个WebSocket服务器来处理客户端连接和数据广播。这里我们使用Node.js和Socket.io:

// server/whiteboard-server.js
const http = require('http');
const socketIo = require('socket.io');
const mysql = require('mysql2/promise');

// 创建HTTP服务器
const server = http.createServer();
const io = socketIo(server, {

cors: {
    origin: process.env.WORDPRESS_URL || "http://localhost",
    methods: ["GET", "POST"]
}

});

// 数据库连接池
const dbPool = mysql.createPool({

host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'wordpress_user',
password: process.env.DB_PASSWORD || 'password',
database: process.env.DB_NAME || 'wordpress_db',
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0

});

// 存储在线用户
const onlineUsers = new Map(); // whiteboardId -> Set of userIds
const userSockets = new Map(); // userId -> Set of socketIds

io.on('connection', (socket) => {

const { whiteboardId, userId } = socket.handshake.query;

console.log(`用户 ${userId} 连接到白板 ${whiteboardId}`);

// 验证用户权限
validateUserPermission(whiteboardId, userId).then(hasPermission => {
    if (!hasPermission) {
        socket.disconnect();
        return;
    }
    
    // 加入白板房间
    socket.join(`whiteboard:${whiteboardId}`);
    
    // 更新在线用户列表
    if (!onlineUsers.has(whiteboardId)) {
        onlineUsers.set(whiteboardId, new Set());
    }
    onlineUsers.get(whiteboardId).add(userId);
    
    // 存储用户socket映射
    if (!userSockets.has(userId)) {
        userSockets.set(userId, new Set());
    }
    userSockets.get(userId).add(socket.id);
    
    // 广播用户加入事件
    io.to(`whiteboard:${whiteboardId}`).emit('user:joined', {
        whiteboardId,
        userId,
        users: Array.from(onlineUsers.get(whiteboardId))
    });
    
    // 处理绘图事件
    socket.on('drawing', (data) => {
        // 验证数据
        if (data.whiteboardId !== whiteboardId) return;
        
        // 广播给同一白板的其他用户
        socket.to(`whiteboard:${whiteboardId}`).emit('drawing', {
            ...data,
            timestamp: new Date().toISOString()
        });
        
        // 保存到数据库(可选,根据需求)
        saveDrawingAction(whiteboardId, userId, data);
    });
    
    // 处理聊天消息
    socket.on('chat:message', (data) => {
        if (data.whiteboardId !== whiteboardId) return;
        
        // 广播聊天消息
        io.to(`whiteboard:${whiteboardId}`).emit('chat:message', {
            ...data,
            timestamp: new Date().toISOString()
        });
        
        // 保存聊天记录到数据库
        saveChatMessage(whiteboardId, userId, data.message);
    });
    
    // 处理断开连接
    socket.on('disconnect', () => {
        console.log(`用户 ${userId} 断开连接`);
        
        // 清理用户socket映射
        if (userSockets.has(userId)) {
            userSockets.get(userId).delete(socket.id);
            if (userSockets.get(userId).size === 0) {
                userSockets.delete(userId);
                
                // 从在线用户中移除
                if (onlineUsers.has(whiteboardId)) {
                    onlineUsers.get(whiteboardId).delete(userId);
                    
                    // 广播用户离开事件
                    io.to(`whiteboard:${whiteboardId}`).emit('user:left', {
                        whiteboardId,
                        userId,
                        users: Array.from(onlineUsers.get(whiteboardId))
                    });
                }
            }
        }
    });
}).catch(error => {
    console.error('权限验证失败:', error);
    socket.disconnect();
});

});

// 验证用户权限
async function validateUserPermission(whiteboardId, userId) {

try {
    const [rows] = await dbPool.execute(
        `SELECT w.is_public, p.permission_level 
         FROM wp_collab_whiteboards w
         LEFT JOIN wp_collab_permissions p ON w.id = p.whiteboard_id AND p.user_id = ?
         WHERE w.id = ?`,
        [userId, whiteboardId]
    );
    
    if (rows.length === 0) return false;
    
    const whiteboard = rows[0];
    
    // 检查权限
    if (whiteboard.is_public) return true;
    if (whiteboard.permission_level) return true;
    
    return false;
} catch (error) {
    console.error('数据库查询错误:', error);
    return false;
}

}

// 保存绘图动作到历史记录
async function saveDrawingAction(whiteboardId, userId, data) {

try {
    await dbPool.execute(
        `INSERT INTO wp_collab_history 
         (whiteboard_id, user_id, action, changes) 
         VALUES (?, ?, ?, ?)`,
        [whiteboardId, userId, data.type, JSON.stringify(data)]
    );
} catch (error) {
    console.error('保存历史记录失败:', error);
}

}

// 保存聊天消息
async function saveChatMessage(whiteboardId, userId, message) {

try {
    await dbPool.execute(
        `INSERT INTO wp_collab_chat 
         (whiteboard_id, user_id, message) 
         VALUES (?, ?, ?)`,
        [whiteboardId, userId, message]
    );
} catch (error) {
    console.error('保存聊天消息失败:', error);
}

}

// 启动服务器
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {

console.log(`白板服务器运行在端口 ${PORT}`);

});


### 4.2 WordPress REST API集成

为了让WebSocket服务器与WordPress通信,我们需要创建REST API端点:

// includes/class-whiteboard-api.php
class Whiteboard_API {

public function register_routes() {
    register_rest_route('collab-whiteboard/v1', '/whiteboard/(?P<id>d+)', [
        [
            'methods' => 'GET',
            'callback' => [$this, 'get_whiteboard'],
            'permission_callback' => [$this, 'check_whiteboard_permission']
        ],
        [
            'methods' => 'POST',
            'callback' => [$this, 'update_whiteboard'],
            'permission_callback' => [$this, 'check_edit_permission']
        ]
    ]);
    
    register_rest_route('collab-whiteboard/v1', '/whiteboard/(?P<id>d+)/history', [
        [
            'methods' => 'GET',
            'callback' => [$this, 'get_whiteboard_history'],
            'permission_callback' => [$this, 'check_whiteboard_permission']
        ]
    ]);
    
    register_rest_route('collab-whiteboard/v1', '/whiteboard/(?P<id>d+)/chat', [
        [
            'methods' => 'GET',
            'callback' => [$this, 'get_chat_messages'],
            'permission_callback' => [$this, 'check_whiteboard_permission']
        ],
        [
            'methods' => 'POST',
            'callback' => [$this, 'post_chat_message'],
            'permission_callback' => [$this, 'check_comment_permission']
        ]
    ]);
}

public function get_whiteboard($request) {
    $whiteboard_id = $request['id'];
    
    global $wpdb;
    $table_name = $wpdb->prefix . 'collab_whiteboards';
    
    $whiteboard = $wpdb->get_row($wpdb->prepare(
        "SELECT * FROM $table_name WHERE id = %d",
        $whiteboard_id
    ));
    
    if (!$whiteboard) {
        return new WP_Error('not_found', '白板不存在', ['status' => 404]);
    }
    
    return rest_ensure_response([
        'id' => $whiteboard->id,
        'title' => $whiteboard->title,
        'description' => $whiteboard->description,
        'content' => json_decode($whiteboard->content, true),
        'settings' => json_decode($whiteboard->settings, true),
        'created_by' => $whiteboard->created_by,
        'created_at' => $whiteboard->created_at,
        'updated_at' => $whiteboard->updated_at,
        'is_public' => (bool)$whiteboard->is_public
    ]);
}

public function update_whiteboard($request) {
    $whiteboard_id = $request['id'];
    $content = $request->get_param('content');
    
    if (empty($content)) {
        return new WP_Error('invalid_data', '内容不能为空', ['status' => 400]);
    }
    
    global $wpdb;
    $table_name = $wpdb->prefix . 'collab_whiteboards';
    
    $result = $wpdb->update(
        $table_name,
        [
            'content' => json_encode($content),
            'updated_at' => current_time('mysql')
        ],
        ['id' => $whiteboard_id]
    );
    
    if ($result === false) {
        return new WP_Error('update_failed', '更新失败', ['status' => 500]);
    }
    
    return rest_ensure_response([
        'success' => true,
        'message' => '白板已更新'
    ]);
}

public function check_whiteboard_permission($request) {
    $whiteboard_id = $request['id'];
    $user_id = get_current_user_id();
    
    return Whiteboard_Permissions::check_permission(
        $whiteboard_id,
        $user_id,
        Whiteboard_Permissions::PERMISSION_VIEW
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5294.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

工作时间:周一至周五,9:00-17:30,节假日休息
返回顶部