文章目录
-
- 在当今数字化媒体时代,网络传媒机构需要在多个平台同步发布内容,包括微信公众号、微博、头条号、知乎等多个渠道。手动同步不仅效率低下,还容易出错。本教程将指导您开发一个WordPress柔性内容多渠道同步插件,实现一键发布到多个平台。 核心需求: 支持自定义内容转换规则,适应不同平台的内容格式要求 可配置的发布渠道管理 定时发布和即时发布两种模式 发布状态跟踪和错误处理 支持图片等媒体资源的自动处理
- 首先,我们需要创建插件的基本文件结构: <?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('FLEX_SYNC_VERSION', '1.0.0'); define('FLEX_SYNC_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('FLEX_SYNC_PLUGIN_URL', plugin_dir_url(__FILE__)); // 初始化插件 class FlexContentSync { 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(); } private function init_hooks() { // 激活/停用插件时的操作 register_activation_hook(__FILE__, array($this, 'activate')); register_deactivation_hook(__FILE__, array($this, 'deactivate')); // 初始化 add_action('plugins_loaded', array($this, 'init')); // 添加管理菜单 add_action('admin_menu', array($this, 'add_admin_menu')); // 保存文章时触发同步 add_action('save_post', array($this, 'on_save_post'), 10, 3); } public function activate() { // 创建必要的数据库表 $this->create_tables(); // 设置默认选项 $this->set_default_options(); } public function deactivate() { // 清理临时数据 // 注意:这里不清除配置数据,以便重新激活时保留设置 } public function init() { // 加载文本域 load_plugin_textdomain('flex-sync', false, dirname(plugin_basename(__FILE__)) . '/languages'); // 加载必要的类 $this->load_dependencies(); } // 其他方法将在后续章节实现 } // 启动插件 FlexContentSync::get_instance(); ?>
- 我们需要设计数据库表来存储渠道配置和同步记录: <?php // 在FlexContentSync类中添加以下方法 private function create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_name_channels = $wpdb->prefix . 'flex_sync_channels'; $table_name_logs = $wpdb->prefix . 'flex_sync_logs'; // 渠道配置表 $sql_channels = "CREATE TABLE IF NOT EXISTS $table_name_channels ( id mediumint(9) NOT NULL AUTO_INCREMENT, channel_name varchar(100) NOT NULL, channel_type varchar(50) NOT NULL, api_config text NOT NULL, is_active tinyint(1) DEFAULT 1, content_rules text, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) $charset_collate;"; // 同步日志表 $sql_logs = "CREATE TABLE IF NOT EXISTS $table_name_logs ( id mediumint(9) NOT NULL AUTO_INCREMENT, post_id bigint(20) NOT NULL, channel_id mediumint(9) NOT NULL, status varchar(20) NOT NULL, response text, published_url varchar(500), published_at datetime, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY post_id (post_id), KEY channel_id (channel_id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql_channels); dbDelta($sql_logs); } // 渠道管理类 class ChannelManager { public static function get_channels($active_only = true) { global $wpdb; $table_name = $wpdb->prefix . 'flex_sync_channels'; $where = $active_only ? "WHERE is_active = 1" : ""; $query = "SELECT * FROM $table_name $where ORDER BY channel_name"; return $wpdb->get_results($query); } public static function add_channel($data) { global $wpdb; $table_name = $wpdb->prefix . 'flex_sync_channels'; $defaults = array( 'is_active' => 1, 'content_rules' => json_encode(array( 'max_length' => 0, 'image_count' => 0, 'format' => 'html' )) ); $data = wp_parse_args($data, $defaults); return $wpdb->insert($table_name, $data); } public static function update_channel($id, $data) { global $wpdb; $table_name = $wpdb->prefix . 'flex_sync_channels'; return $wpdb->update($table_name, $data, array('id' => $id)); } public static function delete_channel($id) { global $wpdb; $table_name = $wpdb->prefix . 'flex_sync_channels'; return $wpdb->delete($table_name, array('id' => $id)); } } ?>
- 不同平台对内容格式有不同的要求,我们需要开发一个灵活的内容转换引擎: <?php class ContentTransformer { private $post; private $channel_rules; public function __construct($post_id, $channel_rules) { $this->post = get_post($post_id); $this->channel_rules = is_string($channel_rules) ? json_decode($channel_rules, true) : $channel_rules; } /** * 转换内容为适合目标平台的格式 */ public function transform() { $content = $this->post->post_content; // 应用转换规则 $content = $this->apply_rules($content); // 处理图片 $content = $this->process_images($content); // 处理长度限制 $content = $this->handle_length_limit($content); return array( 'title' => $this->transform_title(), 'content' => $content, 'excerpt' => $this->transform_excerpt(), 'images' => $this->extract_images() ); } private function apply_rules($content) { $rules = $this->channel_rules; // 移除短代码 if (isset($rules['remove_shortcodes']) && $rules['remove_shortcodes']) { $content = strip_shortcodes($content); } // 转换HTML标签 if (isset($rules['format']) && $rules['format'] === 'plaintext') { $content = wp_strip_all_tags($content); } elseif (isset($rules['format']) && $rules['format'] === 'markdown') { $content = $this->html_to_markdown($content); } // 添加来源声明 if (isset($rules['add_source']) && $rules['add_source']) { $content .= "nn来源: " . get_bloginfo('name'); } return $content; } private function process_images($content) { // 提取内容中的图片 preg_match_all('/<img[^>]+>/i', $content, $img_matches); foreach ($img_matches[0] as $img_tag) { // 根据渠道规则处理图片 // 这里可以添加图片上传到图床、压缩等逻辑 if (isset($this->channel_rules['upload_images']) && $this->channel_rules['upload_images']) { // 上传图片到指定图床 $new_img_tag = $this->upload_and_replace_image($img_tag); $content = str_replace($img_tag, $new_img_tag, $content); } } return $content; } private function handle_length_limit($content) { if (isset($this->channel_rules['max_length']) && $this->channel_rules['max_length'] > 0) { $max_length = intval($this->channel_rules['max_length']); if (mb_strlen($content) > $max_length) { $content = mb_substr($content, 0, $max_length - 3) . '...'; } } return $content; } private function transform_title() { $title = $this->post->post_title; // 这里可以添加标题转换规则 if (isset($this->channel_rules['title_prefix'])) { $title = $this->channel_rules['title_prefix'] . $title; } return $title; } private function extract_images() { $images = array(); $content = $this->post->post_content; preg_match_all('/<img[^>]+src="([^">]+)"/i', $content, $matches); if (!empty($matches[1])) { $images = $matches[1]; // 限制图片数量 if (isset($this->channel_rules['image_count']) && $this->channel_rules['image_count'] > 0) { $max_images = intval($this->channel_rules['image_count']); $images = array_slice($images, 0, $max_images); } } return $images; } // 其他辅助方法... } ?>
- 接下来,我们实现不同平台的API集成: <?php // 发布器基类 abstract class PlatformPublisher { protected $api_config; protected $channel_id; public function __construct($api_config, $channel_id) { $this->api_config = $api_config; $this->channel_id = $channel_id; } abstract public function publish($transformed_content); abstract public function test_connection(); protected function log_result($post_id, $status, $response, $published_url = '') { global $wpdb; $table_name = $wpdb->prefix . 'flex_sync_logs'; $data = array( 'post_id' => $post_id, 'channel_id' => $this->channel_id, 'status' => $status, 'response' => is_array($response) ? json_encode($response) : $response, 'published_url' => $published_url, 'published_at' => current_time('mysql') ); return $wpdb->insert($table_name, $data); } } // 微信公众号发布器 class WeChatPublisher extends PlatformPublisher { public function publish($transformed_content) { // 微信公众号API实现 $api_url = 'https://api.weixin.qq.com/cgi-bin/material/add_news'; $data = array( 'articles' => array( array( 'title' => $transformed_content['title'], 'content' => $transformed_content['content'], 'digest' => $transformed_content['excerpt'], 'thumb_media_id' => $this->upload_cover_image($transformed_content['images']) ) ) ); // 获取访问令牌 $access_token = $this->get_access_token(); // 发送请求 $response = wp_remote_post($api_url . '?access_token=' . $access_token, array( 'body' => json_encode($data, JSON_UNESCAPED_UNICODE), 'headers' => array('Content-Type' => 'application/json') )); if (is_wp_error($response)) { $this->log_result( $transformed_content['post_id'], 'error', $response->get_error_message() ); return false; } $body = json_decode(wp_remote_retrieve_body($response), true); if (isset($body['media_id'])) { $this->log_result( $transformed_content['post_id'], 'success', $body, 'https://mp.weixin.qq.com/s/' . $body['media_id'] ); return true; } else { $this->log_result( $transformed_content['post_id'], 'error', $body ); return false; } } private function get_access_token() { // 实现获取access_token的逻辑 // 这里应该包含缓存机制,避免频繁请求 return 'your_access_token'; } private function upload_cover_image($images) { // 上传封面图片到微信服务器 if (!empty($images)) { // 实现图片上传逻辑 return 'thumb_media_id'; } return ''; } public function test_connection() { // 测试API连接 return array('success' => true, 'message' => '连接成功'); } } // 发布器工厂 class PublisherFactory { public static function create($channel_type, $api_config, $channel_id) { switch ($channel_type) { case 'wechat': return new WeChatPublisher($api_config, $channel_id); case 'weibo': // 返回微博发布器实例 // return new WeiboPublisher($api_config, $channel_id); case 'zhihu': // 返回知乎发布器实例 // return new ZhihuPublisher($api_config, $channel_id); default: throw new Exception('不支持的渠道类型: ' . $channel_type); } } } ?>
- 最后,我们创建插件的管理界面: <?php // 在FlexContentSync类中添加管理菜单方法 public function add_admin_menu() { // 主菜单 add_menu_page( '多渠道同步', '内容同步', 'manage_options', 'flex-sync', array($this, 'render_main_page'), 'dashicons-share', 30 ); // 子菜单 add_submenu_page( 'flex-sync', '渠道管理', '渠道管理', 'manage_options', 'flex-sync-channels', array($this, 'render_channels_page') ); add_submenu_page( 'flex-sync', '同步日志', '同步日志', 'manage_options', 'flex-sync-logs', array($this, 'render_logs_page') ); add_submenu_page( 'flex-sync', '设置', '设置', 'manage_options', 'flex-sync-settings', array($this, 'render_settings_page') ); } public function render_main_page() { ?> <div class="wrap"> <h1><?php echo esc_html(get_admin_page_title()); ?></h1> <div class="card"> <h2>快速发布</h2> <form method="post" action=""> <?php wp_nonce_field('flex_sync_quick_publish', 'flex_sync_nonce'); ?> <table class="form-table"> <tr> <th scope="row">选择文章</th> <td> <select name="post_id" required> <option value="">选择要发布的文章</option> <?php $args = array( 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 50 ); $posts = get_posts($args); foreach ($posts as $post) { echo '<option value="' . $post->ID . '">' . esc_html($post->post_title) . '</option>'; } ?> </select> </td> </tr> <tr> <th scope="row">选择渠道</th> <td> <?php $channels = ChannelManager::get_channels(); foreach ($channels as $channel) { echo '<label>'; echo '<input type="checkbox" name="channels[]" value="' . $channel->id . '"> '; echo esc_html($channel->channel_name); echo '</label><br>'; } ?> </td> </tr> </table> <p class="submit"> <input type="submit" name="submit" class="button button-primary" value="立即发布"> <input type="submit" name="schedule" class="button" value="定时发布"> </p> </form> </div> <div class="card"> <h2>发布统计</h2> <p>最近7天发布情况统计图将显示在这里</p> <!-- 这里可以添加统计图表 --> </div> </div> <?php } // 处理发布请求 public function handle_publish_request() { publish', 'flex_sync_nonce')) { $post_id = intval($_POST['post_id']); $channels = isset($_POST['channels']) ? array_map('intval', $_POST['channels']) : array(); if ($post_id && !empty($channels)) { $this->sync_to_channels($post_id, $channels); // 显示成功消息 add_settings_error( 'flex_sync_messages', 'flex_sync_message', '内容已开始同步到选定的渠道', 'success' ); } } }?> ## 七、文章编辑界面集成 为了让用户在编辑文章时就能同步,我们在文章编辑页面添加元框: <?phpclass PostMetaBox { public static function init() { add_action('add_meta_boxes', array(__CLASS__, 'add_meta_box')); add_action('save_post', array(__CLASS__, 'save_meta_box'), 10, 2); add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_scripts')); } public static function add_meta_box() { add_meta_box( 'flex_sync_meta_box', '多渠道同步', array(__CLASS__, 'render_meta_box'), 'post', 'side', 'high' ); } public static function render_meta_box($post) { wp_nonce_field('flex_sync_meta_box', 'flex_sync_meta_box_nonce'); // 获取已配置的渠道 $channels = ChannelManager::get_channels(); // 获取该文章已同步的渠道 $synced_channels = self::get_synced_channels($post->ID); ?> <div id="flex-sync-meta-box"> <p><strong>选择要同步的渠道:</strong></p> <?php if (empty($channels)): ?> <p class="description">请先在<a href="<?php echo admin_url('admin.php?page=flex-sync-channels'); ?>">渠道管理</a>中添加渠道。</p> <?php else: ?> <div class="channel-list"> <?php foreach ($channels as $channel): ?> <label> <input type="checkbox" name="flex_sync_channels[]" value="<?php echo esc_attr($channel->id); ?>" <?php checked(in_array($channel->id, $synced_channels)); ?>> <?php echo esc_html($channel->channel_name); ?> <?php if (in_array($channel->id, $synced_channels)): ?> <span class="dashicons dashicons-yes" style="color: #46b450;"></span> <?php endif; ?> </label><br> <?php endforeach; ?> </div> <div style="margin-top: 15px;"> <label> <input type="checkbox" name="flex_sync_immediate" value="1"> 发布时立即同步 </label> </div> <div style="margin-top: 10px;"> <button type="button" id="flex-sync-test" class="button button-secondary"> 测试所选渠道 </button> <button type="button" id="flex-sync-preview" class="button button-secondary"> 内容预览 </button> </div> <div id="flex-sync-status" style="margin-top: 10px; display: none;"> <!-- 同步状态将在这里显示 --> </div> <?php endif; ?> </div> <script type="text/javascript"> jQuery(document).ready(function($) { $('#flex-sync-test').on('click', function() { var channels = []; $('input[name="flex_sync_channels[]"]:checked').each(function() { channels.push($(this).val()); }); if (channels.length === 0) { alert('请至少选择一个渠道'); return; } $('#flex-sync-status').html('<p>测试中...</p>').show(); $.post(ajaxurl, { action: 'flex_sync_test_channels', post_id: <?php echo $post->ID; ?>, channels: channels, nonce: '<?php echo wp_create_nonce("flex_sync_test"); ?>' }, function(response) { $('#flex-sync-status').html(response.data); }); }); $('#flex-sync-preview').on('click', function() { var channels = []; $('input[name="flex_sync_channels[]"]:checked').each(function() { channels.push($(this).val()); }); if (channels.length === 0) { alert('请至少选择一个渠道'); return; } // 打开预览窗口 window.open('<?php echo admin_url("admin-ajax.php?action=flex_sync_preview&post_id=" . $post->ID); ?>&channels=' + channels.join(','), 'preview', 'width=800,height=600'); }); }); </script> <?php } public static function save_meta_box($post_id, $post) { // 验证nonce if (!isset($_POST['flex_sync_meta_box_nonce']) || !wp_verify_nonce($_POST['flex_sync_meta_box_nonce'], 'flex_sync_meta_box')) { return; } // 检查自动保存 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } // 检查权限 if (!current_user_can('edit_post', $post_id)) { return; } // 保存选择的渠道 if (isset($_POST['flex_sync_channels'])) { $channels = array_map('intval', $_POST['flex_sync_channels']); update_post_meta($post_id, '_flex_sync_channels', $channels); } else { delete_post_meta($post_id, '_flex_sync_channels'); } // 如果勾选了立即同步,并且文章已发布 if (isset($_POST['flex_sync_immediate']) && $post->post_status === 'publish') { $channels = isset($_POST['flex_sync_channels']) ? array_map('intval', $_POST['flex_sync_channels']) : array(); if (!empty($channels)) { // 异步执行同步,避免阻塞保存过程 wp_schedule_single_event(time() + 5, 'flex_sync_async_publish', array($post_id, $channels)); } } } public static function get_synced_channels($post_id) { global $wpdb; $table_name = $wpdb->prefix . 'flex_sync_logs'; $query = $wpdb->prepare( "SELECT DISTINCT channel_id FROM $table_name WHERE post_id = %d AND status = 'success'", $post_id ); $results = $wpdb->get_col($query); return $results ? array_map('intval', $results) : array(); } public static function enqueue_scripts($hook) { if ('post.php' === $hook || 'post-new.php' === $hook) { wp_enqueue_script( 'flex-sync-admin', FLEX_SYNC_PLUGIN_URL . 'assets/js/admin.js', array('jquery'), FLEX_SYNC_VERSION, true ); wp_localize_script('flex-sync-admin', 'flexSync', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('flex_sync_ajax') )); } } } // 初始化元框PostMetaBox::init();?> ## 八、AJAX处理与异步任务 处理前端AJAX请求和后台异步任务: <?phpclass AjaxHandler { public static function init() { // 测试渠道连接 add_action('wp_ajax_flex_sync_test_channels', array(__CLASS__, 'test_channels')); // 内容预览 add_action('wp_ajax_flex_sync_preview', array(__CLASS__, 'preview_content')); // 手动触发同步 add_action('wp_ajax_flex_sync_manual', array(__CLASS__, 'manual_sync')); // 获取同步状态 add_action('wp_ajax_flex_sync_status', array(__CLASS__, 'get_sync_status')); // 异步发布任务 add_action('flex_sync_async_publish', array(__CLASS__, 'async_publish'), 10, 2); } public static function test_channels() { check_ajax_referer('flex_sync_test', 'nonce'); $post_id = intval($_POST['post_id']); $channel_ids = isset($_POST['channels']) ? array_map('intval', $_POST['channels']) : array(); if (empty($channel_ids)) { wp_send_json_error('请选择要测试的渠道'); } $results = array(); foreach ($channel_ids as $channel_id) { $channel = self::get_channel_by_id($channel_id); if ($channel) { try { $publisher = PublisherFactory::create( $channel->channel_type, json_decode($channel->api_config, true), $channel_id ); $test_result = $publisher->test_connection(); $results[] = array( 'channel' => $channel->channel_name, 'success' => $test_result['success'], 'message' => $test_result['message'] ); } catch (Exception $e) { $results[] = array( 'channel' => $channel->channel_name, 'success' => false, 'message' => $e->getMessage() ); } } } ob_start(); ?> <div class="test-results"> <h4>渠道连接测试结果:</h4> <ul> <?php foreach ($results as $result): ?> <li> <strong><?php echo esc_html($result['channel']); ?>:</strong> <?php if ($result['success']): ?> <span style="color: green;">✓ <?php echo esc_html($result['message']); ?></span> <?php else: ?> <span style="color: red;">✗ <?php echo esc_html($result['message']); ?></span> <?php endif; ?> </li> <?php endforeach; ?> </ul> </div> <?php $html = ob_get_clean(); wp_send_json_success($html); } public static function preview_content() { $post_id = intval($_GET['post_id']); $channel_ids = isset($_GET['channels']) ? array_map('intval', explode(',', $_GET['channels'])) : array(); ?> <!DOCTYPE html> <html> <head> <title>内容预览</title> <style> body { font-family: Arial, sans-serif; margin: 20px; } .preview-container { max-width: 800px; margin: 0 auto; } .channel-preview { margin-bottom: 40px; border: 1px solid #ddd; padding: 20px; } .channel-name { background: #f1f1f1; padding: 10px; margin: -20px -20px 20px -20px; } .content-preview { line-height: 1.6; } .image-preview { max-width: 100%; height: auto; margin: 10px 0; } </style> </head> <body> <div class="preview-container"> <h1>内容预览</h1> <?php foreach ($channel_ids as $channel_id): $channel = self::get_channel_by_id($channel_id); if (!$channel) continue; $transformer = new ContentTransformer($post_id, $channel->content_rules); $transformed = $transformer->transform(); ?> <div class="channel-preview"> <div class="channel-name"> <h2><?php echo esc_html($channel->channel_name); ?></h2> </div> <div class="content-preview"> <h3><?php echo esc_html($transformed['title']); ?></h3> <div><?php echo wpautop($transformed['content']); ?></div> <?php if (!empty($transformed['images'])): ?> <h4>包含的图片:</h4> <?php foreach ($transformed['images'] as $image): ?> <img src="<?php echo esc_url($image); ?>" class="image-preview"> <?php endforeach; ?> <?php endif; ?> </div> </div> <?php endforeach; ?> </div> </body> </html> <?php exit; } public static function async_publish($post_id, $channels) { // 这里调用同步方法 $flex_sync = FlexContentSync::get_instance(); $flex_sync->sync_to_channels($post_id, $channels); } private static function get_channel_by_id($channel_id) { global $wpdb; $table_name = $wpdb->prefix . 'flex_sync_channels'; return $wpdb->get_row($wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $channel_id )); } } // 初始化AJAX处理器AjaxHandler::init();?> ## 九、定时任务与批量处理 实现定时同步和批量处理功能: <?phpclass BatchProcessor { public static function init() { // 注册定时任务 add_filter('cron_schedules', array(__CLASS__, 'add_cron_schedules')); add_action('flex_sync_batch_process', array(__CLASS__, 'process_batch')); // 激活插件时安排定时任务 register_activation_hook(__FILE__, array(__CLASS__, 'schedule_cron')); register_deactivation_hook(__FILE__, array(__CLASS__, 'unschedule_cron')); } public static function add_cron_schedules($schedules) { $schedules['flex_sync_5min'] = array( 'interval' => 300, 'display' => __('每5分钟', 'flex-sync') ); $schedules['flex_sync_hourly'] = array( 'interval' => 3600, 'display' => __('每小时', 'flex-sync') ); return $schedules; } public static function schedule_cron() { if (!wp_next_scheduled('flex_sync_batch_process')) { wp_schedule_event(time(), 'flex_sync_hourly', 'flex_sync_batch_process'); } } public static function unschedule_cron() { $timestamp = wp_next_scheduled('flex_sync_batch_process'); if ($timestamp) { wp_unschedule_event($timestamp, 'flex_sync_batch_process'); } } public static function process_batch() { // 获取需要定时发布的文章 $pending_posts = self::get_pending_posts(); foreach ($pending_posts as $post) { // 获取该文章需要同步的渠道 $channels = get_post_meta($post->ID, '_flex_sync_scheduled', true); if ($channels && is_array($channels)) { $flex_sync = FlexContentSync::get_instance(); $flex_sync->sync_to_channels($post->ID, $channels); // 清除定时标记 delete_post_meta($post->ID, '_flex_sync_scheduled'); } } // 清理旧的同步日志(保留30天) self::cleanup_old_logs(); } private static function get_pending_posts() { global $wpdb; $query = " SELECT p.ID, p.post_title FROM {$wpdb->posts} p INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id WHERE p.post_status = 'publish' AND pm.meta_key = '_flex_sync_scheduled' AND pm.meta_value != '' ORDER BY p.post_date DESC LIMIT 20 "; return $wpdb->get_results($query); } private static function cleanup_old_logs() { global $wpdb; $table_name = $wpdb->prefix . 'flex_sync_logs'; $thirty_days_ago = date('Y-m-d H:i:s', strtotime('-30 days')); $wpdb->query($wpdb->prepare( "DELETE FROM $table_name WHERE created_at < %s", $thirty_days_ago )); } /** * 批量同步多篇文章到多个渠道 */ public static function batch_sync($post_ids, $channel_ids) { $results = array(); foreach ($post_ids as $post_id) { foreach ($channel_ids as $channel_id) { try { $flex_sync = FlexContentSync::get_instance(); $success = $flex_sync->sync_to_channel($post_id, $channel_id); $results[] = array( 'post_id' => $post_id, 'channel_id' => $channel_id, 'success' => $success, 'message' => $success ? '同步成功' : '同步失败' ); } catch (Exception $e) { $results[] = array( 'post_id' => $post_id, 'channel_id' => $channel_id, 'success' => false, 'message' => $e->getMessage() ); } } } return $results; } }
在当今数字化媒体时代,网络传媒机构需要在多个平台同步发布内容,包括微信公众号、微博、头条号、知乎等多个渠道。手动同步不仅效率低下,还容易出错。本教程将指导您开发一个WordPress柔性内容多渠道同步插件,实现一键发布到多个平台。
核心需求:
- 支持自定义内容转换规则,适应不同平台的内容格式要求
- 可配置的发布渠道管理
- 定时发布和即时发布两种模式
- 发布状态跟踪和错误处理
- 支持图片等媒体资源的自动处理
首先,我们需要创建插件的基本文件结构:
<?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('FLEX_SYNC_VERSION', '1.0.0');
define('FLEX_SYNC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('FLEX_SYNC_PLUGIN_URL', plugin_dir_url(__FILE__));
// 初始化插件
class FlexContentSync {
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();
}
private function init_hooks() {
// 激活/停用插件时的操作
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
// 初始化
add_action('plugins_loaded', array($this, 'init'));
// 添加管理菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 保存文章时触发同步
add_action('save_post', array($this, 'on_save_post'), 10, 3);
}
public function activate() {
// 创建必要的数据库表
$this->create_tables();
// 设置默认选项
$this->set_default_options();
}
public function deactivate() {
// 清理临时数据
// 注意:这里不清除配置数据,以便重新激活时保留设置
}
public function init() {
// 加载文本域
load_plugin_textdomain('flex-sync', false, dirname(plugin_basename(__FILE__)) . '/languages');
// 加载必要的类
$this->load_dependencies();
}
// 其他方法将在后续章节实现
}
// 启动插件
FlexContentSync::get_instance();
?>
我们需要设计数据库表来存储渠道配置和同步记录:
<?php
// 在FlexContentSync类中添加以下方法
private function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name_channels = $wpdb->prefix . 'flex_sync_channels';
$table_name_logs = $wpdb->prefix . 'flex_sync_logs';
// 渠道配置表
$sql_channels = "CREATE TABLE IF NOT EXISTS $table_name_channels (
id mediumint(9) NOT NULL AUTO_INCREMENT,
channel_name varchar(100) NOT NULL,
channel_type varchar(50) NOT NULL,
api_config text NOT NULL,
is_active tinyint(1) DEFAULT 1,
content_rules text,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
// 同步日志表
$sql_logs = "CREATE TABLE IF NOT EXISTS $table_name_logs (
id mediumint(9) NOT NULL AUTO_INCREMENT,
post_id bigint(20) NOT NULL,
channel_id mediumint(9) NOT NULL,
status varchar(20) NOT NULL,
response text,
published_url varchar(500),
published_at datetime,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY post_id (post_id),
KEY channel_id (channel_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql_channels);
dbDelta($sql_logs);
}
// 渠道管理类
class ChannelManager {
public static function get_channels($active_only = true) {
global $wpdb;
$table_name = $wpdb->prefix . 'flex_sync_channels';
$where = $active_only ? "WHERE is_active = 1" : "";
$query = "SELECT * FROM $table_name $where ORDER BY channel_name";
return $wpdb->get_results($query);
}
public static function add_channel($data) {
global $wpdb;
$table_name = $wpdb->prefix . 'flex_sync_channels';
$defaults = array(
'is_active' => 1,
'content_rules' => json_encode(array(
'max_length' => 0,
'image_count' => 0,
'format' => 'html'
))
);
$data = wp_parse_args($data, $defaults);
return $wpdb->insert($table_name, $data);
}
public static function update_channel($id, $data) {
global $wpdb;
$table_name = $wpdb->prefix . 'flex_sync_channels';
return $wpdb->update($table_name, $data, array('id' => $id));
}
public static function delete_channel($id) {
global $wpdb;
$table_name = $wpdb->prefix . 'flex_sync_channels';
return $wpdb->delete($table_name, array('id' => $id));
}
}
?>
不同平台对内容格式有不同的要求,我们需要开发一个灵活的内容转换引擎:
<?php
class ContentTransformer {
private $post;
private $channel_rules;
public function __construct($post_id, $channel_rules) {
$this->post = get_post($post_id);
$this->channel_rules = is_string($channel_rules) ?
json_decode($channel_rules, true) : $channel_rules;
}
/**
* 转换内容为适合目标平台的格式
*/
public function transform() {
$content = $this->post->post_content;
// 应用转换规则
$content = $this->apply_rules($content);
// 处理图片
$content = $this->process_images($content);
// 处理长度限制
$content = $this->handle_length_limit($content);
return array(
'title' => $this->transform_title(),
'content' => $content,
'excerpt' => $this->transform_excerpt(),
'images' => $this->extract_images()
);
}
private function apply_rules($content) {
$rules = $this->channel_rules;
// 移除短代码
if (isset($rules['remove_shortcodes']) && $rules['remove_shortcodes']) {
$content = strip_shortcodes($content);
}
// 转换HTML标签
if (isset($rules['format']) && $rules['format'] === 'plaintext') {
$content = wp_strip_all_tags($content);
} elseif (isset($rules['format']) && $rules['format'] === 'markdown') {
$content = $this->html_to_markdown($content);
}
// 添加来源声明
if (isset($rules['add_source']) && $rules['add_source']) {
$content .= "nn来源: " . get_bloginfo('name');
}
return $content;
}
private function process_images($content) {
// 提取内容中的图片
preg_match_all('/<img[^>]+>/i', $content, $img_matches);
foreach ($img_matches[0] as $img_tag) {
// 根据渠道规则处理图片
// 这里可以添加图片上传到图床、压缩等逻辑
if (isset($this->channel_rules['upload_images']) &&
$this->channel_rules['upload_images']) {
// 上传图片到指定图床
$new_img_tag = $this->upload_and_replace_image($img_tag);
$content = str_replace($img_tag, $new_img_tag, $content);
}
}
return $content;
}
private function handle_length_limit($content) {
if (isset($this->channel_rules['max_length']) &&
$this->channel_rules['max_length'] > 0) {
$max_length = intval($this->channel_rules['max_length']);
if (mb_strlen($content) > $max_length) {
$content = mb_substr($content, 0, $max_length - 3) . '...';
}
}
return $content;
}
private function transform_title() {
$title = $this->post->post_title;
// 这里可以添加标题转换规则
if (isset($this->channel_rules['title_prefix'])) {
$title = $this->channel_rules['title_prefix'] . $title;
}
return $title;
}
private function extract_images() {
$images = array();
$content = $this->post->post_content;
preg_match_all('/<img[^>]+src="([^">]+)"/i', $content, $matches);
if (!empty($matches[1])) {
$images = $matches[1];
// 限制图片数量
if (isset($this->channel_rules['image_count']) &&
$this->channel_rules['image_count'] > 0) {
$max_images = intval($this->channel_rules['image_count']);
$images = array_slice($images, 0, $max_images);
}
}
return $images;
}
// 其他辅助方法...
}
?>
接下来,我们实现不同平台的API集成:
<?php
// 发布器基类
abstract class PlatformPublisher {
protected $api_config;
protected $channel_id;
public function __construct($api_config, $channel_id) {
$this->api_config = $api_config;
$this->channel_id = $channel_id;
}
abstract public function publish($transformed_content);
abstract public function test_connection();
protected function log_result($post_id, $status, $response, $published_url = '') {
global $wpdb;
$table_name = $wpdb->prefix . 'flex_sync_logs';
$data = array(
'post_id' => $post_id,
'channel_id' => $this->channel_id,
'status' => $status,
'response' => is_array($response) ? json_encode($response) : $response,
'published_url' => $published_url,
'published_at' => current_time('mysql')
);
return $wpdb->insert($table_name, $data);
}
}
// 微信公众号发布器
class WeChatPublisher extends PlatformPublisher {
public function publish($transformed_content) {
// 微信公众号API实现
$api_url = 'https://api.weixin.qq.com/cgi-bin/material/add_news';
$data = array(
'articles' => array(
array(
'title' => $transformed_content['title'],
'content' => $transformed_content['content'],
'digest' => $transformed_content['excerpt'],
'thumb_media_id' => $this->upload_cover_image($transformed_content['images'])
)
)
);
// 获取访问令牌
$access_token = $this->get_access_token();
// 发送请求
$response = wp_remote_post($api_url . '?access_token=' . $access_token, array(
'body' => json_encode($data, JSON_UNESCAPED_UNICODE),
'headers' => array('Content-Type' => 'application/json')
));
if (is_wp_error($response)) {
$this->log_result(
$transformed_content['post_id'],
'error',
$response->get_error_message()
);
return false;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (isset($body['media_id'])) {
$this->log_result(
$transformed_content['post_id'],
'success',
$body,
'https://mp.weixin.qq.com/s/' . $body['media_id']
);
return true;
} else {
$this->log_result(
$transformed_content['post_id'],
'error',
$body
);
return false;
}
}
private function get_access_token() {
// 实现获取access_token的逻辑
// 这里应该包含缓存机制,避免频繁请求
return 'your_access_token';
}
private function upload_cover_image($images) {
// 上传封面图片到微信服务器
if (!empty($images)) {
// 实现图片上传逻辑
return 'thumb_media_id';
}
return '';
}
public function test_connection() {
// 测试API连接
return array('success' => true, 'message' => '连接成功');
}
}
// 发布器工厂
class PublisherFactory {
public static function create($channel_type, $api_config, $channel_id) {
switch ($channel_type) {
case 'wechat':
return new WeChatPublisher($api_config, $channel_id);
case 'weibo':
// 返回微博发布器实例
// return new WeiboPublisher($api_config, $channel_id);
case 'zhihu':
// 返回知乎发布器实例
// return new ZhihuPublisher($api_config, $channel_id);
default:
throw new Exception('不支持的渠道类型: ' . $channel_type);
}
}
}
?>
最后,我们创建插件的管理界面:
<?php
// 在FlexContentSync类中添加管理菜单方法
public function add_admin_menu() {
// 主菜单
add_menu_page(
'多渠道同步',
'内容同步',
'manage_options',
'flex-sync',
array($this, 'render_main_page'),
'dashicons-share',
30
);
// 子菜单
add_submenu_page(
'flex-sync',
'渠道管理',
'渠道管理',
'manage_options',
'flex-sync-channels',
array($this, 'render_channels_page')
);
add_submenu_page(
'flex-sync',
'同步日志',
'同步日志',
'manage_options',
'flex-sync-logs',
array($this, 'render_logs_page')
);
add_submenu_page(
'flex-sync',
'设置',
'设置',
'manage_options',
'flex-sync-settings',
array($this, 'render_settings_page')
);
}
public function render_main_page() {
?>
<div class="wrap">
<h1><?php echo esc_html(get_admin_page_title()); ?></h1>
<div class="card">
<h2>快速发布</h2>
<form method="post" action="">
<?php wp_nonce_field('flex_sync_quick_publish', 'flex_sync_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row">选择文章</th>
<td>
<select name="post_id" required>
<option value="">选择要发布的文章</option>
<?php
$args = array(
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => 50
);
$posts = get_posts($args);
foreach ($posts as $post) {
echo '<option value="' . $post->ID . '">' .
esc_html($post->post_title) . '</option>';
}
?>
</select>
</td>
</tr>
<tr>
<th scope="row">选择渠道</th>
<td>
<?php
$channels = ChannelManager::get_channels();
foreach ($channels as $channel) {
echo '<label>';
echo '<input type="checkbox" name="channels[]" value="' .
$channel->id . '"> ';
echo esc_html($channel->channel_name);
echo '</label><br>';
}
?>
</td>
</tr>
</table>
<p class="submit">
<input type="submit" name="submit" class="button button-primary"
value="立即发布">
<input type="submit" name="schedule" class="button"
value="定时发布">
</p>
</form>
</div>
<div class="card">
<h2>发布统计</h2>
<p>最近7天发布情况统计图将显示在这里</p>
<!-- 这里可以添加统计图表 -->
</div>
</div>
<?php
}
// 处理发布请求
public function handle_publish_request() {
publish', 'flex_sync_nonce')) {
$post_id = intval($_POST['post_id']);
$channels = isset($_POST['channels']) ? array_map('intval', $_POST['channels']) : array();
if ($post_id && !empty($channels)) {
$this->sync_to_channels($post_id, $channels);
// 显示成功消息
add_settings_error(
'flex_sync_messages',
'flex_sync_message',
'内容已开始同步到选定的渠道',
'success'
);
}
}
}
?>
## 七、文章编辑界面集成
为了让用户在编辑文章时就能同步,我们在文章编辑页面添加元框:
<?php
class PostMetaBox {
public static function init() {
add_action('add_meta_boxes', array(__CLASS__, 'add_meta_box'));
add_action('save_post', array(__CLASS__, 'save_meta_box'), 10, 2);
add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_scripts'));
}
public static function add_meta_box() {
add_meta_box(
'flex_sync_meta_box',
'多渠道同步',
array(__CLASS__, 'render_meta_box'),
'post',
'side',
'high'
);
}
public static function render_meta_box($post) {
wp_nonce_field('flex_sync_meta_box', 'flex_sync_meta_box_nonce');
// 获取已配置的渠道
$channels = ChannelManager::get_channels();
// 获取该文章已同步的渠道
$synced_channels = self::get_synced_channels($post->ID);
?>
<div id="flex-sync-meta-box">
<p><strong>选择要同步的渠道:</strong></p>
<?php if (empty($channels)): ?>
<p class="description">请先在<a href="<?php echo admin_url('admin.php?page=flex-sync-channels'); ?>">渠道管理</a>中添加渠道。</p>
<?php else: ?>
<div class="channel-list">
<?php foreach ($channels as $channel): ?>
<label>
<input type="checkbox"
name="flex_sync_channels[]"
value="<?php echo esc_attr($channel->id); ?>"
<?php checked(in_array($channel->id, $synced_channels)); ?>>
<?php echo esc_html($channel->channel_name); ?>
<?php if (in_array($channel->id, $synced_channels)): ?>
<span class="dashicons dashicons-yes" style="color: #46b450;"></span>
<?php endif; ?>
</label><br>
<?php endforeach; ?>
</div>
<div style="margin-top: 15px;">
<label>
<input type="checkbox" name="flex_sync_immediate" value="1">
发布时立即同步
</label>
</div>
<div style="margin-top: 10px;">
<button type="button" id="flex-sync-test" class="button button-secondary">
测试所选渠道
</button>
<button type="button" id="flex-sync-preview" class="button button-secondary">
内容预览
</button>
</div>
<div id="flex-sync-status" style="margin-top: 10px; display: none;">
<!-- 同步状态将在这里显示 -->
</div>
<?php endif; ?>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
$('#flex-sync-test').on('click', function() {
var channels = [];
$('input[name="flex_sync_channels[]"]:checked').each(function() {
channels.push($(this).val());
});
if (channels.length === 0) {
alert('请至少选择一个渠道');
return;
}
$('#flex-sync-status').html('<p>测试中...</p>').show();
$.post(ajaxurl, {
action: 'flex_sync_test_channels',
post_id: <?php echo $post->ID; ?>,
channels: channels,
nonce: '<?php echo wp_create_nonce("flex_sync_test"); ?>'
}, function(response) {
$('#flex-sync-status').html(response.data);
});
});
$('#flex-sync-preview').on('click', function() {
var channels = [];
$('input[name="flex_sync_channels[]"]:checked').each(function() {
channels.push($(this).val());
});
if (channels.length === 0) {
alert('请至少选择一个渠道');
return;
}
// 打开预览窗口
window.open('<?php echo admin_url("admin-ajax.php?action=flex_sync_preview&post_id=" . $post->ID); ?>&channels=' + channels.join(','),
'preview', 'width=800,height=600');
});
});
</script>
<?php
}
public static function save_meta_box($post_id, $post) {
// 验证nonce
if (!isset($_POST['flex_sync_meta_box_nonce']) ||
!wp_verify_nonce($_POST['flex_sync_meta_box_nonce'], 'flex_sync_meta_box')) {
return;
}
// 检查自动保存
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
// 检查权限
if (!current_user_can('edit_post', $post_id)) {
return;
}
// 保存选择的渠道
if (isset($_POST['flex_sync_channels'])) {
$channels = array_map('intval', $_POST['flex_sync_channels']);
update_post_meta($post_id, '_flex_sync_channels', $channels);
} else {
delete_post_meta($post_id, '_flex_sync_channels');
}
// 如果勾选了立即同步,并且文章已发布
if (isset($_POST['flex_sync_immediate']) && $post->post_status === 'publish') {
$channels = isset($_POST['flex_sync_channels']) ?
array_map('intval', $_POST['flex_sync_channels']) : array();
if (!empty($channels)) {
// 异步执行同步,避免阻塞保存过程
wp_schedule_single_event(time() + 5, 'flex_sync_async_publish', array($post_id, $channels));
}
}
}
public static function get_synced_channels($post_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'flex_sync_logs';
$query = $wpdb->prepare(
"SELECT DISTINCT channel_id FROM $table_name WHERE post_id = %d AND status = 'success'",
$post_id
);
$results = $wpdb->get_col($query);
return $results ? array_map('intval', $results) : array();
}
public static function enqueue_scripts($hook) {
if ('post.php' === $hook || 'post-new.php' === $hook) {
wp_enqueue_script(
'flex-sync-admin',
FLEX_SYNC_PLUGIN_URL . 'assets/js/admin.js',
array('jquery'),
FLEX_SYNC_VERSION,
true
);
wp_localize_script('flex-sync-admin', 'flexSync', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('flex_sync_ajax')
));
}
}
}
// 初始化元框
PostMetaBox::init();
?>
## 八、AJAX处理与异步任务
处理前端AJAX请求和后台异步任务:
<?php
class AjaxHandler {
public static function init() {
// 测试渠道连接
add_action('wp_ajax_flex_sync_test_channels', array(__CLASS__, 'test_channels'));
// 内容预览
add_action('wp_ajax_flex_sync_preview', array(__CLASS__, 'preview_content'));
// 手动触发同步
add_action('wp_ajax_flex_sync_manual', array(__CLASS__, 'manual_sync'));
// 获取同步状态
add_action('wp_ajax_flex_sync_status', array(__CLASS__, 'get_sync_status'));
// 异步发布任务
add_action('flex_sync_async_publish', array(__CLASS__, 'async_publish'), 10, 2);
}
public static function test_channels() {
check_ajax_referer('flex_sync_test', 'nonce');
$post_id = intval($_POST['post_id']);
$channel_ids = isset($_POST['channels']) ? array_map('intval', $_POST['channels']) : array();
if (empty($channel_ids)) {
wp_send_json_error('请选择要测试的渠道');
}
$results = array();
foreach ($channel_ids as $channel_id) {
$channel = self::get_channel_by_id($channel_id);
if ($channel) {
try {
$publisher = PublisherFactory::create(
$channel->channel_type,
json_decode($channel->api_config, true),
$channel_id
);
$test_result = $publisher->test_connection();
$results[] = array(
'channel' => $channel->channel_name,
'success' => $test_result['success'],
'message' => $test_result['message']
);
} catch (Exception $e) {
$results[] = array(
'channel' => $channel->channel_name,
'success' => false,
'message' => $e->getMessage()
);
}
}
}
ob_start();
?>
<div class="test-results">
<h4>渠道连接测试结果:</h4>
<ul>
<?php foreach ($results as $result): ?>
<li>
<strong><?php echo esc_html($result['channel']); ?>:</strong>
<?php if ($result['success']): ?>
<span style="color: green;">✓ <?php echo esc_html($result['message']); ?></span>
<?php else: ?>
<span style="color: red;">✗ <?php echo esc_html($result['message']); ?></span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php
$html = ob_get_clean();
wp_send_json_success($html);
}
public static function preview_content() {
$post_id = intval($_GET['post_id']);
$channel_ids = isset($_GET['channels']) ?
array_map('intval', explode(',', $_GET['channels'])) : array();
?>
<!DOCTYPE html>
<html>
<head>
<title>内容预览</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.preview-container { max-width: 800px; margin: 0 auto; }
.channel-preview { margin-bottom: 40px; border: 1px solid #ddd; padding: 20px; }
.channel-name { background: #f1f1f1; padding: 10px; margin: -20px -20px 20px -20px; }
.content-preview { line-height: 1.6; }
.image-preview { max-width: 100%; height: auto; margin: 10px 0; }
</style>
</head>
<body>
<div class="preview-container">
<h1>内容预览</h1>
<?php foreach ($channel_ids as $channel_id):
$channel = self::get_channel_by_id($channel_id);
if (!$channel) continue;
$transformer = new ContentTransformer($post_id, $channel->content_rules);
$transformed = $transformer->transform();
?>
<div class="channel-preview">
<div class="channel-name">
<h2><?php echo esc_html($channel->channel_name); ?></h2>
</div>
<div class="content-preview">
<h3><?php echo esc_html($transformed['title']); ?></h3>
<div><?php echo wpautop($transformed['content']); ?></div>
<?php if (!empty($transformed['images'])): ?>
<h4>包含的图片:</h4>
<?php foreach ($transformed['images'] as $image): ?>
<img src="<?php echo esc_url($image); ?>" class="image-preview">
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</body>
</html>
<?php
exit;
}
public static function async_publish($post_id, $channels) {
// 这里调用同步方法
$flex_sync = FlexContentSync::get_instance();
$flex_sync->sync_to_channels($post_id, $channels);
}
private static function get_channel_by_id($channel_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'flex_sync_channels';
return $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table_name WHERE id = %d",
$channel_id
));
}
}
// 初始化AJAX处理器
AjaxHandler::init();
?>
## 九、定时任务与批量处理
实现定时同步和批量处理功能:
<?php
class BatchProcessor {
public static function init() {
// 注册定时任务
add_filter('cron_schedules', array(__CLASS__, 'add_cron_schedules'));
add_action('flex_sync_batch_process', array(__CLASS__, 'process_batch'));
// 激活插件时安排定时任务
register_activation_hook(__FILE__, array(__CLASS__, 'schedule_cron'));
register_deactivation_hook(__FILE__, array(__CLASS__, 'unschedule_cron'));
}
public static function add_cron_schedules($schedules) {
$schedules['flex_sync_5min'] = array(
'interval' => 300,
'display' => __('每5分钟', 'flex-sync')
);
$schedules['flex_sync_hourly'] = array(
'interval' => 3600,
'display' => __('每小时', 'flex-sync')
);
return $schedules;
}
public static function schedule_cron() {
if (!wp_next_scheduled('flex_sync_batch_process')) {
wp_schedule_event(time(), 'flex_sync_hourly', 'flex_sync_batch_process');
}
}
public static function unschedule_cron() {
$timestamp = wp_next_scheduled('flex_sync_batch_process');
if ($timestamp) {
wp_unschedule_event($timestamp, 'flex_sync_batch_process');
}
}
public static function process_batch() {
// 获取需要定时发布的文章
$pending_posts = self::get_pending_posts();
foreach ($pending_posts as $post) {
// 获取该文章需要同步的渠道
$channels = get_post_meta($post->ID, '_flex_sync_scheduled', true);
if ($channels && is_array($channels)) {
$flex_sync = FlexContentSync::get_instance();
$flex_sync->sync_to_channels($post->ID, $channels);
// 清除定时标记
delete_post_meta($post->ID, '_flex_sync_scheduled');
}
}
// 清理旧的同步日志(保留30天)
self::cleanup_old_logs();
}
private static function get_pending_posts() {
global $wpdb;
$query = "
SELECT p.ID, p.post_title
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
WHERE p.post_status = 'publish'
AND pm.meta_key = '_flex_sync_scheduled'
AND pm.meta_value != ''
ORDER BY p.post_date DESC
LIMIT 20
";
return $wpdb->get_results($query);
}
private static function cleanup_old_logs() {
global $wpdb;
$table_name = $wpdb->prefix . 'flex_sync_logs';
$thirty_days_ago = date('Y-m-d H:i:s', strtotime('-30 days'));
$wpdb->query($wpdb->prepare(
"DELETE FROM $table_name WHERE created_at < %s",
$thirty_days_ago
));
}
/**
* 批量同步多篇文章到多个渠道
*/
public static function batch_sync($post_ids, $channel_ids) {
$results = array();
foreach ($post_ids as $post_id) {
foreach ($channel_ids as $channel_id) {
try {
$flex_sync = FlexContentSync::get_instance();
$success = $flex_sync->sync_to_channel($post_id, $channel_id);
$results[] = array(
'post_id' => $post_id,
'channel_id' => $channel_id,
'success' => $success,
'message' => $success ? '同步成功' : '同步失败'
);
} catch (Exception $e) {
$results[] = array(
'post_id' => $post_id,
'channel_id' => $channel_id,
'success' => false,
'message' => $e->getMessage()
);
}
}
}
return $results;
}
}


