文章目录
-
- 在当今快速变化的商业环境中,柔性供应链已成为企业保持竞争力的关键。WordPress作为全球最流行的内容管理系统,在供应链管理中扮演着重要角色。本文将详细介绍如何在WordPress中开发实时协同编辑功能,使供应链各环节参与者能够同步协作,提高工作效率和响应速度。
-
- <?php /** * WordPress实时协同编辑插件 - WebSocket服务器集成 * 文件名:ws-supplychain-collab.php */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } class SupplyChain_WebSocket_Server { private $server; private $clients; private $document_states; public function __construct() { $this->clients = new SplObjectStorage(); $this->document_states = []; // 初始化WebSocket服务器 add_action('init', [$this, 'init_websocket_server']); } /** * 初始化WebSocket服务器 */ public function init_websocket_server() { // 检查是否在CLI模式下运行 if (php_sapi_name() === 'cli') { $this->start_server(); } } /** * 启动WebSocket服务器 */ private function start_server() { // 创建WebSocket服务器实例 $this->server = new RatchetApp('localhost', 8080, '0.0.0.0'); // 注册消息处理类 $this->server->route('/supplychain', new SupplyChain_Collaboration_Handler(), ['*']); echo "WebSocket服务器启动在端口 8080n"; $this->server->run(); } } /** * WebSocket消息处理器 */ class SupplyChain_Collaboration_Handler implements RatchetMessageComponentInterface { public function onOpen(RatchetConnectionInterface $conn) { // 新客户端连接 global $clients; $clients->attach($conn); echo "新客户端连接: {$conn->resourceId}n"; } public function onMessage(RatchetConnectionInterface $from, $msg) { // 处理客户端消息 $data = json_decode($msg, true); switch ($data['type']) { case 'join_document': $this->handleJoinDocument($from, $data); break; case 'edit_operation': $this->handleEditOperation($from, $data); break; case 'cursor_update': $this->handleCursorUpdate($from, $data); break; } } /** * 处理加入文档请求 */ private function handleJoinDocument($conn, $data) { $document_id = $data['document_id']; $user_id = $data['user_id']; // 发送当前文档状态给新用户 $response = [ 'type' => 'document_state', 'content' => $this->getDocumentState($document_id), 'users' => $this->getDocumentUsers($document_id) ]; $conn->send(json_encode($response)); // 通知其他用户有新成员加入 $this->broadcastToDocument($document_id, [ 'type' => 'user_joined', 'user_id' => $user_id, 'username' => get_userdata($user_id)->display_name ], $conn); } public function onClose(RatchetConnectionInterface $conn) { // 客户端断开连接 global $clients; $clients->detach($conn); echo "客户端断开: {$conn->resourceId}n"; } public function onError(RatchetConnectionInterface $conn, Exception $e) { echo "错误: {$e->getMessage()}n"; $conn->close(); } } // 初始化插件 new SupplyChain_WebSocket_Server(); ?>
- /** * WordPress实时协同编辑器 - 前端组件 * 文件名:supplychain-editor.js */ import React, { useState, useEffect, useRef } from 'react'; import { io } from 'socket.io-client'; import { Editor, EditorState, ContentState, convertToRaw } from 'draft-js'; import 'draft-js/dist/Draft.css'; const SupplyChainEditor = ({ documentId, userId }) => { const [editorState, setEditorState] = useState(EditorState.createEmpty()); const [connectedUsers, setConnectedUsers] = useState([]); const [socket, setSocket] = useState(null); const editorRef = useRef(null); // 初始化WebSocket连接 useEffect(() => { const newSocket = io('wss://your-domain.com:8080/supplychain', { transports: ['websocket'], secure: true }); setSocket(newSocket); // 加入文档 newSocket.emit('join_document', { document_id: documentId, user_id: userId }); // 监听文档状态更新 newSocket.on('document_state', (data) => { const contentState = ContentState.createFromText(data.content); setEditorState(EditorState.createWithContent(contentState)); setConnectedUsers(data.users); }); // 监听编辑操作 newSocket.on('edit_operation', (operation) => { applyRemoteOperation(operation); }); // 监听用户加入/离开 newSocket.on('user_joined', (user) => { setConnectedUsers(prev => [...prev, user]); }); newSocket.on('user_left', (userId) => { setConnectedUsers(prev => prev.filter(u => u.id !== userId)); }); return () => newSocket.close(); }, [documentId, userId]); /** * 处理本地编辑操作 */ const handleEditorChange = (newEditorState) => { const oldContent = editorState.getCurrentContent(); const newContent = newEditorState.getCurrentContent(); // 检测变化并生成操作 const operation = generateOperation(oldContent, newContent); if (operation && socket) { // 发送操作到服务器 socket.emit('edit_operation', { document_id: documentId, operation: operation, user_id: userId, timestamp: Date.now() }); } setEditorState(newEditorState); }; /** * 生成编辑操作 */ const generateOperation = (oldContent, newContent) => { const oldText = oldContent.getPlainText(); const newText = newContent.getPlainText(); // 简化实现:实际应使用OT算法 if (oldText !== newText) { return { type: 'text_change', oldText: oldText, newText: newText, selection: editorState.getSelection() }; } return null; }; /** * 应用远程操作 */ const applyRemoteOperation = (operation) => { if (operation.type === 'text_change') { const contentState = ContentState.createFromText(operation.newText); const newEditorState = EditorState.createWithContent(contentState); // 保留当前选择状态 const selectionState = editorState.getSelection(); const finalEditorState = EditorState.forceSelection( newEditorState, selectionState ); setEditorState(finalEditorState); } }; /** * 发送光标位置更新 */ const sendCursorUpdate = (selection) => { if (socket) { socket.emit('cursor_update', { document_id: documentId, user_id: userId, cursor_position: selection.getStartOffset(), selection_range: { start: selection.getStartOffset(), end: selection.getEndOffset() } }); } }; return ( <div className="supplychain-editor-container"> <div className="editor-header"> <h3>供应链协同编辑器</h3> <div className="user-list"> <span>在线用户: </span> {connectedUsers.map(user => ( <span key={user.id} className="user-badge"> {user.username} </span> ))} </div> </div> <div className="editor-wrapper"> <Editor ref={editorRef} editorState={editorState} onChange={handleEditorChange} onFocus={() => sendCursorUpdate(editorState.getSelection())} placeholder="开始协同编辑供应链文档..." /> </div> <div className="editor-footer"> <div className="document-info"> 文档ID: {documentId} | 自动保存已启用 </div> <div className="editor-tools"> <button className="btn-save" onClick={handleSave}> 保存版本 </button> <button className="btn-history" onClick={showHistory}> 查看历史 </button> </div> </div> </div> ); }; export default SupplyChainEditor;
- <?php /** * WordPress实时协同编辑插件主文件 * 文件名:supplychain-collaboration.php */ /* Plugin Name: 供应链协同编辑系统 Description: 为WordPress添加实时协同编辑功能,特别适用于柔性供应链管理 Version: 1.0.0 Author: 您的名称 */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } class SupplyChain_Collaboration_Plugin { private static $instance = null; public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } private function __construct() { // 初始化钩子 $this->init_hooks(); } /** * 初始化WordPress钩子 */ private function init_hooks() { // 添加管理菜单 add_action('admin_menu', [$this, 'add_admin_menu']); // 注册短代码 add_shortcode('supplychain_editor', [$this, 'render_editor_shortcode']); // 注册REST API端点 add_action('rest_api_init', [$this, 'register_rest_endpoints']); // 加载脚本和样式 add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']); add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']); } /** * 添加管理菜单 */ public function add_admin_menu() { add_menu_page( '供应链协同编辑', '供应链协同', 'manage_options', 'supplychain-collab', [$this, 'render_admin_page'], 'dashicons-edit', 30 ); } /** * 渲染编辑器短代码 */ public function render_editor_shortcode($atts) { $atts = shortcode_atts([ 'document_id' => 'default', 'title' => '供应链文档' ], $atts); // 检查用户权限 if (!is_user_logged_in()) { return '<p>请先登录以使用协同编辑功能</p>'; } $user_id = get_current_user_id(); ob_start(); ?> <div id="supplychain-editor-root" data-document-id="<?php echo esc_attr($atts['document_id']); ?>" data-user-id="<?php echo esc_attr($user_id); ?>"> <!-- React组件将在这里渲染 --> </div> <script> document.addEventListener('DOMContentLoaded', function() { const rootElement = document.getElementById('supplychain-editor-root'); const documentId = rootElement.dataset.documentId; const userId = parseInt(rootElement.dataset.userId); // 这里应该初始化React应用 console.log('初始化协同编辑器:', documentId, userId); }); </script> <?php return ob_get_clean(); } /** * 注册REST API端点 */ public function register_rest_endpoints() { // 获取文档历史记录 register_rest_route('supplychain/v1', '/document/(?P<id>d+)/history', [ 'methods' => 'GET', 'callback' => [$this, 'get_document_history'], 'permission_callback' => [$this, 'check_api_permission'] ]); // 保存文档版本 register_rest_route('supplychain/v1', '/document/(?P<id>d+)/save', [ 'methods' => 'POST', 'callback' => [$this, 'save_document_version'], 'permission_callback' => [$this, 'check_api_permission'] ]); } /** * 获取文档历史记录 */ public function get_document_history($request) { $document_id = $request->get_param('id'); global $wpdb; $table_name = $wpdb->prefix . 'supplychain_document_history'; $history = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $table_name WHERE document_id = %d ORDER BY created_at DESC LIMIT 50", $document_id ) ); return rest_ensure_response($history); } /** * 检查API权限 */ public function check_api_permission($request) { return current_user_can('edit_posts'); } /** * 加载前端脚本 */ public function enqueue_scripts() { wp_enqueue_script( 'supplychain-editor', plugin_dir_url(__FILE__) . 'js/supplychain-editor.js', ['wp-element', 'wp-components'], '1.0.0', true ); wp_enqueue_style( 'supplychain-editor-style', plugin_dir_url(__FILE__) . 'css/editor-style.css' ); } } // 初始化插件 SupplyChain_Collaboration_Plugin::get_instance(); /** * 插件激活时创建数据库表 */ register_activation_hook(__FILE__, 'supplychain_collab_activate'); function supplychain_collab_activate() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // 创建文档历史表 $table_name = $wpdb->prefix . 'supplychain_document_history'; $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id bigint(20) NOT NULL AUTO_INCREMENT, document_id varchar(100) NOT NULL, content longtext NOT NULL, user_id bigint(20) NOT NULL, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY document_id (document_id), KEY user_id (user_id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } ?>
- 通过本文的教程,您已经学会了如何在WordPress中开发一个完整的实时协同编辑系统,特别适用于柔性供应链管理场景。这个系统允许供应链中的不同参与者(供应商、制造商、分销商)实时协作编辑文档,大大提高供应链的响应速度和协作效率。 系统采用了现代化的技术栈,包括WebSocket实时通信、React前端框架和WordPress REST API,确保了系统的可扩展性和性能。您可以根据实际需求进一步扩展功能,如添加版本控制、冲突解决算法或与现有供应链管理系统的集成。 记住,实时协同编辑系统的成功不仅取决于技术实现,还需要考虑用户体验、培训支持和组织流程的配合。建议从小规模试点开始,逐步推广到整个供应链网络。
-
- /** * 操作转换(OT)算法核心实现 * 文件名:ot-algorithm.js */ class OperationTransformer { constructor() { this.operations = []; this.revision = 0; } /** * 生成基本操作对象 * @param {string} type - 操作类型: insert/delete/retain * @param {*} value - 操作值 * @param {number} position - 位置 * @returns {Object} 操作对象 */ static createOperation(type, value, position) { return { type, value, position, revision: Date.now(), clientId: this.clientId }; } /** * 转换操作以解决冲突 * @param {Object} op1 - 第一个操作 * @param {Object} op2 - 第二个操作 * @returns {Array} 转换后的操作对 */ transform(op1, op2) { if (op1.type === 'retain' && op2.type === 'retain') { return this.transformRetainRetain(op1, op2); } else if (op1.type === 'insert' && op2.type === 'insert') { return this.transformInsertInsert(op1, op2); } else if (op1.type === 'delete' && op2.type === 'delete') { return this.transformDeleteDelete(op1, op2); } else if (op1.type === 'insert' && op2.type === 'delete') { return this.transformInsertDelete(op1, op2); } else if (op1.type === 'delete' && op2.type === 'insert') { return this.transformDeleteInsert(op1, op2); } return [op1, op2]; } /** * 转换 retain-retain 操作 */ transformRetainRetain(op1, op2) { if (op1.position === op2.position) { return [op1, op2]; } if (op1.position < op2.position) { return [ op1, { ...op2, position: op2.position - 1 } ]; } else { return [ { ...op1, position: op1.position - 1 }, op2 ]; } } /** * 转换 insert-insert 操作 */ transformInsertInsert(op1, op2) { if (op1.position <= op2.position) { return [ op1, { ...op2, position: op2.position + op1.value.length } ]; } else { return [ { ...op1, position: op1.position + op2.value.length }, op2 ]; } } /** * 应用操作到文本 * @param {string} text - 原始文本 * @param {Object} operation - 要应用的操作 * @returns {string} 应用后的文本 */ applyOperation(text, operation) { switch (operation.type) { case 'insert': return this.applyInsert(text, operation); case 'delete': return this.applyDelete(text, operation); case 'retain': return text; default: return text; } } applyInsert(text, operation) { const before = text.slice(0, operation.position); const after = text.slice(operation.position); return before + operation.value + after; } applyDelete(text, operation) { const before = text.slice(0, operation.position); const after = text.slice(operation.position + operation.value.length); return before + after; } /** * 批量应用操作 * @param {string} text - 原始文本 * @param {Array} operations - 操作数组 * @returns {string} 应用所有操作后的文本 */ applyOperations(text, operations) { let result = text; operations.forEach(op => { result = this.applyOperation(result, op); }); return result; } } // 导出供其他模块使用 export default OperationTransformer;
- <?php /** * 冲突解决与版本控制系统 * 文件名:conflict-resolver.php */ class ConflictResolver { private $wpdb; private $table_name; public function __construct() { global $wpdb; $this->wpdb = $wpdb; $this->table_name = $wpdb->prefix . 'supplychain_operations'; } /** * 保存操作记录 */ public function save_operation($document_id, $operation, $user_id, $parent_revision) { $data = [ 'document_id' => $document_id, 'operation' => json_encode($operation), 'user_id' => $user_id, 'parent_revision' => $parent_revision, 'revision' => $this->get_next_revision($document_id), 'created_at' => current_time('mysql'), 'applied' => 0 ]; $result = $this->wpdb->insert($this->table_name, $data); if ($result) { return $this->wpdb->insert_id; } return false; } /** * 获取下一个版本号 */ private function get_next_revision($document_id) { $max_revision = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT MAX(revision) FROM {$this->table_name} WHERE document_id = %s", $document_id ) ); return $max_revision ? $max_revision + 1 : 1; } /** * 解决操作冲突 */ public function resolve_conflicts($document_id, $new_operation, $client_revision) { // 获取未应用的操作 $pending_operations = $this->get_pending_operations($document_id, $client_revision); if (empty($pending_operations)) { return [$new_operation]; } $transformed_operations = []; $current_op = $new_operation; // 依次转换操作 foreach ($pending_operations as $pending_op) { $operation_data = json_decode($pending_op->operation, true); list($transformed_current, $transformed_pending) = $this->transform_operations( $current_op, $operation_data ); $current_op = $transformed_current; $transformed_operations[] = $transformed_pending; } // 标记已解决的操作为已应用 $this->mark_operations_applied($pending_operations); return array_merge($transformed_operations, [$current_op]); } /** * 获取待处理操作 */ private function get_pending_operations($document_id, $since_revision) { return $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE document_id = %s AND revision > %d AND applied = 0 ORDER BY revision ASC", $document_id, $since_revision ) ); } /** * 标记操作为已应用 */ private function mark_operations_applied($operations) { $ids = array_map(function($op) { return $op->id; }, $operations); if (!empty($ids)) { $ids_string = implode(',', array_map('intval', $ids)); $this->wpdb->query( "UPDATE {$this->table_name} SET applied = 1 WHERE id IN ({$ids_string})" ); } } /** * 获取文档完整历史 */ public function get_document_history($document_id, $limit = 100) { return $this->wpdb->get_results( $this->wpdb->prepare( "SELECT o.*, u.display_name as user_name FROM {$this->table_name} o LEFT JOIN {$this->wpdb->users} u ON o.user_id = u.ID WHERE o.document_id = %s ORDER BY o.revision DESC LIMIT %d", $document_id, $limit ) ); } /** * 回滚到指定版本 */ public function rollback_to_revision($document_id, $target_revision) { // 获取当前内容 $current_content = $this->get_document_content($document_id); // 获取从目标版本到当前版本的所有操作 $operations = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT operation FROM {$this->table_name} WHERE document_id = %s AND revision > %d ORDER BY revision ASC", $document_id, $target_revision ) ); // 反向应用操作以回滚 $reversed_operations = array_reverse($operations); $rolled_back_content = $current_content; foreach ($reversed_operations as $op) { $operation = json_decode($op->operation, true); $inverse_op = $this->inverse_operation($operation); $rolled_back_content = $this->apply_single_operation( $rolled_back_content, $inverse_op ); } return $rolled_back_content; } /** * 生成逆操作 */ private function inverse_operation($operation) { switch ($operation['type']) { case 'insert': return [ 'type' => 'delete', 'position' => $operation['position'], 'value' => $operation['value'] ]; case 'delete': return [ 'type' => 'insert', 'position' => $operation['position'], 'value' => $operation['value'] ]; default: return $operation; } } }
- /** * 实时协同状态管理器 * 文件名:collaboration-state.js */ class CollaborationStateManager { constructor(documentId, userId) { this.documentId = documentId; this.userId = userId; this.connectedUsers = new Map(); this.cursorPositions = new Map(); this.selectionRanges = new Map(); this.presenceTimeout = 30000; // 30秒超时 // 初始化心跳检测 this.initHeartbeat(); } /** * 初始化心跳检测 */ initHeartbeat() { this.heartbeatInterval = setInterval(() => { this.sendHeartbeat(); }, 10000); // 清理超时用户 this.cleanupInterval = setInterval(() => { this.cleanupInactiveUsers(); }, 5000); } /** * 发送心跳 */ sendHeartbeat() { if (this.socket && this.socket.connected) { this.socket.emit('heartbeat', { document_id: this.documentId, user_id: this.userId, timestamp: Date.now() }); } } /** * 更新用户状态 */ updateUserPresence(userId, data) { const userState = this.connectedUsers.get(userId) || { id: userId, lastSeen: Date.now(), active: true }; userState.lastSeen = Date.now(); userState.active = true; userState.name = data.name || userState.name; userState.color = data.color || this.generateUserColor(userId); this.connectedUsers.set(userId, userState); // 触发状态更新事件 this.emitStateChange(); } /** * 更新光标位置 */ updateCursorPosition(userId, position, selection) { this.cursorPositions.set(userId, { position, timestamp: Date.now() }); if (selection) { this.selectionRanges.set(userId, selection); } // 广播光标更新 this.broadcastCursorUpdate(userId, position, selection); } /** * 广播光标更新 */ broadcastCursorUpdate(userId, position, selection) { if (this.socket) { this.socket.emit('cursor_update', { document_id: this.documentId, user_id: userId, cursor_position: position, selection_range: selection, timestamp: Date.now() }); } } /** * 清理不活跃用户 */ cleanupInactiveUsers() { const now = Date.now(); const inactiveUsers = []; this.connectedUsers.forEach((user, userId) => { if (now - user.lastSeen > this.presenceTimeout) { user.active = false; inactiveUsers.push(userId); } }); // 移除长时间不活跃的用户 inactiveUsers.forEach(userId => { if (userId !== this.userId) { this.connectedUsers.delete(userId); this.cursorPositions.delete(userId); this.selectionRanges.delete(userId); } }); if (inactiveUsers.length > 0) { this.emitStateChange(); } } /** * 生成用户颜色 */ generateUserColor(userId) { const colors = [ '#FF6B6B', '#4ECDC4', '#FFD166', '#06D6A0', '#118AB2', '#073B4C', '#EF476F', '#7209B7' ]; const hash = userId.toString().split('').reduce((acc, char) => { return char.charCodeAt(0) + ((acc << 5) - acc); }, 0); return colors[Math.abs(hash) % colors.length]; } /** * 获取用户光标位置 */ getUserCursor(userId) { return this.cursorPositions.get(userId); } /** * 获取所有活跃用户 */ getActiveUsers() { const activeUsers = []; this.connectedUsers.forEach(user => { if (user.active) { activeUsers.push(user); } }); return activeUsers; } /** * 获取用户选择范围 */ getUserSelection(userId) { return this.selectionRanges.get(userId); } /** * 触发状态变化事件 */ emitStateChange() { const event = new CustomEvent('collaborationStateChange', { detail: { users: this.getActiveUsers(), cursors: Array.from(this.cursorPositions.entries()), selections: Array.from(this.selectionRanges.entries()) } }); window.dispatchEvent(event); } /** * 销毁清理 */ destroy() { if (this.heartbeatInterval) { clearInterval(this.heartbeatInterval); } if (this.cleanupInterval) { clearInterval(this.cleanupInterval); } // 发送离开通知 if (this.socket) { this.socket.emit('user_left', { document_id: this.documentId, user_id: this.userId }); } } } // 导出状态管理器 export default CollaborationStateManager;
- <?php /** * 供应链特定功能模块 * 文件名:supplychain-features.php */ class SupplyChainFeatures { /** * 初始化供应链功能 */ public function init() { // 添加上下文感知功能 add_filter('supplychain_document_context', [$this, 'add_supplychain_context']); // 添加审批工作流 add_action('supplychain_document_updated', [$this, 'trigger_approval_workflow']); // 添加库存关联 add_action('supplychain_item_mentioned', [$this, 'link_inventory_data']); } /** * 添加上下文感知数据 */ public function add_supplychain_context($document) { global $wpdb; // 获取相关订单信息 $order_data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}supplychain_orders WHERE document_id = %s", $document['id'] ) ); // 获取供应商信息 $supplier_data = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}supplychain_suppliers WHERE id = %d", $order_data->supplier_id ?? 0 ) ); // 获取库存状态 $inventory_status = $this->get_inventory_status($document['items']); return array_merge($document, [ 'order_info' => $order_data, 'supplier_info' => $supplier_data, 'inventory_status' => $inventory_status, 'lead_time' => $this->calculate_lead_time($order_data), 'risk_level' => $this->assess_risk_level($document) ]); } /** * 触发审批工作流 */ public function trigger_approval_workflow($document_id, $changes) { $document = $this->get_document($document_id); // 检查是否需要审批 if ($this->requires_approval($document, $changes)) { $approvers = $this->get_approvers($document); foreach ($approvers as $approver) { $this->send_approval_request($approver, $document, $changes); } // 更新文档状态为待审批 $this->update_document_status($document_id, 'pending_approval'); } } /** * 链接库存数据 */
在当今快速变化的商业环境中,柔性供应链已成为企业保持竞争力的关键。WordPress作为全球最流行的内容管理系统,在供应链管理中扮演着重要角色。本文将详细介绍如何在WordPress中开发实时协同编辑功能,使供应链各环节参与者能够同步协作,提高工作效率和响应速度。
我们的实时协同编辑系统将采用以下技术栈:
- 前端:JavaScript + React.js
- 实时通信:WebSocket (Socket.io)
- 后端:WordPress REST API + 自定义PHP插件
- 数据同步:Operational Transformation (OT)算法
- 数据库:MySQL (WordPress默认)
在开始开发前,请确保您的WordPress环境满足以下要求:
- WordPress 5.0+
- PHP 7.4+
- MySQL 5.6+
- SSL证书(用于安全的WebSocket连接)
<?php
/**
* WordPress实时协同编辑插件 - WebSocket服务器集成
* 文件名:ws-supplychain-collab.php
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class SupplyChain_WebSocket_Server {
private $server;
private $clients;
private $document_states;
public function __construct() {
$this->clients = new SplObjectStorage();
$this->document_states = [];
// 初始化WebSocket服务器
add_action('init', [$this, 'init_websocket_server']);
}
/**
* 初始化WebSocket服务器
*/
public function init_websocket_server() {
// 检查是否在CLI模式下运行
if (php_sapi_name() === 'cli') {
$this->start_server();
}
}
/**
* 启动WebSocket服务器
*/
private function start_server() {
// 创建WebSocket服务器实例
$this->server = new RatchetApp('localhost', 8080, '0.0.0.0');
// 注册消息处理类
$this->server->route('/supplychain', new SupplyChain_Collaboration_Handler(), ['*']);
echo "WebSocket服务器启动在端口 8080n";
$this->server->run();
}
}
/**
* WebSocket消息处理器
*/
class SupplyChain_Collaboration_Handler implements RatchetMessageComponentInterface {
public function onOpen(RatchetConnectionInterface $conn) {
// 新客户端连接
global $clients;
$clients->attach($conn);
echo "新客户端连接: {$conn->resourceId}n";
}
public function onMessage(RatchetConnectionInterface $from, $msg) {
// 处理客户端消息
$data = json_decode($msg, true);
switch ($data['type']) {
case 'join_document':
$this->handleJoinDocument($from, $data);
break;
case 'edit_operation':
$this->handleEditOperation($from, $data);
break;
case 'cursor_update':
$this->handleCursorUpdate($from, $data);
break;
}
}
/**
* 处理加入文档请求
*/
private function handleJoinDocument($conn, $data) {
$document_id = $data['document_id'];
$user_id = $data['user_id'];
// 发送当前文档状态给新用户
$response = [
'type' => 'document_state',
'content' => $this->getDocumentState($document_id),
'users' => $this->getDocumentUsers($document_id)
];
$conn->send(json_encode($response));
// 通知其他用户有新成员加入
$this->broadcastToDocument($document_id, [
'type' => 'user_joined',
'user_id' => $user_id,
'username' => get_userdata($user_id)->display_name
], $conn);
}
public function onClose(RatchetConnectionInterface $conn) {
// 客户端断开连接
global $clients;
$clients->detach($conn);
echo "客户端断开: {$conn->resourceId}n";
}
public function onError(RatchetConnectionInterface $conn, Exception $e) {
echo "错误: {$e->getMessage()}n";
$conn->close();
}
}
// 初始化插件
new SupplyChain_WebSocket_Server();
?>
<?php
/**
* WordPress实时协同编辑插件 - WebSocket服务器集成
* 文件名:ws-supplychain-collab.php
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class SupplyChain_WebSocket_Server {
private $server;
private $clients;
private $document_states;
public function __construct() {
$this->clients = new SplObjectStorage();
$this->document_states = [];
// 初始化WebSocket服务器
add_action('init', [$this, 'init_websocket_server']);
}
/**
* 初始化WebSocket服务器
*/
public function init_websocket_server() {
// 检查是否在CLI模式下运行
if (php_sapi_name() === 'cli') {
$this->start_server();
}
}
/**
* 启动WebSocket服务器
*/
private function start_server() {
// 创建WebSocket服务器实例
$this->server = new RatchetApp('localhost', 8080, '0.0.0.0');
// 注册消息处理类
$this->server->route('/supplychain', new SupplyChain_Collaboration_Handler(), ['*']);
echo "WebSocket服务器启动在端口 8080n";
$this->server->run();
}
}
/**
* WebSocket消息处理器
*/
class SupplyChain_Collaboration_Handler implements RatchetMessageComponentInterface {
public function onOpen(RatchetConnectionInterface $conn) {
// 新客户端连接
global $clients;
$clients->attach($conn);
echo "新客户端连接: {$conn->resourceId}n";
}
public function onMessage(RatchetConnectionInterface $from, $msg) {
// 处理客户端消息
$data = json_decode($msg, true);
switch ($data['type']) {
case 'join_document':
$this->handleJoinDocument($from, $data);
break;
case 'edit_operation':
$this->handleEditOperation($from, $data);
break;
case 'cursor_update':
$this->handleCursorUpdate($from, $data);
break;
}
}
/**
* 处理加入文档请求
*/
private function handleJoinDocument($conn, $data) {
$document_id = $data['document_id'];
$user_id = $data['user_id'];
// 发送当前文档状态给新用户
$response = [
'type' => 'document_state',
'content' => $this->getDocumentState($document_id),
'users' => $this->getDocumentUsers($document_id)
];
$conn->send(json_encode($response));
// 通知其他用户有新成员加入
$this->broadcastToDocument($document_id, [
'type' => 'user_joined',
'user_id' => $user_id,
'username' => get_userdata($user_id)->display_name
], $conn);
}
public function onClose(RatchetConnectionInterface $conn) {
// 客户端断开连接
global $clients;
$clients->detach($conn);
echo "客户端断开: {$conn->resourceId}n";
}
public function onError(RatchetConnectionInterface $conn, Exception $e) {
echo "错误: {$e->getMessage()}n";
$conn->close();
}
}
// 初始化插件
new SupplyChain_WebSocket_Server();
?>
/**
* WordPress实时协同编辑器 - 前端组件
* 文件名:supplychain-editor.js
*/
import React, { useState, useEffect, useRef } from 'react';
import { io } from 'socket.io-client';
import { Editor, EditorState, ContentState, convertToRaw } from 'draft-js';
import 'draft-js/dist/Draft.css';
const SupplyChainEditor = ({ documentId, userId }) => {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const [connectedUsers, setConnectedUsers] = useState([]);
const [socket, setSocket] = useState(null);
const editorRef = useRef(null);
// 初始化WebSocket连接
useEffect(() => {
const newSocket = io('wss://your-domain.com:8080/supplychain', {
transports: ['websocket'],
secure: true
});
setSocket(newSocket);
// 加入文档
newSocket.emit('join_document', {
document_id: documentId,
user_id: userId
});
// 监听文档状态更新
newSocket.on('document_state', (data) => {
const contentState = ContentState.createFromText(data.content);
setEditorState(EditorState.createWithContent(contentState));
setConnectedUsers(data.users);
});
// 监听编辑操作
newSocket.on('edit_operation', (operation) => {
applyRemoteOperation(operation);
});
// 监听用户加入/离开
newSocket.on('user_joined', (user) => {
setConnectedUsers(prev => [...prev, user]);
});
newSocket.on('user_left', (userId) => {
setConnectedUsers(prev => prev.filter(u => u.id !== userId));
});
return () => newSocket.close();
}, [documentId, userId]);
/**
* 处理本地编辑操作
*/
const handleEditorChange = (newEditorState) => {
const oldContent = editorState.getCurrentContent();
const newContent = newEditorState.getCurrentContent();
// 检测变化并生成操作
const operation = generateOperation(oldContent, newContent);
if (operation && socket) {
// 发送操作到服务器
socket.emit('edit_operation', {
document_id: documentId,
operation: operation,
user_id: userId,
timestamp: Date.now()
});
}
setEditorState(newEditorState);
};
/**
* 生成编辑操作
*/
const generateOperation = (oldContent, newContent) => {
const oldText = oldContent.getPlainText();
const newText = newContent.getPlainText();
// 简化实现:实际应使用OT算法
if (oldText !== newText) {
return {
type: 'text_change',
oldText: oldText,
newText: newText,
selection: editorState.getSelection()
};
}
return null;
};
/**
* 应用远程操作
*/
const applyRemoteOperation = (operation) => {
if (operation.type === 'text_change') {
const contentState = ContentState.createFromText(operation.newText);
const newEditorState = EditorState.createWithContent(contentState);
// 保留当前选择状态
const selectionState = editorState.getSelection();
const finalEditorState = EditorState.forceSelection(
newEditorState,
selectionState
);
setEditorState(finalEditorState);
}
};
/**
* 发送光标位置更新
*/
const sendCursorUpdate = (selection) => {
if (socket) {
socket.emit('cursor_update', {
document_id: documentId,
user_id: userId,
cursor_position: selection.getStartOffset(),
selection_range: {
start: selection.getStartOffset(),
end: selection.getEndOffset()
}
});
}
};
return (
<div className="supplychain-editor-container">
<div className="editor-header">
<h3>供应链协同编辑器</h3>
<div className="user-list">
<span>在线用户: </span>
{connectedUsers.map(user => (
<span key={user.id} className="user-badge">
{user.username}
</span>
))}
</div>
</div>
<div className="editor-wrapper">
<Editor
ref={editorRef}
editorState={editorState}
onChange={handleEditorChange}
onFocus={() => sendCursorUpdate(editorState.getSelection())}
placeholder="开始协同编辑供应链文档..."
/>
</div>
<div className="editor-footer">
<div className="document-info">
文档ID: {documentId} | 自动保存已启用
</div>
<div className="editor-tools">
<button className="btn-save" onClick={handleSave}>
保存版本
</button>
<button className="btn-history" onClick={showHistory}>
查看历史
</button>
</div>
</div>
</div>
);
};
export default SupplyChainEditor;
/**
* WordPress实时协同编辑器 - 前端组件
* 文件名:supplychain-editor.js
*/
import React, { useState, useEffect, useRef } from 'react';
import { io } from 'socket.io-client';
import { Editor, EditorState, ContentState, convertToRaw } from 'draft-js';
import 'draft-js/dist/Draft.css';
const SupplyChainEditor = ({ documentId, userId }) => {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
const [connectedUsers, setConnectedUsers] = useState([]);
const [socket, setSocket] = useState(null);
const editorRef = useRef(null);
// 初始化WebSocket连接
useEffect(() => {
const newSocket = io('wss://your-domain.com:8080/supplychain', {
transports: ['websocket'],
secure: true
});
setSocket(newSocket);
// 加入文档
newSocket.emit('join_document', {
document_id: documentId,
user_id: userId
});
// 监听文档状态更新
newSocket.on('document_state', (data) => {
const contentState = ContentState.createFromText(data.content);
setEditorState(EditorState.createWithContent(contentState));
setConnectedUsers(data.users);
});
// 监听编辑操作
newSocket.on('edit_operation', (operation) => {
applyRemoteOperation(operation);
});
// 监听用户加入/离开
newSocket.on('user_joined', (user) => {
setConnectedUsers(prev => [...prev, user]);
});
newSocket.on('user_left', (userId) => {
setConnectedUsers(prev => prev.filter(u => u.id !== userId));
});
return () => newSocket.close();
}, [documentId, userId]);
/**
* 处理本地编辑操作
*/
const handleEditorChange = (newEditorState) => {
const oldContent = editorState.getCurrentContent();
const newContent = newEditorState.getCurrentContent();
// 检测变化并生成操作
const operation = generateOperation(oldContent, newContent);
if (operation && socket) {
// 发送操作到服务器
socket.emit('edit_operation', {
document_id: documentId,
operation: operation,
user_id: userId,
timestamp: Date.now()
});
}
setEditorState(newEditorState);
};
/**
* 生成编辑操作
*/
const generateOperation = (oldContent, newContent) => {
const oldText = oldContent.getPlainText();
const newText = newContent.getPlainText();
// 简化实现:实际应使用OT算法
if (oldText !== newText) {
return {
type: 'text_change',
oldText: oldText,
newText: newText,
selection: editorState.getSelection()
};
}
return null;
};
/**
* 应用远程操作
*/
const applyRemoteOperation = (operation) => {
if (operation.type === 'text_change') {
const contentState = ContentState.createFromText(operation.newText);
const newEditorState = EditorState.createWithContent(contentState);
// 保留当前选择状态
const selectionState = editorState.getSelection();
const finalEditorState = EditorState.forceSelection(
newEditorState,
selectionState
);
setEditorState(finalEditorState);
}
};
/**
* 发送光标位置更新
*/
const sendCursorUpdate = (selection) => {
if (socket) {
socket.emit('cursor_update', {
document_id: documentId,
user_id: userId,
cursor_position: selection.getStartOffset(),
selection_range: {
start: selection.getStartOffset(),
end: selection.getEndOffset()
}
});
}
};
return (
<div className="supplychain-editor-container">
<div className="editor-header">
<h3>供应链协同编辑器</h3>
<div className="user-list">
<span>在线用户: </span>
{connectedUsers.map(user => (
<span key={user.id} className="user-badge">
{user.username}
</span>
))}
</div>
</div>
<div className="editor-wrapper">
<Editor
ref={editorRef}
editorState={editorState}
onChange={handleEditorChange}
onFocus={() => sendCursorUpdate(editorState.getSelection())}
placeholder="开始协同编辑供应链文档..."
/>
</div>
<div className="editor-footer">
<div className="document-info">
文档ID: {documentId} | 自动保存已启用
</div>
<div className="editor-tools">
<button className="btn-save" onClick={handleSave}>
保存版本
</button>
<button className="btn-history" onClick={showHistory}>
查看历史
</button>
</div>
</div>
</div>
);
};
export default SupplyChainEditor;
<?php
/**
* WordPress实时协同编辑插件主文件
* 文件名:supplychain-collaboration.php
*/
/*
Plugin Name: 供应链协同编辑系统
Description: 为WordPress添加实时协同编辑功能,特别适用于柔性供应链管理
Version: 1.0.0
Author: 您的名称
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class SupplyChain_Collaboration_Plugin {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// 初始化钩子
$this->init_hooks();
}
/**
* 初始化WordPress钩子
*/
private function init_hooks() {
// 添加管理菜单
add_action('admin_menu', [$this, 'add_admin_menu']);
// 注册短代码
add_shortcode('supplychain_editor', [$this, 'render_editor_shortcode']);
// 注册REST API端点
add_action('rest_api_init', [$this, 'register_rest_endpoints']);
// 加载脚本和样式
add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
}
/**
* 添加管理菜单
*/
public function add_admin_menu() {
add_menu_page(
'供应链协同编辑',
'供应链协同',
'manage_options',
'supplychain-collab',
[$this, 'render_admin_page'],
'dashicons-edit',
30
);
}
/**
* 渲染编辑器短代码
*/
public function render_editor_shortcode($atts) {
$atts = shortcode_atts([
'document_id' => 'default',
'title' => '供应链文档'
], $atts);
// 检查用户权限
if (!is_user_logged_in()) {
return '<p>请先登录以使用协同编辑功能</p>';
}
$user_id = get_current_user_id();
ob_start();
?>
<div id="supplychain-editor-root"
data-document-id="<?php echo esc_attr($atts['document_id']); ?>"
data-user-id="<?php echo esc_attr($user_id); ?>">
<!-- React组件将在这里渲染 -->
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const rootElement = document.getElementById('supplychain-editor-root');
const documentId = rootElement.dataset.documentId;
const userId = parseInt(rootElement.dataset.userId);
// 这里应该初始化React应用
console.log('初始化协同编辑器:', documentId, userId);
});
</script>
<?php
return ob_get_clean();
}
/**
* 注册REST API端点
*/
public function register_rest_endpoints() {
// 获取文档历史记录
register_rest_route('supplychain/v1', '/document/(?P<id>d+)/history', [
'methods' => 'GET',
'callback' => [$this, 'get_document_history'],
'permission_callback' => [$this, 'check_api_permission']
]);
// 保存文档版本
register_rest_route('supplychain/v1', '/document/(?P<id>d+)/save', [
'methods' => 'POST',
'callback' => [$this, 'save_document_version'],
'permission_callback' => [$this, 'check_api_permission']
]);
}
/**
* 获取文档历史记录
*/
public function get_document_history($request) {
$document_id = $request->get_param('id');
global $wpdb;
$table_name = $wpdb->prefix . 'supplychain_document_history';
$history = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $table_name WHERE document_id = %d ORDER BY created_at DESC LIMIT 50",
$document_id
)
);
return rest_ensure_response($history);
}
/**
* 检查API权限
*/
public function check_api_permission($request) {
return current_user_can('edit_posts');
}
/**
* 加载前端脚本
*/
public function enqueue_scripts() {
wp_enqueue_script(
'supplychain-editor',
plugin_dir_url(__FILE__) . 'js/supplychain-editor.js',
['wp-element', 'wp-components'],
'1.0.0',
true
);
wp_enqueue_style(
'supplychain-editor-style',
plugin_dir_url(__FILE__) . 'css/editor-style.css'
);
}
}
// 初始化插件
SupplyChain_Collaboration_Plugin::get_instance();
/**
* 插件激活时创建数据库表
*/
register_activation_hook(__FILE__, 'supplychain_collab_activate');
function supplychain_collab_activate() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 创建文档历史表
$table_name = $wpdb->prefix . 'supplychain_document_history';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
document_id varchar(100) NOT NULL,
content longtext NOT NULL,
user_id bigint(20) NOT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY document_id (document_id),
KEY user_id (user_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
?>
<?php
/**
* WordPress实时协同编辑插件主文件
* 文件名:supplychain-collaboration.php
*/
/*
Plugin Name: 供应链协同编辑系统
Description: 为WordPress添加实时协同编辑功能,特别适用于柔性供应链管理
Version: 1.0.0
Author: 您的名称
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class SupplyChain_Collaboration_Plugin {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// 初始化钩子
$this->init_hooks();
}
/**
* 初始化WordPress钩子
*/
private function init_hooks() {
// 添加管理菜单
add_action('admin_menu', [$this, 'add_admin_menu']);
// 注册短代码
add_shortcode('supplychain_editor', [$this, 'render_editor_shortcode']);
// 注册REST API端点
add_action('rest_api_init', [$this, 'register_rest_endpoints']);
// 加载脚本和样式
add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']);
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_scripts']);
}
/**
* 添加管理菜单
*/
public function add_admin_menu() {
add_menu_page(
'供应链协同编辑',
'供应链协同',
'manage_options',
'supplychain-collab',
[$this, 'render_admin_page'],
'dashicons-edit',
30
);
}
/**
* 渲染编辑器短代码
*/
public function render_editor_shortcode($atts) {
$atts = shortcode_atts([
'document_id' => 'default',
'title' => '供应链文档'
], $atts);
// 检查用户权限
if (!is_user_logged_in()) {
return '<p>请先登录以使用协同编辑功能</p>';
}
$user_id = get_current_user_id();
ob_start();
?>
<div id="supplychain-editor-root"
data-document-id="<?php echo esc_attr($atts['document_id']); ?>"
data-user-id="<?php echo esc_attr($user_id); ?>">
<!-- React组件将在这里渲染 -->
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const rootElement = document.getElementById('supplychain-editor-root');
const documentId = rootElement.dataset.documentId;
const userId = parseInt(rootElement.dataset.userId);
// 这里应该初始化React应用
console.log('初始化协同编辑器:', documentId, userId);
});
</script>
<?php
return ob_get_clean();
}
/**
* 注册REST API端点
*/
public function register_rest_endpoints() {
// 获取文档历史记录
register_rest_route('supplychain/v1', '/document/(?P<id>d+)/history', [
'methods' => 'GET',
'callback' => [$this, 'get_document_history'],
'permission_callback' => [$this, 'check_api_permission']
]);
// 保存文档版本
register_rest_route('supplychain/v1', '/document/(?P<id>d+)/save', [
'methods' => 'POST',
'callback' => [$this, 'save_document_version'],
'permission_callback' => [$this, 'check_api_permission']
]);
}
/**
* 获取文档历史记录
*/
public function get_document_history($request) {
$document_id = $request->get_param('id');
global $wpdb;
$table_name = $wpdb->prefix . 'supplychain_document_history';
$history = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $table_name WHERE document_id = %d ORDER BY created_at DESC LIMIT 50",
$document_id
)
);
return rest_ensure_response($history);
}
/**
* 检查API权限
*/
public function check_api_permission($request) {
return current_user_can('edit_posts');
}
/**
* 加载前端脚本
*/
public function enqueue_scripts() {
wp_enqueue_script(
'supplychain-editor',
plugin_dir_url(__FILE__) . 'js/supplychain-editor.js',
['wp-element', 'wp-components'],
'1.0.0',
true
);
wp_enqueue_style(
'supplychain-editor-style',
plugin_dir_url(__FILE__) . 'css/editor-style.css'
);
}
}
// 初始化插件
SupplyChain_Collaboration_Plugin::get_instance();
/**
* 插件激活时创建数据库表
*/
register_activation_hook(__FILE__, 'supplychain_collab_activate');
function supplychain_collab_activate() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 创建文档历史表
$table_name = $wpdb->prefix . 'supplychain_document_history';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
document_id varchar(100) NOT NULL,
content longtext NOT NULL,
user_id bigint(20) NOT NULL,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY document_id (document_id),
KEY user_id (user_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
?>
- 使用Nginx作为WebSocket代理
- 配置SSL证书确保安全连接
- 设置适当的WebSocket超时和心跳机制
- 实现操作压缩和批量处理
- 添加离线编辑支持
- 使用Redis缓存频繁访问的文档状态
- 实现文档级别的权限控制
- 添加操作验证和冲突解决机制
- 记录完整的操作日志用于审计
通过本文的教程,您已经学会了如何在WordPress中开发一个完整的实时协同编辑系统,特别适用于柔性供应链管理场景。这个系统允许供应链中的不同参与者(供应商、制造商、分销商)实时协作编辑文档,大大提高供应链的响应速度和协作效率。
系统采用了现代化的技术栈,包括WebSocket实时通信、React前端框架和WordPress REST API,确保了系统的可扩展性和性能。您可以根据实际需求进一步扩展功能,如添加版本控制、冲突解决算法或与现有供应链管理系统的集成。
记住,实时协同编辑系统的成功不仅取决于技术实现,还需要考虑用户体验、培训支持和组织流程的配合。建议从小规模试点开始,逐步推广到整个供应链网络。
/**
* 操作转换(OT)算法核心实现
* 文件名:ot-algorithm.js
*/
class OperationTransformer {
constructor() {
this.operations = [];
this.revision = 0;
}
/**
* 生成基本操作对象
* @param {string} type - 操作类型: insert/delete/retain
* @param {*} value - 操作值
* @param {number} position - 位置
* @returns {Object} 操作对象
*/
static createOperation(type, value, position) {
return {
type,
value,
position,
revision: Date.now(),
clientId: this.clientId
};
}
/**
* 转换操作以解决冲突
* @param {Object} op1 - 第一个操作
* @param {Object} op2 - 第二个操作
* @returns {Array} 转换后的操作对
*/
transform(op1, op2) {
if (op1.type === 'retain' && op2.type === 'retain') {
return this.transformRetainRetain(op1, op2);
} else if (op1.type === 'insert' && op2.type === 'insert') {
return this.transformInsertInsert(op1, op2);
} else if (op1.type === 'delete' && op2.type === 'delete') {
return this.transformDeleteDelete(op1, op2);
} else if (op1.type === 'insert' && op2.type === 'delete') {
return this.transformInsertDelete(op1, op2);
} else if (op1.type === 'delete' && op2.type === 'insert') {
return this.transformDeleteInsert(op1, op2);
}
return [op1, op2];
}
/**
* 转换 retain-retain 操作
*/
transformRetainRetain(op1, op2) {
if (op1.position === op2.position) {
return [op1, op2];
}
if (op1.position < op2.position) {
return [
op1,
{ ...op2, position: op2.position - 1 }
];
} else {
return [
{ ...op1, position: op1.position - 1 },
op2
];
}
}
/**
* 转换 insert-insert 操作
*/
transformInsertInsert(op1, op2) {
if (op1.position <= op2.position) {
return [
op1,
{ ...op2, position: op2.position + op1.value.length }
];
} else {
return [
{ ...op1, position: op1.position + op2.value.length },
op2
];
}
}
/**
* 应用操作到文本
* @param {string} text - 原始文本
* @param {Object} operation - 要应用的操作
* @returns {string} 应用后的文本
*/
applyOperation(text, operation) {
switch (operation.type) {
case 'insert':
return this.applyInsert(text, operation);
case 'delete':
return this.applyDelete(text, operation);
case 'retain':
return text;
default:
return text;
}
}
applyInsert(text, operation) {
const before = text.slice(0, operation.position);
const after = text.slice(operation.position);
return before + operation.value + after;
}
applyDelete(text, operation) {
const before = text.slice(0, operation.position);
const after = text.slice(operation.position + operation.value.length);
return before + after;
}
/**
* 批量应用操作
* @param {string} text - 原始文本
* @param {Array} operations - 操作数组
* @returns {string} 应用所有操作后的文本
*/
applyOperations(text, operations) {
let result = text;
operations.forEach(op => {
result = this.applyOperation(result, op);
});
return result;
}
}
// 导出供其他模块使用
export default OperationTransformer;
/**
* 操作转换(OT)算法核心实现
* 文件名:ot-algorithm.js
*/
class OperationTransformer {
constructor() {
this.operations = [];
this.revision = 0;
}
/**
* 生成基本操作对象
* @param {string} type - 操作类型: insert/delete/retain
* @param {*} value - 操作值
* @param {number} position - 位置
* @returns {Object} 操作对象
*/
static createOperation(type, value, position) {
return {
type,
value,
position,
revision: Date.now(),
clientId: this.clientId
};
}
/**
* 转换操作以解决冲突
* @param {Object} op1 - 第一个操作
* @param {Object} op2 - 第二个操作
* @returns {Array} 转换后的操作对
*/
transform(op1, op2) {
if (op1.type === 'retain' && op2.type === 'retain') {
return this.transformRetainRetain(op1, op2);
} else if (op1.type === 'insert' && op2.type === 'insert') {
return this.transformInsertInsert(op1, op2);
} else if (op1.type === 'delete' && op2.type === 'delete') {
return this.transformDeleteDelete(op1, op2);
} else if (op1.type === 'insert' && op2.type === 'delete') {
return this.transformInsertDelete(op1, op2);
} else if (op1.type === 'delete' && op2.type === 'insert') {
return this.transformDeleteInsert(op1, op2);
}
return [op1, op2];
}
/**
* 转换 retain-retain 操作
*/
transformRetainRetain(op1, op2) {
if (op1.position === op2.position) {
return [op1, op2];
}
if (op1.position < op2.position) {
return [
op1,
{ ...op2, position: op2.position - 1 }
];
} else {
return [
{ ...op1, position: op1.position - 1 },
op2
];
}
}
/**
* 转换 insert-insert 操作
*/
transformInsertInsert(op1, op2) {
if (op1.position <= op2.position) {
return [
op1,
{ ...op2, position: op2.position + op1.value.length }
];
} else {
return [
{ ...op1, position: op1.position + op2.value.length },
op2
];
}
}
/**
* 应用操作到文本
* @param {string} text - 原始文本
* @param {Object} operation - 要应用的操作
* @returns {string} 应用后的文本
*/
applyOperation(text, operation) {
switch (operation.type) {
case 'insert':
return this.applyInsert(text, operation);
case 'delete':
return this.applyDelete(text, operation);
case 'retain':
return text;
default:
return text;
}
}
applyInsert(text, operation) {
const before = text.slice(0, operation.position);
const after = text.slice(operation.position);
return before + operation.value + after;
}
applyDelete(text, operation) {
const before = text.slice(0, operation.position);
const after = text.slice(operation.position + operation.value.length);
return before + after;
}
/**
* 批量应用操作
* @param {string} text - 原始文本
* @param {Array} operations - 操作数组
* @returns {string} 应用所有操作后的文本
*/
applyOperations(text, operations) {
let result = text;
operations.forEach(op => {
result = this.applyOperation(result, op);
});
return result;
}
}
// 导出供其他模块使用
export default OperationTransformer;
<?php
/**
* 冲突解决与版本控制系统
* 文件名:conflict-resolver.php
*/
class ConflictResolver {
private $wpdb;
private $table_name;
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->table_name = $wpdb->prefix . 'supplychain_operations';
}
/**
* 保存操作记录
*/
public function save_operation($document_id, $operation, $user_id, $parent_revision) {
$data = [
'document_id' => $document_id,
'operation' => json_encode($operation),
'user_id' => $user_id,
'parent_revision' => $parent_revision,
'revision' => $this->get_next_revision($document_id),
'created_at' => current_time('mysql'),
'applied' => 0
];
$result = $this->wpdb->insert($this->table_name, $data);
if ($result) {
return $this->wpdb->insert_id;
}
return false;
}
/**
* 获取下一个版本号
*/
private function get_next_revision($document_id) {
$max_revision = $this->wpdb->get_var(
$this->wpdb->prepare(
"SELECT MAX(revision) FROM {$this->table_name} WHERE document_id = %s",
$document_id
)
);
return $max_revision ? $max_revision + 1 : 1;
}
/**
* 解决操作冲突
*/
public function resolve_conflicts($document_id, $new_operation, $client_revision) {
// 获取未应用的操作
$pending_operations = $this->get_pending_operations($document_id, $client_revision);
if (empty($pending_operations)) {
return [$new_operation];
}
$transformed_operations = [];
$current_op = $new_operation;
// 依次转换操作
foreach ($pending_operations as $pending_op) {
$operation_data = json_decode($pending_op->operation, true);
list($transformed_current, $transformed_pending) = $this->transform_operations(
$current_op,
$operation_data
);
$current_op = $transformed_current;
$transformed_operations[] = $transformed_pending;
}
// 标记已解决的操作为已应用
$this->mark_operations_applied($pending_operations);
return array_merge($transformed_operations, [$current_op]);
}
/**
* 获取待处理操作
*/
private function get_pending_operations($document_id, $since_revision) {
return $this->wpdb->get_results(
$this->wpdb->prepare(
"SELECT * FROM {$this->table_name}
WHERE document_id = %s
AND revision > %d
AND applied = 0
ORDER BY revision ASC",
$document_id,
$since_revision
)
);
}
/**
* 标记操作为已应用
*/
private function mark_operations_applied($operations) {
$ids = array_map(function($op) {
return $op->id;
}, $operations);
if (!empty($ids)) {
$ids_string = implode(',', array_map('intval', $ids));
$this->wpdb->query(
"UPDATE {$this->table_name} SET applied = 1 WHERE id IN ({$ids_string})"
);
}
}
/**
* 获取文档完整历史
*/
public function get_document_history($document_id, $limit = 100) {
return $this->wpdb->get_results(
$this->wpdb->prepare(
"SELECT o.*, u.display_name as user_name
FROM {$this->table_name} o
LEFT JOIN {$this->wpdb->users} u ON o.user_id = u.ID
WHERE o.document_id = %s
ORDER BY o.revision DESC
LIMIT %d",
$document_id,
$limit
)
);
}
/**
* 回滚到指定版本
*/
public function rollback_to_revision($document_id, $target_revision) {
// 获取当前内容
$current_content = $this->get_document_content($document_id);
// 获取从目标版本到当前版本的所有操作
$operations = $this->wpdb->get_results(
$this->wpdb->prepare(
"SELECT operation FROM {$this->table_name}
WHERE document_id = %s
AND revision > %d
ORDER BY revision ASC",
$document_id,
$target_revision
)
);
// 反向应用操作以回滚
$reversed_operations = array_reverse($operations);
$rolled_back_content = $current_content;
foreach ($reversed_operations as $op) {
$operation = json_decode($op->operation, true);
$inverse_op = $this->inverse_operation($operation);
$rolled_back_content = $this->apply_single_operation(
$rolled_back_content,
$inverse_op
);
}
return $rolled_back_content;
}
/**
* 生成逆操作
*/
private function inverse_operation($operation) {
switch ($operation['type']) {
case 'insert':
return [
'type' => 'delete',
'position' => $operation['position'],
'value' => $operation['value']
];
case 'delete':
return [
'type' => 'insert',
'position' => $operation['position'],
'value' => $operation['value']
];
default:
return $operation;
}
}
}
<?php
/**
* 冲突解决与版本控制系统
* 文件名:conflict-resolver.php
*/
class ConflictResolver {
private $wpdb;
private $table_name;
public function __construct() {
global $wpdb;
$this->wpdb = $wpdb;
$this->table_name = $wpdb->prefix . 'supplychain_operations';
}
/**
* 保存操作记录
*/
public function save_operation($document_id, $operation, $user_id, $parent_revision) {
$data = [
'document_id' => $document_id,
'operation' => json_encode($operation),
'user_id' => $user_id,
'parent_revision' => $parent_revision,
'revision' => $this->get_next_revision($document_id),
'created_at' => current_time('mysql'),
'applied' => 0
];
$result = $this->wpdb->insert($this->table_name, $data);
if ($result) {
return $this->wpdb->insert_id;
}
return false;
}
/**
* 获取下一个版本号
*/
private function get_next_revision($document_id) {
$max_revision = $this->wpdb->get_var(
$this->wpdb->prepare(
"SELECT MAX(revision) FROM {$this->table_name} WHERE document_id = %s",
$document_id
)
);
return $max_revision ? $max_revision + 1 : 1;
}
/**
* 解决操作冲突
*/
public function resolve_conflicts($document_id, $new_operation, $client_revision) {
// 获取未应用的操作
$pending_operations = $this->get_pending_operations($document_id, $client_revision);
if (empty($pending_operations)) {
return [$new_operation];
}
$transformed_operations = [];
$current_op = $new_operation;
// 依次转换操作
foreach ($pending_operations as $pending_op) {
$operation_data = json_decode($pending_op->operation, true);
list($transformed_current, $transformed_pending) = $this->transform_operations(
$current_op,
$operation_data
);
$current_op = $transformed_current;
$transformed_operations[] = $transformed_pending;
}
// 标记已解决的操作为已应用
$this->mark_operations_applied($pending_operations);
return array_merge($transformed_operations, [$current_op]);
}
/**
* 获取待处理操作
*/
private function get_pending_operations($document_id, $since_revision) {
return $this->wpdb->get_results(
$this->wpdb->prepare(
"SELECT * FROM {$this->table_name}
WHERE document_id = %s
AND revision > %d
AND applied = 0
ORDER BY revision ASC",
$document_id,
$since_revision
)
);
}
/**
* 标记操作为已应用
*/
private function mark_operations_applied($operations) {
$ids = array_map(function($op) {
return $op->id;
}, $operations);
if (!empty($ids)) {
$ids_string = implode(',', array_map('intval', $ids));
$this->wpdb->query(
"UPDATE {$this->table_name} SET applied = 1 WHERE id IN ({$ids_string})"
);
}
}
/**
* 获取文档完整历史
*/
public function get_document_history($document_id, $limit = 100) {
return $this->wpdb->get_results(
$this->wpdb->prepare(
"SELECT o.*, u.display_name as user_name
FROM {$this->table_name} o
LEFT JOIN {$this->wpdb->users} u ON o.user_id = u.ID
WHERE o.document_id = %s
ORDER BY o.revision DESC
LIMIT %d",
$document_id,
$limit
)
);
}
/**
* 回滚到指定版本
*/
public function rollback_to_revision($document_id, $target_revision) {
// 获取当前内容
$current_content = $this->get_document_content($document_id);
// 获取从目标版本到当前版本的所有操作
$operations = $this->wpdb->get_results(
$this->wpdb->prepare(
"SELECT operation FROM {$this->table_name}
WHERE document_id = %s
AND revision > %d
ORDER BY revision ASC",
$document_id,
$target_revision
)
);
// 反向应用操作以回滚
$reversed_operations = array_reverse($operations);
$rolled_back_content = $current_content;
foreach ($reversed_operations as $op) {
$operation = json_decode($op->operation, true);
$inverse_op = $this->inverse_operation($operation);
$rolled_back_content = $this->apply_single_operation(
$rolled_back_content,
$inverse_op
);
}
return $rolled_back_content;
}
/**
* 生成逆操作
*/
private function inverse_operation($operation) {
switch ($operation['type']) {
case 'insert':
return [
'type' => 'delete',
'position' => $operation['position'],
'value' => $operation['value']
];
case 'delete':
return [
'type' => 'insert',
'position' => $operation['position'],
'value' => $operation['value']
];
default:
return $operation;
}
}
}
/**
* 实时协同状态管理器
* 文件名:collaboration-state.js
*/
class CollaborationStateManager {
constructor(documentId, userId) {
this.documentId = documentId;
this.userId = userId;
this.connectedUsers = new Map();
this.cursorPositions = new Map();
this.selectionRanges = new Map();
this.presenceTimeout = 30000; // 30秒超时
// 初始化心跳检测
this.initHeartbeat();
}
/**
* 初始化心跳检测
*/
initHeartbeat() {
this.heartbeatInterval = setInterval(() => {
this.sendHeartbeat();
}, 10000);
// 清理超时用户
this.cleanupInterval = setInterval(() => {
this.cleanupInactiveUsers();
}, 5000);
}
/**
* 发送心跳
*/
sendHeartbeat() {
if (this.socket && this.socket.connected) {
this.socket.emit('heartbeat', {
document_id: this.documentId,
user_id: this.userId,
timestamp: Date.now()
});
}
}
/**
* 更新用户状态
*/
updateUserPresence(userId, data) {
const userState = this.connectedUsers.get(userId) || {
id: userId,
lastSeen: Date.now(),
active: true
};
userState.lastSeen = Date.now();
userState.active = true;
userState.name = data.name || userState.name;
userState.color = data.color || this.generateUserColor(userId);
this.connectedUsers.set(userId, userState);
// 触发状态更新事件
this.emitStateChange();
}
/**
* 更新光标位置
*/
updateCursorPosition(userId, position, selection) {
this.cursorPositions.set(userId, {
position,
timestamp: Date.now()
});
if (selection) {
this.selectionRanges.set(userId, selection);
}
// 广播光标更新
this.broadcastCursorUpdate(userId, position, selection);
}
/**
* 广播光标更新
*/
broadcastCursorUpdate(userId, position, selection) {
if (this.socket) {
this.socket.emit('cursor_update', {
document_id: this.documentId,
user_id: userId,
cursor_position: position,
selection_range: selection,
timestamp: Date.now()
});
}
}
/**
* 清理不活跃用户
*/
cleanupInactiveUsers() {
const now = Date.now();
const inactiveUsers = [];
this.connectedUsers.forEach((user, userId) => {
if (now - user.lastSeen > this.presenceTimeout) {
user.active = false;
inactiveUsers.push(userId);
}
});
// 移除长时间不活跃的用户
inactiveUsers.forEach(userId => {
if (userId !== this.userId) {
this.connectedUsers.delete(userId);
this.cursorPositions.delete(userId);
this.selectionRanges.delete(userId);
}
});
if (inactiveUsers.length > 0) {
this.emitStateChange();
}
}
/**
* 生成用户颜色
*/
generateUserColor(userId) {
const colors = [
'#FF6B6B', '#4ECDC4', '#FFD166', '#06D6A0',
'#118AB2', '#073B4C', '#EF476F', '#7209B7'
];
const hash = userId.toString().split('').reduce((acc, char) => {
return char.charCodeAt(0) + ((acc << 5) - acc);
}, 0);
return colors[Math.abs(hash) % colors.length];
}
/**
* 获取用户光标位置
*/
getUserCursor(userId) {
return this.cursorPositions.get(userId);
}
/**
* 获取所有活跃用户
*/
getActiveUsers() {
const activeUsers = [];
this.connectedUsers.forEach(user => {
if (user.active) {
activeUsers.push(user);
}
});
return activeUsers;
}
/**
* 获取用户选择范围
*/
getUserSelection(userId) {
return this.selectionRanges.get(userId);
}
/**
* 触发状态变化事件
*/
emitStateChange() {
const event = new CustomEvent('collaborationStateChange', {
detail: {
users: this.getActiveUsers(),
cursors: Array.from(this.cursorPositions.entries()),
selections: Array.from(this.selectionRanges.entries())
}
});
window.dispatchEvent(event);
}
/**
* 销毁清理
*/
destroy() {
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
}
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
// 发送离开通知
if (this.socket) {
this.socket.emit('user_left', {
document_id: this.documentId,
user_id: this.userId
});
}
}
}
// 导出状态管理器
export default CollaborationStateManager;
/**
* 实时协同状态管理器
* 文件名:collaboration-state.js
*/
class CollaborationStateManager {
constructor(documentId, userId) {
this.documentId = documentId;
this.userId = userId;
this.connectedUsers = new Map();
this.cursorPositions = new Map();
this.selectionRanges = new Map();
this.presenceTimeout = 30000; // 30秒超时
// 初始化心跳检测
this.initHeartbeat();
}
/**
* 初始化心跳检测
*/
initHeartbeat() {
this.heartbeatInterval = setInterval(() => {
this.sendHeartbeat();
}, 10000);
// 清理超时用户
this.cleanupInterval = setInterval(() => {
this.cleanupInactiveUsers();
}, 5000);
}
/**
* 发送心跳
*/
sendHeartbeat() {
if (this.socket && this.socket.connected) {
this.socket.emit('heartbeat', {
document_id: this.documentId,
user_id: this.userId,
timestamp: Date.now()
});
}
}
/**
* 更新用户状态
*/
updateUserPresence(userId, data) {
const userState = this.connectedUsers.get(userId) || {
id: userId,
lastSeen: Date.now(),
active: true
};
userState.lastSeen = Date.now();
userState.active = true;
userState.name = data.name || userState.name;
userState.color = data.color || this.generateUserColor(userId);
this.connectedUsers.set(userId, userState);
// 触发状态更新事件
this.emitStateChange();
}
/**
* 更新光标位置
*/
updateCursorPosition(userId, position, selection) {
this.cursorPositions.set(userId, {
position,
timestamp: Date.now()
});
if (selection) {
this.selectionRanges.set(userId, selection);
}
// 广播光标更新
this.broadcastCursorUpdate(userId, position, selection);
}
/**
* 广播光标更新
*/
broadcastCursorUpdate(userId, position, selection) {
if (this.socket) {
this.socket.emit('cursor_update', {
document_id: this.documentId,
user_id: userId,
cursor_position: position,
selection_range: selection,
timestamp: Date.now()
});
}
}
/**
* 清理不活跃用户
*/
cleanupInactiveUsers() {
const now = Date.now();
const inactiveUsers = [];
this.connectedUsers.forEach((user, userId) => {
if (now - user.lastSeen > this.presenceTimeout) {
user.active = false;
inactiveUsers.push(userId);
}
});
// 移除长时间不活跃的用户
inactiveUsers.forEach(userId => {
if (userId !== this.userId) {
this.connectedUsers.delete(userId);
this.cursorPositions.delete(userId);
this.selectionRanges.delete(userId);
}
});
if (inactiveUsers.length > 0) {
this.emitStateChange();
}
}
/**
* 生成用户颜色
*/
generateUserColor(userId) {
const colors = [
'#FF6B6B', '#4ECDC4', '#FFD166', '#06D6A0',
'#118AB2', '#073B4C', '#EF476F', '#7209B7'
];
const hash = userId.toString().split('').reduce((acc, char) => {
return char.charCodeAt(0) + ((acc << 5) - acc);
}, 0);
return colors[Math.abs(hash) % colors.length];
}
/**
* 获取用户光标位置
*/
getUserCursor(userId) {
return this.cursorPositions.get(userId);
}
/**
* 获取所有活跃用户
*/
getActiveUsers() {
const activeUsers = [];
this.connectedUsers.forEach(user => {
if (user.active) {
activeUsers.push(user);
}
});
return activeUsers;
}
/**
* 获取用户选择范围
*/
getUserSelection(userId) {
return this.selectionRanges.get(userId);
}
/**
* 触发状态变化事件
*/
emitStateChange() {
const event = new CustomEvent('collaborationStateChange', {
detail: {
users: this.getActiveUsers(),
cursors: Array.from(this.cursorPositions.entries()),
selections: Array.from(this.selectionRanges.entries())
}
});
window.dispatchEvent(event);
}
/**
* 销毁清理
*/
destroy() {
if (this.heartbeatInterval) {
clearInterval(this.heartbeatInterval);
}
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
// 发送离开通知
if (this.socket) {
this.socket.emit('user_left', {
document_id: this.documentId,
user_id: this.userId
});
}
}
}
// 导出状态管理器
export default CollaborationStateManager;
<?php
/**
* 供应链特定功能模块
* 文件名:supplychain-features.php
*/
class SupplyChainFeatures {
/**
* 初始化供应链功能
*/
public function init() {
// 添加上下文感知功能
add_filter('supplychain_document_context', [$this, 'add_supplychain_context']);
// 添加审批工作流
add_action('supplychain_document_updated', [$this, 'trigger_approval_workflow']);
// 添加库存关联
add_action('supplychain_item_mentioned', [$this, 'link_inventory_data']);
}
/**
* 添加上下文感知数据
*/
public function add_supplychain_context($document) {
global $wpdb;
// 获取相关订单信息
$order_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}supplychain_orders
WHERE document_id = %s",
$document['id']
)
);
// 获取供应商信息
$supplier_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}supplychain_suppliers
WHERE id = %d",
$order_data->supplier_id ?? 0
)
);
// 获取库存状态
$inventory_status = $this->get_inventory_status($document['items']);
return array_merge($document, [
'order_info' => $order_data,
'supplier_info' => $supplier_data,
'inventory_status' => $inventory_status,
'lead_time' => $this->calculate_lead_time($order_data),
'risk_level' => $this->assess_risk_level($document)
]);
}
/**
* 触发审批工作流
*/
public function trigger_approval_workflow($document_id, $changes) {
$document = $this->get_document($document_id);
// 检查是否需要审批
if ($this->requires_approval($document, $changes)) {
$approvers = $this->get_approvers($document);
foreach ($approvers as $approver) {
$this->send_approval_request($approver, $document, $changes);
}
// 更新文档状态为待审批
$this->update_document_status($document_id, 'pending_approval');
}
}
/**
* 链接库存数据
*/
<?php
/**
* 供应链特定功能模块
* 文件名:supplychain-features.php
*/
class SupplyChainFeatures {
/**
* 初始化供应链功能
*/
public function init() {
// 添加上下文感知功能
add_filter('supplychain_document_context', [$this, 'add_supplychain_context']);
// 添加审批工作流
add_action('supplychain_document_updated', [$this, 'trigger_approval_workflow']);
// 添加库存关联
add_action('supplychain_item_mentioned', [$this, 'link_inventory_data']);
}
/**
* 添加上下文感知数据
*/
public function add_supplychain_context($document) {
global $wpdb;
// 获取相关订单信息
$order_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}supplychain_orders
WHERE document_id = %s",
$document['id']
)
);
// 获取供应商信息
$supplier_data = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}supplychain_suppliers
WHERE id = %d",
$order_data->supplier_id ?? 0
)
);
// 获取库存状态
$inventory_status = $this->get_inventory_status($document['items']);
return array_merge($document, [
'order_info' => $order_data,
'supplier_info' => $supplier_data,
'inventory_status' => $inventory_status,
'lead_time' => $this->calculate_lead_time($order_data),
'risk_level' => $this->assess_risk_level($document)
]);
}
/**
* 触发审批工作流
*/
public function trigger_approval_workflow($document_id, $changes) {
$document = $this->get_document($document_id);
// 检查是否需要审批
if ($this->requires_approval($document, $changes)) {
$approvers = $this->get_approvers($document);
foreach ($approvers as $approver) {
$this->send_approval_request($approver, $document, $changes);
}
// 更新文档状态为待审批
$this->update_document_status($document_id, 'pending_approval');
}
}
/**
* 链接库存数据
*/


