文章目录
-
- 在当今网络传媒环境中,内容合规性已成为网站运营的关键环节。WordPress作为全球最流行的内容管理系统,承载着大量媒体内容,但缺乏内置的内容审查机制。本教程将指导您开发一个柔性内容合规审查插件,既能自动检测敏感内容,又保留人工审核的灵活性,帮助网络传媒站点在遵守法规的同时保持内容多样性。
-
- 首先,我们创建插件的基本文件结构: wp-content/plugins/ └── flexible-content-audit/ ├── flexible-content-audit.php # 主插件文件 ├── includes/ │ ├── class-audit-core.php # 核心审查类 │ ├── class-content-scanner.php # 内容扫描器 │ ├── class-admin-interface.php # 管理界面 │ └── class-audit-logger.php # 日志记录器 ├── assets/ │ ├── css/ │ │ └── admin-styles.css # 管理界面样式 │ └── js/ │ └── admin-scripts.js # 管理界面脚本 └── languages/ # 国际化文件
- <?php /** * Plugin Name: 柔性内容合规审查插件 * Plugin URI: https://example.com/flexible-content-audit * Description: 为网络传媒WordPress站点提供柔性内容合规审查功能 * Version: 1.0.0 * Author: 您的名称 * License: GPL v2 or later * Text Domain: flexible-content-audit */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('FCA_PLUGIN_VERSION', '1.0.0'); define('FCA_PLUGIN_PATH', plugin_dir_path(__FILE__)); define('FCA_PLUGIN_URL', plugin_dir_url(__FILE__)); // 自动加载类文件 spl_autoload_register(function ($class_name) { if (strpos($class_name, 'FCA_') === 0) { $file = FCA_PLUGIN_PATH . 'includes/class-' . strtolower(str_replace('_', '-', $class_name)) . '.php'; if (file_exists($file)) { require_once $file; } } }); // 初始化插件 function fca_init_plugin() { // 检查WordPress版本 if (version_compare(get_bloginfo('version'), '5.0', '<')) { add_action('admin_notices', function() { echo '<div class="notice notice-error"><p>'; echo __('柔性内容合规审查插件需要WordPress 5.0或更高版本', 'flexible-content-audit'); echo '</p></div>'; }); return; } // 实例化核心类 $GLOBALS['flexible_content_audit'] = new FCA_Audit_Core(); } add_action('plugins_loaded', 'fca_init_plugin'); // 插件激活钩子 register_activation_hook(__FILE__, 'fca_activate_plugin'); function fca_activate_plugin() { // 创建必要的数据库表 fca_create_audit_tables(); // 设置默认选项 $default_options = array( 'audit_mode' => 'flexible', // flexible, strict, manual 'sensitive_keywords' => '敏感词1,敏感词2,违规词1', 'auto_audit_post_types' => array('post', 'page'), 'notification_email' => get_option('admin_email'), 'audit_threshold' => 70, // 敏感度阈值(0-100) ); add_option('fca_settings', $default_options); } // 创建审查记录表 function fca_create_audit_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_name = $wpdb->prefix . 'fca_audit_logs'; $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id bigint(20) NOT NULL AUTO_INCREMENT, post_id bigint(20) NOT NULL, audit_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, audit_status varchar(20) NOT NULL, -- pending, approved, rejected, flagged sensitivity_score int(3) DEFAULT 0, matched_keywords text, auditor_id bigint(20), audit_notes text, PRIMARY KEY (id), KEY post_id (post_id), KEY audit_status (audit_status) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } ?>
-
- <?php /** * 内容扫描器类 * 负责扫描文章内容中的敏感信息 */ class FCA_Content_Scanner { private $sensitive_keywords = array(); private $sensitivity_score = 0; private $matched_items = array(); /** * 初始化扫描器 * @param array $keywords 敏感关键词数组 */ public function __construct($keywords = array()) { $this->sensitive_keywords = $keywords; } /** * 扫描文章内容 * @param string $content 文章内容 * @param string $title 文章标题 * @return array 扫描结果 */ public function scan_content($content, $title = '') { $this->sensitivity_score = 0; $this->matched_items = array(); // 合并标题和内容进行扫描 $full_text = $title . ' ' . $content; // 扫描敏感关键词 $this->scan_keywords($full_text); // 扫描链接(可选扩展功能) $this->scan_links($content); // 扫描图片ALT文本(可选扩展功能) $this->scan_images($content); return array( 'score' => $this->sensitivity_score, 'matches' => $this->matched_items, 'status' => $this->get_audit_status() ); } /** * 扫描敏感关键词 * @param string $text 待扫描文本 */ private function scan_keywords($text) { foreach ($this->sensitive_keywords as $keyword) { $keyword = trim($keyword); if (empty($keyword)) continue; // 使用正则表达式进行匹配(支持中文) $pattern = '/' . preg_quote($keyword, '/') . '/iu'; if (preg_match_all($pattern, $text, $matches)) { $count = count($matches[0]); $this->sensitivity_score += $count * 10; // 每个匹配加10分 $this->matched_items[] = array( 'type' => 'keyword', 'value' => $keyword, 'count' => $count, 'risk_level' => $this->get_keyword_risk_level($keyword) ); } } // 限制分数在0-100之间 $this->sensitivity_score = min(100, $this->sensitivity_score); } /** * 扫描外部链接 * @param string $content 文章内容 */ private function scan_links($content) { // 使用DOM解析器提取所有链接 if (class_exists('DOMDocument')) { $dom = new DOMDocument(); @$dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8')); $links = $dom->getElementsByTagName('a'); foreach ($links as $link) { $href = $link->getAttribute('href'); // 检查是否为外部链接 if ($this->is_external_link($href)) { $this->sensitivity_score += 5; // 外部链接加5分 $this->matched_items[] = array( 'type' => 'external_link', 'url' => $href, 'text' => $link->textContent ); } } } } /** * 判断是否为外部链接 * @param string $url 链接地址 * @return bool */ private function is_external_link($url) { $site_url = site_url(); $parsed_url = parse_url($url); $parsed_site = parse_url($site_url); if (!isset($parsed_url['host'])) { return false; } return $parsed_url['host'] !== $parsed_site['host']; } /** * 根据关键词获取风险等级 * @param string $keyword 关键词 * @return string 风险等级 */ private function get_keyword_risk_level($keyword) { // 这里可以扩展为从数据库或配置文件中读取风险等级 $high_risk = array('违规词1', '违规词2'); $medium_risk = array('敏感词1', '敏感词2'); if (in_array($keyword, $high_risk)) { return 'high'; } elseif (in_array($keyword, $medium_risk)) { return 'medium'; } else { return 'low'; } } /** * 获取审查状态建议 * @return string 状态建议 */ private function get_audit_status() { if ($this->sensitivity_score >= 80) { return 'rejected'; // 建议拒绝 } elseif ($this->sensitivity_score >= 50) { return 'flagged'; // 需要人工审核 } else { return 'approved'; // 自动通过 } } } ?>
- <?php /** * 管理界面类 * 提供插件设置和内容审查界面 */ class FCA_Admin_Interface { private $settings; public function __construct() { $this->settings = get_option('fca_settings', array()); // 添加管理菜单 add_action('admin_menu', array($this, 'add_admin_menu')); // 添加文章列表栏位 add_filter('manage_posts_columns', array($this, 'add_audit_status_column')); add_action('manage_posts_custom_column', array($this, 'display_audit_status_column'), 10, 2); // 添加文章编辑页面元框 add_action('add_meta_boxes', array($this, 'add_audit_metabox')); // 保存文章时触发审查 add_action('save_post', array($this, 'audit_on_save'), 10, 3); } /** * 添加管理菜单 */ public function add_admin_menu() { // 主菜单 add_menu_page( '内容合规审查', '内容审查', 'manage_options', 'fca-dashboard', array($this, 'display_dashboard'), 'dashicons-shield', 30 ); // 子菜单 add_submenu_page( 'fca-dashboard', '审查设置', '设置', 'manage_options', 'fca-settings', array($this, 'display_settings_page') ); add_submenu_page( 'fca-dashboard', '审查日志', '日志', 'manage_options', 'fca-logs', array($this, 'display_logs_page') ); } /** * 显示仪表板页面 */ public function display_dashboard() { ?> <div class="wrap fca-dashboard"> <h1><?php echo esc_html__('内容合规审查仪表板', 'flexible-content-audit'); ?></h1> <div class="fca-stats-container"> <div class="fca-stat-box"> <h3>待审查文章</h3> <p class="stat-number"><?php echo $this->get_pending_count(); ?></p> </div> <div class="fca-stat-box"> <h3>已通过文章</h3> <p class="stat-number"><?php echo $this->get_approved_count(); ?></p> </div> <div class="fca-stat-box"> <h3>需修改文章</h3> <p class="stat-number"><?php echo $this->get_flagged_count(); ?></p> </div> </div> <div class="fca-recent-audits"> <h2>最近审查记录</h2> <?php $this->display_recent_audits(); ?> </div> </div> <?php } /** * 显示设置页面 */ public function display_settings_page() { // 处理表单提交 if (isset($_POST['fca_settings_nonce']) && wp_verify_nonce($_POST['fca_settings_nonce'], 'fca_save_settings')) { $this->save_settings(); } $settings = $this->settings; ?> <div class="wrap"> <h1><?php echo esc_html__('内容审查设置', 'flexible-content-audit'); ?></h1> <form method="post" action=""> <?php wp_nonce_field('fca_save_settings', 'fca_settings_nonce'); ?> <table class="form-table"> <tr> <th scope="row"> <label for="audit_mode">审查模式</label> </th> <td> <select name="fca_settings[audit_mode]" id="audit_mode"> <option value="flexible" <?php selected($settings['audit_mode'], 'flexible'); ?>> 柔性模式(自动+人工) </option> <option value="strict" <?php selected($settings['audit_mode'], 'strict'); ?>> 严格模式(自动审查) </option> <option value="manual" <?php selected($settings['audit_mode'], 'manual'); ?>> 人工模式 </option> </select> <p class="description"> 柔性模式:系统自动审查并标记,最终由人工决定<br> 严格模式:系统自动决定是否通过<br> 人工模式:所有内容都需要人工审查 </p> </td> </tr> <tr> <th scope="row"> <label for="sensitive_keywords">敏感关键词</label> </th> <td> <textarea name="fca_settings[sensitive_keywords]" id="sensitive_keywords" rows="5" class="large-text"><?php echo esc_textarea($settings['sensitive_keywords']); ?></textarea> <p class="description"> 每行一个关键词,或使用逗号分隔。支持中文关键词。 </p> </td> </tr> <tr> <th scope="row"> <label>自动审查的文章类型</label> </th> <td> <?php $post_types = get_post_types(array('public' => true), 'objects'); foreach ($post_types as $post_type) { $checked = in_array($post_type->name, $settings['auto_audit_post_types'] ?? array()) ? 'checked' : ''; ?> <label style="display: block; margin-bottom: 5px;"> <input type="checkbox" name="fca_settings[auto_audit_post_types][]" value="<?php echo esc_attr($post_type->name); ?>" <?php echo $checked; ?>> <?php echo esc_html($post_type->label); ?> </label> <?php } ?> </td> </tr> <tr> <th scope="row"> <label for="audit_threshold">敏感度阈值</label> </th> <td> <input type="range" name="fca_settings[audit_threshold]" id="audit_threshold" min="0" max="100" value="<?php echo esc_attr($settings['audit_threshold'] ?? 70); ?>"> <span id="threshold_value"><?php echo esc_html($settings['audit_threshold'] ?? 70); ?></span>% <p class="description"> 敏感度评分超过此阈值的内容将被标记为需要人工审核 </p> </td> </tr> </table> <?php submit_button('保存设置'); ?> </form> </div> <script> jQuery(document).ready(function($) { // 更新阈值显示 $('#audit_threshold').on('input', function() { $('#threshold_value').text($(this).val()); }); }); </script> <?php } /** * 保存设置 */ private function save_settings() { if (!current_user_can('manage_options')) { return; } $new_settings = array( 'audit_mode' => sanitize_text_field($_POST['fca_settings']['audit_mode']), 'sensitive_keywords' => sanitize_textarea_field($_POST['fca_settings']['sensitive_keywords']), 'auto_audit_post_types' => array_map('sanitize_text_field', $_POST['fca_settings']['auto_audit_post_types'] ?? array()), 'audit_threshold' => intval($_POST['fca_settings']['audit_threshold']), 'notification_email' => sanitize_email($this->settings['notification_email']), ); update_option('fca_settings', $new_settings); = $new_settings; echo '<div class="notice notice-success"><p>设置已保存!</p></div>'; } /** * 在文章列表添加审查状态列 */ public function add_audit_status_column($columns) { $columns['audit_status'] = '审查状态'; return $columns; } /** * 显示审查状态列内容 */ public function display_audit_status_column($column, $post_id) { if ($column === 'audit_status') { global $wpdb; $table_name = $wpdb->prefix . 'fca_audit_logs'; $latest_audit = $wpdb->get_row($wpdb->prepare( "SELECT audit_status, sensitivity_score FROM $table_name WHERE post_id = %d ORDER BY audit_time DESC LIMIT 1", $post_id )); if ($latest_audit) { $status_labels = array( 'pending' => '<span class="fca-status pending">待审查</span>', 'approved' => '<span class="fca-status approved">已通过</span>', 'rejected' => '<span class="fca-status rejected">未通过</span>', 'flagged' => '<span class="fca-status flagged">需修改</span>' ); $status = $latest_audit->audit_status; echo $status_labels[$status] ?? '<span class="fca-status unknown">未知</span>'; if ($latest_audit->sensitivity_score > 0) { echo '<br><small>敏感度: ' . $latest_audit->sensitivity_score . '%</small>'; } } else { echo '<span class="fca-status not-audited">未审查</span>'; } } } /** * 添加文章编辑页面的审查元框 */ public function add_audit_metabox() { $post_types = $this->settings['auto_audit_post_types'] ?? array('post'); foreach ($post_types as $post_type) { add_meta_box( 'fca_audit_metabox', '内容合规审查', array($this, 'render_audit_metabox'), $post_type, 'side', 'high' ); } } /** * 渲染审查元框 */ public function render_audit_metabox($post) { global $wpdb; $table_name = $wpdb->prefix . 'fca_audit_logs'; // 获取最新的审查记录 $audit_log = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $table_name WHERE post_id = %d ORDER BY audit_time DESC LIMIT 1", $post->ID )); wp_nonce_field('fca_audit_action', 'fca_audit_nonce'); ?> <div class="fca-audit-info"> <?php if ($audit_log): ?> <p><strong>审查状态:</strong> <?php echo $this->get_status_label($audit_log->audit_status); ?> </p> <p><strong>敏感度评分:</strong> <span class="fca-score"><?php echo $audit_log->sensitivity_score; ?>%</span> </p> <p><strong>审查时间:</strong><br> <?php echo date('Y-m-d H:i', strtotime($audit_log->audit_time)); ?> </p> <?php if ($audit_log->matched_keywords): ?> <p><strong>匹配关键词:</strong><br> <?php $keywords = maybe_unserialize($audit_log->matched_keywords); if (is_array($keywords)) { echo implode(', ', array_slice($keywords, 0, 3)); if (count($keywords) > 3) echo '...'; } ?> </p> <?php endif; ?> <div class="fca-audit-actions"> <button type="button" class="button button-small fca-rescan"> 重新扫描 </button> <?php if ($audit_log->audit_status === 'pending' || $audit_log->audit_status === 'flagged'): ?> <button type="button" class="button button-primary button-small fca-approve"> 通过审查 </button> <button type="button" class="button button-small fca-reject"> 拒绝发布 </button> <?php endif; ?> </div> <?php else: ?> <p>本文尚未进行合规审查。</p> <button type="button" class="button button-primary fca-scan-now"> 立即审查 </button> <?php endif; ?> </div> <div id="fca-audit-result" style="display:none; margin-top:10px;"></div> <script> jQuery(document).ready(function($) { // 立即审查按钮 $('.fca-scan-now').click(function() { scanContent(<?php echo $post->ID; ?>); }); // 重新扫描按钮 $('.fca-rescan').click(function() { scanContent(<?php echo $post->ID; ?>); }); // 通过审查按钮 $('.fca-approve').click(function() { updateAuditStatus(<?php echo $post->ID; ?>, 'approved'); }); // 拒绝发布按钮 $('.fca-reject').click(function() { updateAuditStatus(<?php echo $post->ID; ?>, 'rejected'); }); function scanContent(postId) { $('#fca-audit-result').html('<p>正在扫描内容...</p>').show(); $.post(ajaxurl, { action: 'fca_scan_content', post_id: postId, nonce: '<?php echo wp_create_nonce("fca_ajax_nonce"); ?>' }, function(response) { if (response.success) { $('#fca-audit-result').html( '<div class="notice notice-success"><p>' + response.data.message + '</p></div>' ); setTimeout(function() { location.reload(); }, 1500); } else { $('#fca-audit-result').html( '<div class="notice notice-error"><p>' + response.data + '</p></div>' ); } }); } function updateAuditStatus(postId, status) { $.post(ajaxurl, { action: 'fca_update_status', post_id: postId, status: status, nonce: '<?php echo wp_create_nonce("fca_ajax_nonce"); ?>' }, function(response) { if (response.success) { location.reload(); } else { alert('操作失败: ' + response.data); } }); } }); </script> <?php } /** * 获取状态标签 */ private function get_status_label($status) { $labels = array( 'pending' => '<span class="fca-status pending">待审查</span>', 'approved' => '<span class="fca-status approved">已通过</span>', 'rejected' => '<span class="fca-status rejected">未通过</span>', 'flagged' => '<span class="fca-status flagged">需修改</span>' ); return $labels[$status] ?? '<span class="fca-status unknown">未知</span>'; } }?> ## 3. 核心审查逻辑实现 ### 3.1 核心审查类 <?php/** 核心审查类 协调各个组件的工作 */ class FCA_Audit_Core { private $scanner; private $settings; private $logger; public function __construct() { $this->settings = get_option('fca_settings', array()); $this->logger = new FCA_Audit_Logger(); // 初始化关键词数组 $keywords = explode(',', $this->settings['sensitive_keywords'] ?? ''); $keywords = array_map('trim', $keywords); $keywords = array_filter($keywords); $this->scanner = new FCA_Content_Scanner($keywords); // 初始化AJAX处理 $this->init_ajax_handlers(); // 初始化管理界面 if (is_admin()) { new FCA_Admin_Interface(); } } /** * 初始化AJAX处理器 */ private function init_ajax_handlers() { add_action('wp_ajax_fca_scan_content', array($this, 'ajax_scan_content')); add_action('wp_ajax_fca_update_status', array($this, 'ajax_update_status')); add_action('wp_ajax_fca_get_audit_stats', array($this, 'ajax_get_audit_stats')); } /** * AJAX扫描内容 */ public function ajax_scan_content() { check_ajax_referer('fca_ajax_nonce', 'nonce'); if (!current_user_can('edit_posts')) { wp_die('权限不足'); } $post_id = intval($_POST['post_id']); $post = get_post($post_id); if (!$post) { wp_send_json_error('文章不存在'); } // 扫描内容 $result = $this->scanner->scan_content($post->post_content, $post->post_title); // 记录审查结果 $this->logger->log_audit( $post_id, $result['status'], $result['score'], $result['matches'] ); // 根据设置自动处理 $this->auto_handle_audit($post_id, $result); wp_send_json_success(array( 'message' => '内容审查完成。敏感度评分: ' . $result['score'] . '%', 'result' => $result )); } /** * 自动处理审查结果 */ private function auto_handle_audit($post_id, $result) { $mode = $this->settings['audit_mode'] ?? 'flexible'; $threshold = $this->settings['audit_threshold'] ?? 70; switch ($mode) { case 'strict': // 严格模式:自动决定 if ($result['score'] >= $threshold) { $this->logger->update_audit_status($post_id, 'rejected'); $this->send_notification($post_id, 'rejected', $result); } else { $this->logger->update_audit_status($post_id, 'approved'); } break; case 'flexible': // 柔性模式:标记需要人工审核的内容 if ($result['score'] >= $threshold) { $this->logger->update_audit_status($post_id, 'flagged'); $this->send_notification($post_id, 'flagged', $result); } break; case 'manual': // 人工模式:全部标记为待审查 $this->logger->update_audit_status($post_id, 'pending'); $this->send_notification($post_id, 'pending', $result); break; } } /** * 发送通知 */ private function send_notification($post_id, $status, $result) { $email = $this->settings['notification_email'] ?? get_option('admin_email'); $post = get_post($post_id); $author = get_userdata($post->post_author); $subject = sprintf('[内容审查] 文章 "%s" 需要处理', $post->post_title); $message = sprintf( "文章标题: %sn作者: %sn审查状态: %sn敏感度评分: %d%%nn文章链接: %sn管理链接: %snn匹配到的敏感内容:n", $post->post_title, $author->display_name, $this->get_status_text($status), $result['score'], get_permalink($post_id), admin_url('post.php?post=' . $post_id . '&action=edit') ); foreach ($result['matches'] as $match) { $message .= sprintf("- %s: %s (出现%d次)n", $match['type'], $match['value'] ?? $match['url'] ?? '', $match['count'] ?? 1 ); } wp_mail($email, $subject, $message); } /** * 获取状态文本 */ private function get_status_text($status) { $texts = array( 'pending' => '待审查', 'approved' => '已通过', 'rejected' => '未通过', 'flagged' => '需修改' ); return $texts[$status] ?? '未知状态'; } }?> ## 4. 日志记录器实现 <?php/** 审查日志记录器 */ class FCA_Audit_Logger { private $table_name; public function __construct() { global $wpdb; $this->table_name = $wpdb->prefix . 'fca_audit_logs'; } /** * 记录审查结果 */ public function log_audit($post_id, $status, $score, $matches) { global $wpdb; $matched_keywords = array(); foreach ($matches as $match) { if ($match['type'] === 'keyword') { $matched_keywords[] = $match['value']; } } $data = array( 'post_id' => $post_id, 'audit_status' => $status, 'sensitivity_score' => $score, 'matched_keywords' => maybe_serialize($matched_keywords), 'auditor_id' => get_current_user_id(), 'audit_time' => current_time('mysql') ); $wpdb->insert($this->table_name, $data); // 更新文章元数据 update_post_meta($post_id, '_fca_last_audit', $data); update_post_meta($post_id, '_fca_audit_status', $status); } /** * 更新审查状态 */ public function update_audit_status($post_id, $status, $notes = '') { global $wpdb; $data = array( 'audit_status' => $status, 'auditor_id' => get_current_user_id(), 'audit_notes' => $notes, 'audit_time' => current_time('mysql') ); $wpdb->update( $this->table_name, $data, array('post_id' => $post_id), array('%s', '%d', '%s', '%s'), array('%d') ); update_post_meta($post_id, '_fca_audit_status', $status); // 如果文章被批准,更新文章状态 if ($status === 'approved') { wp_update_post(array( 'ID' => $post_id, 'post_status' => 'publish' )); } } /** * 获取审查统计 */ public function get_stats($period = 'all') { global $wpdb; $where = ''; if ($period === 'today') { $where = " WHERE DATE(audit_time) = CURDATE()"; } elseif ($period === 'week') { $where = " WHERE audit_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)"; } elseif ($period === 'month') { $where = " WHERE audit_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)"; } $query = " SELECT audit_status, COUNT(*) as count, AVG(sensitivity_score) as avg_score FROM {$this->table_name} {$where} GROUP BY audit_status "; return $wpdb->get_results($query); } }?> ## 5. 前端样式和脚本 ### 5.1 管理界面样式 (admin-styles.css) / 审查状态标签 /.fca-status { display: inline-block; padding: 3px 8px; border-radius: 3px; font-size: 12px; font-weight: bold; } .fca-status.pending { background: #f0ad4e; color: #fff; } .fca-status.approved { background: #5cb85c; color: #fff; } .fca-status.rejected { background: #d9534f; color: #fff; } .fca-status.flagged { background: #5bc0de; color: #fff; } .fca-status.not-audited { background: #777; color: #fff; } / 仪表板统计 /.fca-stats-container { display: flex; gap: 20px; margin: 20px 0; } .fca-stat-box { flex: 1; background: #fff; border: 1px solid #ccd0d4; border-radius: 4px; padding: 20px; text-align: center; } .fca-stat-box h3 { margin: 0 0 10px 0; color: #23282d; } .fca-stat-box .stat-number { font-size: 36px; font-weight: bold; margin: 0; color: #0073aa; } / 审查元框 /.fca-audit-info { line-height: 1.6; }
在当今网络传媒环境中,内容合规性已成为网站运营的关键环节。WordPress作为全球最流行的内容管理系统,承载着大量媒体内容,但缺乏内置的内容审查机制。本教程将指导您开发一个柔性内容合规审查插件,既能自动检测敏感内容,又保留人工审核的灵活性,帮助网络传媒站点在遵守法规的同时保持内容多样性。
首先,我们创建插件的基本文件结构:
wp-content/plugins/
└── flexible-content-audit/
├── flexible-content-audit.php # 主插件文件
├── includes/
│ ├── class-audit-core.php # 核心审查类
│ ├── class-content-scanner.php # 内容扫描器
│ ├── class-admin-interface.php # 管理界面
│ └── class-audit-logger.php # 日志记录器
├── assets/
│ ├── css/
│ │ └── admin-styles.css # 管理界面样式
│ └── js/
│ └── admin-scripts.js # 管理界面脚本
└── languages/ # 国际化文件
<?php
/**
* Plugin Name: 柔性内容合规审查插件
* Plugin URI: https://example.com/flexible-content-audit
* Description: 为网络传媒WordPress站点提供柔性内容合规审查功能
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
* Text Domain: flexible-content-audit
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('FCA_PLUGIN_VERSION', '1.0.0');
define('FCA_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('FCA_PLUGIN_URL', plugin_dir_url(__FILE__));
// 自动加载类文件
spl_autoload_register(function ($class_name) {
if (strpos($class_name, 'FCA_') === 0) {
$file = FCA_PLUGIN_PATH . 'includes/class-' . strtolower(str_replace('_', '-', $class_name)) . '.php';
if (file_exists($file)) {
require_once $file;
}
}
});
// 初始化插件
function fca_init_plugin() {
// 检查WordPress版本
if (version_compare(get_bloginfo('version'), '5.0', '<')) {
add_action('admin_notices', function() {
echo '<div class="notice notice-error"><p>';
echo __('柔性内容合规审查插件需要WordPress 5.0或更高版本', 'flexible-content-audit');
echo '</p></div>';
});
return;
}
// 实例化核心类
$GLOBALS['flexible_content_audit'] = new FCA_Audit_Core();
}
add_action('plugins_loaded', 'fca_init_plugin');
// 插件激活钩子
register_activation_hook(__FILE__, 'fca_activate_plugin');
function fca_activate_plugin() {
// 创建必要的数据库表
fca_create_audit_tables();
// 设置默认选项
$default_options = array(
'audit_mode' => 'flexible', // flexible, strict, manual
'sensitive_keywords' => '敏感词1,敏感词2,违规词1',
'auto_audit_post_types' => array('post', 'page'),
'notification_email' => get_option('admin_email'),
'audit_threshold' => 70, // 敏感度阈值(0-100)
);
add_option('fca_settings', $default_options);
}
// 创建审查记录表
function fca_create_audit_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'fca_audit_logs';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
post_id bigint(20) NOT NULL,
audit_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
audit_status varchar(20) NOT NULL, -- pending, approved, rejected, flagged
sensitivity_score int(3) DEFAULT 0,
matched_keywords text,
auditor_id bigint(20),
audit_notes text,
PRIMARY KEY (id),
KEY post_id (post_id),
KEY audit_status (audit_status)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
?>
<?php
/**
* Plugin Name: 柔性内容合规审查插件
* Plugin URI: https://example.com/flexible-content-audit
* Description: 为网络传媒WordPress站点提供柔性内容合规审查功能
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
* Text Domain: flexible-content-audit
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('FCA_PLUGIN_VERSION', '1.0.0');
define('FCA_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('FCA_PLUGIN_URL', plugin_dir_url(__FILE__));
// 自动加载类文件
spl_autoload_register(function ($class_name) {
if (strpos($class_name, 'FCA_') === 0) {
$file = FCA_PLUGIN_PATH . 'includes/class-' . strtolower(str_replace('_', '-', $class_name)) . '.php';
if (file_exists($file)) {
require_once $file;
}
}
});
// 初始化插件
function fca_init_plugin() {
// 检查WordPress版本
if (version_compare(get_bloginfo('version'), '5.0', '<')) {
add_action('admin_notices', function() {
echo '<div class="notice notice-error"><p>';
echo __('柔性内容合规审查插件需要WordPress 5.0或更高版本', 'flexible-content-audit');
echo '</p></div>';
});
return;
}
// 实例化核心类
$GLOBALS['flexible_content_audit'] = new FCA_Audit_Core();
}
add_action('plugins_loaded', 'fca_init_plugin');
// 插件激活钩子
register_activation_hook(__FILE__, 'fca_activate_plugin');
function fca_activate_plugin() {
// 创建必要的数据库表
fca_create_audit_tables();
// 设置默认选项
$default_options = array(
'audit_mode' => 'flexible', // flexible, strict, manual
'sensitive_keywords' => '敏感词1,敏感词2,违规词1',
'auto_audit_post_types' => array('post', 'page'),
'notification_email' => get_option('admin_email'),
'audit_threshold' => 70, // 敏感度阈值(0-100)
);
add_option('fca_settings', $default_options);
}
// 创建审查记录表
function fca_create_audit_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'fca_audit_logs';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
post_id bigint(20) NOT NULL,
audit_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
audit_status varchar(20) NOT NULL, -- pending, approved, rejected, flagged
sensitivity_score int(3) DEFAULT 0,
matched_keywords text,
auditor_id bigint(20),
audit_notes text,
PRIMARY KEY (id),
KEY post_id (post_id),
KEY audit_status (audit_status)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
?>
<?php
/**
* 内容扫描器类
* 负责扫描文章内容中的敏感信息
*/
class FCA_Content_Scanner {
private $sensitive_keywords = array();
private $sensitivity_score = 0;
private $matched_items = array();
/**
* 初始化扫描器
* @param array $keywords 敏感关键词数组
*/
public function __construct($keywords = array()) {
$this->sensitive_keywords = $keywords;
}
/**
* 扫描文章内容
* @param string $content 文章内容
* @param string $title 文章标题
* @return array 扫描结果
*/
public function scan_content($content, $title = '') {
$this->sensitivity_score = 0;
$this->matched_items = array();
// 合并标题和内容进行扫描
$full_text = $title . ' ' . $content;
// 扫描敏感关键词
$this->scan_keywords($full_text);
// 扫描链接(可选扩展功能)
$this->scan_links($content);
// 扫描图片ALT文本(可选扩展功能)
$this->scan_images($content);
return array(
'score' => $this->sensitivity_score,
'matches' => $this->matched_items,
'status' => $this->get_audit_status()
);
}
/**
* 扫描敏感关键词
* @param string $text 待扫描文本
*/
private function scan_keywords($text) {
foreach ($this->sensitive_keywords as $keyword) {
$keyword = trim($keyword);
if (empty($keyword)) continue;
// 使用正则表达式进行匹配(支持中文)
$pattern = '/' . preg_quote($keyword, '/') . '/iu';
if (preg_match_all($pattern, $text, $matches)) {
$count = count($matches[0]);
$this->sensitivity_score += $count * 10; // 每个匹配加10分
$this->matched_items[] = array(
'type' => 'keyword',
'value' => $keyword,
'count' => $count,
'risk_level' => $this->get_keyword_risk_level($keyword)
);
}
}
// 限制分数在0-100之间
$this->sensitivity_score = min(100, $this->sensitivity_score);
}
/**
* 扫描外部链接
* @param string $content 文章内容
*/
private function scan_links($content) {
// 使用DOM解析器提取所有链接
if (class_exists('DOMDocument')) {
$dom = new DOMDocument();
@$dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
$links = $dom->getElementsByTagName('a');
foreach ($links as $link) {
$href = $link->getAttribute('href');
// 检查是否为外部链接
if ($this->is_external_link($href)) {
$this->sensitivity_score += 5; // 外部链接加5分
$this->matched_items[] = array(
'type' => 'external_link',
'url' => $href,
'text' => $link->textContent
);
}
}
}
}
/**
* 判断是否为外部链接
* @param string $url 链接地址
* @return bool
*/
private function is_external_link($url) {
$site_url = site_url();
$parsed_url = parse_url($url);
$parsed_site = parse_url($site_url);
if (!isset($parsed_url['host'])) {
return false;
}
return $parsed_url['host'] !== $parsed_site['host'];
}
/**
* 根据关键词获取风险等级
* @param string $keyword 关键词
* @return string 风险等级
*/
private function get_keyword_risk_level($keyword) {
// 这里可以扩展为从数据库或配置文件中读取风险等级
$high_risk = array('违规词1', '违规词2');
$medium_risk = array('敏感词1', '敏感词2');
if (in_array($keyword, $high_risk)) {
return 'high';
} elseif (in_array($keyword, $medium_risk)) {
return 'medium';
} else {
return 'low';
}
}
/**
* 获取审查状态建议
* @return string 状态建议
*/
private function get_audit_status() {
if ($this->sensitivity_score >= 80) {
return 'rejected'; // 建议拒绝
} elseif ($this->sensitivity_score >= 50) {
return 'flagged'; // 需要人工审核
} else {
return 'approved'; // 自动通过
}
}
}
?>
<?php
/**
* 内容扫描器类
* 负责扫描文章内容中的敏感信息
*/
class FCA_Content_Scanner {
private $sensitive_keywords = array();
private $sensitivity_score = 0;
private $matched_items = array();
/**
* 初始化扫描器
* @param array $keywords 敏感关键词数组
*/
public function __construct($keywords = array()) {
$this->sensitive_keywords = $keywords;
}
/**
* 扫描文章内容
* @param string $content 文章内容
* @param string $title 文章标题
* @return array 扫描结果
*/
public function scan_content($content, $title = '') {
$this->sensitivity_score = 0;
$this->matched_items = array();
// 合并标题和内容进行扫描
$full_text = $title . ' ' . $content;
// 扫描敏感关键词
$this->scan_keywords($full_text);
// 扫描链接(可选扩展功能)
$this->scan_links($content);
// 扫描图片ALT文本(可选扩展功能)
$this->scan_images($content);
return array(
'score' => $this->sensitivity_score,
'matches' => $this->matched_items,
'status' => $this->get_audit_status()
);
}
/**
* 扫描敏感关键词
* @param string $text 待扫描文本
*/
private function scan_keywords($text) {
foreach ($this->sensitive_keywords as $keyword) {
$keyword = trim($keyword);
if (empty($keyword)) continue;
// 使用正则表达式进行匹配(支持中文)
$pattern = '/' . preg_quote($keyword, '/') . '/iu';
if (preg_match_all($pattern, $text, $matches)) {
$count = count($matches[0]);
$this->sensitivity_score += $count * 10; // 每个匹配加10分
$this->matched_items[] = array(
'type' => 'keyword',
'value' => $keyword,
'count' => $count,
'risk_level' => $this->get_keyword_risk_level($keyword)
);
}
}
// 限制分数在0-100之间
$this->sensitivity_score = min(100, $this->sensitivity_score);
}
/**
* 扫描外部链接
* @param string $content 文章内容
*/
private function scan_links($content) {
// 使用DOM解析器提取所有链接
if (class_exists('DOMDocument')) {
$dom = new DOMDocument();
@$dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
$links = $dom->getElementsByTagName('a');
foreach ($links as $link) {
$href = $link->getAttribute('href');
// 检查是否为外部链接
if ($this->is_external_link($href)) {
$this->sensitivity_score += 5; // 外部链接加5分
$this->matched_items[] = array(
'type' => 'external_link',
'url' => $href,
'text' => $link->textContent
);
}
}
}
}
/**
* 判断是否为外部链接
* @param string $url 链接地址
* @return bool
*/
private function is_external_link($url) {
$site_url = site_url();
$parsed_url = parse_url($url);
$parsed_site = parse_url($site_url);
if (!isset($parsed_url['host'])) {
return false;
}
return $parsed_url['host'] !== $parsed_site['host'];
}
/**
* 根据关键词获取风险等级
* @param string $keyword 关键词
* @return string 风险等级
*/
private function get_keyword_risk_level($keyword) {
// 这里可以扩展为从数据库或配置文件中读取风险等级
$high_risk = array('违规词1', '违规词2');
$medium_risk = array('敏感词1', '敏感词2');
if (in_array($keyword, $high_risk)) {
return 'high';
} elseif (in_array($keyword, $medium_risk)) {
return 'medium';
} else {
return 'low';
}
}
/**
* 获取审查状态建议
* @return string 状态建议
*/
private function get_audit_status() {
if ($this->sensitivity_score >= 80) {
return 'rejected'; // 建议拒绝
} elseif ($this->sensitivity_score >= 50) {
return 'flagged'; // 需要人工审核
} else {
return 'approved'; // 自动通过
}
}
}
?>
<?php
/**
* 管理界面类
* 提供插件设置和内容审查界面
*/
class FCA_Admin_Interface {
private $settings;
public function __construct() {
$this->settings = get_option('fca_settings', array());
// 添加管理菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 添加文章列表栏位
add_filter('manage_posts_columns', array($this, 'add_audit_status_column'));
add_action('manage_posts_custom_column', array($this, 'display_audit_status_column'), 10, 2);
// 添加文章编辑页面元框
add_action('add_meta_boxes', array($this, 'add_audit_metabox'));
// 保存文章时触发审查
add_action('save_post', array($this, 'audit_on_save'), 10, 3);
}
/**
* 添加管理菜单
*/
public function add_admin_menu() {
// 主菜单
add_menu_page(
'内容合规审查',
'内容审查',
'manage_options',
'fca-dashboard',
array($this, 'display_dashboard'),
'dashicons-shield',
30
);
// 子菜单
add_submenu_page(
'fca-dashboard',
'审查设置',
'设置',
'manage_options',
'fca-settings',
array($this, 'display_settings_page')
);
add_submenu_page(
'fca-dashboard',
'审查日志',
'日志',
'manage_options',
'fca-logs',
array($this, 'display_logs_page')
);
}
/**
* 显示仪表板页面
*/
public function display_dashboard() {
?>
<div class="wrap fca-dashboard">
<h1><?php echo esc_html__('内容合规审查仪表板', 'flexible-content-audit'); ?></h1>
<div class="fca-stats-container">
<div class="fca-stat-box">
<h3>待审查文章</h3>
<p class="stat-number"><?php echo $this->get_pending_count(); ?></p>
</div>
<div class="fca-stat-box">
<h3>已通过文章</h3>
<p class="stat-number"><?php echo $this->get_approved_count(); ?></p>
</div>
<div class="fca-stat-box">
<h3>需修改文章</h3>
<p class="stat-number"><?php echo $this->get_flagged_count(); ?></p>
</div>
</div>
<div class="fca-recent-audits">
<h2>最近审查记录</h2>
<?php $this->display_recent_audits(); ?>
</div>
</div>
<?php
}
/**
* 显示设置页面
*/
public function display_settings_page() {
// 处理表单提交
if (isset($_POST['fca_settings_nonce']) &&
wp_verify_nonce($_POST['fca_settings_nonce'], 'fca_save_settings')) {
$this->save_settings();
}
$settings = $this->settings;
?>
<div class="wrap">
<h1><?php echo esc_html__('内容审查设置', 'flexible-content-audit'); ?></h1>
<form method="post" action="">
<?php wp_nonce_field('fca_save_settings', 'fca_settings_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="audit_mode">审查模式</label>
</th>
<td>
<select name="fca_settings[audit_mode]" id="audit_mode">
<option value="flexible" <?php selected($settings['audit_mode'], 'flexible'); ?>>
柔性模式(自动+人工)
</option>
<option value="strict" <?php selected($settings['audit_mode'], 'strict'); ?>>
严格模式(自动审查)
</option>
<option value="manual" <?php selected($settings['audit_mode'], 'manual'); ?>>
人工模式
</option>
</select>
<p class="description">
柔性模式:系统自动审查并标记,最终由人工决定<br>
严格模式:系统自动决定是否通过<br>
人工模式:所有内容都需要人工审查
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="sensitive_keywords">敏感关键词</label>
</th>
<td>
<textarea name="fca_settings[sensitive_keywords]"
id="sensitive_keywords"
rows="5"
class="large-text"><?php
echo esc_textarea($settings['sensitive_keywords']);
?></textarea>
<p class="description">
每行一个关键词,或使用逗号分隔。支持中文关键词。
</p>
</td>
</tr>
<tr>
<th scope="row">
<label>自动审查的文章类型</label>
</th>
<td>
<?php
$post_types = get_post_types(array('public' => true), 'objects');
foreach ($post_types as $post_type) {
$checked = in_array($post_type->name,
$settings['auto_audit_post_types'] ?? array()) ? 'checked' : '';
?>
<label style="display: block; margin-bottom: 5px;">
<input type="checkbox"
name="fca_settings[auto_audit_post_types][]"
value="<?php echo esc_attr($post_type->name); ?>"
<?php echo $checked; ?>>
<?php echo esc_html($post_type->label); ?>
</label>
<?php
}
?>
</td>
</tr>
<tr>
<th scope="row">
<label for="audit_threshold">敏感度阈值</label>
</th>
<td>
<input type="range"
name="fca_settings[audit_threshold]"
id="audit_threshold"
min="0" max="100"
value="<?php echo esc_attr($settings['audit_threshold'] ?? 70); ?>">
<span id="threshold_value"><?php echo esc_html($settings['audit_threshold'] ?? 70); ?></span>%
<p class="description">
敏感度评分超过此阈值的内容将被标记为需要人工审核
</p>
</td>
</tr>
</table>
<?php submit_button('保存设置'); ?>
</form>
</div>
<script>
jQuery(document).ready(function($) {
// 更新阈值显示
$('#audit_threshold').on('input', function() {
$('#threshold_value').text($(this).val());
});
});
</script>
<?php
}
/**
* 保存设置
*/
private function save_settings() {
if (!current_user_can('manage_options')) {
return;
}
$new_settings = array(
'audit_mode' => sanitize_text_field($_POST['fca_settings']['audit_mode']),
'sensitive_keywords' => sanitize_textarea_field($_POST['fca_settings']['sensitive_keywords']),
'auto_audit_post_types' => array_map('sanitize_text_field',
$_POST['fca_settings']['auto_audit_post_types'] ?? array()),
'audit_threshold' => intval($_POST['fca_settings']['audit_threshold']),
'notification_email' => sanitize_email($this->settings['notification_email']),
);
update_option('fca_settings', $new_settings);
= $new_settings;
echo '<div class="notice notice-success"><p>设置已保存!</p></div>';
}
/**
* 在文章列表添加审查状态列
*/
public function add_audit_status_column($columns) {
$columns['audit_status'] = '审查状态';
return $columns;
}
/**
* 显示审查状态列内容
*/
public function display_audit_status_column($column, $post_id) {
if ($column === 'audit_status') {
global $wpdb;
$table_name = $wpdb->prefix . 'fca_audit_logs';
$latest_audit = $wpdb->get_row($wpdb->prepare(
"SELECT audit_status, sensitivity_score FROM $table_name
WHERE post_id = %d ORDER BY audit_time DESC LIMIT 1",
$post_id
));
if ($latest_audit) {
$status_labels = array(
'pending' => '<span class="fca-status pending">待审查</span>',
'approved' => '<span class="fca-status approved">已通过</span>',
'rejected' => '<span class="fca-status rejected">未通过</span>',
'flagged' => '<span class="fca-status flagged">需修改</span>'
);
$status = $latest_audit->audit_status;
echo $status_labels[$status] ?? '<span class="fca-status unknown">未知</span>';
if ($latest_audit->sensitivity_score > 0) {
echo '<br><small>敏感度: ' . $latest_audit->sensitivity_score . '%</small>';
}
} else {
echo '<span class="fca-status not-audited">未审查</span>';
}
}
}
/**
* 添加文章编辑页面的审查元框
*/
public function add_audit_metabox() {
$post_types = $this->settings['auto_audit_post_types'] ?? array('post');
foreach ($post_types as $post_type) {
add_meta_box(
'fca_audit_metabox',
'内容合规审查',
array($this, 'render_audit_metabox'),
$post_type,
'side',
'high'
);
}
}
/**
* 渲染审查元框
*/
public function render_audit_metabox($post) {
global $wpdb;
$table_name = $wpdb->prefix . 'fca_audit_logs';
// 获取最新的审查记录
$audit_log = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table_name WHERE post_id = %d ORDER BY audit_time DESC LIMIT 1",
$post->ID
));
wp_nonce_field('fca_audit_action', 'fca_audit_nonce');
?>
<div class="fca-audit-info">
<?php if ($audit_log): ?>
<p><strong>审查状态:</strong>
<?php echo $this->get_status_label($audit_log->audit_status); ?>
</p>
<p><strong>敏感度评分:</strong>
<span class="fca-score"><?php echo $audit_log->sensitivity_score; ?>%</span>
</p>
<p><strong>审查时间:</strong><br>
<?php echo date('Y-m-d H:i', strtotime($audit_log->audit_time)); ?>
</p>
<?php if ($audit_log->matched_keywords): ?>
<p><strong>匹配关键词:</strong><br>
<?php
$keywords = maybe_unserialize($audit_log->matched_keywords);
if (is_array($keywords)) {
echo implode(', ', array_slice($keywords, 0, 3));
if (count($keywords) > 3) echo '...';
}
?>
</p>
<?php endif; ?>
<div class="fca-audit-actions">
<button type="button" class="button button-small fca-rescan">
重新扫描
</button>
<?php if ($audit_log->audit_status === 'pending' || $audit_log->audit_status === 'flagged'): ?>
<button type="button" class="button button-primary button-small fca-approve">
通过审查
</button>
<button type="button" class="button button-small fca-reject">
拒绝发布
</button>
<?php endif; ?>
</div>
<?php else: ?>
<p>本文尚未进行合规审查。</p>
<button type="button" class="button button-primary fca-scan-now">
立即审查
</button>
<?php endif; ?>
</div>
<div id="fca-audit-result" style="display:none; margin-top:10px;"></div>
<script>
jQuery(document).ready(function($) {
// 立即审查按钮
$('.fca-scan-now').click(function() {
scanContent(<?php echo $post->ID; ?>);
});
// 重新扫描按钮
$('.fca-rescan').click(function() {
scanContent(<?php echo $post->ID; ?>);
});
// 通过审查按钮
$('.fca-approve').click(function() {
updateAuditStatus(<?php echo $post->ID; ?>, 'approved');
});
// 拒绝发布按钮
$('.fca-reject').click(function() {
updateAuditStatus(<?php echo $post->ID; ?>, 'rejected');
});
function scanContent(postId) {
$('#fca-audit-result').html('<p>正在扫描内容...</p>').show();
$.post(ajaxurl, {
action: 'fca_scan_content',
post_id: postId,
nonce: '<?php echo wp_create_nonce("fca_ajax_nonce"); ?>'
}, function(response) {
if (response.success) {
$('#fca-audit-result').html(
'<div class="notice notice-success"><p>' +
response.data.message + '</p></div>'
);
setTimeout(function() {
location.reload();
}, 1500);
} else {
$('#fca-audit-result').html(
'<div class="notice notice-error"><p>' +
response.data + '</p></div>'
);
}
});
}
function updateAuditStatus(postId, status) {
$.post(ajaxurl, {
action: 'fca_update_status',
post_id: postId,
status: status,
nonce: '<?php echo wp_create_nonce("fca_ajax_nonce"); ?>'
}, function(response) {
if (response.success) {
location.reload();
} else {
alert('操作失败: ' + response.data);
}
});
}
});
</script>
<?php
}
/**
* 获取状态标签
*/
private function get_status_label($status) {
$labels = array(
'pending' => '<span class="fca-status pending">待审查</span>',
'approved' => '<span class="fca-status approved">已通过</span>',
'rejected' => '<span class="fca-status rejected">未通过</span>',
'flagged' => '<span class="fca-status flagged">需修改</span>'
);
return $labels[$status] ?? '<span class="fca-status unknown">未知</span>';
}
<?php
/**
* 管理界面类
* 提供插件设置和内容审查界面
*/
class FCA_Admin_Interface {
private $settings;
public function __construct() {
$this->settings = get_option('fca_settings', array());
// 添加管理菜单
add_action('admin_menu', array($this, 'add_admin_menu'));
// 添加文章列表栏位
add_filter('manage_posts_columns', array($this, 'add_audit_status_column'));
add_action('manage_posts_custom_column', array($this, 'display_audit_status_column'), 10, 2);
// 添加文章编辑页面元框
add_action('add_meta_boxes', array($this, 'add_audit_metabox'));
// 保存文章时触发审查
add_action('save_post', array($this, 'audit_on_save'), 10, 3);
}
/**
* 添加管理菜单
*/
public function add_admin_menu() {
// 主菜单
add_menu_page(
'内容合规审查',
'内容审查',
'manage_options',
'fca-dashboard',
array($this, 'display_dashboard'),
'dashicons-shield',
30
);
// 子菜单
add_submenu_page(
'fca-dashboard',
'审查设置',
'设置',
'manage_options',
'fca-settings',
array($this, 'display_settings_page')
);
add_submenu_page(
'fca-dashboard',
'审查日志',
'日志',
'manage_options',
'fca-logs',
array($this, 'display_logs_page')
);
}
/**
* 显示仪表板页面
*/
public function display_dashboard() {
?>
<div class="wrap fca-dashboard">
<h1><?php echo esc_html__('内容合规审查仪表板', 'flexible-content-audit'); ?></h1>
<div class="fca-stats-container">
<div class="fca-stat-box">
<h3>待审查文章</h3>
<p class="stat-number"><?php echo $this->get_pending_count(); ?></p>
</div>
<div class="fca-stat-box">
<h3>已通过文章</h3>
<p class="stat-number"><?php echo $this->get_approved_count(); ?></p>
</div>
<div class="fca-stat-box">
<h3>需修改文章</h3>
<p class="stat-number"><?php echo $this->get_flagged_count(); ?></p>
</div>
</div>
<div class="fca-recent-audits">
<h2>最近审查记录</h2>
<?php $this->display_recent_audits(); ?>
</div>
</div>
<?php
}
/**
* 显示设置页面
*/
public function display_settings_page() {
// 处理表单提交
if (isset($_POST['fca_settings_nonce']) &&
wp_verify_nonce($_POST['fca_settings_nonce'], 'fca_save_settings')) {
$this->save_settings();
}
$settings = $this->settings;
?>
<div class="wrap">
<h1><?php echo esc_html__('内容审查设置', 'flexible-content-audit'); ?></h1>
<form method="post" action="">
<?php wp_nonce_field('fca_save_settings', 'fca_settings_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="audit_mode">审查模式</label>
</th>
<td>
<select name="fca_settings[audit_mode]" id="audit_mode">
<option value="flexible" <?php selected($settings['audit_mode'], 'flexible'); ?>>
柔性模式(自动+人工)
</option>
<option value="strict" <?php selected($settings['audit_mode'], 'strict'); ?>>
严格模式(自动审查)
</option>
<option value="manual" <?php selected($settings['audit_mode'], 'manual'); ?>>
人工模式
</option>
</select>
<p class="description">
柔性模式:系统自动审查并标记,最终由人工决定<br>
严格模式:系统自动决定是否通过<br>
人工模式:所有内容都需要人工审查
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="sensitive_keywords">敏感关键词</label>
</th>
<td>
<textarea name="fca_settings[sensitive_keywords]"
id="sensitive_keywords"
rows="5"
class="large-text"><?php
echo esc_textarea($settings['sensitive_keywords']);
?></textarea>
<p class="description">
每行一个关键词,或使用逗号分隔。支持中文关键词。
</p>
</td>
</tr>
<tr>
<th scope="row">
<label>自动审查的文章类型</label>
</th>
<td>
<?php
$post_types = get_post_types(array('public' => true), 'objects');
foreach ($post_types as $post_type) {
$checked = in_array($post_type->name,
$settings['auto_audit_post_types'] ?? array()) ? 'checked' : '';
?>
<label style="display: block; margin-bottom: 5px;">
<input type="checkbox"
name="fca_settings[auto_audit_post_types][]"
value="<?php echo esc_attr($post_type->name); ?>"
<?php echo $checked; ?>>
<?php echo esc_html($post_type->label); ?>
</label>
<?php
}
?>
</td>
</tr>
<tr>
<th scope="row">
<label for="audit_threshold">敏感度阈值</label>
</th>
<td>
<input type="range"
name="fca_settings[audit_threshold]"
id="audit_threshold"
min="0" max="100"
value="<?php echo esc_attr($settings['audit_threshold'] ?? 70); ?>">
<span id="threshold_value"><?php echo esc_html($settings['audit_threshold'] ?? 70); ?></span>%
<p class="description">
敏感度评分超过此阈值的内容将被标记为需要人工审核
</p>
</td>
</tr>
</table>
<?php submit_button('保存设置'); ?>
</form>
</div>
<script>
jQuery(document).ready(function($) {
// 更新阈值显示
$('#audit_threshold').on('input', function() {
$('#threshold_value').text($(this).val());
});
});
</script>
<?php
}
/**
* 保存设置
*/
private function save_settings() {
if (!current_user_can('manage_options')) {
return;
}
$new_settings = array(
'audit_mode' => sanitize_text_field($_POST['fca_settings']['audit_mode']),
'sensitive_keywords' => sanitize_textarea_field($_POST['fca_settings']['sensitive_keywords']),
'auto_audit_post_types' => array_map('sanitize_text_field',
$_POST['fca_settings']['auto_audit_post_types'] ?? array()),
'audit_threshold' => intval($_POST['fca_settings']['audit_threshold']),
'notification_email' => sanitize_email($this->settings['notification_email']),
);
update_option('fca_settings', $new_settings);
= $new_settings;
echo '<div class="notice notice-success"><p>设置已保存!</p></div>';
}
/**
* 在文章列表添加审查状态列
*/
public function add_audit_status_column($columns) {
$columns['audit_status'] = '审查状态';
return $columns;
}
/**
* 显示审查状态列内容
*/
public function display_audit_status_column($column, $post_id) {
if ($column === 'audit_status') {
global $wpdb;
$table_name = $wpdb->prefix . 'fca_audit_logs';
$latest_audit = $wpdb->get_row($wpdb->prepare(
"SELECT audit_status, sensitivity_score FROM $table_name
WHERE post_id = %d ORDER BY audit_time DESC LIMIT 1",
$post_id
));
if ($latest_audit) {
$status_labels = array(
'pending' => '<span class="fca-status pending">待审查</span>',
'approved' => '<span class="fca-status approved">已通过</span>',
'rejected' => '<span class="fca-status rejected">未通过</span>',
'flagged' => '<span class="fca-status flagged">需修改</span>'
);
$status = $latest_audit->audit_status;
echo $status_labels[$status] ?? '<span class="fca-status unknown">未知</span>';
if ($latest_audit->sensitivity_score > 0) {
echo '<br><small>敏感度: ' . $latest_audit->sensitivity_score . '%</small>';
}
} else {
echo '<span class="fca-status not-audited">未审查</span>';
}
}
}
/**
* 添加文章编辑页面的审查元框
*/
public function add_audit_metabox() {
$post_types = $this->settings['auto_audit_post_types'] ?? array('post');
foreach ($post_types as $post_type) {
add_meta_box(
'fca_audit_metabox',
'内容合规审查',
array($this, 'render_audit_metabox'),
$post_type,
'side',
'high'
);
}
}
/**
* 渲染审查元框
*/
public function render_audit_metabox($post) {
global $wpdb;
$table_name = $wpdb->prefix . 'fca_audit_logs';
// 获取最新的审查记录
$audit_log = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table_name WHERE post_id = %d ORDER BY audit_time DESC LIMIT 1",
$post->ID
));
wp_nonce_field('fca_audit_action', 'fca_audit_nonce');
?>
<div class="fca-audit-info">
<?php if ($audit_log): ?>
<p><strong>审查状态:</strong>
<?php echo $this->get_status_label($audit_log->audit_status); ?>
</p>
<p><strong>敏感度评分:</strong>
<span class="fca-score"><?php echo $audit_log->sensitivity_score; ?>%</span>
</p>
<p><strong>审查时间:</strong><br>
<?php echo date('Y-m-d H:i', strtotime($audit_log->audit_time)); ?>
</p>
<?php if ($audit_log->matched_keywords): ?>
<p><strong>匹配关键词:</strong><br>
<?php
$keywords = maybe_unserialize($audit_log->matched_keywords);
if (is_array($keywords)) {
echo implode(', ', array_slice($keywords, 0, 3));
if (count($keywords) > 3) echo '...';
}
?>
</p>
<?php endif; ?>
<div class="fca-audit-actions">
<button type="button" class="button button-small fca-rescan">
重新扫描
</button>
<?php if ($audit_log->audit_status === 'pending' || $audit_log->audit_status === 'flagged'): ?>
<button type="button" class="button button-primary button-small fca-approve">
通过审查
</button>
<button type="button" class="button button-small fca-reject">
拒绝发布
</button>
<?php endif; ?>
</div>
<?php else: ?>
<p>本文尚未进行合规审查。</p>
<button type="button" class="button button-primary fca-scan-now">
立即审查
</button>
<?php endif; ?>
</div>
<div id="fca-audit-result" style="display:none; margin-top:10px;"></div>
<script>
jQuery(document).ready(function($) {
// 立即审查按钮
$('.fca-scan-now').click(function() {
scanContent(<?php echo $post->ID; ?>);
});
// 重新扫描按钮
$('.fca-rescan').click(function() {
scanContent(<?php echo $post->ID; ?>);
});
// 通过审查按钮
$('.fca-approve').click(function() {
updateAuditStatus(<?php echo $post->ID; ?>, 'approved');
});
// 拒绝发布按钮
$('.fca-reject').click(function() {
updateAuditStatus(<?php echo $post->ID; ?>, 'rejected');
});
function scanContent(postId) {
$('#fca-audit-result').html('<p>正在扫描内容...</p>').show();
$.post(ajaxurl, {
action: 'fca_scan_content',
post_id: postId,
nonce: '<?php echo wp_create_nonce("fca_ajax_nonce"); ?>'
}, function(response) {
if (response.success) {
$('#fca-audit-result').html(
'<div class="notice notice-success"><p>' +
response.data.message + '</p></div>'
);
setTimeout(function() {
location.reload();
}, 1500);
} else {
$('#fca-audit-result').html(
'<div class="notice notice-error"><p>' +
response.data + '</p></div>'
);
}
});
}
function updateAuditStatus(postId, status) {
$.post(ajaxurl, {
action: 'fca_update_status',
post_id: postId,
status: status,
nonce: '<?php echo wp_create_nonce("fca_ajax_nonce"); ?>'
}, function(response) {
if (response.success) {
location.reload();
} else {
alert('操作失败: ' + response.data);
}
});
}
});
</script>
<?php
}
/**
* 获取状态标签
*/
private function get_status_label($status) {
$labels = array(
'pending' => '<span class="fca-status pending">待审查</span>',
'approved' => '<span class="fca-status approved">已通过</span>',
'rejected' => '<span class="fca-status rejected">未通过</span>',
'flagged' => '<span class="fca-status flagged">需修改</span>'
);
return $labels[$status] ?? '<span class="fca-status unknown">未知</span>';
}
}
?>
## 3. 核心审查逻辑实现
### 3.1 核心审查类
<?php
/**
- 核心审查类
- 协调各个组件的工作
*/
class FCA_Audit_Core {
private $scanner;
private $settings;
private $logger;
public function __construct() {
$this->settings = get_option('fca_settings', array());
$this->logger = new FCA_Audit_Logger();
// 初始化关键词数组
$keywords = explode(',', $this->settings['sensitive_keywords'] ?? '');
$keywords = array_map('trim', $keywords);
$keywords = array_filter($keywords);
$this->scanner = new FCA_Content_Scanner($keywords);
// 初始化AJAX处理
$this->init_ajax_handlers();
// 初始化管理界面
if (is_admin()) {
new FCA_Admin_Interface();
}
}
/**
* 初始化AJAX处理器
*/
private function init_ajax_handlers() {
add_action('wp_ajax_fca_scan_content', array($this, 'ajax_scan_content'));
add_action('wp_ajax_fca_update_status', array($this, 'ajax_update_status'));
add_action('wp_ajax_fca_get_audit_stats', array($this, 'ajax_get_audit_stats'));
}
/**
* AJAX扫描内容
*/
public function ajax_scan_content() {
check_ajax_referer('fca_ajax_nonce', 'nonce');
if (!current_user_can('edit_posts')) {
wp_die('权限不足');
}
$post_id = intval($_POST['post_id']);
$post = get_post($post_id);
if (!$post) {
wp_send_json_error('文章不存在');
}
// 扫描内容
$result = $this->scanner->scan_content($post->post_content, $post->post_title);
// 记录审查结果
$this->logger->log_audit(
$post_id,
$result['status'],
$result['score'],
$result['matches']
);
// 根据设置自动处理
$this->auto_handle_audit($post_id, $result);
wp_send_json_success(array(
'message' => '内容审查完成。敏感度评分: ' . $result['score'] . '%',
'result' => $result
));
}
/**
* 自动处理审查结果
*/
private function auto_handle_audit($post_id, $result) {
$mode = $this->settings['audit_mode'] ?? 'flexible';
$threshold = $this->settings['audit_threshold'] ?? 70;
switch ($mode) {
case 'strict':
// 严格模式:自动决定
if ($result['score'] >= $threshold) {
$this->logger->update_audit_status($post_id, 'rejected');
$this->send_notification($post_id, 'rejected', $result);
} else {
$this->logger->update_audit_status($post_id, 'approved');
}
break;
case 'flexible':
// 柔性模式:标记需要人工审核的内容
if ($result['score'] >= $threshold) {
$this->logger->update_audit_status($post_id, 'flagged');
$this->send_notification($post_id, 'flagged', $result);
}
break;
case 'manual':
// 人工模式:全部标记为待审查
$this->logger->update_audit_status($post_id, 'pending');
$this->send_notification($post_id, 'pending', $result);
break;
}
}
/**
* 发送通知
*/
private function send_notification($post_id, $status, $result) {
$email = $this->settings['notification_email'] ?? get_option('admin_email');
$post = get_post($post_id);
$author = get_userdata($post->post_author);
$subject = sprintf('[内容审查] 文章 "%s" 需要处理', $post->post_title);
$message = sprintf(
"文章标题: %sn作者: %sn审查状态: %sn敏感度评分: %d%%nn文章链接: %sn管理链接: %snn匹配到的敏感内容:n",
$post->post_title,
$author->display_name,
$this->get_status_text($status),
$result['score'],
get_permalink($post_id),
admin_url('post.php?post=' . $post_id . '&action=edit')
);
foreach ($result['matches'] as $match) {
$message .= sprintf("- %s: %s (出现%d次)n",
$match['type'],
$match['value'] ?? $match['url'] ?? '',
$match['count'] ?? 1
);
}
wp_mail($email, $subject, $message);
}
/**
* 获取状态文本
*/
private function get_status_text($status) {
$texts = array(
'pending' => '待审查',
'approved' => '已通过',
'rejected' => '未通过',
'flagged' => '需修改'
);
return $texts[$status] ?? '未知状态';
}
}
?>
## 4. 日志记录器实现
<?php
/**
- 审查日志记录器
*/
class FCA_Audit_Logger {
private $table_name;
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'fca_audit_logs';
}
/**
* 记录审查结果
*/
public function log_audit($post_id, $status, $score, $matches) {
global $wpdb;
$matched_keywords = array();
foreach ($matches as $match) {
if ($match['type'] === 'keyword') {
$matched_keywords[] = $match['value'];
}
}
$data = array(
'post_id' => $post_id,
'audit_status' => $status,
'sensitivity_score' => $score,
'matched_keywords' => maybe_serialize($matched_keywords),
'auditor_id' => get_current_user_id(),
'audit_time' => current_time('mysql')
);
$wpdb->insert($this->table_name, $data);
// 更新文章元数据
update_post_meta($post_id, '_fca_last_audit', $data);
update_post_meta($post_id, '_fca_audit_status', $status);
}
/**
* 更新审查状态
*/
public function update_audit_status($post_id, $status, $notes = '') {
global $wpdb;
$data = array(
'audit_status' => $status,
'auditor_id' => get_current_user_id(),
'audit_notes' => $notes,
'audit_time' => current_time('mysql')
);
$wpdb->update(
$this->table_name,
$data,
array('post_id' => $post_id),
array('%s', '%d', '%s', '%s'),
array('%d')
);
update_post_meta($post_id, '_fca_audit_status', $status);
// 如果文章被批准,更新文章状态
if ($status === 'approved') {
wp_update_post(array(
'ID' => $post_id,
'post_status' => 'publish'
));
}
}
/**
* 获取审查统计
*/
public function get_stats($period = 'all') {
global $wpdb;
$where = '';
if ($period === 'today') {
$where = " WHERE DATE(audit_time) = CURDATE()";
} elseif ($period === 'week') {
$where = " WHERE audit_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)";
} elseif ($period === 'month') {
$where = " WHERE audit_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)";
}
$query = "
SELECT
audit_status,
COUNT(*) as count,
AVG(sensitivity_score) as avg_score
FROM {$this->table_name}
{$where}
GROUP BY audit_status
";
return $wpdb->get_results($query);
}
}
?>
## 5. 前端样式和脚本
### 5.1 管理界面样式 (admin-styles.css)
/ 审查状态标签 /
.fca-status {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-size: 12px;
font-weight: bold;
}
.fca-status.pending {
background: #f0ad4e;
color: #fff;
}
.fca-status.approved {
background: #5cb85c;
color: #fff;
}
.fca-status.rejected {
background: #d9534f;
color: #fff;
}
.fca-status.flagged {
background: #5bc0de;
color: #fff;
}
.fca-status.not-audited {
background: #777;
color: #fff;
}
/ 仪表板统计 /
.fca-stats-container {
display: flex;
gap: 20px;
margin: 20px 0;
}
.fca-stat-box {
flex: 1;
background: #fff;
border: 1px solid #ccd0d4;
border-radius: 4px;
padding: 20px;
text-align: center;
}
.fca-stat-box h3 {
margin: 0 0 10px 0;
color: #23282d;
}
.fca-stat-box .stat-number {
font-size: 36px;
font-weight: bold;
margin: 0;
color: #0073aa;
}
/ 审查元框 /
.fca-audit-info {
line-height: 1.6;
}


