跳至内容

分类: 应用软件

WordPress开发教程,集成网站用户反馈收集与需求投票系统

WordPress开发教程:集成用户反馈收集与需求投票系统 引言:为什么WordPress需要用户反馈系统 在当今互联网时代,用户参与度已成为网站成功的关键因素之一。无论是企业官网、博客还是电子商务平台,了解用户需求、收集反馈意见对于优化用户体验、提升产品价值至关重要。WordPress作为全球最流行的内容管理系统,虽然拥有丰富的插件生态系统,但有时特定需求仍需要通过代码二次开发来实现。 本教程将详细介绍如何在WordPress中通过代码开发集成用户反馈收集与需求投票系统。这种集成不仅能够增强用户参与感,还能为网站优化提供数据支持,帮助站长更好地理解用户需求,从而做出更明智的决策。 系统架构设计 1.1 功能需求分析 在开始编码之前,我们需要明确系统应具备的功能: 用户反馈收集:允许用户提交问题、建议或bug报告 需求投票系统:用户可以对提出的功能需求进行投票 反馈分类管理:将反馈按类型(如功能建议、bug报告、用户体验问题等)分类 状态跟踪:跟踪反馈的处理状态(如待处理、已审核、开发中、已解决等) 用户通知:当反馈状态更新时通知提交者 管理后台:管理员可以查看、管理和回复所有反馈 数据统计:提供反馈和投票数据的可视化统计 1.2 数据库设计 我们需要在WordPress数据库中添加以下自定义表: -- 反馈主表 CREATE TABLE wp_feedback_items ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT, title VARCHAR(255) NOT NULL, content TEXT NOT NULL, type ENUM('feature', 'bug', 'improvement', 'other') DEFAULT 'feature', status ENUM('pending', 'reviewed', 'planned', 'in_progress', 'completed', 'rejected') DEFAULT 'pending', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES wp_users(ID) ON DELETE SET NULL ); -- 投票表 CREATE TABLE wp_feedback_votes ( id INT AUTO_INCREMENT PRIMARY KEY, feedback_id INT NOT NULL, user_id INT NOT NULL, vote_type ENUM('up', 'down') DEFAULT 'up', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY unique_vote (feedback_id, user_id), FOREIGN KEY (feedback_id) REFERENCES wp_feedback_items(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES wp_users(ID) ON DELETE CASCADE ); -- 评论/回复表 CREATE TABLE wp_feedback_comments ( id INT AUTO_INCREMENT PRIMARY KEY, feedback_id INT NOT NULL, user_id INT, comment TEXT NOT NULL, is_admin BOOLEAN DEFAULT FALSE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (feedback_id) REFERENCES wp_feedback_items(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES wp_users(ID) ON DELETE SET NULL ); 开发环境准备 2.1 创建自定义插件 首先,我们需要创建一个独立的WordPress插件来管理所有功能: <?php /** * Plugin Name: WordPress用户反馈与投票系统 * Plugin URI: https://yourwebsite.com/ * Description: 集成用户反馈收集与需求投票系统的WordPress插件 * Version: 1.0.0 * Author: 你的名字 * License: GPL v2 or later */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('WPFB_VERSION', '1.0.0'); define('WPFB_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('WPFB_PLUGIN_URL', plugin_dir_url(__FILE__)); // 初始化插件 require_once WPFB_PLUGIN_DIR . 'includes/class-feedback-system.php'; // 激活和停用钩子 register_activation_hook(__FILE__, array('Feedback_System', 'activate')); register_deactivation_hook(__FILE__, array('Feedback_System', 'deactivate')); // 初始化插件 add_action('plugins_loaded', array('Feedback_System', 'get_instance')); 2.2 创建数据库表 在插件激活时创建必要的数据库表: class Feedback_System { 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() { // 初始化数据库 add_action('init', array($this, 'init')); // 添加管理菜单 add_action('admin_menu', array($this, 'add_admin_menu')); // 加载脚本和样式 add_action('wp_enqueue_scripts', array($this, 'enqueue_public_scripts')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); // 处理AJAX请求 add_action('wp_ajax_submit_feedback', array($this, 'ajax_submit_feedback')); add_action('wp_ajax_nopriv_submit_feedback', array($this, 'ajax_submit_feedback')); add_action('wp_ajax_vote_feedback', array($this, 'ajax_vote_feedback')); add_action('wp_ajax_nopriv_vote_feedback', array($this, 'ajax_vote_feedback')); } public static function activate() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // 创建反馈表 $feedback_table = $wpdb->prefix . 'feedback_items'; $sql = "CREATE TABLE IF NOT EXISTS $feedback_table ( id INT AUTO_INCREMENT PRIMARY KEY, user_id INT, title VARCHAR(255) NOT NULL, content TEXT NOT NULL, type VARCHAR(50) DEFAULT 'feature', status VARCHAR(50) DEFAULT 'pending', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) $charset_collate;"; // 创建投票表 $votes_table = $wpdb->prefix . 'feedback_votes'; $sql .= "CREATE TABLE IF NOT EXISTS $votes_table ( id INT AUTO_INCREMENT PRIMARY KEY, feedback_id INT NOT NULL, user_id INT NOT NULL, vote_type VARCHAR(10) DEFAULT 'up', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY unique_vote (feedback_id, user_id) ) $charset_collate;"; // 创建评论表 $comments_table = $wpdb->prefix . 'feedback_comments'; $sql .= "CREATE TABLE IF NOT EXISTS $comments_table ( id INT AUTO_INCREMENT PRIMARY KEY, feedback_id INT NOT NULL, user_id INT, comment TEXT NOT NULL, is_admin BOOLEAN DEFAULT FALSE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); // 添加版本号 add_option('wpfb_version', WPFB_VERSION); } } 前端反馈表单实现 3.1 创建反馈提交表单 我们需要创建一个用户友好的前端表单,让用户可以轻松提交反馈: class Feedback_Form { public static function render_form() { // 检查用户是否登录 $user_id = get_current_user_id(); $user_name = $user_id ? wp_get_current_user()->display_name : ''; $user_email = $user_id ? wp_get_current_user()->user_email : ''; ob_start(); ?> <div class="feedback-form-container"> <h3>提交反馈或建议</h3> <?php if (isset($_GET['feedback_submitted']) && $_GET['feedback_submitted'] == 'success'): ?> <div class="feedback-success"> 感谢您的反馈!我们已经收到您的提交。 </div> <?php endif; ?> <form id="feedback-form" method="post" action="<?php echo admin_url('admin-ajax.php'); ?>"> <input type="hidden" name="action" value="submit_feedback"> <?php wp_nonce_field('submit_feedback_nonce', 'feedback_nonce'); ?> <div class="form-group"> <label for="feedback-title">标题 *</label> <input type="text" id="feedback-title" name="title" required placeholder="请简要描述您的反馈或建议"> </div> <div class="form-group"> <label for="feedback-type">反馈类型 *</label> <select id="feedback-type" name="type" required> <option value="">请选择类型</option> <option value="feature">功能建议</option> <option value="bug">错误报告</option> <option value="improvement">改进建议</option> <option value="other">其他</option> </select> </div> <div class="form-group"> <label for="feedback-content">详细描述 *</label> <textarea id="feedback-content" name="content" rows="6" required placeholder="请详细描述您的反馈或建议..."></textarea> </div> <?php if (!$user_id): ?> <div class="form-row"> <div class="form-group"> <label for="feedback-name">您的姓名 *</label> <input type="text" id="feedback-name" name="user_name" required> </div> <div class="form-group"> <label for="feedback-email">电子邮箱 *</label> <input type="email" id="feedback-email" name="user_email" required> </div> </div> <?php endif; ?> <div class="form-group"> <button type="submit" class="submit-feedback-btn">提交反馈</button> <div class="form-loading" style="display:none;"> 提交中,请稍候... </div> </div> </form> </div> <script> jQuery(document).ready(function($) { $('#feedback-form').on('submit', function(e) { e.preventDefault(); var form = $(this); var submitBtn = form.find('.submit-feedback-btn'); var loading = form.find('.form-loading'); // 验证表单 if (!form[0].checkValidity()) { form[0].reportValidity(); return; } // 显示加载状态 submitBtn.prop('disabled', true); loading.show(); // 收集表单数据 var formData = new FormData(this); // 发送AJAX请求 $.ajax({ url: '<?php echo admin_url("admin-ajax.php"); ?>', type: 'POST', data: formData, processData: false, contentType: false, success: function(response) { if (response.success) { // 提交成功,重定向到成功页面 window.location.href = window.location.href + '?feedback_submitted=success'; } else { alert(response.data || '提交失败,请重试。'); submitBtn.prop('disabled', false); loading.hide(); } }, error: function() { alert('网络错误,请重试。'); submitBtn.prop('disabled', false); loading.hide(); } }); }); }); </script> <?php return ob_get_clean(); } } 3.2 创建短代码 为了让用户可以在任何文章或页面中插入反馈表单,我们创建一个短代码: // 在Feedback_System类中添加短代码注册 add_shortcode('feedback_form', array($this, 'feedback_form_shortcode')); public function feedback_form_shortcode($atts) { $atts = shortcode_atts(array( 'title' => '提交反馈', 'show_type' => true ), $atts, 'feedback_form'); return Feedback_Form::render_form(); } 现在用户可以在文章或页面中使用 [feedback_form] 短代码来显示反馈表单。 需求投票系统实现 4.1 创建投票界面 投票系统允许用户对已有的反馈进行投票,帮助确定需求的优先级: class Voting_System { public static function render_voting_section($feedback_id = null) { global $wpdb; if (!$feedback_id) { return ''; } $user_id = get_current_user_id(); $table_name = $wpdb->prefix . 'feedback_items'; $votes_table = $wpdb->prefix . 'feedback_votes'; // 获取反馈详情 $feedback = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $table_name WHERE id = %d", $feedback_id )); if (!$feedback) { return ''; } // 获取投票统计 $upvotes = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM $votes_table WHERE feedback_id = %d AND vote_type = 'up'", $feedback_id )); $downvotes = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM $votes_table WHERE feedback_id = %d AND vote_type = 'down'", $feedback_id )); // 检查当前用户是否已投票 $user_vote = null; if ($user_id) { $user_vote = $wpdb->get_var($wpdb->prepare( "SELECT vote_type FROM $votes_table WHERE feedback_id = %d AND user_id = %d", $feedback_id, $user_id )); } ob_start(); ?> <div class="feedback-voting" data-feedback-id="<?php echo esc_attr($feedback_id); ?>"> <div class="vote-count"> <span class="vote-total"><?php echo ($upvotes - $downvotes); ?></span> <span class="vote-label">票</span> </div> <div class="vote-buttons"> <button class="vote-btn vote-up <?php echo ($user_vote == 'up') ? 'voted' : ''; ?>" data-vote-type="up" <?php echo (!$user_id) ? 'disabled title="请登录后投票"' : ''; ?>> <span class="dashicons dashicons-thumbs-up"></span> <span class="vote-count"><?php echo $upvotes; ?></span> </button> <button class="vote-btn vote-down <?php echo ($user_vote == 'down') ? 'voted' : ''; ?>" data-vote-type="down" <?php echo (!$user_id) ? 'disabled title="请登录后投票"' : ''; ?>> <span class="dashicons dashicons-thumbs-down"></span> <span class="vote-count"><?php echo $downvotes; ?></span> </button> </div> <?php if (!$user_id): ?> <p class="vote-login-notice">请<a href="<?php echo wp_login_url(get_permalink()); ?>">登录</a>后投票</p> <?php endif; ?> </div> <script> jQuery(document).ready(function($) { $('.vote-btn').on('click', function(e) { e.preventDefault(); var button = $(this); var feedbackId = button.closest('.feedback-voting').data('feedback-id'); var voteType = button.data('vote-type'); // 如果已经投票,则取消投票 if (button.hasClass('voted')) { var newVoteType = null; } else { var newVoteType = voteType; } $.ajax({ url: '<?php echo admin_url("admin-ajax.php"); ?>', type: 'POST', data: { action: 'vote_feedback', feedback_id: feedbackId, vote_type: newVoteType, nonce: '<?php echo wp_create_nonce("vote_feedback_nonce"); ?>' }, success: function(response) { if (response.success) { // 更新UI var votingSection = button.closest('.feedback-voting'); votingSection.find('.vote-total').text(response.data.total_votes); votingSection.find('.vote-up .vote-count').text(response.data.upvotes); votingSection.find('.vote-down .vote-count').text(response.data.downvotes); // 更新按钮状态 votingSection.find('.vote-btn').removeClass('voted'); if (newVoteType) { votingSection.find('.vote-btn[data-vote-type="' + newVoteType + '"]').addClass('voted'); } } else { alert(response.data || '投票失败,请重试。'); } } }); }); }); </script> <?php return ob_get_clean(); } } 4.2 处理投票的AJAX请求 // 在Feedback_System类中添加投票处理 public function ajax_vote_feedback() { // 验证nonce if (!wp_verify_nonce($_POST['nonce'], 'vote_feedback_nonce')) { wp_die('安全验证失败'); } $user_id = get_current_user_id(); if (!$user_id) { wp_send_json_error('请登录后投票'); } $feedback_id = intval($_POST['feedback_id']); $vote_type = $_POST['vote_type'] ? sanitize_text_field($_POST['vote_type']) : null; global $wpdb; $votes_table = $wpdb->prefix . 'feedback_votes'; // 检查是否已投票 $existing_vote = $wpdb->get_row($wpdb->prepare( WordPress开发教程:集成用户反馈收集与需求投票系统(续) 投票系统实现(续) 4.3 处理投票的AJAX请求(续) // 在Feedback_System类中添加投票处理 public function ajax_vote_feedback() { // 验证nonce if (!wp_verify_nonce($_POST['nonce'], 'vote_feedback_nonce')) { wp_die('安全验证失败'); } $user_id = get_current_user_id(); if (!$user_id) { wp_send_json_error('请登录后投票'); } $feedback_id = intval($_POST['feedback_id']); $vote_type = $_POST['vote_type'] ? sanitize_text_field($_POST['vote_type']) : null; global $wpdb; $votes_table = $wpdb->prefix . 'feedback_votes'; // 检查是否已投票 $existing_vote = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $votes_table WHERE feedback_id = %d AND user_id = %d", $feedback_id, $user_id )); if ($existing_vote) { if ($vote_type) { // 更新现有投票 $wpdb->update( $votes_table, array('vote_type' => $vote_type, 'created_at' => current_time('mysql')), array('id' => $existing_vote->id) ); } else { // 删除投票(取消投票) $wpdb->delete( $votes_table, array('id' => $existing_vote->id) ); } } else { if ($vote_type) { // 插入新投票 $wpdb->insert( $votes_table, array( 'feedback_id' => $feedback_id, 'user_id' => $user_id, 'vote_type' => $vote_type, 'created_at' => current_time('mysql') ) ); } } // 重新计算投票统计 $upvotes = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM $votes_table WHERE feedback_id = %d AND vote_type = 'up'", $feedback_id )); $downvotes = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM $votes_table WHERE feedback_id = %d AND vote_type = 'down'", $feedback_id )); $total_votes = $upvotes - $downvotes; wp_send_json_success(array( 'total_votes' => $total_votes, 'upvotes' => $upvotes, 'downvotes' => $downvotes )); } 反馈列表与展示页面 5.1 创建反馈列表页面 用户需要能够查看所有提交的反馈并进行投票: class Feedback_List { public static function render_feedback_list($atts = array()) { global $wpdb; $atts = shortcode_atts(array( 'type' => 'all', 'status' => 'all', 'per_page' => 10, 'show_voting' => true, 'show_filters' => true ), $atts, 'feedback_list'); $page = isset($_GET['fb_page']) ? max(1, intval($_GET['fb_page'])) : 1; $offset = ($page - 1) * $atts['per_page']; $table_name = $wpdb->prefix . 'feedback_items'; $votes_table = $wpdb->prefix . 'feedback_votes'; // 构建查询条件 $where_conditions = array('1=1'); $query_params = array(); if ($atts['type'] != 'all') { $where_conditions[] = "type = %s"; $query_params[] = $atts['type']; } if ($atts['status'] != 'all') { $where_conditions[] = "status = %s"; $query_params[] = $atts['status']; } $where_clause = implode(' AND ', $where_conditions); // 获取总数量 $count_query = "SELECT COUNT(*) FROM $table_name WHERE $where_clause"; if ($query_params) { $count_query = $wpdb->prepare($count_query, $query_params); } $total_items = $wpdb->get_var($count_query); $total_pages = ceil($total_items / $atts['per_page']); // 获取反馈列表 $query = "SELECT f.*, COUNT(CASE WHEN v.vote_type = 'up' THEN 1 END) as upvotes, COUNT(CASE WHEN v.vote_type = 'down' THEN 1 END) as downvotes FROM $table_name f LEFT JOIN $votes_table v ON f.id = v.feedback_id WHERE $where_clause GROUP BY f.id ORDER BY (upvotes - downvotes) DESC, f.created_at DESC LIMIT %d OFFSET %d"; $query_params[] = $atts['per_page']; $query_params[] = $offset; $feedback_items = $wpdb->get_results($wpdb->prepare($query, $query_params)); ob_start(); ?> <div class="feedback-list-container"> <?php if ($atts['show_filters']): ?> <div class="feedback-filters"> <form method="get" class="filter-form"> <input type="hidden" name="fb_page" value="1"> <div class="filter-group"> <label for="filter-type">反馈类型:</label> <select id="filter-type" name="fb_type" onchange="this.form.submit()"> <option value="all" <?php selected($atts['type'], 'all'); ?>>全部类型</option> <option value="feature" <?php selected($atts['type'], 'feature'); ?>>功能建议</option> <option value="bug" <?php selected($atts['type'], 'bug'); ?>>错误报告</option> <option value="improvement" <?php selected($atts['type'], 'improvement'); ?>>改进建议</option> </select> </div> <div class="filter-group"> <label for="filter-status">状态:</label> <select id="filter-status" name="fb_status" onchange="this.form.submit()"> <option value="all" <?php selected($atts['status'], 'all'); ?>>全部状态</option> <option value="pending" <?php selected($atts['status'], 'pending'); ?>>待处理</option> <option value="reviewed" <?php selected($atts['status'], 'reviewed'); ?>>已审核</option> <option value="planned" <?php selected($atts['status'], 'planned'); ?>>计划中</option> <option value="in_progress" <?php selected($atts['status'], 'in_progress'); ?>>开发中</option> <option value="completed" <?php selected($atts['status'], 'completed'); ?>>已完成</option> </select> </div> <button type="submit" class="filter-btn">筛选</button> </form> </div> <?php endif; ?> <div class="feedback-items"> <?php if (empty($feedback_items)): ?> <div class="no-feedback"> <p>暂无反馈记录</p> </div> <?php else: ?> <?php foreach ($feedback_items as $item): ?> <?php echo self::render_feedback_item($item, $atts['show_voting']); ?> <?php endforeach; ?> <?php endif; ?> </div> <?php if ($total_pages > 1): ?> <div class="feedback-pagination"> <?php echo paginate_links(array( 'base' => add_query_arg('fb_page', '%#%'), 'format' => '', 'prev_text' => '&laquo;', 'next_text' => '&raquo;', 'total' => $total_pages, 'current' => $page, 'add_args' => array( 'fb_type' => $atts['type'], 'fb_status' => $atts['status'] ) )); ?> </div> <?php endif; ?> </div> <?php return ob_get_clean(); } private static function render_feedback_item($item, $show_voting = true) { $type_labels = array( 'feature' => '功能建议', 'bug' => '错误报告', 'improvement' => '改进建议', 'other' => '其他' ); $status_labels = array( 'pending' => array('label' => '待处理', 'class' => 'status-pending'), 'reviewed' => array('label' => '已审核', 'class' => 'status-reviewed'), 'planned' => array('label' => '计划中', 'class' => 'status-planned'), 'in_progress' => array('label' => '开发中', 'class' => 'status-in-progress'), 'completed' => array('label' => '已完成', 'class' => 'status-completed'), 'rejected' => array('label' => '已拒绝', 'class' => 'status-rejected') ); $user_info = $item->user_id ? get_userdata($item->user_id) : null; $user_name = $user_info ? $user_info->display_name : '匿名用户'; $user_avatar = $user_info ? get_avatar($item->user_id, 40) : get_avatar(0, 40); $total_votes = $item->upvotes - $item->downvotes; ob_start(); ?> <div class="feedback-item" id="feedback-<?php echo $item->id; ?>"> <div class="feedback-header"> <div class="user-info"> <div class="user-avatar"> <?php echo $user_avatar; ?> </div> <div class="user-details"> <span class="user-name"><?php echo esc_html($user_name); ?></span> <span class="feedback-date"><?php echo date('Y-m-d H:i', strtotime($item->created_at)); ?></span> </div> </div> <div class="feedback-meta"> <span class="feedback-type type-<?php echo $item->type; ?>"> <?php echo $type_labels[$item->type]; ?> </span> <span class="feedback-status <?php echo $status_labels[$item->status]['class']; ?>"> <?php echo $status_labels[$item->status]['label']; ?> </span> </div> </div> <div class="feedback-content"> <h3 class="feedback-title"><?php echo esc_html($item->title); ?></h3> <div class="feedback-description"> <?php echo wpautop(esc_html($item->content)); ?> </div> </div> <div class="feedback-footer"> <?php if ($show_voting): ?> <div class="feedback-voting-section"> <?php echo Voting_System::render_voting_section($item->id); ?> </div> <?php endif; ?> <div class="feedback-actions"> <a href="<?php echo add_query_arg('feedback_id', $item->id, get_permalink()); ?>" class="view-details-btn"> 查看详情 </a> <?php if (current_user_can('manage_options') || get_current_user_id() == $item->user_id): ?> <button class="add-comment-btn" data-feedback-id="<?php echo $item->id; ?>"> 添加评论 </button> <?php endif; ?> </div> </div> <?php echo self::render_comments_section($item->id); ?> </div> <?php return ob_get_clean(); } } 5.2 添加短代码支持 // 在Feedback_System类中添加短代码 add_shortcode('feedback_list', array($this, 'feedback_list_shortcode')); public function feedback_list_shortcode($atts) { return Feedback_List::render_feedback_list($atts); } 管理后台界面开发 6.1 创建管理菜单和页面 class Feedback_Admin { public static function add_admin_menu() { // 主菜单 add_menu_page( '用户反馈管理', '用户反馈', 'manage_options', 'feedback-management', array(__CLASS__, 'render_admin_page'), 'dashicons-feedback', 30 ); // 子菜单 add_submenu_page( 'feedback-management', '所有反馈', '所有反馈', 'manage_options', 'feedback-management', array(__CLASS__, 'render_admin_page') ); add_submenu_page( 'feedback-management', '反馈统计', '统计报表', 'manage_options', 'feedback-statistics', array(__CLASS__, 'render_statistics_page') ); add_submenu_page( 'feedback-management', '反馈设置', '设置', 'manage_options', 'feedback-settings', array(__CLASS__, 'render_settings_page') ); } public static function render_admin_page() { global $wpdb; $table_name = $wpdb->prefix . 'feedback_items'; $votes_table = $wpdb->prefix . 'feedback_votes'; // 处理批量操作 if (isset($_POST['bulk_action']) && isset($_POST['feedback_ids'])) { self::handle_bulk_actions($_POST['bulk_action'], $_POST['feedback_ids']); } // 处理单个操作 if (isset($_GET['action']) && isset($_GET['feedback_id'])) { self::handle_single_action($_GET['action'], $_GET['feedback_id']); } // 分页参数 $per_page = 20; $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1; $offset = ($page - 1) * $per_page; // 搜索和筛选条件 $where_conditions = array('1=1'); $query_params = array(); if (!empty($_GET['s'])) { $where_conditions[] = "(title LIKE %s OR content LIKE %s)"; $search_term = '%' . $wpdb->esc_like($_GET['s']) . '%'; $query_params[] = $search_term; $query_params[] = $search_term; } if (!empty($_GET['type'])) { $where_conditions[] = "type = %s"; $query_params[] = $_GET['type']; } if (!empty($_GET['status'])) { $where_conditions[] = "status = %s"; $query_params[] = $_GET['status']; } $where_clause = implode(' AND ', $where_conditions); // 获取总数 $count_query = "SELECT COUNT(*) FROM $table_name WHERE $where_clause"; if ($query_params) { $count_query = $wpdb->prepare($count_query, $query_params); } $total_items = $wpdb->get_var($count_query); $total_pages = ceil($total_items / $per_page); // 获取数据 $query = "SELECT f.*, u.user_login, u.display_name, u.user_email, COUNT(CASE WHEN v.vote_type = 'up' THEN 1 END) as upvotes, COUNT(CASE WHEN v.vote_type = 'down' THEN 1 END) as downvotes FROM $table_name f LEFT JOIN {$wpdb->users} u ON f.user_id = u.ID LEFT JOIN $votes_table v ON f.id = v.feedback_id WHERE $where_clause GROUP BY f.id ORDER BY f.created_at DESC LIMIT %d OFFSET %d"; $query_params[] = $per_page; $query_params[] = $offset; $feedback_items = $wpdb->get_results($wpdb->prepare($query, $query_params)); ?> <div class="wrap"> <h1 class="wp-heading-inline">用户反馈管理</h1> <form method="get" class="search-form"> <input type="hidden" name="page" value="feedback-management"> <div class="tablenav top"> <div class="alignleft actions bulkactions"> <select name="bulk_action"> <option value="">批量操作</option> <option value="mark_reviewed">标记为已审核</option> <option value="mark_in_progress">标记为开发中</option> <option value="mark_completed">标记为已完成</option> <option value="delete">删除</option> </select> <button type="submit" class="button action">应用</button> </div> <div class="alignleft actions"> <select name="type"> <option value="">所有类型</option> <option value="feature" <?php selected(@$_GET['type'], 'feature'); ?>>功能建议</option> <option value="bug" <?php selected(@$_GET['type'], 'bug'); ?>>错误报告</option> <option value="improvement" <?php selected(@$_GET['type'], 'improvement'); ?>>改进建议</option> </select> <select name="status"> <option value="">所有状态</option> <option value="pending" <?php selected(@$_GET['status'], 'pending'); ?>>待处理</option>

发表评论

实战教学,为网站添加基于AR的虚拟产品摆放与场景体验功能

实战教学:为WordPress网站添加基于AR的虚拟产品摆放与场景体验功能 引言:AR技术如何重塑电商体验 在当今数字化时代,增强现实(AR)技术正以前所未有的速度改变着消费者的购物体验。想象一下,当用户访问您的网站时,他们不仅能看到产品的平面图片,还能通过手机摄像头将虚拟产品"放置"在自己的真实环境中,从各个角度查看产品细节,甚至与产品进行互动。这种沉浸式体验不仅显著提升用户参与度,还能大幅降低退货率,提高转化率。 根据最新市场研究,采用AR技术的电商平台平均转化率提升了40%,客户互动时间增加了近一倍。对于WordPress网站所有者来说,集成AR功能不再是遥不可及的高科技梦想,而是可以通过代码二次开发实现的实用功能。 本教程将详细指导您如何为WordPress网站添加基于AR的虚拟产品摆放与场景体验功能,通过实用的代码示例和分步指南,帮助您打造前沿的交互式购物体验。 第一部分:AR技术基础与准备工作 1.1 AR技术原理简介 增强现实(AR)是一种将虚拟信息叠加到真实世界中的技术,通过设备摄像头捕捉现实场景,并在其上叠加计算机生成的图像、视频或3D模型。在电商领域,AR主要应用于: 虚拟试穿/试戴:用户可以看到产品穿戴在身上的效果 虚拟摆放:将家具、装饰品等放置在实际环境中查看效果 产品交互:允许用户旋转、缩放、自定义虚拟产品 1.2 技术选型与工具准备 在开始开发前,我们需要选择合适的AR技术方案: 方案一:基于Web的AR(WebAR) 优点:无需安装应用,跨平台兼容性好 技术栈:A-Frame、AR.js、Three.js 适合:轻量级AR体验,快速部署 方案二:原生AR SDK集成 优点:性能更好,功能更丰富 技术栈:ARKit(iOS)、ARCore(Android) 适合:高性能要求的复杂AR体验 方案三:混合方案 结合WebAR和原生SDK的优势 使用WebXR Device API 对于WordPress网站,我们推荐使用WebAR方案,因为它具有最好的兼容性和部署便利性。 开发环境准备: WordPress开发环境(本地或测试服务器) 代码编辑器(VS Code、Sublime Text等) 基础的前端开发知识(HTML、CSS、JavaScript) 3D模型处理工具(如Blender,用于准备产品模型) 1.3 3D模型准备与优化 AR体验的核心是3D模型,我们需要为每个产品准备高质量的3D模型: 模型格式选择: GLTF/GLB:推荐格式,体积小,加载快 OBJ+MTL:通用格式,但文件较大 FBX:功能丰富,但需要转换 模型优化技巧: 减少多边形数量(在保持质量的前提下) 压缩纹理图像 使用合理的LOD(细节层次)系统 确保模型尺寸与实际产品一致 模型创建流程: 产品测量 → 3D建模 → 纹理贴图 → 模型优化 → 格式转换 → 测试验证 第二部分:WordPress环境配置与基础架构 2.1 创建自定义插件框架 首先,我们需要创建一个WordPress插件来管理所有AR相关功能: <?php /** * Plugin Name: AR Product Viewer for WordPress * Plugin URI: https://yourwebsite.com/ * Description: 为WordPress网站添加AR产品查看和虚拟摆放功能 * Version: 1.0.0 * Author: Your Name * License: GPL v2 or later */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('ARPV_VERSION', '1.0.0'); define('ARPV_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('ARPV_PLUGIN_URL', plugin_dir_url(__FILE__)); // 初始化插件 class AR_Product_Viewer { 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() { // 后台初始化 add_action('admin_init', array($this, 'admin_init')); // 添加管理菜单 add_action('admin_menu', array($this, 'add_admin_menu')); // 前端资源加载 add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_assets')); // 后台资源加载 add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); // 添加短代码 add_shortcode('ar_product_viewer', array($this, 'ar_product_viewer_shortcode')); // 为产品添加自定义字段 add_action('add_meta_boxes', array($this, 'add_product_ar_meta_box')); add_action('save_post', array($this, 'save_product_ar_meta')); } // 更多方法将在后续章节实现... } // 启动插件 AR_Product_Viewer::get_instance(); ?> 2.2 数据库设计与模型管理 我们需要扩展WordPress数据库来存储AR相关数据: // 在插件激活时创建数据库表 register_activation_hook(__FILE__, 'arpv_create_database_tables'); function arpv_create_database_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_name = $wpdb->prefix . 'arpv_models'; $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, product_id bigint(20) NOT NULL, model_name varchar(255) NOT NULL, model_file_url varchar(500) NOT NULL, model_type varchar(50) DEFAULT 'glb', model_size int(11) DEFAULT 0, scale_x float DEFAULT 1.0, scale_y float DEFAULT 1.0, scale_z float DEFAULT 1.0, created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY product_id (product_id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); // 添加版本选项 add_option('arpv_db_version', '1.0'); } 2.3 后台管理界面设计 创建用户友好的后台界面来管理AR模型: // 添加管理菜单 public function add_admin_menu() { add_menu_page( 'AR产品查看器', 'AR产品查看器', 'manage_options', 'ar-product-viewer', array($this, 'render_admin_page'), 'dashicons-visibility', 30 ); add_submenu_page( 'ar-product-viewer', '模型管理', '模型管理', 'manage_options', 'arpv-model-manager', array($this, 'render_model_manager_page') ); add_submenu_page( 'ar-product-viewer', 'AR设置', '设置', 'manage_options', 'arpv-settings', array($this, 'render_settings_page') ); } // 渲染模型管理页面 public function render_model_manager_page() { ?> <div class="wrap"> <h1>AR模型管理</h1> <div class="arpv-admin-container"> <div class="arpv-admin-header"> <button id="arpv-add-model" class="button button-primary">添加新模型</button> <div class="arpv-search-box"> <input type="text" id="arpv-model-search" placeholder="搜索模型..."> </div> </div> <div class="arpv-model-list"> <table class="wp-list-table widefat fixed striped"> <thead> <tr> <th>ID</th> <th>产品名称</th> <th>模型名称</th> <th>文件类型</th> <th>文件大小</th> <th>操作</th> </tr> </thead> <tbody id="arpv-model-table-body"> <!-- 通过AJAX动态加载 --> </tbody> </table> </div> <!-- 模型上传模态框 --> <div id="arpv-upload-modal" class="arpv-modal" style="display:none;"> <div class="arpv-modal-content"> <span class="arpv-close-modal">&times;</span> <h2>上传3D模型</h2> <form id="arpv-upload-form"> <div class="arpv-form-group"> <label for="arpv-product-select">关联产品</label> <select id="arpv-product-select" required> <option value="">选择产品...</option> <?php $products = get_posts(array( 'post_type' => 'product', 'posts_per_page' => -1, 'post_status' => 'publish' )); foreach ($products as $product) { echo '<option value="' . $product->ID . '">' . $product->post_title . '</option>'; } ?> </select> </div> <div class="arpv-form-group"> <label for="arpv-model-name">模型名称</label> <input type="text" id="arpv-model-name" required> </div> <div class="arpv-form-group"> <label for="arpv-model-file">模型文件</label> <input type="file" id="arpv-model-file" accept=".glb,.gltf,.fbx,.obj" required> <p class="description">支持GLB, GLTF, FBX, OBJ格式,建议使用GLB格式以获得最佳性能</p> </div> <div class="arpv-form-group"> <label for="arpv-model-scale">模型缩放比例</label> <input type="number" id="arpv-model-scale" step="0.1" value="1.0" min="0.1" max="10"> </div> <button type="submit" class="button button-primary">上传模型</button> </form> </div> </div> </div> </div> <?php } 第三部分:WebAR核心功能实现 3.1 集成AR.js与Three.js框架 首先,我们需要在前端加载必要的AR和3D渲染库: // 前端资源加载 public function enqueue_frontend_assets() { // 只在需要AR功能的页面加载 if ($this->should_load_ar_assets()) { // Three.js - 3D渲染引擎 wp_enqueue_script('three-js', 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js', array(), 'r128', true); // AR.js - WebAR框架 wp_enqueue_script('ar-js', 'https://cdn.jsdelivr.net/npm/ar.js@latest/aframe/build/aframe-ar.min.js', array('three-js'), null, true); // GLTF加载器 wp_enqueue_script('gltf-loader', ARPV_PLUGIN_URL . 'assets/js/loaders/GLTFLoader.js', array('three-js'), '1.0', true); // 自定义AR控制器 wp_enqueue_script('arpv-ar-controller', ARPV_PLUGIN_URL . 'assets/js/ar-controller.js', array('three-js', 'ar-js', 'gltf-loader'), ARPV_VERSION, true); // AR样式 wp_enqueue_style('arpv-ar-style', ARPV_PLUGIN_URL . 'assets/css/ar-style.css', array(), ARPV_VERSION); // 传递数据到前端 wp_localize_script('arpv-ar-controller', 'arpv_ajax', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('arpv_nonce') )); } } // 判断是否需要加载AR资源 private function should_load_ar_assets() { // 检查是否在产品页面 if (is_product() || has_shortcode(get_post()->post_content, 'ar_product_viewer')) { return true; } // 检查是否在支持AR的页面模板 $template = get_page_template_slug(); if ($template === 'template-ar-product.php') { return true; } return false; } 3.2 创建AR查看器组件 实现核心的AR查看器组件: // assets/js/ar-controller.js class ARProductViewer { constructor(options) { this.options = { containerId: 'ar-viewer-container', productId: 0, modelUrl: '', modelScale: 1.0, ...options }; this.scene = null; this.camera = null; this.renderer = null; this.controls = null; this.model = null; this.arToolkitSource = null; this.arToolkitContext = null; this.init(); } init() { this.createARScene(); this.loadProductModel(); this.setupEventListeners(); this.animate(); } createARScene() { // 创建场景 this.scene = new THREE.Scene(); // 创建相机 this.camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 20); // 创建渲染器 this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); this.renderer.setPixelRatio(window.devicePixelRatio); this.renderer.setSize(window.innerWidth, window.innerHeight); this.renderer.outputEncoding = THREE.sRGBEncoding; // 添加到容器 const container = document.getElementById(this.options.containerId); if (container) { container.appendChild(this.renderer.domElement); } // 添加光源 const ambientLight = new THREE.AmbientLight(0xffffff, 0.8); this.scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); directionalLight.position.set(0, 10, 5); this.scene.add(directionalLight); // 初始化AR.js this.initARToolkit(); } initARToolkit() { // 创建AR.js源 this.arToolkitSource = new THREEx.ArToolkitSource({ sourceType: 'webcam', sourceWidth: 1280, sourceHeight: 720, displayWidth: window.innerWidth, displayHeight: window.innerHeight }); // 初始化源 this.arToolkitSource.init(() => { // 调整渲染器大小 this.onResize(); }); // 创建AR.js上下文 this.arToolkitContext = new THREEx.ArToolkitContext({ cameraParametersUrl: ARPV_PLUGIN_URL + 'assets/data/camera_para.dat', detectionMode: 'mono', maxDetectionRate: 30, canvasWidth: this.arToolkitSource.domElement.clientWidth, canvasHeight: this.arToolkitSource.domElement.clientHeight }); // 初始化上下文 this.arToolkitContext.init(() => { // 相机投影矩阵将根据AR上下文更新 this.camera.projectionMatrix.copy(this.arToolkitContext.getProjectionMatrix()); }); // 创建AR标记 const markerRoot = new THREE.Group(); this.scene.add(markerRoot); // 创建标记控制器 const markerControls = new THREEx.ArMarkerControls(this.arToolkitContext, markerRoot, { type: 'pattern', patternUrl: ARPV_PLUGIN_URL + 'assets/data/patt.hiro', changeMatrixMode: 'cameraTransformMatrix' }); // 将模型添加到标记根组 this.modelContainer = markerRoot; } async loadProductModel() { if (!this.options.modelUrl) { console.error('未提供模型URL'); return; } try { const loader = new THREE.GLTFLoader(); loader.load( this.options.modelUrl, (gltf) => { this.model = gltf.scene; // 调整模型大小和位置 const box = new THREE.Box3().setFromObject(this.model); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y, size.z); const scale = this.options.modelScale / maxDim; this.model.scale.set(scale, scale, scale); this.model.position.set(0, 0, 0); // 添加到场景 if (this.modelContainer) { this.modelContainer.add(this.model); } else { this.scene.add(this.model); } // 触发模型加载完成事件 this.dispatchEvent('modelLoaded', { model: this.model }); console.log('模型加载成功'); }, (progress) => { // 加载进度 const percent = (progress.loaded / progress.total * 100).toFixed(2); this.dispatchEvent('modelLoading', { percent }); }, (error) => { console.error('模型加载失败:', error); this.dispatchEvent('modelError', { error }); } ); } catch (error) { console.error('加载模型时出错:', error); } 3.3 实现模型交互控制 // 继续 assets/js/ar-controller.js setupEventListeners() { // 窗口大小调整 window.addEventListener('resize', () => this.onResize()); // 触摸/鼠标事件 this.setupInteractionEvents(); // 键盘控制 window.addEventListener('keydown', (e) => this.onKeyDown(e)); // 自定义事件监听 this.eventListeners = {}; } setupInteractionEvents() { const canvas = this.renderer.domElement; let isDragging = false; let previousMousePosition = { x: 0, y: 0 }; // 触摸事件 canvas.addEventListener('touchstart', (e) => { e.preventDefault(); if (e.touches.length === 1) { // 单指触摸 - 旋转 isDragging = true; previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY }; } else if (e.touches.length === 2) { // 双指触摸 - 缩放 this.startPinchDistance = this.getPinchDistance(e); } }); canvas.addEventListener('touchmove', (e) => { e.preventDefault(); if (e.touches.length === 1 && isDragging && this.model) { // 单指拖动 - 旋转模型 const currentMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY }; const delta = { x: currentMousePosition.x - previousMousePosition.x, y: currentMousePosition.y - previousMousePosition.y }; // 根据拖动距离旋转模型 this.rotateModel(delta.x * 0.01, delta.y * 0.01); previousMousePosition = currentMousePosition; } else if (e.touches.length === 2 && this.startPinchDistance && this.model) { // 双指捏合 - 缩放模型 const currentPinchDistance = this.getPinchDistance(e); const scaleFactor = currentPinchDistance / this.startPinchDistance; this.scaleModel(scaleFactor); this.startPinchDistance = currentPinchDistance; } }); canvas.addEventListener('touchend', (e) => { isDragging = false; this.startPinchDistance = null; }); // 鼠标事件(桌面设备) canvas.addEventListener('mousedown', (e) => { if (e.button === 0) { // 左键 isDragging = true; previousMousePosition = { x: e.clientX, y: e.clientY }; } }); canvas.addEventListener('mousemove', (e) => { if (isDragging && this.model) { const currentMousePosition = { x: e.clientX, y: e.clientY }; const delta = { x: currentMousePosition.x - previousMousePosition.x, y: currentMousePosition.y - previousMousePosition.y }; this.rotateModel(delta.x * 0.01, delta.y * 0.01); previousMousePosition = currentMousePosition; } }); canvas.addEventListener('mouseup', () => { isDragging = false; }); // 鼠标滚轮缩放 canvas.addEventListener('wheel', (e) => { e.preventDefault(); if (this.model) { const scaleFactor = e.deltaY > 0 ? 0.9 : 1.1; this.scaleModel(scaleFactor); } }); } getPinchDistance(e) { const dx = e.touches[0].clientX - e.touches[1].clientX; const dy = e.touches[0].clientY - e.touches[1].clientY; return Math.sqrt(dx * dx + dy * dy); } rotateModel(deltaX, deltaY) { if (!this.model) return; // 限制旋转角度 this.model.rotation.y += deltaX; this.model.rotation.x = Math.max(-Math.PI/2, Math.min(Math.PI/2, this.model.rotation.x + deltaY)); } scaleModel(factor) { if (!this.model) return; // 限制缩放范围 const currentScale = this.model.scale.x; const newScale = Math.max(0.1, Math.min(5, currentScale * factor)); this.model.scale.set(newScale, newScale, newScale); } onKeyDown(e) { if (!this.model) return; switch(e.key) { case 'r': // 重置模型位置和旋转 this.model.rotation.set(0, 0, 0); this.model.scale.set(1, 1, 1); break; case '+': // 放大 this.scaleModel(1.1); break; case '-': // 缩小 this.scaleModel(0.9); break; case ' ': // 空格键 - 切换AR模式 this.toggleARMode(); break; } } toggleARMode() { // 切换AR模式和普通3D查看模式 this.isARMode = !this.isARMode; if (this.isARMode) { this.enterARMode(); } else { this.exitARMode(); } } enterARMode() { // 启动摄像头 this.arToolkitSource.init(() => { this.arToolkitContext.arController.play(); }); this.dispatchEvent('arModeEntered'); } exitARMode() { // 停止摄像头 this.arToolkitContext.arController.stop(); this.dispatchEvent('arModeExited'); } onResize() { if (this.arToolkitSource) { this.arToolkitSource.onResizeElement(); this.arToolkitSource.copyElementSizeTo(this.renderer.domElement); } if (this.camera) { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); } this.renderer.setSize(window.innerWidth, window.innerHeight); } animate() { requestAnimationFrame(() => this.animate()); // 更新AR上下文 if (this.arToolkitSource && this.arToolkitSource.ready) { this.arToolkitContext.update(this.arToolkitSource.domElement); } // 渲染场景 this.renderer.render(this.scene, this.camera); } // 事件系统 addEventListener(event, callback) { if (!this.eventListeners[event]) { this.eventListeners[event] = []; } this.eventListeners[event].push(callback); } removeEventListener(event, callback) { if (this.eventListeners[event]) { const index = this.eventListeners[event].indexOf(callback); if (index > -1) { this.eventListeners[event].splice(index, 1); } } } dispatchEvent(event, data) { if (this.eventListeners[event]) { this.eventListeners[event].forEach(callback => { callback(data); }); } } // 销毁清理 destroy() { window.removeEventListener('resize', this.onResize); if (this.model && this.scene) { this.scene.remove(this.model); } if (this.renderer) { this.renderer.dispose(); this.renderer.forceContextLoss(); } if (this.arToolkitContext) { this.arToolkitContext.arController.stop(); } } } // 全局访问 window.ARProductViewer = ARProductViewer; 3.4 创建产品页面集成短代码 // 在插件主类中添加短代码处理 public function ar_product_viewer_shortcode($atts) { $atts = shortcode_atts(array( 'product_id' => 0, 'width' => '100%', 'height' => '500px', 'mode' => 'both' // '3d', 'ar', 'both' ), $atts, 'ar_product_viewer'); // 获取产品信息 $product_id = $atts['product_id'] ?: get_the_ID(); $model_data = $this->get_product_model_data($product_id); if (!$model_data) { return '<p class="arpv-error">该产品暂无AR模型</p>'; } // 生成唯一ID $viewer_id = 'arpv-' . uniqid(); ob_start(); ?> <div class="arpv-product-viewer-container"> <div id="<?php echo esc_attr($viewer_id); ?>" class="arpv-viewer" style="width: <?php echo esc_attr($atts['width']); ?>; height: <?php echo esc_attr($atts['height']); ?>;"> </div> <div class="arpv-controls"> <div class="arpv-control-group"> <button class="arpv-btn arpv-btn-rotate" title="旋转"> <span class="dashicons dashicons-image-rotate"></span> </button> <button class="arpv-btn arpv-btn-zoom-in" title="放大"> <span class="dashicons dashicons-plus"></span> </button> <button class="arpv-btn arpv-btn-zoom-out" title="缩小"> <span class="dashicons dashicons-minus"></span> </button> <button class="arpv-btn arpv-btn-reset" title="重置"> <span class="dashicons dashicons-image-rotate"></span> </button> </div> <?php if ($atts['mode'] !== '3d') : ?> <div class="arpv-control-group"> <button class="arpv-btn arpv-btn-ar arpv-btn-primary" title="AR模式"> <span class="dashicons dashicons-camera"></span> <span>在您的空间中查看</span> </button> </div> <?php endif; ?> <div class="arpv-control-group arpv-instructions"> <p class="arpv-hint"> <span class="dashicons dashicons-info"></span> 提示:使用鼠标拖动旋转,滚轮缩放,或点击AR按钮在真实环境中查看 </p> </div> </div> <div class="arpv-ar-overlay" style="display: none;"> <div class="arpv-ar-header"> <button class="arpv-btn arpv-btn-close-ar">返回</button> <h3>AR模式</h3> <p>将相机对准平面表面,等待模型出现</p> </div> <div class="arpv-ar-hint"> <p>移动设备:单指旋转模型,双指缩放</p> </div> </div> </div> <script type="text/javascript"> jQuery(document).ready(function($) { // 初始化AR查看器 const viewer = new ARProductViewer({ containerId: '<?php echo esc_js($viewer_id); ?>', productId: <?php echo intval($product_id); ?>, modelUrl: '<?php echo esc_js($model_data['url']); ?>', modelScale: <?php echo floatval($model_data['scale']); ?> }); // 绑定控制按钮 $('.arpv-btn-rotate').on('click', function() { // 自动旋转动画 viewer.startAutoRotate(); }); $('.arpv-btn-zoom-in').on('click', function() { viewer.scaleModel(1.2); }); $('.arpv-btn-zoom-out').on('click', function() { viewer.scaleModel(0.8); }); $('.arpv-btn-reset').on('click', function() { viewer.resetModel(); }); $('.arpv-btn-ar').on('click', function() { $('.arpv-ar-overlay').show(); viewer.enterARMode(); }); $('.arpv-btn-close-ar').on('click', function() { $('.arpv-ar-overlay').hide(); viewer.exitARMode(); }); // 处理模型加载事件 viewer.addEventListener('modelLoaded', function(data) { console.log('模型加载完成', data); $('.arpv-hint').fadeOut(); }); viewer.addEventListener('modelLoading', function(data) { console.log('模型加载进度:', data.percent + '%'); }); }); </script> <?php return ob_get_clean(); } private function get_product_model_data($product_id) { global $wpdb; $table_name = $wpdb->prefix . 'arpv_models'; $model = $wpdb->get_row($wpdb->prepare( "SELECT * FROM $table_name WHERE product_id = %d ORDER BY id DESC LIMIT 1", $product_id )); if ($model) { return array( 'url' => $model->model_file_url, 'scale' => $model->scale_x, 'name' => $model->model_name ); } return false; } 第四部分:高级功能与优化 4.1 多模型场景与产品搭配 // 添加多模型支持 public function add_scene_builder_meta_box() { add_meta_box( 'arpv_scene_builder', 'AR场景构建器', array($this, 'render_scene_builder_meta_box'), 'product', 'normal', 'high' ); } public function render_scene_builder_meta_box($post) { wp_nonce_field('arpv_scene_builder', 'arpv_scene_builder_nonce'); $scene_data = get_post_meta($post->ID, '_arpv_scene_data', true); $scene_data = $scene_data ? json_decode($scene_data, true) : array(); ?> <div class="arpv-scene-builder"> <div class="arpv-scene-controls"> <button type="button" class="button arpv-add-to-scene">添加产品到场景</button> <button type="button" class="button arpv-save-scene">保存场景</button> <button type="button" class="button arpv-reset-scene">重置场景</button> </div> <div class="arpv-scene-preview"> <div id="arpv-scene-viewer" style="width: 100%; height: 400px; background: #f5f5f5;"> <!-- 3D场景预览 --> </div> </div> <div class="arpv-scene-items"> <h3>场景中的产品</h3> <ul id="arpv-scene-list"> <?php if (!empty($scene_data['items'])) : ?> <?php foreach ($scene_data['items'] as $item) : ?> <li data-product-id="<?php echo esc_attr($item['product_id']); ?>"> <span class="arpv-item-name"><?php echo esc_html($item['name']); ?></span> <button type="button" class="button arpv-remove-item">移除</button> </li> <?php endforeach; ?> <?php endif; ?> </ul> </div> <input type="hidden" id="arpv-scene-data" name="arpv_scene_data" value="<?php echo esc_attr(json_encode($scene_data)); ?>"> </div> <script type="text/javascript"> jQuery(document).ready(function($) { let sceneViewer = null; let sceneItems = <?php echo $scene_data ? json_encode($scene_data['items']) : '[]'; ?>; // 初始化场景查看器 function initSceneViewer() { sceneViewer = new ARSceneViewer({ containerId: 'arpv-scene-viewer', items: sceneItems }); } // 添加产品到场景 $('.arpv-add-to-scene').on('click', function() { // 打开产品选择模态框 $('#arpv-product-selector').show(); }); // 保存场景 $('.arpv-save-scene').on('click', function() { const sceneData = { items: sceneItems, camera_position: sceneViewer ? sceneViewer.getCameraPosition() : null, lighting: sceneViewer ? sceneViewer.getLightingSettings() : null }; $('#arpv-scene-data').val(JSON.stringify(sceneData)); alert('场景已保存'); }); // 初始化 initSceneViewer(); }); </script> <?php } 4.2 性能优化与缓存策略 // 实现模型缓存和CDN集成 class ARPV_Cache_Manager { private static $instance = null; public static function get_instance() { if (null === self::$instance) { self::$instance = new self(); } return self::$instance; } public function get_model_url($model_id, $use_cache = true) { $cache_key = 'arpv_model_' . $model_id; if ($use_cache) { $cached_url = get_transient($cache_key); if ($cached_url !== false) { return $cached_url; } } // 从数据库获取模型信息 global $wpdb; $table_name = $wpdb->prefix . 'arpv_models'; $model = $wpdb->get_row($wpdb->prepare(

发表评论

手把手教程,在WordPress中实现自动化社交媒体内容同步与发布

手把手教程:在WordPress中实现自动化社交媒体内容同步与发布 引言:为什么需要自动化社交媒体同步? 在当今数字营销时代,社交媒体已成为企业与个人品牌推广不可或缺的渠道。然而,手动将WordPress网站内容同步到各个社交媒体平台不仅耗时耗力,还容易出错。据统计,营销人员平均每周花费6-15小时在社交媒体内容管理上,其中大部分时间用于内容分发和调度。 通过WordPress代码二次开发实现自动化社交媒体同步,不仅可以节省大量时间,还能确保内容在不同平台间的一致性,提高发布效率。本教程将引导您从零开始,通过自定义开发实现这一功能,同时探索如何将常用互联网小工具集成到WordPress中。 第一部分:准备工作与环境配置 1.1 理解WordPress钩子系统 WordPress的强大之处在于其完善的钩子(Hooks)系统,包括动作(Actions)和过滤器(Filters)。要实现自动化社交媒体同步,我们需要利用这些钩子在适当的时候触发我们的代码。 // 示例:基本的WordPress钩子使用 add_action('publish_post', 'sync_to_social_media', 10, 2); function sync_to_social_media($post_id, $post) { // 当文章发布时执行社交媒体同步 // 具体实现代码将在后续部分添加 } 1.2 创建自定义插件 为了避免主题更新导致代码丢失,我们建议将功能实现为独立插件: 在wp-content/plugins/目录下创建新文件夹social-auto-sync 创建主插件文件social-auto-sync.php 添加插件头部信息: <?php /** * Plugin Name: 社交媒体自动同步 * Plugin URI: https://yourwebsite.com/ * Description: 自动将WordPress内容同步到各大社交媒体平台 * Version: 1.0.0 * Author: 您的名字 * License: GPL v2 or later */ 1.3 配置开发环境 确保您的开发环境满足以下要求: WordPress 5.0+ PHP 7.4+ 启用curl扩展(用于API调用) 文本编辑器或IDE(如VS Code、PHPStorm) 第二部分:社交媒体API集成基础 2.1 获取API密钥与权限 要实现自动化同步,首先需要获取各社交媒体平台的API访问权限: Twitter(X):通过Twitter开发者门户创建应用 Facebook:使用Facebook开发者工具创建应用并获取访问令牌 LinkedIn:在LinkedIn开发者门户创建应用 Instagram:通过Facebook开发者工具(Instagram属于Facebook生态) 2.2 安全存储API凭证 永远不要在代码中硬编码API密钥。使用WordPress选项API安全存储: // 创建设置页面存储API密钥 add_action('admin_menu', 'social_sync_add_admin_menu'); function social_sync_add_admin_menu() { add_options_page( '社交媒体同步设置', '社媒同步', 'manage_options', 'social-sync-settings', 'social_sync_settings_page' ); } function social_sync_settings_page() { ?> <div class="wrap"> <h1>社交媒体同步设置</h1> <form method="post" action="options.php"> <?php settings_fields('social_sync_settings_group'); do_settings_sections('social-sync-settings'); submit_button(); ?> </form> </div> <?php } // 注册设置 add_action('admin_init', 'social_sync_settings_init'); function social_sync_settings_init() { register_setting('social_sync_settings_group', 'social_sync_settings'); add_settings_section( 'social_sync_api_section', 'API设置', null, 'social-sync-settings' ); add_settings_field( 'twitter_api_key', 'Twitter API密钥', 'social_sync_api_key_callback', 'social-sync-settings', 'social_sync_api_section', ['label_for' => 'twitter_api_key'] ); // 添加更多API字段... } function social_sync_api_key_callback($args) { $options = get_option('social_sync_settings'); $id = $args['label_for']; $value = isset($options[$id]) ? $options[$id] : ''; ?> <input type="password" id="<?php echo esc_attr($id); ?>" name="social_sync_settings[<?php echo esc_attr($id); ?>]" value="<?php echo esc_attr($value); ?>" class="regular-text"> <?php } 第三部分:核心同步功能实现 3.1 文章发布自动同步 当WordPress文章发布时,自动提取内容并分享到社交媒体: // 监听文章发布 add_action('publish_post', 'auto_share_on_publish', 10, 2); function auto_share_on_publish($post_id, $post) { // 防止无限循环 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return; if (wp_is_post_revision($post_id)) return; // 检查是否已经分享过 if (get_post_meta($post_id, '_social_shared', true)) return; // 获取文章信息 $title = get_the_title($post_id); $excerpt = wp_trim_words(get_the_excerpt($post_id), 30, '...'); $permalink = get_permalink($post_id); $featured_image = get_the_post_thumbnail_url($post_id, 'medium'); // 构建分享内容 $message = $title . "nn" . $excerpt . "nn阅读更多: " . $permalink; // 分享到各个平台 $results = []; // Twitter分享 if (get_option('social_sync_twitter_enable')) { $results['twitter'] = share_to_twitter($message, $featured_image); } // Facebook分享 if (get_option('social_sync_facebook_enable')) { $results['facebook'] = share_to_facebook($message, $permalink, $featured_image); } // LinkedIn分享 if (get_option('social_sync_linkedin_enable')) { $results['linkedin'] = share_to_linkedin($title, $excerpt, $permalink, $featured_image); } // 标记为已分享 update_post_meta($post_id, '_social_shared', true); update_post_meta($post_id, '_social_share_results', $results); // 记录日志 social_sync_log('文章发布同步', $post_id, $results); } 3.2 Twitter API集成实现 function share_to_twitter($message, $image_url = null) { $settings = get_option('social_sync_settings'); $api_key = $settings['twitter_api_key'] ?? ''; $api_secret = $settings['twitter_api_secret'] ?? ''; $access_token = $settings['twitter_access_token'] ?? ''; $access_secret = $settings['twitter_access_secret'] ?? ''; if (empty($api_key) || empty($access_token)) { return ['success' => false, 'error' => 'API凭证未配置']; } // 使用Twitter API v2 $endpoint = 'https://api.twitter.com/2/tweets'; // 构建请求数据 $data = ['text' => substr($message, 0, 280)]; // Twitter限制280字符 // 如果有图片,先上传媒体 if ($image_url) { $media_id = upload_twitter_media($image_url, $api_key, $api_secret, $access_token, $access_secret); if ($media_id) { $data['media'] = ['media_ids' => [$media_id]]; } } // 发送请求 $response = wp_remote_post($endpoint, [ 'headers' => [ 'Authorization' => 'Bearer ' . $access_token, 'Content-Type' => 'application/json', ], 'body' => json_encode($data), 'timeout' => 30, ]); if (is_wp_error($response)) { return ['success' => false, 'error' => $response->get_error_message()]; } $body = json_decode(wp_remote_retrieve_body($response), true); if (isset($body['data']['id'])) { return ['success' => true, 'tweet_id' => $body['data']['id']]; } else { return ['success' => false, 'error' => $body['errors'][0]['detail'] ?? '未知错误']; } } function upload_twitter_media($image_url, $api_key, $api_secret, $access_token, $access_secret) { // 下载图片 $image_data = wp_remote_get($image_url); if (is_wp_error($image_data)) { return false; } $image_content = wp_remote_retrieve_body($image_data); // 上传到Twitter媒体端点 $upload_endpoint = 'https://upload.twitter.com/1.1/media/upload.json'; $response = wp_remote_post($upload_endpoint, [ 'headers' => [ 'Authorization' => 'OAuth oauth_consumer_key="' . $api_key . '", oauth_nonce="' . wp_generate_uuid4() . '", oauth_signature_method="HMAC-SHA1", oauth_timestamp="' . time() . '", oauth_token="' . $access_token . '", oauth_version="1.0"', ], 'body' => [ 'media_data' => base64_encode($image_content) ], 'timeout' => 30, ]); if (is_wp_error($response)) { return false; } $body = json_decode(wp_remote_retrieve_body($response), true); return $body['media_id_string'] ?? false; } 3.3 Facebook API集成实现 function share_to_facebook($message, $link, $image_url = null) { $settings = get_option('social_sync_settings'); $page_id = $settings['facebook_page_id'] ?? ''; $access_token = $settings['facebook_access_token'] ?? ''; if (empty($page_id) || empty($access_token)) { return ['success' => false, 'error' => 'Facebook配置不完整']; } $endpoint = "https://graph.facebook.com/v12.0/{$page_id}/feed"; // 构建请求数据 $data = [ 'message' => $message, 'link' => $link, 'access_token' => $access_token, ]; // 如果有图片,附加图片URL if ($image_url) { $data['picture'] = $image_url; } // 发送请求 $response = wp_remote_post($endpoint, [ 'body' => $data, 'timeout' => 30, ]); if (is_wp_error($response)) { return ['success' => false, 'error' => $response->get_error_message()]; } $body = json_decode(wp_remote_retrieve_body($response), true); if (isset($body['id'])) { return ['success' => true, 'post_id' => $body['id']]; } else { return ['success' => false, 'error' => $body['error']['message'] ?? '未知错误']; } } 第四部分:高级功能与优化 4.1 定时与计划发布 除了即时发布,还可以实现定时发布到社交媒体: // 创建自定义计划任务 add_action('social_sync_scheduled_share', 'scheduled_social_share', 10, 1); function scheduled_social_share($post_id) { $post = get_post($post_id); if (!$post || $post->post_status !== 'future') { return; } // 执行社交媒体分享 auto_share_on_publish($post_id, $post); } // 当定时文章发布时触发 add_action('publish_future_post', 'schedule_social_share_on_publish', 10, 1); function schedule_social_share_on_publish($post_id) { $post = get_post($post_id); $publish_time = strtotime($post->post_date); // 安排在文章发布时间执行社交媒体分享 wp_schedule_single_event($publish_time, 'social_sync_scheduled_share', [$post_id]); } 4.2 内容格式化与优化 不同社交媒体平台有不同的内容要求,需要针对性地优化: function format_content_for_platform($content, $platform, $post_id) { $formatted = $content; switch ($platform) { case 'twitter': // Twitter: 280字符限制,添加话题标签 $formatted = substr($content, 0, 250); // 留空间给链接和标签 // 自动提取或添加话题标签 $tags = get_post_tags($post_id); if ($tags) { $tag_names = array_slice(wp_list_pluck($tags, 'name'), 0, 3); $hashtags = ' ' . implode(' ', array_map(function($tag) { return '#' . preg_replace('/s+/', '', $tag); }, $tag_names)); if (strlen($formatted . $hashtags) <= 280) { $formatted .= $hashtags; } } break; case 'linkedin': // LinkedIn: 更专业的语气,可以更长 $formatted = $content . "nn#WordPress #内容营销"; break; case 'facebook': // Facebook: 更友好,可以添加表情符号 $formatted = "📢 新文章发布!nn" . $content . "nn点击链接阅读全文 👇"; break; } return $formatted; } 4.3 错误处理与日志系统 完善的错误处理是自动化系统可靠性的关键: function social_sync_log($action, $post_id, $data) { $log_entry = [ 'timestamp' => current_time('mysql'), 'action' => $action, 'post_id' => $post_id, 'data' => $data, 'ip' => $_SERVER['REMOTE_ADDR'] ?? '未知', ]; // 获取现有日志 $logs = get_option('social_sync_logs', []); // 限制日志数量(保留最近100条) if (count($logs) >= 100) { array_shift($logs); } // 添加新日志 $logs[] = $log_entry; // 保存日志 update_option('social_sync_logs', $logs, false); } // 创建日志查看页面 add_action('admin_menu', 'social_sync_logs_page'); function social_sync_logs_page() { add_submenu_page( 'options-general.php', '社交媒体同步日志', '社媒同步日志', 'manage_options', 'social-sync-logs', 'social_sync_logs_page_content' ); } function social_sync_logs_page_content() { $logs = get_option('social_sync_logs', []); ?> <div class="wrap"> <h1>社交媒体同步日志</h1> <?php if (empty($logs)): ?> <p>暂无日志记录。</p> <?php else: ?> <table class="wp-list-table widefat fixed striped"> <thead> <tr> <th>时间</th> <th>操作</th> <th>文章ID</th> <th>结果</th> <th>IP地址</th> </tr> </thead> <tbody> <?php foreach (array_reverse($logs) as $log): ?> <tr> <td><?php echo esc_html($log['timestamp']); ?></td> <td><?php echo esc_html($log['action']); ?></td> <td> <?php if ($log['post_id']): ?> <a href="<?php echo get_edit_post_link($log['post_id']); ?>"> 文章#<?php echo esc_html($log['post_id']); ?> </a> <?php else: ?> N/A <?php endif; ?> </td> <td> <?php if (is_array($log['data'])) { foreach ($log['data'] as $platform => $result) { echo esc_html($platform) . ': ' . ($result['success'] ? '成功' : '失败') . '<br>'; if (!$result['success'] && isset($result['error'])) { echo '<small style="color:red;">' . esc_html($result['error']) . '</small><br>'; } } } ?> </td> <td><?php echo esc_html($log['ip']); ?></td> </tr> <?php endforeach; ?> </tbody> </table> <form method="post" style="margin-top: 20px;"> <?php wp_nonce_field('clear_social_sync_logs', 'social_sync_nonce'); ?> <input type="hidden" name="action" value="clear_logs"> <input type="submit" class="button button-secondary" value="清空日志"> </form> <?php endif; ?> </div> <?php // 处理清空日志请求 if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'clear_logs' && `clear_social_sync_logs', 'social_sync_nonce')) { update_option('social_sync_logs', []); echo '<div class="notice notice-success is-dismissible"><p>日志已清空。</p></div>'; } } ## 第五部分:常用互联网小工具集成 ### 5.1 URL短链接生成器 集成短链接服务,优化社交媒体分享: function generate_short_url($long_url, $service = 'bitly') { $settings = get_option('social_sync_settings'); switch ($service) { case 'bitly': $access_token = $settings['bitly_access_token'] ?? ''; if (empty($access_token)) return $long_url; $endpoint = 'https://api-ssl.bitly.com/v4/shorten'; $response = wp_remote_post($endpoint, [ 'headers' => [ 'Authorization' => 'Bearer ' . $access_token, 'Content-Type' => 'application/json', ], 'body' => json_encode(['long_url' => $long_url]), 'timeout' => 15, ]); if (!is_wp_error($response)) { $body = json_decode(wp_remote_retrieve_body($response), true); if (isset($body['link'])) { return $body['link']; } } break; case 'tinyurl': $endpoint = 'https://tinyurl.com/api-create.php?url=' . urlencode($long_url); $response = wp_remote_get($endpoint); if (!is_wp_error($response)) { $short_url = wp_remote_retrieve_body($response); if (filter_var($short_url, FILTER_VALIDATE_URL)) { return $short_url; } } break; } return $long_url; // 失败时返回原链接 } // 在分享函数中使用短链接function auto_share_on_publish_with_shorturl($post_id, $post) { // ... 之前的代码 ... $permalink = get_permalink($post_id); $short_url = generate_short_url($permalink, 'bitly'); // 使用短链接构建消息 $message = $title . "nn" . $excerpt . "nn" . $short_url; // ... 后续分享代码 ... } ### 5.2 内容分析工具集成 集成内容分析功能,优化分享效果: function analyze_content_for_sharing($post_id) { $post = get_post($post_id); $content = $post->post_content; $title = $post->post_title; $analysis = [ 'readability_score' => calculate_readability($content), 'keyword_density' => get_keyword_density($content, $title), 'optimal_length' => check_length_optimization($content), 'image_count' => count_images_in_content($content), 'has_video' => has_video_content($content), ]; // 根据分析结果给出建议 $suggestions = generate_sharing_suggestions($analysis); return [ 'analysis' => $analysis, 'suggestions' => $suggestions, ]; } function calculate_readability($content) { // 实现Flesch-Kincaid可读性测试简化版 $text = strip_tags($content); $words = str_word_count($text); $sentences = preg_split('/[.!?]+/', $text); $sentence_count = count(array_filter($sentences)); if ($words === 0 || $sentence_count === 0) { return 0; } $average_words_per_sentence = $words / $sentence_count; $average_syllables_per_word = estimate_syllables($text) / $words; // Flesch Reading Ease公式 $score = 206.835 - (1.015 * $average_words_per_sentence) - (84.6 * $average_syllables_per_word); return round($score, 2); } function generate_sharing_suggestions($analysis) { $suggestions = []; if ($analysis['readability_score'] < 60) { $suggestions[] = '内容可读性较低,建议简化句子结构'; } if ($analysis['image_count'] === 0) { $suggestions[] = '建议添加相关图片以提高社交媒体参与度'; } if ($analysis['has_video']) { $suggestions[] = '检测到视频内容,建议在社交媒体上突出显示'; } return $suggestions; } ### 5.3 自动话题标签生成器 function generate_hashtags($content, $title, $count = 5) { // 提取关键词 $keywords = extract_keywords($content . ' ' . $title); // 过滤和排序关键词 $filtered_keywords = array_filter($keywords, function($word) { // 移除太短或太常见的词 return strlen($word) > 3 && !in_array(strtolower($word), [ 'this', 'that', 'with', 'from', 'have', 'were', 'they' ]); }); // 转换为话题标签 $hashtags = array_map(function($word) { // 移除特殊字符,转换为小写 $clean_word = preg_replace('/[^a-zA-Z0-9]/', '', $word); return '#' . ucfirst(strtolower($clean_word)); }, array_slice($filtered_keywords, 0, $count)); return $hashtags; } function extract_keywords($text) { // 移除HTML标签和特殊字符 $clean_text = strip_tags($text); $clean_text = preg_replace('/[^p{L}p{N}s]/u', ' ', $clean_text); // 分词 $words = preg_split('/s+/', $clean_text); // 统计词频 $word_counts = array_count_values(array_map('strtolower', $words)); // 按频率排序 arsort($word_counts); return array_keys($word_counts); } ## 第六部分:用户界面与交互优化 ### 6.1 文章编辑界面集成 在文章编辑界面添加社交媒体分享控制面板: add_action('add_meta_boxes', 'add_social_sync_meta_box'); function add_social_sync_meta_box() { add_meta_box( 'social_sync_meta_box', '社交媒体分享设置', 'render_social_sync_meta_box', 'post', 'side', 'high' ); } function render_social_sync_meta_box($post) { wp_nonce_field('social_sync_meta_box', 'social_sync_nonce'); $shared = get_post_meta($post->ID, '_social_shared', true); $share_results = get_post_meta($post->ID, '_social_share_results', true); ?> <div id="social-sync-controls"> <p> <label> <input type="checkbox" name="auto_share" value="1" <?php checked(!$shared); ?> <?php echo $shared ? 'disabled' : ''; ?>> 发布时自动分享到社交媒体 </label> </p> <?php if ($shared): ?> <div class="notice notice-success inline"> <p>✅ 已分享到社交媒体</p> <?php if ($share_results): ?> <ul style="margin: 5px 0; padding-left: 20px;"> <?php foreach ($share_results as $platform => $result): ?> <li> <?php echo esc_html(ucfirst($platform)); ?>: <?php echo $result['success'] ? '✅ 成功' : '❌ 失败'; ?> <?php if (!$result['success'] && isset($result['error'])): ?> <br><small style="color: #666;"><?php echo esc_html($result['error']); ?></small> <?php endif; ?> </li> <?php endforeach; ?> </ul> <?php endif; ?> </div> <p> <button type="button" id="reshare-button" class="button button-secondary"> 重新分享 </button> <span class="description">强制重新分享到所有平台</span> </p> <?php endif; ?> <div id="platform-selection" style="margin-top: 10px; <?php echo $shared ? 'display:none;' : ''; ?>"> <p><strong>选择分享平台:</strong></p> <?php $platforms = [ 'twitter' => 'Twitter', 'facebook' => 'Facebook', 'linkedin' => 'LinkedIn', ]; foreach ($platforms as $key => $label): $enabled = get_option("social_sync_{$key}_enable", true); ?> <p> <label> <input type="checkbox" name="share_platforms[]" value="<?php echo esc_attr($key); ?>" <?php checked($enabled); ?> <?php echo $enabled ? '' : 'disabled'; ?>> <?php echo esc_html($label); ?> <?php if (!$enabled): ?> <br><small style="color: #999;">(在设置中启用)</small> <?php endif; ?> </label> </p> <?php endforeach; ?> </div> <div id="custom-message" style="margin-top: 10px; display: none;"> <p><strong>自定义分享消息:</strong></p> <textarea name="custom_share_message" rows="3" style="width:100%;"></textarea> <p class="description">留空使用自动生成的消息</p> </div> <p> <a href="#" id="toggle-custom-message">自定义消息</a> | <a href="#" id="preview-share">预览</a> </p> </div> <script> jQuery(document).ready(function($) { // 切换自定义消息区域 $('#toggle-custom-message').click(function(e) { e.preventDefault(); $('#custom-message').toggle(); }); // 预览分享 $('#preview-share').click(function(e) { e.preventDefault(); $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'preview_social_share', post_id: <?php echo $post->ID; ?>, nonce: '<?php echo wp_create_nonce("preview_share_{$post->ID}"); ?>' }, success: function(response) { if (response.success) { // 显示预览模态框 showPreviewModal(response.data); } } }); }); // 重新分享按钮 $('#reshare-button').click(function() { if (confirm('确定要重新分享这篇文章吗?')) { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'reshare_post', post_id: <?php echo $post->ID; ?>, nonce: '<?php echo wp_create_nonce("reshare_{$post->ID}"); ?>' }, success: function(response) { if (response.success) { location.reload(); } else { alert('重新分享失败:' + response.data); } } }); } }); function showPreviewModal(previews) { // 创建并显示预览模态框 // 实现模态框显示逻辑 } }); </script> <?php } ### 6.2 AJAX交互处理 add_action('wp_ajax_preview_social_share', 'handle_preview_social_share');add_action('wp_ajax_reshare_post', 'handle_reshare_post'); function handle_preview_social_share() { check_ajax_referer('preview_share_' . $_POST['post_id'], 'nonce'); $post_id = intval($_POST['post_id']); $post = get_post($post_id); if (!$post) { wp_die('文章不存在'); } $previews = []; $platforms = ['twitter', 'facebook', 'linkedin']; foreach ($platforms as $platform) { if (get_option("social_sync_{$platform}_enable")) { $message = generate_share_message($post, $platform); $previews[$platform] = [ 'message' => $message, 'length' => strlen($message), 'max_length' => $platform === 'twitter' ? 280 : 5000, ]; } } wp_send_json_success($previews); } function handle_reshare_post() { check_ajax_referer('reshare_' . $_POST['post_id'], 'nonce'); $post_id = intval($_POST['post_id']); $post = get_post($post_id); if (!$post) { wp_send_json_error('文章不存在'); } // 清除之前的分享标记 delete_post_meta($post_id, '_social_shared'); delete_post_meta($post_id, '_social_share_results'); // 重新分享 auto_share_on_publish($post_id, $post); wp_send_json_success('重新分享成功'); } ## 第七部分:性能优化与安全考虑 ### 7.1 异步处理优化 对于耗时的社交媒体API调用,使用异步处理避免阻塞: function async_share_to_social_media($post_id) { // 使用WordPress后台任务系统 if (!wp_next_scheduled('async_social_share', [$post_id])) { wp_schedule_single_event(time() + 5, 'async_social_share', [$post_id]); } } add_action('async_social_share', 'execute_async_social_share'); function execute_async_social_share($post_id) { $post = get_post($post_id); if ($post && $post->post_status === 'publish') { auto_share_on_publish($post_id, $post); } } // 修改发布钩子使用异步处理add_action('publish_post', function($post_id, $post) { if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return; if (wp_is_post_revision($post_id)) return; // 使用异步处理 async_share_to_social_media($post_id); }, 10, 2); ### 7.2 缓存策略 class SocialSyncCache { private static $cache_group = 'social_sync'; private static $cache_expiration = 3600; // 1小时 public static function get($key) { return wp_cache_get($key, self::$cache_group); } public static function set($key, $data) { return wp_cache_set($key, $data, self::$cache_group, self::$cache_expiration); } public static function delete($key) { return wp_cache_delete($key, self::$cache_group); } // 缓存API响应 public static function cached_api_call($callback, $args, $cache_key) { $cached = self::get($cache_key); if ($cached !== false) { return $cached; } $result = call_user_func_array($callback, $args); self::set($cache_key, $result); return $result; } } // 使用缓存的API调用示例function get_cached_twitter_profile() { $cache_key = 'twitter_profile_' . md5(get_option('twitter_access_token')); return SocialSyncCache::cached_api_call( 'fetch_twitter_profile', [], $cache_key ); } ### 7.3 安全加固 class SocialSyncSecurity { // 验证API响应 public static function validate_api_response($response, $expected_structure) { if (is_wp_error($response)) { return false; } $body = json_decode(wp_remote_retrieve_body($response), true); foreach ($expected_structure as $key => $type) { if (!isset($body[$key])) { return false; } if (gettype($body[$key]) !== $type) { return false; } } return true; } // 清理用户输入 public static function sanitize_social_message($message) { // 移除潜在的危险内容 $message = strip_tags($message); $message = preg_replace('/[^p{L}p{N}p{P}p{S}s]/u', '', $message); // 限制长度 $message = substr($message, 0, 5000); return $message; } // 验证访问权限 public static function check_api_permissions($platform) { $user_id = get_current_user_id(); if (!current_user_can('publish_posts')) { return false; } // 检查用户是否有该平台的分享权限 $user_permissions = get_user_meta($user_id, 'social_sync_permissions', true); if (empty($user_permissions) || !in_array($platform, $user_permissions)) { return false; } return true; } } ## 第八部分:测试与部署 ### 8.1 单元测试示例 // 测试文件:test-social-sync.phpclass SocialSyncTest extends WP_UnitTestCase { public function setUp() { parent::setUp(); // 初始化插件 require_once plugin_dir_path(__FILE__) . 'social-auto-sync.php'; }

发表评论

详细教程,为网站集成在线心理测评与职业倾向分析工具

详细教程:为WordPress网站集成在线心理测评与职业倾向分析工具 引言:为什么网站需要集成心理测评与职业分析工具 在当今数字化时代,网站已不仅仅是信息展示平台,更是用户互动与价值传递的重要载体。对于教育机构、职业咨询平台、人力资源网站或企业招聘门户而言,集成专业的心理测评与职业倾向分析工具能够显著提升用户体验和网站价值。 通过WordPress这一全球最流行的内容管理系统,我们可以通过代码二次开发实现这些功能,而无需依赖昂贵的第三方服务。本教程将详细指导您如何从零开始,为您的WordPress网站集成专业的在线心理测评与职业倾向分析工具,同时确保数据安全、用户体验和系统稳定性。 第一部分:准备工作与环境配置 1.1 系统需求分析 在开始开发之前,我们需要明确技术需求: WordPress版本:5.0或更高(推荐最新版本) PHP版本:7.4或更高(推荐8.0+) MySQL版本:5.6或更高 服务器内存:至少512MB(推荐1GB以上) 必要的WordPress权限:主题编辑、插件安装、文件上传 1.2 开发环境搭建 为了安全地进行开发,建议首先在本地或测试服务器搭建环境: 本地开发环境选择: XAMPP(Windows/Mac/Linux) Local by Flywheel(特别适合WordPress开发) Docker WordPress开发环境 测试服务器配置: # 示例:通过SSH配置测试服务器环境 sudo apt update sudo apt install apache2 mysql-server php libapache2-mod-php php-mysql sudo systemctl restart apache2 WordPress安装与基础配置: 下载最新WordPress版本 创建数据库和用户 完成五步安装流程 设置固定链接结构(推荐"文章名"格式) 1.3 安全备份策略 在进行任何代码修改前,必须建立完整备份: 数据库备份: -- 通过phpMyAdmin或命令行备份 mysqldump -u username -p database_name > backup_date.sql 文件备份: # 备份整个WordPress目录 tar -czf wordpress_backup_date.tar.gz /path/to/wordpress 使用备份插件: UpdraftPlus BackWPup Duplicator(特别适合迁移) 第二部分:心理测评系统设计与数据库规划 2.1 测评类型与结构设计 我们需要设计两种主要测评类型: 心理测评:包含人格测试、情绪评估、压力测试等 职业倾向分析:基于霍兰德职业兴趣理论、MBTI等经典模型 2.2 数据库表设计 在WordPress数据库中添加自定义表来存储测评数据: -- 测评问卷表 CREATE TABLE wp_assessment_questionnaires ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, description TEXT, type ENUM('psychological', 'career') NOT NULL, questions_count INT DEFAULT 0, time_estimate INT COMMENT '预计完成时间(分钟)', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, status ENUM('active', 'inactive', 'draft') DEFAULT 'draft' ); -- 测评问题表 CREATE TABLE wp_assessment_questions ( id INT AUTO_INCREMENT PRIMARY KEY, questionnaire_id INT NOT NULL, question_text TEXT NOT NULL, question_type ENUM('single_choice', 'multiple_choice', 'likert_scale', 'open_ended') NOT NULL, options JSON COMMENT 'JSON格式存储选项', sort_order INT DEFAULT 0, FOREIGN KEY (questionnaire_id) REFERENCES wp_assessment_questionnaires(id) ON DELETE CASCADE ); -- 测评结果表 CREATE TABLE wp_assessment_results ( id INT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT(20) UNSIGNED, questionnaire_id INT NOT NULL, answers JSON NOT NULL COMMENT '用户答案JSON', score_data JSON COMMENT '得分与解析JSON', completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ip_address VARCHAR(45), session_id VARCHAR(255), FOREIGN KEY (user_id) REFERENCES wp_users(ID) ON DELETE SET NULL, FOREIGN KEY (questionnaire_id) REFERENCES wp_assessment_questionnaires(id) ON DELETE CASCADE ); -- 职业倾向分析结果表 CREATE TABLE wp_career_analysis ( id INT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT(20) UNSIGNED, holland_code CHAR(3) COMMENT '霍兰德代码如RIA, SEC等', mbti_type VARCHAR(4) COMMENT 'MBTI类型如INTJ, ENFP等', strengths TEXT COMMENT '优势分析', career_suggestions JSON COMMENT '职业建议', analysis_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES wp_users(ID) ON DELETE SET NULL ); 2.3 WordPress自定义表集成 在插件或主题中注册自定义表: // 在插件激活时创建表 function assessment_tools_install_tables() { global $wpdb; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); // 获取字符集和排序规则 $charset_collate = $wpdb->get_charset_collate(); // 创建问卷表 $table_name = $wpdb->prefix . 'assessment_questionnaires'; $sql = "CREATE TABLE $table_name ( id INT AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) NOT NULL, description TEXT, type ENUM('psychological', 'career') NOT NULL, questions_count INT DEFAULT 0, time_estimate INT COMMENT '预计完成时间(分钟)', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, status ENUM('active', 'inactive', 'draft') DEFAULT 'draft' ) $charset_collate;"; dbDelta($sql); // 类似创建其他表... // 添加默认测评数据 assessment_tools_add_default_data(); } register_activation_hook(__FILE__, 'assessment_tools_install_tables'); 第三部分:前端测评界面开发 3.1 响应式测评界面设计 使用HTML5、CSS3和JavaScript创建现代化测评界面: <!-- 测评容器模板 --> <div class="assessment-container" id="assessment-container"> <!-- 进度指示器 --> <div class="assessment-progress"> <div class="progress-bar"> <div class="progress-fill" id="progress-fill"></div> </div> <div class="progress-text"> 问题 <span id="current-question">1</span> / <span id="total-questions">10</span> </div> </div> <!-- 问题展示区 --> <div class="question-container"> <h2 class="question-title" id="question-title"></h2> <div class="question-options" id="question-options"></div> </div> <!-- 导航按钮 --> <div class="assessment-navigation"> <button class="btn btn-secondary" id="prev-btn">上一题</button> <button class="btn btn-primary" id="next-btn">下一题</button> <button class="btn btn-success" id="submit-btn" style="display:none;">提交测评</button> </div> </div> <!-- 结果展示模态框 --> <div class="modal fade" id="result-modal" tabindex="-1"> <div class="modal-dialog modal-lg"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title">测评结果</h5> </div> <div class="modal-body" id="result-content"> <!-- 结果内容将通过AJAX加载 --> </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button> <button type="button" class="btn btn-primary" id="download-report">下载报告</button> </div> </div> </div> </div> 3.2 交互逻辑与状态管理 使用JavaScript实现测评流程控制: // 测评状态管理 class AssessmentManager { constructor(questionnaireId) { this.questionnaireId = questionnaireId; this.currentQuestionIndex = 0; this.questions = []; this.answers = {}; this.isLoading = false; this.init(); } async init() { await this.loadQuestions(); this.renderQuestion(); this.setupEventListeners(); } async loadQuestions() { try { const response = await fetch( `/wp-json/assessment/v1/questions/${this.questionnaireId}` ); this.questions = await response.json(); this.updateProgress(); } catch (error) { console.error('加载问题失败:', error); } } renderQuestion() { if (this.questions.length === 0) return; const question = this.questions[this.currentQuestionIndex]; document.getElementById('question-title').textContent = question.text; const optionsContainer = document.getElementById('question-options'); optionsContainer.innerHTML = ''; // 根据问题类型渲染不同选项 switch(question.type) { case 'single_choice': this.renderSingleChoiceOptions(question, optionsContainer); break; case 'likert_scale': this.renderLikertScaleOptions(question, optionsContainer); break; // 其他类型处理... } this.updateNavigationButtons(); } renderSingleChoiceOptions(question, container) { question.options.forEach((option, index) => { const optionId = `option-${index}`; const isChecked = this.answers[question.id] === option.value; const optionElement = document.createElement('div'); optionElement.className = 'form-check'; optionElement.innerHTML = ` <input class="form-check-input" type="radio" name="question-${question.id}" id="${optionId}" value="${option.value}" ${isChecked ? 'checked' : ''}> <label class="form-check-label" for="${optionId}"> ${option.text} </label> `; container.appendChild(optionElement); }); } updateNavigationButtons() { const prevBtn = document.getElementById('prev-btn'); const nextBtn = document.getElementById('next-btn'); const submitBtn = document.getElementById('submit-btn'); prevBtn.disabled = this.currentQuestionIndex === 0; if (this.currentQuestionIndex === this.questions.length - 1) { nextBtn.style.display = 'none'; submitBtn.style.display = 'inline-block'; } else { nextBtn.style.display = 'inline-block'; submitBtn.style.display = 'none'; } } setupEventListeners() { document.getElementById('prev-btn').addEventListener('click', () => { if (this.currentQuestionIndex > 0) { this.saveCurrentAnswer(); this.currentQuestionIndex--; this.renderQuestion(); this.updateProgress(); } }); document.getElementById('next-btn').addEventListener('click', () => { if (this.validateCurrentAnswer()) { this.saveCurrentAnswer(); this.currentQuestionIndex++; this.renderQuestion(); this.updateProgress(); } }); document.getElementById('submit-btn').addEventListener('click', () => { this.submitAssessment(); }); } saveCurrentAnswer() { const question = this.questions[this.currentQuestionIndex]; const selectedOption = document.querySelector( `input[name="question-${question.id}"]:checked` ); if (selectedOption) { this.answers[question.id] = selectedOption.value; } } async submitAssessment() { this.saveCurrentAnswer(); const submissionData = { questionnaire_id: this.questionnaireId, answers: this.answers, user_id: window.currentUserId || 0 }; try { const response = await fetch('/wp-json/assessment/v1/submit', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': window.wpApiSettings.nonce }, body: JSON.stringify(submissionData) }); const result = await response.json(); this.showResults(result); } catch (error) { console.error('提交失败:', error); alert('提交失败,请稍后重试'); } } showResults(resultData) { // 显示结果模态框 const modal = new bootstrap.Modal(document.getElementById('result-modal')); document.getElementById('result-content').innerHTML = this.formatResults(resultData); modal.show(); } formatResults(resultData) { // 根据测评类型格式化结果 let html = `<h3>${resultData.title}</h3>`; html += `<div class="score-section">得分: ${resultData.score}/100</div>`; html += `<div class="interpretation">${resultData.interpretation}</div>`; if (resultData.recommendations) { html += `<h4>建议与推荐</h4><ul>`; resultData.recommendations.forEach(rec => { html += `<li>${rec}</li>`; }); html += `</ul>`; } return html; } updateProgress() { const progress = ((this.currentQuestionIndex + 1) / this.questions.length) * 100; document.getElementById('progress-fill').style.width = `${progress}%`; document.getElementById('current-question').textContent = this.currentQuestionIndex + 1; document.getElementById('total-questions').textContent = this.questions.length; } } // 初始化测评管理器 document.addEventListener('DOMContentLoaded', function() { const questionnaireId = document.getElementById('assessment-container').dataset.questionnaireId; window.assessmentManager = new AssessmentManager(questionnaireId); }); 第四部分:后端API与数据处理 4.1 创建REST API端点 在WordPress中注册自定义REST API端点: // 注册测评相关REST API add_action('rest_api_init', function() { // 获取测评列表 register_rest_route('assessment/v1', '/questionnaires', [ 'methods' => 'GET', 'callback' => 'get_questionnaires', 'permission_callback' => function() { return current_user_can('read'); } ]); // 获取特定测评的问题 register_rest_route('assessment/v1', '/questions/(?P<id>d+)', [ 'methods' => 'GET', 'callback' => 'get_questions_by_questionnaire', 'permission_callback' => function() { return true; // 公开访问 } ]); // 提交测评答案 register_rest_route('assessment/v1', '/submit', [ 'methods' => 'POST', 'callback' => 'submit_assessment', 'permission_callback' => function() { return true; // 允许非登录用户提交 } ]); // 获取测评结果 register_rest_route('assessment/v1', '/results/(?P<id>d+)', [ 'methods' => 'GET', 'callback' => 'get_assessment_result', 'permission_callback' => function($request) { // 验证用户只能访问自己的结果 $result_id = $request->get_param('id'); return can_user_access_result($result_id); } ]); }); // 获取测评列表 function get_questionnaires($request) { global $wpdb; $table_name = $wpdb->prefix . 'assessment_questionnaires'; $status = $request->get_param('status') ?: 'active'; $type = $request->get_param('type'); $query = "SELECT * FROM $table_name WHERE status = %s"; $params = [$status]; if ($type) { $query .= " AND type = %s"; $params[] = $type; } $query .= " ORDER BY created_at DESC"; $results = $wpdb->get_results($wpdb->prepare($query, $params)); return rest_ensure_response($results); } // 获取测评问题 function get_questions_by_questionnaire($request) { $questionnaire_id = $request->get_param('id'); global $wpdb; $questions_table = $wpdb->prefix . 'assessment_questions'; $questions = $wpdb->get_results($wpdb->prepare( "SELECT * FROM $questions_table WHERE questionnaire_id = %d ORDER BY sort_order ASC", $questionnaire_id )); // 处理选项JSON foreach ($questions as &$question) { if ($question->options) { $question->options = json_decode($question->options); } } return rest_ensure_response($questions); } // 提交测评处理 function submit_assessment($request) { $data = $request->get_json_params(); // 验证数据 if (empty($data['questionnaire_id']) || empty($data['answers'])) { return new WP_Error('invalid_data', '无效的提交数据', ['status' => 400]); } global $wpdb; // 获取用户ID(如果已登录) $user_id = is_user_logged_in() ? get_current_user_id() : 0; // 获取问卷信息 $questionnaire = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}assessment_questionnaires WHERE id = %d", $data['questionnaire_id'] )); if (!$questionnaire) { return new WP_Error('not_found', '测评不存在', ['status' => 404]); 第五部分:测评算法与结果分析系统 5.1 心理测评评分算法实现 // 心理测评评分系统 class PsychologicalAssessmentScorer { /** * 计算测评得分 */ public static function calculateScore($questionnaire_id, $answers) { global $wpdb; // 获取测评的评分规则 $scoring_rules = self::getScoringRules($questionnaire_id); $total_score = 0; $dimension_scores = []; $max_possible_score = 0; foreach ($answers as $question_id => $answer_value) { // 获取问题信息 $question = $wpdb->get_row($wpdb->prepare( "SELECT * FROM {$wpdb->prefix}assessment_questions WHERE id = %d", $question_id )); if (!$question) continue; // 解析问题选项 $options = json_decode($question->options, true); // 根据问题类型计算得分 $question_score = self::calculateQuestionScore( $question->question_type, $answer_value, $options, $scoring_rules ); $total_score += $question_score; // 记录维度得分(如果问题属于特定维度) if (!empty($question->dimension)) { $dimension = $question->dimension; if (!isset($dimension_scores[$dimension])) { $dimension_scores[$dimension] = [ 'score' => 0, 'count' => 0 ]; } $dimension_scores[$dimension]['score'] += $question_score; $dimension_scores[$dimension]['count']++; } // 计算最大可能得分 $max_possible_score += self::getMaxQuestionScore($question, $options); } // 计算百分比得分 $percentage_score = $max_possible_score > 0 ? round(($total_score / $max_possible_score) * 100, 1) : 0; // 计算维度平均分 foreach ($dimension_scores as $dimension => &$data) { $data['average'] = $data['count'] > 0 ? round($data['score'] / $data['count'], 2) : 0; } return [ 'total_score' => $total_score, 'percentage_score' => $percentage_score, 'dimension_scores' => $dimension_scores, 'max_possible_score' => $max_possible_score ]; } /** * 根据问题类型计算单个问题得分 */ private static function calculateQuestionScore($question_type, $answer_value, $options, $scoring_rules) { switch ($question_type) { case 'single_choice': return self::scoreSingleChoice($answer_value, $options); case 'likert_scale': return self::scoreLikertScale($answer_value, $options); case 'multiple_choice': return self::scoreMultipleChoice($answer_value, $options); default: return 0; } } /** * 单选题评分 */ private static function scoreSingleChoice($answer_value, $options) { foreach ($options as $option) { if ($option['value'] == $answer_value) { return isset($option['score']) ? (int)$option['score'] : 0; } } return 0; } /** * 李克特量表评分 */ private static function scoreLikertScale($answer_value, $options) { // 李克特量表通常为1-5分或1-7分 $score_map = [ 'strongly_disagree' => 1, 'disagree' => 2, 'neutral' => 3, 'agree' => 4, 'strongly_agree' => 5 ]; return isset($score_map[$answer_value]) ? $score_map[$answer_value] : 3; } /** * 获取评分规则 */ private static function getScoringRules($questionnaire_id) { // 从数据库或配置文件中获取评分规则 $rules = [ 'depression_scale' => [ 'ranges' => [ ['min' => 0, 'max' => 4, 'level' => 'normal', 'interpretation' => '无抑郁症状'], ['min' => 5, 'max' => 9, 'level' => 'mild', 'interpretation' => '轻度抑郁'], ['min' => 10, 'max' => 14, 'level' => 'moderate', 'interpretation' => '中度抑郁'], ['min' => 15, 'max' => 19, 'level' => 'severe', 'interpretation' => '中重度抑郁'], ['min' => 20, 'max' => 27, 'level' => 'extreme', 'interpretation' => '重度抑郁'] ] ], // 其他测评的评分规则... ]; return $rules; } } 5.2 职业倾向分析算法(霍兰德模型) // 霍兰德职业兴趣测评系统 class HollandCareerAnalyzer { // 霍兰德六种人格类型 const HOLLAND_TYPES = ['R', 'I', 'A', 'S', 'E', 'C']; // 类型描述 const TYPE_DESCRIPTIONS = [ 'R' => ['name' => '现实型', 'traits' => ['动手能力强', '喜欢具体任务', '实际', '稳定']], 'I' => ['name' => '研究型', 'traits' => ['分析能力强', '喜欢思考', '独立', '好奇']], 'A' => ['name' => '艺术型', 'traits' => ['创造力强', '喜欢表达', '独立', '理想化']], 'S' => ['name' => '社会型', 'traits' => ['善于沟通', '喜欢帮助他人', '友善', '合作']], 'E' => ['name' => '企业型', 'traits' => ['领导能力强', '喜欢影响他人', '自信', '冒险']], 'C' => ['name' => '常规型', 'traits' => ['注重细节', '喜欢有序', '谨慎', '高效']] ]; // 职业代码对应表 const CAREER_CODES = [ 'RIA' => ['机械工程师', '电子工程师', '技术员'], 'RIS' => ['外科医生', '牙医', '兽医'], 'RIE' => ['建筑师', '土木工程师', '制图员'], 'RSE' => ['消防员', '警察', '保安'], 'RSC' => ['理发师', '厨师', '工匠'], 'RAI' => ['摄影师', '音乐家', '画家'], 'RCE' => ['司机', '操作员', '装配工'], 'IAS' => ['心理学家', '社会学家', '人类学家'], 'IAR' => ['数学家', '物理学家', '天文学家'], 'ISC' => ['医生', '药剂师', '营养师'], 'ISR' => ['生物学家', '化学家', '地质学家'], 'IRA' => ['计算机科学家', '统计学家', '系统分析师'], 'IRE' => ['工程师', '技术专家', '研究员'], 'AIC' => ['作家', '编辑', '记者'], 'AIR' => ['作曲家', '指挥家', '音乐教师'], 'AIS' => ['演员', '导演', '舞蹈家'], 'ASE' => ['设计师', '艺术指导', '创意总监'], 'ASC' => ['广告经理', '公关专员', '活动策划'], 'AEI' => ['艺术家', '摄影师', '插画师'], 'AER' => ['建筑师', '室内设计师', '景观设计师'], 'SEC' => ['教师', '辅导员', '社工'], 'SEI' => ['护士', '理疗师', '营养师'], 'SER' => ['教练', '健身指导', '体育教师'], 'SEA' => ['心理咨询师', '职业顾问', '人力资源'], 'SAC' => ['销售员', '客户服务', '接待员'], 'SAI' => ['教师', '培训师', '教育顾问'], 'ECI' => ['经理', '主管', '执行官'], 'ECS' => ['销售经理', '市场经理', '业务经理'], 'ERI' => ['企业家', '投资人', '商业顾问'], 'ERA' => ['项目经理', '产品经理', '运营经理'], 'ESC' => ['行政主管', '办公室主任', '秘书'], 'ESR' => ['零售经理', '酒店经理', '餐厅经理'], 'CRI' => ['会计师', '审计师', '财务分析师'], 'CRA' => ['图书管理员', '档案管理员', '数据录入员'], 'CRE' => ['银行职员', '税务专员', '保险代理'], 'CSE' => ['行政助理', '文员', '接待员'], 'CSR' => ['客服代表', '技术支持', '电话销售'], 'CSI' => ['计算机操作员', '数据管理员', '统计员'] ]; /** * 分析霍兰德代码 */ public static function analyzeHollandCode($scores) { // 按得分排序 arsort($scores); // 获取前三高的类型代码 $top_three = array_slice(array_keys($scores), 0, 3); // 生成霍兰德三字代码 $holland_code = implode('', $top_three); // 获取类型描述 $type_descriptions = []; foreach ($top_three as $type) { if (isset(self::TYPE_DESCRIPTIONS[$type])) { $type_descriptions[$type] = self::TYPE_DESCRIPTIONS[$type]; } } // 获取推荐职业 $recommended_careers = self::getRecommendedCareers($holland_code); // 生成分析报告 $analysis = self::generateAnalysisReport($holland_code, $scores, $type_descriptions, $recommended_careers); return $analysis; } /** * 获取推荐职业 */ private static function getRecommendedCareers($holland_code) { $careers = []; // 精确匹配 if (isset(self::CAREER_CODES[$holland_code])) { $careers = self::CAREER_CODES[$holland_code]; } else { // 模糊匹配:尝试前两个字母匹配 $partial_code = substr($holland_code, 0, 2); foreach (self::CAREER_CODES as $code => $career_list) { if (strpos($code, $partial_code) === 0) { $careers = array_merge($careers, $career_list); } } // 如果还是没有匹配,返回通用职业建议 if (empty($careers)) { $careers = self::getGeneralCareers($holland_code); } } // 去重并限制数量 $careers = array_unique($careers); return array_slice($careers, 0, 10); } /** * 生成分析报告 */ private static function generateAnalysisReport($holland_code, $scores, $type_descriptions, $recommended_careers) { $report = [ 'holland_code' => $holland_code, 'primary_type' => [ 'code' => $holland_code[0], 'name' => $type_descriptions[$holland_code[0]]['name'] ?? '', 'description' => '这是您最突出的职业兴趣类型。', 'traits' => $type_descriptions[$holland_code[0]]['traits'] ?? [] ], 'secondary_type' => [ 'code' => $holland_code[1], 'name' => $type_descriptions[$holland_code[1]]['name'] ?? '', 'description' => '这是您的次要职业兴趣类型。', 'traits' => $type_descriptions[$holland_code[1]]['traits'] ?? [] ], 'tertiary_type' => [ 'code' => $holland_code[2], 'name' => $type_descriptions[$holland_code[2]]['name'] ?? '', 'description' => '这是您的第三职业兴趣类型。', 'traits' => $type_descriptions[$holland_code[2]]['traits'] ?? [] ], 'score_distribution' => $scores, 'career_recommendations' => $recommended_careers, 'interpretation' => self::generateInterpretation($holland_code, $scores), 'development_suggestions' => self::getDevelopmentSuggestions($holland_code) ]; return $report; } /** * 生成个性化解读 */ private static function generateInterpretation($holland_code, $scores) { $interpretations = [ 'R' => '您喜欢动手操作,倾向于从事技术性、机械性的工作。', 'I' => '您善于思考分析,适合研究型、探索性的工作环境。', 'A' => '您富有创造力,适合在艺术、设计等需要创新的领域发展。', 'S' => '您乐于助人,擅长人际交往,适合教育、服务等行业。', 'E' => '您具有领导才能,适合管理、销售等需要影响力的工作。', 'C' => '您注重细节和秩序,适合行政、财务等需要精确性的工作。' ]; $primary = $holland_code[0]; $secondary = $holland_code[1]; $text = "根据测评结果,您的霍兰德职业代码是<strong>{$holland_code}</strong>。"; $text .= "这表明您的主要职业兴趣是{$interpretations[$primary]}"; $text .= "同时,您还具有{$interpretations[$secondary]}的特点。"; // 添加组合类型的特殊解读 $combination_interpretations = [ 'RIS' => '您兼具研究精神和动手能力,适合医疗、科研等技术性工作。', 'ASE' => '您的创造力和社交能力结合,适合创意产业和市场营销。', 'SEC' => '您的服务精神和细致特点,适合教育、行政等支持性工作。', 'EIC' => '您的领导力、分析能力和条理性结合,适合管理和咨询工作。' ]; if (isset($combination_interpretations[$holland_code])) { $text .= " " . $combination_interpretations[$holland_code]; } return $text; } } 第六部分:数据可视化与报告生成 6.1 测评结果可视化图表 // 测评结果可视化类 class AssessmentVisualization { /** * 生成雷达图数据(用于多维度测评) */ public static function generateRadarChartData($dimension_scores) { $labels = []; $data = []; $max_values = []; foreach ($dimension_scores as $dimension => $score_data) { $labels[] = self::getDimensionLabel($dimension); $data[] = $score_data['average']; $max_values[] = 5; // 假设5分制 } return [ 'type' => 'radar', 'data' => [ 'labels' => $labels, 'datasets' => [[ 'label' => '您的得分', 'data' => $data, 'backgroundColor' => 'rgba(54, 162, 235, 0.2)', 'borderColor' => 'rgba(54, 162, 235, 1)', 'pointBackgroundColor' => 'rgba(54, 162, 235, 1)', 'pointBorderColor' => '#fff', 'pointHoverBackgroundColor' => '#fff', 'pointHoverBorderColor' => 'rgba(54, 162, 235, 1)' ]] ], 'options' => [ 'scale' => [ 'ticks' => [ 'beginAtZero' => true, 'max' => 5, 'stepSize' => 1 ] ], 'responsive' => true, 'maintainAspectRatio' => false ] ]; } /** * 生成柱状图数据(用于霍兰德测评) */ public static function generateHollandBarChart($holland_scores) { $colors = [ 'R' => '#FF6384', // 红色 'I' => '#36A2EB', // 蓝色 'A' => '#FFCE56', // 黄色 'S' => '#4BC0C0', // 青色 'E' => '#9966FF', // 紫色 'C' => '#FF9F40' // 橙色 ]; $labels = []; $data = []; $backgroundColors = []; $borderColors = []; foreach ($holland_scores as $type => $score) { $labels[] = self::getHollandTypeName($type); $data[] = $score; $backgroundColors[] = $colors[$type]; $borderColors[] = str_replace('0.2', '1', $colors[$type]); } return [ 'type' => 'bar', 'data' => [ 'labels' => $labels, 'datasets' => [[ 'label' => '兴趣强度', 'data' => $data, 'backgroundColor' => $backgroundColors, 'borderColor' => $borderColors, 'borderWidth' => 1 ]] ], 'options' => [

发表评论

WordPress高级教程,打造网站智能问答机器人集成知识图谱

WordPress高级教程:打造网站智能问答机器人集成知识图谱,通过WordPress程序的代码二次开发实现常用互联网小工具功能 引言:当WordPress遇见人工智能与知识图谱 在当今数字化浪潮中,企业网站已从简单的信息展示平台演变为智能交互门户。WordPress作为全球最受欢迎的内容管理系统,其灵活性和可扩展性为网站智能化提供了无限可能。本教程将深入探讨如何通过WordPress代码二次开发,集成智能问答机器人并构建知识图谱,同时实现一系列实用的互联网小工具功能,将您的网站从被动展示转变为主动服务的智能平台。 传统网站面临的核心问题是信息查找效率低下和用户交互体验单一。访问者需要浏览多个页面才能找到所需信息,而网站管理者则难以有效收集用户需求。智能问答机器人与知识图谱的结合,正是解决这些痛点的关键技术路径。通过本教程,您将学习如何利用WordPress的强大扩展能力,打造一个能够理解自然语言、基于结构化知识提供精准答案的智能系统。 第一章:WordPress二次开发基础与环境配置 1.1 WordPress开发环境搭建 在进行高级功能开发前,必须建立专业的开发环境。推荐使用Local by Flywheel或Docker WordPress开发环境,这些工具提供了完整的PHP、MySQL和Web服务器集成。对于本教程涉及的AI功能,建议使用PHP 7.4或更高版本,以确保最佳的性能和兼容性。 在主题开发方面,建议创建子主题而非直接修改父主题。在wp-content/themes目录下创建新文件夹,如“my-smart-theme”,并创建style.css和functions.php文件。在style.css头部添加主题信息: /* Theme Name: My Smart Theme Template: parent-theme-folder-name Version: 1.0 */ 1.2 WordPress核心概念与钩子系统 深入理解WordPress的钩子(Hooks)系统是进行高级开发的基础。动作钩子(Action Hooks)允许在特定点插入自定义代码,而过滤器钩子(Filter Hooks)则允许修改数据。例如,要在文章保存时执行自定义操作,可以使用以下代码: add_action('save_post', 'my_custom_save_function'); function my_custom_save_function($post_id) { // 自定义保存逻辑 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return; // 更新文章元数据 update_post_meta($post_id, 'last_processed', current_time('mysql')); } 1.3 自定义文章类型与字段 为知识图谱创建专门的数据结构是至关重要的。使用register_post_type函数创建自定义文章类型“知识条目”: add_action('init', 'register_knowledge_post_type'); function register_knowledge_post_type() { $labels = array( 'name' => '知识条目', 'singular_name' => '知识条目' ); $args = array( 'labels' => $labels, 'public' => true, 'has_archive' => true, 'supports' => array('title', 'editor', 'custom-fields'), 'show_in_rest' => true // 启用Gutenberg编辑器支持 ); register_post_type('knowledge', $args); } 结合高级自定义字段(ACF)插件或自定义元框,可以为知识条目添加结构化字段,如实体类型、属性、关系等。 第二章:知识图谱的构建与管理 2.1 知识图谱基础架构设计 知识图谱本质上是一种语义网络,由实体、属性和关系组成。在WordPress中,我们可以利用自定义分类法(Taxonomy)来表示实体类型和关系。例如,创建“实体类型”分类法: add_action('init', 'register_entity_type_taxonomy'); function register_entity_type_taxonomy() { $args = array( 'hierarchical' => true, 'labels' => array('name' => '实体类型'), 'show_ui' => true, 'show_admin_column' => true, 'query_var' => true, 'rewrite' => array('slug' => 'entity-type'), 'show_in_rest' => true ); register_taxonomy('entity_type', array('knowledge'), $args); } 2.2 知识抽取与结构化存储 从现有WordPress内容中自动提取知识是构建知识图谱的关键步骤。可以利用自然语言处理技术,通过以下方式实现: 实体识别:使用PHP的NLP库或调用外部API识别文章中的命名实体 关系抽取:分析句子结构,提取实体间的关系 属性提取:从内容中识别实体的属性信息 以下是一个简化的实体识别函数示例: function extract_entities_from_content($content) { // 简化示例:使用规则匹配实体 $entities = array(); // 匹配产品名称(示例规则) preg_match_all('/b(?:iPhone|iPad|MacBook)s+w+b/i', $content, $product_matches); if (!empty($product_matches[0])) { foreach ($product_matches[0] as $match) { $entities[] = array( 'text' => $match, 'type' => '产品', 'start_pos' => strpos($content, $match) ); } } // 在实际应用中,这里应集成更复杂的NLP算法 // 或调用如Stanford NER、spaCy等工具 return $entities; } 2.3 知识图谱可视化与查询 构建知识图谱后,需要提供直观的查询和展示方式。可以使用D3.js或Vis.js等JavaScript库实现知识图谱的可视化。创建一个短代码,将知识图谱嵌入文章或页面: add_shortcode('knowledge_graph', 'render_knowledge_graph'); function render_knowledge_graph($atts) { wp_enqueue_script('d3-js', 'https://d3js.org/d3.v6.min.js'); wp_enqueue_script('knowledge-graph-js', get_stylesheet_directory_uri() . '/js/knowledge-graph.js'); ob_start(); ?> <div id="knowledge-graph-container" style="width:100%; height:600px;"></div> <script> // 知识图谱数据将通过AJAX加载 jQuery(document).ready(function($) { loadKnowledgeGraph(); }); </script> <?php return ob_get_clean(); } 第三章:智能问答机器人的集成与开发 3.1 问答系统架构设计 智能问答系统通常包含以下核心组件: 自然语言理解(NLU)模块:解析用户问题,识别意图和实体 知识检索模块:从知识图谱中查找相关信息 答案生成模块:基于检索结果生成自然语言回答 对话管理模块:维护对话上下文和状态 在WordPress中,我们可以通过REST API端点提供问答服务: add_action('rest_api_init', 'register_qa_endpoints'); function register_qa_endpoints() { register_rest_route('smart-qa/v1', '/ask', array( 'methods' => 'POST', 'callback' => 'handle_question', 'permission_callback' => '__return_true' )); } function handle_question($request) { $question = sanitize_text_field($request->get_param('question')); $session_id = $request->get_param('session_id'); // 处理问题并生成答案 $answer = process_question($question, $session_id); return rest_ensure_response(array( 'answer' => $answer['text'], 'confidence' => $answer['confidence'], 'sources' => $answer['sources'] )); } 3.2 自然语言处理集成 对于中小型网站,可以使用以下方式集成NLP能力: 本地NLP库:使用PHP-ML或调用Python服务 云API服务:集成如Google Dialogflow、Microsoft LUIS或阿里云NLP 混合方案:简单问题本地处理,复杂问题调用云端服务 以下是一个使用简单模式匹配的本地问答处理器: function process_question_local($question) { $patterns = array( '/价格|多少钱|报价/' => 'price_intent', '/功能|特点|特色/' => 'feature_intent', '/教程|指南|如何使用/' => 'tutorial_intent', '/比较|对比|区别/' => 'comparison_intent' ); $intent = 'general'; foreach ($patterns as $pattern => $matched_intent) { if (preg_match($pattern, $question)) { $intent = $matched_intent; break; } } // 根据意图处理问题 switch ($intent) { case 'price_intent': return process_price_question($question); case 'feature_intent': return process_feature_question($question); // 其他意图处理... default: return process_general_question($question); } } 3.3 上下文感知与多轮对话 实现多轮对话需要维护对话状态。可以使用WordPress的Transients API临时存储对话上下文: function manage_conversation_context($session_id, $question, $answer = null) { $context_key = 'qa_context_' . $session_id; $context = get_transient($context_key); if (!$context) { $context = array( 'history' => array(), 'current_topic' => '', 'pending_slots' => array() ); } // 添加当前对话到历史 $context['history'][] = array( 'question' => $question, 'answer' => $answer, 'time' => time() ); // 限制历史记录长度 if (count($context['history']) > 10) { array_shift($context['history']); } // 保存更新后的上下文 set_transient($context_key, $context, 3600); // 1小时过期 return $context; } 第四章:常用互联网小工具的实现 4.1 实时数据展示小工具 在网站侧边栏或内容区域添加实时数据展示小工具,如加密货币价格、天气预报或股票信息。首先创建一个小工具类: class RealTime_Data_Widget extends WP_Widget { public function __construct() { parent::__construct( 'realtime_data_widget', '实时数据小工具', array('description' => '显示实时数据如加密货币价格、天气等') ); } public function widget($args, $instance) { echo $args['before_widget']; if (!empty($instance['title'])) { echo $args['before_title'] . apply_filters('widget_title', $instance['title']) . $args['after_title']; } // 根据类型显示不同数据 $data_type = !empty($instance['data_type']) ? $instance['data_type'] : 'crypto'; switch ($data_type) { case 'crypto': $this->display_crypto_data($instance); break; case 'weather': $this->display_weather_data($instance); break; case 'stock': $this->display_stock_data($instance); break; } echo $args['after_widget']; } private function display_crypto_data($instance) { $crypto_symbol = !empty($instance['crypto_symbol']) ? $instance['crypto_symbol'] : 'BTC'; // 使用AJAX获取实时数据 ?> <div class="crypto-widget" data-symbol="<?php echo esc_attr($crypto_symbol); ?>"> <div class="crypto-name"><?php echo esc_html($crypto_symbol); ?></div> <div class="crypto-price">加载中...</div> <div class="crypto-change"></div> </div> <script> jQuery(document).ready(function($) { function updateCryptoPrice() { var symbol = $('.crypto-widget').data('symbol'); $.get('<?php echo admin_url('admin-ajax.php'); ?>', { action: 'get_crypto_price', symbol: symbol }, function(response) { if (response.success) { $('.crypto-price').text('$' + response.data.price); $('.crypto-change').text(response.data.change + '%'); $('.crypto-change').removeClass('positive negative') .addClass(response.data.change >= 0 ? 'positive' : 'negative'); } }); } updateCryptoPrice(); setInterval(updateCryptoPrice, 60000); // 每分钟更新 }); </script> <?php } // 其他数据显示方法... } 4.2 交互式计算工具 创建实用的交互式计算工具,如贷款计算器、货币转换器或单位换算器。以下是一个货币转换器的实现示例: add_shortcode('currency_converter', 'currency_converter_shortcode'); function currency_converter_shortcode($atts) { wp_enqueue_script('currency-converter', get_stylesheet_directory_uri() . '/js/currency-converter.js'); $default_currencies = array('USD', 'EUR', 'GBP', 'JPY', 'CNY'); ob_start(); ?> <div class="currency-converter-widget"> <h3>货币转换器</h3> <div class="converter-form"> <div class="input-group"> <input type="number" id="amount" value="1" min="0" step="0.01"> <select id="from_currency"> <?php foreach ($default_currencies as $currency): ?> <option value="<?php echo esc_attr($currency); ?>"><?php echo esc_html($currency); ?></option> <?php endforeach; ?> </select> </div> <div class="conversion-arrow">→</div> <div class="input-group"> <input type="text" id="converted_amount" readonly> <select id="to_currency"> <?php foreach ($default_currencies as $currency): ?> <option value="<?php echo esc_attr($currency); ?>" <?php selected($currency, 'CNY'); ?>> <?php echo esc_html($currency); ?> </option> <?php endforeach; ?> </select> </div> <button id="convert_currency">转换</button> <div class="update-time">汇率更新时间: <span id="rate_update_time">-</span></div> </div> </div> <script> // 内联JavaScript或外部文件 jQuery(document).ready(function($) { var exchangeRates = {}; function loadExchangeRates() { $.get('<?php echo admin_url('admin-ajax.php'); ?>', { action: 'get_exchange_rates' }, function(response) { if (response.success) { exchangeRates = response.data.rates; $('#rate_update_time').text(response.data.timestamp); convertCurrency(); } }); } function convertCurrency() { var amount = parseFloat($('#amount').val()); var from = $('#from_currency').val(); var to = $('#to_currency').val(); if (isNaN(amount) || amount < 0) { alert('请输入有效的金额'); return; } // 转换逻辑 if (from === 'USD') { var converted = amount * exchangeRates[to]; } else if (to === 'USD') { var converted = amount / exchangeRates[from]; } else { // 通过美元中转 var toUSD = amount / exchangeRates[from]; var converted = toUSD * exchangeRates[to]; } $('#converted_amount').val(converted.toFixed(2)); } $('#convert_currency').on('click', convertCurrency); $('#amount, #from_currency, #to_currency').on('change', convertCurrency); // 初始加载 loadExchangeRates(); setInterval(loadExchangeRates, 3600000); // 每小时更新汇率 }); </script> <?php return ob_get_clean(); } 4.3 内容增强与交互工具 创建增强内容交互的小工具,如交互式时间线、产品比较工具或个性化推荐引擎。以下是一个简单的个性化内容推荐小工具: class Personalized_Recommendation_Widget extends WP_Widget { public function widget($args, $instance) { // 基于用户行为生成推荐 $user_id = get_current_user_id(); $recommendations = $this->get_personalized_recommendations($user_id); if (empty($recommendations)) { $recommendations = $this->get_popular_content(); } echo $args['before_widget']; echo '<h3 class="widget-title">为您推荐</h3>'; echo '<ul class="recommendation-list">'; foreach ($recommendations as $post) { echo '<li>'; echo '<a href="' . get_permalink($post->ID) . '">'; echo get_the_title($post->ID); echo '</a>'; echo '<span class="recommendation-reason">' . $this->get_recommendation_reason($post->ID, $user_id) . '</span>'; echo '</li>'; } echo '</ul>'; echo $args['after_widget']; } private function get_personalized_recommendations($user_id) { if (!$user_id) { return array(); } // 基于用户浏览历史、搜索记录等生成推荐 $viewed_posts = get_user_meta($user_id, 'viewed_posts', true); $searched_terms = get_user_meta($user_id, 'searched_terms', true); // 简化的推荐算法:查找相似内容 $args = array( 'post_type' => 'post', 'posts_per_page' => 5, 'post__not_in' => $viewed_posts ? array_slice($viewed_posts, -10) : array(), // 排除最近浏览的10篇文章 'meta_query' => array( 'relation' => 'OR' ) ); // 如果用户有搜索记录,基于搜索词推荐 if ($searched_terms && is_array($searched_terms)) { $recent_searches = array_slice($searched_terms, -3); // 取最近3个搜索词 foreach ($recent_searches as $term) { $args['meta_query'][] = array( 'key' => '_search_related', 'value' => $term, 'compare' => 'LIKE' ); } } // 如果用户有浏览历史,基于标签推荐相似内容 if ($viewed_posts && count($viewed_posts) > 0) { $recent_viewed = array_slice($viewed_posts, -5); $tags = wp_get_post_tags($recent_viewed, array('fields' => 'ids')); if (!empty($tags)) { $args['tag__in'] = $tags; $args['tag__in_operator'] = 'IN'; } } return get_posts($args); } private function get_recommendation_reason($post_id, $user_id) { // 生成推荐理由 $reasons = array(); // 基于标签匹配 $user_tags = get_user_meta($user_id, 'preferred_tags', true); $post_tags = wp_get_post_tags($post_id, array('fields' => 'slugs')); if ($user_tags && $post_tags) { $matched_tags = array_intersect($user_tags, $post_tags); if (count($matched_tags) > 0) { $reasons[] = '与您关注的"' . implode('", "', $matched_tags) . '"相关'; } } // 基于浏览历史 $viewed_posts = get_user_meta($user_id, 'viewed_posts', true); if ($viewed_posts && in_array($post_id, $viewed_posts)) { $reasons[] = '您之前浏览过此内容'; } return !empty($reasons) ? '(' . implode(';', $reasons) . ')' : '(热门内容)'; } } // 注册小工具add_action('widgets_init', function() { register_widget('Personalized_Recommendation_Widget'); }); ### 第五章:系统集成与性能优化 #### 5.1 前端交互与用户体验优化 智能问答机器人的前端交互体验至关重要。创建一个美观且响应式的聊天界面: add_shortcode('smart_qa_chat', 'smart_qa_chat_shortcode');function smart_qa_chat_shortcode() { wp_enqueue_style('qa-chat-css', get_stylesheet_directory_uri() . '/css/qa-chat.css'); wp_enqueue_script('qa-chat-js', get_stylesheet_directory_uri() . '/js/qa-chat.js', array('jquery'), '1.0', true); // 传递AJAX URL到JavaScript wp_localize_script('qa-chat-js', 'qa_chat_params', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('qa_chat_nonce') )); ob_start(); ?> <div class="qa-chat-container"> <div class="chat-header"> <h3>智能问答助手</h3> <div class="chat-status"> <span class="status-indicator online"></span> <span class="status-text">在线</span> </div> </div> <div class="chat-messages" id="chatMessages"> <div class="message bot"> <div class="avatar">AI</div> <div class="content"> 您好!我是智能问答助手,可以回答关于本网站的各种问题。 您可以问我产品信息、使用教程、价格等问题。 </div> <div class="timestamp"><?php echo current_time('H:i'); ?></div> </div> </div> <div class="quick-questions"> <button class="quick-question" data-question="你们有哪些产品?">产品列表</button> <button class="quick-question" data-question="如何购买?">购买指南</button> <button class="quick-question" data-question="技术支持联系方式">技术支持</button> </div> <div class="chat-input-area"> <textarea id="chatInput" placeholder="请输入您的问题..." rows="2"></textarea> <button id="sendMessage">发送</button> <button id="voiceInput" class="voice-btn"> <span class="voice-icon">🎤</span> </button> </div> <div class="chat-features"> <label><input type="checkbox" id="enableVoice"> 语音输入</label> <label><input type="checkbox" id="saveHistory" checked> 保存对话记录</label> <button id="clearChat">清空对话</button> </div> </div> <!-- 知识图谱可视化模态框 --> <div id="knowledgeGraphModal" class="modal"> <div class="modal-content"> <span class="close-modal">&times;</span> <h3>相关知识图谱</h3> <div id="modalGraphContainer"></div> </div> </div> <?php return ob_get_clean(); } #### 5.2 缓存策略与性能优化 智能问答系统涉及复杂的计算和数据库查询,必须实施有效的缓存策略: class QA_Cache_Manager { private $cache_group = 'smart_qa'; private $cache_expiration = 3600; // 1小时 public function get_cached_answer($question_hash) { $cached = wp_cache_get($question_hash, $this->cache_group); if ($cached !== false) { // 检查缓存是否仍然有效 if ($this->validate_cache($cached)) { return $cached['answer']; } } return false; } public function cache_answer($question_hash, $answer, $dependencies = array()) { $cache_data = array( 'answer' => $answer, 'timestamp' => time(), 'dependencies' => $dependencies, 'version' => '1.0' ); wp_cache_set($question_hash, $cache_data, $this->cache_group, $this->cache_expiration); // 建立依赖关系 foreach ($dependencies as $dep) { $this->add_dependency($dep, $question_hash); } } public function invalidate_by_dependency($dependency) { $dependent_keys = get_transient('qa_cache_dep_' . md5($dependency)); if ($dependent_keys) { foreach ($dependent_keys as $key) { wp_cache_delete($key, $this->cache_group); } } } private function add_dependency($dependency, $question_hash) { $key = 'qa_cache_dep_' . md5($dependency); $dependents = get_transient($key); if (!$dependents) { $dependents = array(); } if (!in_array($question_hash, $dependents)) { $dependents[] = $question_hash; set_transient($key, $dependents, $this->cache_expiration * 2); } } } // 数据库查询优化function optimize_qa_queries($question, $context) { global $wpdb; // 使用预处理语句防止SQL注入 $query = $wpdb->prepare( "SELECT p.ID, p.post_title, p.post_content, MATCH(p.post_title, p.post_content) AGAINST (%s) as relevance FROM {$wpdb->posts} p WHERE p.post_status = 'publish' AND (p.post_type = 'post' OR p.post_type = 'page' OR p.post_type = 'knowledge') AND MATCH(p.post_title, p.post_content) AGAINST (%s IN BOOLEAN MODE) ORDER BY relevance DESC LIMIT 10", $question, $question ); return $wpdb->get_results($query); } // 实施懒加载和分页function get_knowledge_graph_data($page = 1, $per_page = 50) { $cache_key = 'knowledge_graph_page_' . $page; $cached = get_transient($cache_key); if ($cached !== false) { return $cached; } $offset = ($page - 1) * $per_page; $args = array( 'post_type' => 'knowledge', 'posts_per_page' => $per_page, 'offset' => $offset, 'meta_query' => array( array( 'key' => 'entity_type', 'compare' => 'EXISTS' ) ) ); $query = new WP_Query($args); $data = array(); if ($query->have_posts()) { while ($query->have_posts()) { $query->the_post(); $entity_type = get_post_meta(get_the_ID(), 'entity_type', true); $relations = get_post_meta(get_the_ID(), 'relations', true); $data[] = array( 'id' => get_the_ID(), 'title' => get_the_title(), 'type' => $entity_type, 'relations' => $relations ? json_decode($relations, true) : array(), 'excerpt' => get_the_excerpt() ); } } wp_reset_postdata(); // 缓存结果 set_transient($cache_key, $data, HOUR_IN_SECONDS); return $data; } #### 5.3 安全性与隐私保护 处理用户数据时必须确保安全性和隐私保护: class QA_Security_Manager { public function sanitize_user_input($input) { // 多层清理和验证 $cleaned = array(); if (is_array($input)) { foreach ($input as $key => $value) { $cleaned[$key] = $this->sanitize_single_input($value); } } else { $cleaned = $this->sanitize_single_input($input); } return $cleaned; } private function sanitize_single_input($value) { // 移除危险标签和属性 $value = wp_kses_post($value); // 限制长度 $value = substr($value, 0, 1000); // 编码特殊字符 $value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8'); return $value; } public function validate_question($question) { // 检查问题是否包含恶意内容 $malicious_patterns = array( '/<script/i', '/javascript:/i', '/onload=/i', '/onerror=/i', '/eval(/i', '/union.*select/i', '/sleep(/i' ); foreach ($malicious_patterns as $pattern) { if (preg_match($pattern, $question)) { return false; } } // 检查问题长度 if (strlen($question) < 2 || strlen($question) > 500) { return false; } return true; } public function anonymize_user_data($user_data) { // GDPR合规:匿名化用户数据 $anonymized = array(); foreach ($user_data as $key => $value) { if (in_array($key, array('ip_address', 'user_agent', 'session_id'))) { $anonymized[$key] = $this->hash_sensitive_data($value); } else { $anonymized[$key] = $value; } } return $anonymized; } private function hash_sensitive_data($data) { // 使用盐值哈希敏感数据 $salt = defined('NONCE_SALT') ? NONCE_SALT : 'default_salt'; return hash('sha256', $data . $salt); } } // 实施速率限制防止滥用add_filter('rest_pre_dispatch', 'qa_rate_limit', 10, 3);function qa_rate_limit($result, $server, $request) { if (strpos($request->get_route(), '/smart-qa/v1/') === 0) { $ip = $_SERVER['REMOTE_ADDR']; $transient_key = 'qa_rate_limit_' . md5($ip); $requests = get_transient($transient_key); if (!$requests) { $requests = array(); } $current_time = time(); $requests[] = $current_time; // 保留最近1分钟的请求 $requests = array_filter($requests, function($time) use ($current_time) { return $current_time - $time < 60; }); // 限制每分钟最多30个请求 if (count($requests) > 30) { return new WP_Error( 'rate_limit_exceeded', '请求过于频繁,请稍后再试。', array('status' => 429) ); } set_transient($transient_key, $requests, 60); } return $result; } ### 第六章:部署、监控与维护 #### 6.1 系统部署与配置 创建一键部署脚本和配置管理: class QA_Deployment_Manager { public function run_installation() { // 创建必要的数据库表 $this->create_database_tables(); // 创建默认设置 $this->create_default_settings(); // 导入初始知识库 $this->import_initial_knowledge_base(); // 设置定时任务 $this->setup_cron_jobs(); return true; } private function create_database_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // 创建对话记录表 $table_name = $wpdb->prefix . 'qa_conversations'; $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id bigint(20) NOT NULL AUTO_INCREMENT, session_id varchar(64) NOT NULL, user_id bigint(20) DEFAULT 0, question text NOT NULL, answer text NOT NULL, intent varchar(100) DEFAULT '', confidence float DEFAULT 0, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY session_id (session_id), KEY user_id (user_id), KEY created_at (created_at) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); // 创建知识图谱关系表 $table_name = $wpdb->prefix . 'knowledge_relations'; $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id bigint(20) NOT NULL AUTO_INCREMENT, source_id bigint(20) NOT NULL, target_id bigint(20) NOT NULL, relation_type varchar(100) NOT NULL, weight float DEFAULT 1.0, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY source_id (source_id), KEY target_id (target_id), KEY relation_type (relation_type), UNIQUE KEY unique_relation (source_id, target_id, relation_type) ) $charset_collate;"; dbDelta($sql); } private function setup_cron_jobs() { // 每天清理旧对话记录 if (!wp_next_scheduled('qa_daily_cleanup')) { wp_schedule_event(time(), 'daily', 'qa_daily_cleanup'); } // 每小时更新知识图谱索引 if (!wp_next_scheduled('qa_hourly_index_update')) { wp_schedule_event(time(), 'hourly', 'qa_hourly_index_update'); } // 每周生成使用报告 if (!wp_next_scheduled('qa_weekly_report')) { wp_schedule_event(time(), 'weekly', 'qa_weekly_report'); } // 添加清理任务 add_action('qa_daily_cleanup', array($this, 'cleanup_old_conversations')); add_action('qa_hourly_index_update', array($this, 'update_knowledge_index')); add_action('qa_weekly_report', array($this, 'generate_usage_report')); } public function cleanup_old_conversations() { global $wpdb; $table_name = $wpdb->prefix . 'qa_conversations'; // 删除30天前的对话记录 $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 ) ); } } #### 6.2 监控与日志系统 实现全面的监控和日志记录: class QA_Monitoring_System { private $log_table; public function __construct() { global $wpdb; $this->log_table = $wpdb->prefix . 'qa_system_logs'; } public function log_event($event_type, $message, $data = array(), $level = 'info') { global $wpdb; $log_entry = array( 'event_type' => $event_type, 'message' => substr($message, 0,

发表评论

一步步教你,为WordPress集成多源比价与优惠券聚合展示功能

一步步教你,为WordPress集成多源比价与优惠券聚合展示功能 引言:为什么WordPress需要比价与优惠券功能? 在当今电子商务蓬勃发展的时代,价格比较和优惠券获取已成为消费者购物决策的重要环节。对于内容创作者、博主和电商网站运营者而言,在WordPress网站上集成多源比价与优惠券聚合展示功能,不仅能显著提升用户体验,还能通过联盟营销等方式创造额外收入。 传统的WordPress网站通常只展示单一商品信息或有限的优惠内容,而通过代码二次开发实现多源比价与优惠券聚合功能,可以让您的网站成为用户购物的第一站。本文将详细指导您如何通过WordPress程序代码二次开发,实现这一实用功能,无需依赖昂贵的第三方插件,完全自主控制数据展示和更新逻辑。 第一部分:准备工作与环境搭建 1.1 开发环境要求 在开始开发之前,请确保您的环境满足以下要求: WordPress 5.0及以上版本 PHP 7.4及以上版本(推荐PHP 8.0+) MySQL 5.6及以上或MariaDB 10.1及以上 至少512MB内存(推荐1GB以上) 支持cURL扩展的PHP环境 一个代码编辑器(如VS Code、Sublime Text或PHPStorm) 1.2 创建子主题保护核心文件 为了避免主题更新导致自定义代码丢失,我们首先创建一个子主题: 在WordPress的wp-content/themes/目录下创建新文件夹,命名为my-custom-theme 在该文件夹中创建style.css文件,添加以下内容: /* Theme Name: My Custom Theme Template: your-parent-theme-folder-name Version: 1.0.0 Description: 子主题用于添加比价和优惠券功能 */ 创建functions.php文件,初始内容可以为空 在WordPress后台启用这个子主题 1.3 安全备份策略 在进行任何代码修改前,请确保: 完整备份网站文件和数据库 使用版本控制系统(如Git)跟踪代码变更 在本地或 staging 环境测试后再部署到生产环境 第二部分:设计数据库结构 2.1 创建自定义数据表 我们需要创建几个自定义数据表来存储比价和优惠券信息。在子主题的functions.php中添加以下代码: // 创建自定义数据表 function create_price_comparison_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // 商品信息表 $products_table = $wpdb->prefix . 'price_comparison_products'; $products_sql = "CREATE TABLE IF NOT EXISTS $products_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, product_name varchar(255) NOT NULL, description text, category varchar(100), image_url varchar(500), created_at datetime DEFAULT CURRENT_TIMESTAMP, updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) $charset_collate;"; // 价格信息表 $prices_table = $wpdb->prefix . 'price_comparison_prices'; $prices_sql = "CREATE TABLE IF NOT EXISTS $prices_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, product_id mediumint(9) NOT NULL, merchant_name varchar(100) NOT NULL, price decimal(10,2) NOT NULL, original_price decimal(10,2), currency varchar(10) DEFAULT 'CNY', product_url varchar(500), availability tinyint(1) DEFAULT 1, last_updated datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY product_id (product_id), KEY merchant_name (merchant_name) ) $charset_collate;"; // 优惠券信息表 $coupons_table = $wpdb->prefix . 'price_comparison_coupons'; $coupons_sql = "CREATE TABLE IF NOT EXISTS $coupons_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, merchant_name varchar(100) NOT NULL, coupon_code varchar(100), coupon_description text, discount_value varchar(100), discount_type varchar(50), expiry_date date, terms_conditions text, product_category varchar(100), is_active tinyint(1) DEFAULT 1, click_count mediumint(9) DEFAULT 0, created_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY merchant_name (merchant_name), KEY expiry_date (expiry_date), KEY is_active (is_active) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($products_sql); dbDelta($prices_sql); dbDelta($coupons_sql); } // 在插件激活时创建表 register_activation_hook(__FILE__, 'create_price_comparison_tables'); 2.2 添加数据库更新钩子 为了确保数据库表在主题激活时创建,添加以下代码: // 主题激活时创建表 add_action('after_switch_theme', 'create_price_comparison_tables'); // 添加数据库版本控制 $price_comparison_db_version = '1.0'; function update_price_comparison_tables() { global $price_comparison_db_version; $installed_version = get_option('price_comparison_db_version'); if ($installed_version != $price_comparison_db_version) { create_price_comparison_tables(); update_option('price_comparison_db_version', $price_comparison_db_version); } } add_action('init', 'update_price_comparison_tables'); 第三部分:构建数据采集系统 3.1 设计API接口类 我们需要创建一个类来处理与各大电商平台API的交互。在子主题目录下创建includes/class-price-api-manager.php: <?php if (!defined('ABSPATH')) { exit; } class Price_API_Manager { private $api_keys = array(); public function __construct() { // 从WordPress选项获取API密钥 $this->api_keys = get_option('price_comparison_api_keys', array()); } /** * 从京东获取商品信息 */ public function fetch_jd_product($keyword, $category = '') { $api_url = 'https://api.jd.com/routerjson'; $params = array( 'method' => 'jingdong.ware.search', 'app_key' => isset($this->api_keys['jd_app_key']) ? $this->api_keys['jd_app_key'] : '', 'timestamp' => date('Y-m-d H:i:s'), 'format' => 'json', 'v' => '2.0', 'sign_method' => 'md5', 'param_json' => json_encode(array( 'keyword' => $keyword, 'catId' => $category, 'page' => 1, 'pageSize' => 20 )) ); // 生成签名(简化版,实际需要按照京东API文档实现) $params['sign'] = $this->generate_jd_signature($params); $response = wp_remote_get($api_url . '?' . http_build_query($params)); if (is_wp_error($response)) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); return $this->parse_jd_response($data); } /** * 从淘宝/天猫获取商品信息 */ public function fetch_taobao_product($keyword, $category = '') { // 使用淘宝开放平台API // 注意:需要申请正式API权限 $api_url = 'https://eco.taobao.com/router/rest'; $params = array( 'method' => 'taobao.tbk.item.get', 'app_key' => isset($this->api_keys['taobao_app_key']) ? $this->api_keys['taobao_app_key'] : '', 'timestamp' => date('Y-m-d H:i:s'), 'format' => 'json', 'v' => '2.0', 'sign_method' => 'md5', 'q' => $keyword, 'cat' => $category, 'page_no' => 1, 'page_size' => 20 ); $params['sign'] = $this->generate_taobao_signature($params); $response = wp_remote_get($api_url . '?' . http_build_query($params)); if (is_wp_error($response)) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); return $this->parse_taobao_response($data); } /** * 从拼多多获取商品信息 */ public function fetch_pdd_product($keyword) { // 拼多多联盟API $api_url = 'https://gw-api.pinduoduo.com/api/router'; $params = array( 'type' => 'pdd.ddk.goods.search', 'client_id' => isset($this->api_keys['pdd_client_id']) ? $this->api_keys['pdd_client_id'] : '', 'timestamp' => time(), 'data_type' => 'JSON', 'keyword' => $keyword, 'page' => 1, 'page_size' => 20 ); $params['sign'] = $this->generate_pdd_signature($params); $response = wp_remote_post($api_url, array( 'body' => json_encode($params), 'headers' => array('Content-Type' => 'application/json') )); if (is_wp_error($response)) { return false; } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); return $this->parse_pdd_response($data); } /** * 获取优惠券信息 */ public function fetch_coupons($merchant = '', $category = '') { $coupons = array(); // 这里可以集成多个优惠券API // 例如:淘宝客优惠券、京东优惠券等 // 示例:从本地数据库获取优惠券 global $wpdb; $table_name = $wpdb->prefix . 'price_comparison_coupons'; $query = "SELECT * FROM $table_name WHERE is_active = 1"; if (!empty($merchant)) { $query .= $wpdb->prepare(" AND merchant_name = %s", $merchant); } if (!empty($category)) { $query .= $wpdb->prepare(" AND product_category = %s", $category); } $query .= " AND (expiry_date IS NULL OR expiry_date >= CURDATE()) ORDER BY created_at DESC LIMIT 50"; $coupons = $wpdb->get_results($query, ARRAY_A); return $coupons; } // 以下为辅助方法,实际开发中需要完整实现 private function generate_jd_signature($params) { // 实现京东签名算法 return md5(implode('', $params) . (isset($this->api_keys['jd_app_secret']) ? $this->api_keys['jd_app_secret'] : '')); } private function generate_taobao_signature($params) { // 实现淘宝签名算法 return md5(implode('', $params) . (isset($this->api_keys['taobao_app_secret']) ? $this->api_keys['taobao_app_secret'] : '')); } private function generate_pdd_signature($params) { // 实现拼多多签名算法 return md5(implode('', $params) . (isset($this->api_keys['pdd_client_secret']) ? $this->api_keys['pdd_client_secret'] : '')); } private function parse_jd_response($data) { // 解析京东API响应 if (!isset($data['code']) || $data['code'] != '0') { return array(); } $products = array(); // 解析逻辑... return $products; } private function parse_taobao_response($data) { // 解析淘宝API响应 if (!isset($data['error_response'])) { return array(); } $products = array(); // 解析逻辑... return $products; } private function parse_pdd_response($data) { // 解析拼多多API响应 if (!isset($data['error_response'])) { return array(); } $products = array(); // 解析逻辑... return $products; } } ?> 3.2 创建数据采集调度器 为了定期更新价格和优惠券信息,我们需要创建一个调度器。创建includes/class-data-collector.php: <?php if (!defined('ABSPATH')) { exit; } class Price_Data_Collector { private $api_manager; public function __construct() { $this->api_manager = new Price_API_Manager(); // 设置定时任务 add_action('price_comparison_daily_cron', array($this, 'daily_data_collection')); add_action('price_comparison_hourly_cron', array($this, 'hourly_price_check')); // 初始化定时任务 $this->schedule_cron_jobs(); } /** * 安排定时任务 */ private function schedule_cron_jobs() { if (!wp_next_scheduled('price_comparison_daily_cron')) { wp_schedule_event(time(), 'daily', 'price_comparison_daily_cron'); } if (!wp_next_scheduled('price_comparison_hourly_cron')) { wp_schedule_event(time(), 'hourly', 'price_comparison_hourly_cron'); } } /** * 每日数据收集 */ public function daily_data_collection() { global $wpdb; // 收集热门商品的价格信息 $popular_products = $this->get_popular_products(); foreach ($popular_products as $product) { $this->update_product_prices($product['product_name'], $product['category']); } // 更新优惠券信息 $this->update_coupon_data(); // 清理过期数据 $this->cleanup_old_data(); } /** * 每小时价格检查 */ public function hourly_price_check() { // 检查价格变动,特别是促销商品 $promotional_products = $this->get_promotional_products(); foreach ($promotional_products as $product) { $this->check_price_changes($product['id']); } } /** * 更新商品价格 */ private function update_product_prices($product_name, $category = '') { // 从多个平台获取价格 $jd_prices = $this->api_manager->fetch_jd_product($product_name, $category); $taobao_prices = $this->api_manager->fetch_taobao_product($product_name, $category); $pdd_prices = $this->api_manager->fetch_pdd_product($product_name); // 合并价格数据 $all_prices = array_merge( $this->format_prices($jd_prices, '京东'), $this->format_prices($taobao_prices, '淘宝'), $this->format_prices($pdd_prices, '拼多多') ); // 保存到数据库 $this->save_prices_to_db($product_name, $all_prices); } /** * 更新优惠券数据 */ private function update_coupon_data() { // 从各平台API获取最新优惠券 $coupons = $this->api_manager->fetch_coupons(); // 保存到数据库 $this->save_coupons_to_db($coupons); } /** * 获取热门商品 */ private function get_popular_products() { global $wpdb; $products_table = $wpdb->prefix . 'price_comparison_products'; // 获取最近30天有搜索或点击的商品 $query = "SELECT * FROM $products_table WHERE updated_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) ORDER BY (SELECT COUNT(*) FROM {$wpdb->prefix}price_comparison_prices WHERE product_id = $products_table.id) DESC LIMIT 50"; return $wpdb->get_results($query, ARRAY_A); } /** * 获取促销商品 */ private function get_promotional_products() { global $wpdb; $prices_table = $wpdb->prefix . 'price_comparison_prices'; // 获取最近有价格变动的商品 $query = "SELECT DISTINCT product_id as id FROM $prices_table WHERE last_updated >= DATE_SUB(NOW(), INTERVAL 7 DAY) AND original_price > price LIMIT 20"; return $wpdb->get_results($query, ARRAY_A); } /** * 检查价格变动 */ private function check_price_changes($product_id) { global $wpdb; $prices_table = $wpdb->prefix . 'price_comparison_prices'; // 获取商品当前价格 $current_prices = $wpdb->get_results( $wpdb->prepare("SELECT * FROM $prices_table WHERE product_id = %d", $product_id), A ); // 重新从API获取最新价格 $product_info = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}price_comparison_products WHERE id = %d", $product_id), ARRAY_A ); if ($product_info) { $new_prices = $this->api_manager->fetch_jd_product($product_info['product_name'], $product_info['category']); // 比较价格变动 foreach ($current_prices as $current) { foreach ($new_prices as $new) { if ($current['merchant_name'] == $new['merchant']) { if ($current['price'] != $new['price']) { // 价格变动,记录并可能发送通知 $this->record_price_change($product_id, $current, $new); } } } } } } /** * 格式化价格数据 */ private function format_prices($prices, $merchant) { $formatted = array(); if (is_array($prices)) { foreach ($prices as $price) { $formatted[] = array( 'merchant' => $merchant, 'price' => $price['price'] ?? 0, 'original_price' => $price['original_price'] ?? $price['price'], 'product_url' => $price['url'] ?? '', 'availability' => $price['availability'] ?? 1 ); } } return $formatted; } /** * 保存价格到数据库 */ private function save_prices_to_db($product_name, $prices) { global $wpdb; $products_table = $wpdb->prefix . 'price_comparison_products'; $prices_table = $wpdb->prefix . 'price_comparison_prices'; // 查找或创建商品记录 $product = $wpdb->get_row( $wpdb->prepare("SELECT id FROM $products_table WHERE product_name = %s", $product_name), ARRAY_A ); if (!$product) { $wpdb->insert($products_table, array( 'product_name' => $product_name, 'created_at' => current_time('mysql') )); $product_id = $wpdb->insert_id; } else { $product_id = $product['id']; } // 保存价格信息 foreach ($prices as $price) { $existing = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM $prices_table WHERE product_id = %d AND merchant_name = %s", $product_id, $price['merchant'] ), ARRAY_A ); if ($existing) { // 更新现有记录 $wpdb->update($prices_table, array( 'price' => $price['price'], 'original_price' => $price['original_price'], 'product_url' => $price['product_url'], 'availability' => $price['availability'], 'last_updated' => current_time('mysql') ), array('id' => $existing['id'])); } else { // 插入新记录 $wpdb->insert($prices_table, array( 'product_id' => $product_id, 'merchant_name' => $price['merchant'], 'price' => $price['price'], 'original_price' => $price['original_price'], 'product_url' => $price['product_url'], 'availability' => $price['availability'], 'last_updated' => current_time('mysql') )); } } } /** * 保存优惠券到数据库 */ private function save_coupons_to_db($coupons) { global $wpdb; $coupons_table = $wpdb->prefix . 'price_comparison_coupons'; foreach ($coupons as $coupon) { // 检查是否已存在相同优惠券 $existing = $wpdb->get_row( $wpdb->prepare( "SELECT id FROM $coupons_table WHERE merchant_name = %s AND coupon_code = %s", $coupon['merchant_name'], $coupon['coupon_code'] ), ARRAY_A ); if (!$existing) { $wpdb->insert($coupons_table, $coupon); } } } /** * 记录价格变动 */ private function record_price_change($product_id, $old_price, $new_price) { global $wpdb; $changes_table = $wpdb->prefix . 'price_comparison_changes'; // 创建价格变动记录表(如果不存在) if ($wpdb->get_var("SHOW TABLES LIKE '$changes_table'") != $changes_table) { $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE $changes_table ( id mediumint(9) NOT NULL AUTO_INCREMENT, product_id mediumint(9) NOT NULL, merchant_name varchar(100) NOT NULL, old_price decimal(10,2) NOT NULL, new_price decimal(10,2) NOT NULL, change_percent decimal(5,2), change_type varchar(20), recorded_at datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } // 计算变动百分比 $change_percent = (($new_price['price'] - $old_price['price']) / $old_price['price']) * 100; $change_type = $new_price['price'] < $old_price['price'] ? '降价' : '涨价'; // 插入变动记录 $wpdb->insert($changes_table, array( 'product_id' => $product_id, 'merchant_name' => $old_price['merchant_name'], 'old_price' => $old_price['price'], 'new_price' => $new_price['price'], 'change_percent' => $change_percent, 'change_type' => $change_type, 'recorded_at' => current_time('mysql') )); // 可以在这里添加邮件通知或站内通知逻辑 $this->notify_price_change($product_id, $old_price, $new_price, $change_percent, $change_type); } /** * 通知价格变动 */ private function notify_price_change($product_id, $old_price, $new_price, $change_percent, $change_type) { // 获取关注此商品的用户 $followers = $this->get_product_followers($product_id); if (!empty($followers)) { $product_info = $wpdb->get_row( $wpdb->prepare("SELECT product_name FROM {$wpdb->prefix}price_comparison_products WHERE id = %d", $product_id), ARRAY_A ); foreach ($followers as $follower) { // 发送邮件通知 $subject = "价格变动通知:{$product_info['product_name']} {$change_type}了" . abs($change_percent) . "%"; $message = "商品:{$product_info['product_name']}n"; $message .= "商家:{$old_price['merchant_name']}n"; $message .= "原价:{$old_price['price']}元n"; $message .= "现价:{$new_price['price']}元n"; $message .= "变动幅度:{$change_percent}%n"; $message .= "查看详情:" . home_url("/product/{$product_id}"); wp_mail($follower['email'], $subject, $message); } } } /** * 清理旧数据 */ private function cleanup_old_data() { global $wpdb; $prices_table = $wpdb->prefix . 'price_comparison_prices'; $coupons_table = $wpdb->prefix . 'price_comparison_coupons'; // 删除30天前的价格记录(保留最新记录) $wpdb->query( "DELETE FROM $prices_table WHERE last_updated < DATE_SUB(NOW(), INTERVAL 30 DAY) AND id NOT IN ( SELECT id FROM ( SELECT MAX(id) as id FROM $prices_table GROUP BY product_id, merchant_name ) as latest )" ); // 标记过期优惠券为无效 $wpdb->query( "UPDATE $coupons_table SET is_active = 0 WHERE expiry_date < CURDATE() AND is_active = 1" ); } }?> ## 第四部分:创建管理界面 ### 4.1 添加WordPress管理菜单 在`functions.php`中添加管理菜单: // 添加管理菜单function price_comparison_admin_menu() { add_menu_page( '比价与优惠券管理', '比价管理', 'manage_options', 'price-comparison', 'price_comparison_admin_page', 'dashicons-chart-line', 30 ); add_submenu_page( 'price-comparison', '商品管理', '商品管理', 'manage_options', 'price-comparison-products', 'price_comparison_products_page' ); add_submenu_page( 'price-comparison', '价格监控', '价格监控', 'manage_options', 'price-comparison-monitor', 'price_comparison_monitor_page' ); add_submenu_page( 'price-comparison', '优惠券管理', '优惠券管理', 'manage_options', 'price-comparison-coupons', 'price_comparison_coupons_page' ); add_submenu_page( 'price-comparison', 'API设置', 'API设置', 'manage_options', 'price-comparison-settings', 'price_comparison_settings_page' ); }add_action('admin_menu', 'price_comparison_admin_menu'); // 主管理页面function price_comparison_admin_page() { ?> <div class="wrap"> <h1>比价与优惠券管理系统</h1> <div class="price-comparison-dashboard"> <div class="dashboard-card"> <h3>商品总数</h3> <p class="dashboard-number"><?php echo get_product_count(); ?></p> </div> <div class="dashboard-card"> <h3>有效优惠券</h3> <p class="dashboard-number"><?php echo get_active_coupon_count(); ?></p> </div> <div class="dashboard-card"> <h3>今日价格更新</h3> <p class="dashboard-number"><?php echo get_today_price_updates(); ?></p> </div> <div class="dashboard-card"> <h3>用户搜索量</h3> <p class="dashboard-number"><?php echo get_today_searches(); ?></p> </div> </div> <div class="recent-activity"> <h2>最近活动</h2> <?php display_recent_activity(); ?> </div> </div> <style> .price-comparison-dashboard { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin: 20px 0; } .dashboard-card { background: #fff; padding: 20px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); text-align: center; } .dashboard-number { font-size: 2em; font-weight: bold; color: #0073aa; margin: 10px 0; } </style> <?php } // 商品管理页面function price_comparison_products_page() { global $wpdb; // 处理表单提交 if (isset($_POST['add_product'])) { $product_name = sanitize_text_field($_POST['product_name']); $category = sanitize_text_field($_POST['category']); $wpdb->insert( $wpdb->prefix . 'price_comparison_products', array( 'product_name' => $product_name, 'category' => $category, 'created_at' => current_time('mysql') ) ); echo '<div class="notice notice-success"><p>商品添加成功!</p></div>'; } // 获取商品列表 $products = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}price_comparison_products ORDER BY created_at DESC LIMIT 50", ARRAY_A ); ?> <div class="wrap"> <h1>商品管理</h1> <div class="add-product-form"> <h2>添加新商品</h2> <form method="post"> <table class="form-table"> <tr> <th><label for="product_name">商品名称</label></th> <td><input type="text" id="product_name" name="product_name" class="regular-text" required></td> </tr> <tr> <th><label for="category">商品分类</label></th> <td> <input type="text" id="category" name="category" class="regular-text"> <p class="description">例如:手机、笔记本电脑、家电等</p> </td> </tr> </table> <?php submit_button('添加商品', 'primary', 'add_product'); ?> </form> </div> <div class="product-list"> <h2>商品列表</h2> <table class="wp-list-table widefat fixed striped"> <thead> <tr> <th>ID</th> <th>商品名称</th> <th>分类</th> <th>创建时间</th> <th>操作</th> </tr> </thead> <tbody> <?php foreach ($products as $product): ?> <tr> <td><?php echo $product['id']; ?></td> <td><?php echo esc_html($product['product_name']); ?></td> <td><?php echo esc_html($product['category']); ?></td> <td><?php echo $product['created_at']; ?></td> <td> <a href="<?php echo admin_url('admin.php?page=price-comparison-monitor&product_id=' . $product['id']); ?>">查看价格</a> | <a href="#" class="delete-product" data-id="<?php echo $product['id']; ?>">删除</a> </td> </tr> <?php endforeach; ?> </tbody> </table> </div> </div> <script> jQuery(document).ready(function($) { $('.delete-product').click(function(e) { e.preventDefault(); if (confirm('确定要删除这个商品吗?')) { var productId = $(this).data('id'); $.post(ajaxurl, { action: 'delete_product', product_id: productId, nonce: '<?php echo wp_create_nonce('delete_product_nonce'); ?>' }, function(response) { if (response.success) { location.reload(); } else { alert('删除失败:' + response.data); } }); } }); }); </script> <?php } // 价格监控页面function price_comparison_monitor_page() { global $wpdb; $product_id = isset($_GET['product_id']) ? intval($_GET['product_id']) : 0; if ($product_id) { // 显示单个商品的价格信息 $product = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}price_comparison_products WHERE id = %d", $product_id), ARRAY_A ); $prices = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}price_comparison_prices WHERE product_id = %d ORDER BY price ASC", $product_id ), ARRAY_A ); ?> <div class="wrap"> <h1>价格监控:<?php echo esc_html($product['product_name']); ?></h1> <div class="price-comparison-chart"> <canvas id="priceHistoryChart" width="800" height="400"></canvas> </div> <div class="current-prices"> <h2>当前价格对比</h2> <table class="wp-list-table widefat fixed striped"> <thead> <tr> <th>商家</th> <th>当前价格</th> <th>原价</th> <th>优惠幅度</th> <th>最后更新</th> <th>操作</th> </tr> </thead> <tbody> <?php foreach ($prices as $price): ?> <tr> <td><?php echo esc_html($price['merchant_name']); ?></td> <td><strong style="color: #d63638;"><?php echo $price['price']; ?>元</strong></td> <td><del><?php echo $price['original_price']; ?>元</del></td> <td> <?php if ($price['original_price'] > $price['price']): ?> <span class="discount-badge"> -<?php echo round((($price['original_price'] - $price['price']) /

发表评论

实战教程,开发网站内嵌的在线Logo设计与名片制作工具

实战教程:开发网站内嵌的在线Logo设计与名片制作工具 摘要 在当今数字化时代,企业网站的功能需求日益多样化。本文将详细介绍如何通过WordPress程序的代码二次开发,实现网站内嵌的在线Logo设计与名片制作工具。我们将从需求分析、技术选型、开发流程到最终部署,全面解析这一实用功能的实现过程,帮助网站管理员和技术开发者扩展网站功能,提升用户体验。 一、项目背景与需求分析 1.1 为什么需要内嵌设计工具 随着中小企业数字化转型加速,越来越多的企业主希望在自己的网站上直接完成品牌视觉元素的创建。传统方式下,用户需要离开网站使用第三方设计工具,这不仅打断了用户体验流程,还可能导致潜在客户的流失。 内嵌设计工具的优势: 提升用户停留时间:用户在网站内完成设计,增加互动时长 增强品牌专业性:展示网站功能的全面性 降低使用门槛:无需安装额外软件或注册新平台 数据自主控制:所有设计数据保留在自有服务器 1.2 功能需求定义 基于实际应用场景,我们需要实现以下核心功能: Logo设计工具需求: 基础形状库(圆形、方形、三角形等几何图形) 图标库(分类图标资源) 文本编辑功能(字体、大小、颜色、间距调整) 图层管理系统 颜色选择器与调色板 导出功能(支持PNG、SVG格式) 名片制作工具需求: 标准名片尺寸模板(90×54mm) 企业信息字段(姓名、职位、联系方式等) 双面设计支持 印刷出血线提示 多种设计风格模板 导出打印质量PDF 通用需求: 响应式设计,适配各种设备 用户作品保存与加载 设计分享功能 水印添加选项(免费版) 二、技术架构与开发环境 2.1 技术选型 前端技术栈: HTML5 Canvas:核心绘图引擎 Fabric.js:强大的Canvas操作库 React/Vue.js:可选前端框架(本教程使用原生JavaScript) Bootstrap 5:响应式UI框架 Coloris:轻量级颜色选择器 FileSaver.js:客户端文件保存 后端技术栈: WordPress核心:内容管理系统 PHP 7.4+:服务器端逻辑 MySQL 5.7+:数据存储 WordPress REST API:前后端通信 GD库/ImageMagick:图像处理 2.2 开发环境搭建 本地WordPress安装 # 使用Docker快速搭建WordPress环境 docker-compose up -d 开发工具准备 代码编辑器:VS Code 浏览器开发者工具 Git版本控制 本地测试服务器 WordPress主题结构 /wp-content/themes/your-theme/ ├── assets/ │ ├── css/ │ ├── js/ │ └── images/ ├── template-parts/ ├── functions.php └── style.css 三、Logo设计工具开发实战 3.1 基础HTML结构 首先创建Logo设计工具的前端界面: <!-- logo-designer.php --> <div class="logo-designer-container"> <div class="designer-header"> <h2>在线Logo设计工具</h2> <div class="toolbar"> <button id="save-logo" class="btn btn-primary">保存设计</button> <button id="export-png" class="btn btn-success">导出PNG</button> <button id="export-svg" class="btn btn-info">导出SVG</button> </div> </div> <div class="designer-body"> <!-- 左侧工具栏 --> <div class="sidebar-left"> <div class="tool-section"> <h4>形状</h4> <div class="shape-list"> <button class="shape-btn" data-type="rectangle">矩形</button> <button class="shape-btn" data-type="circle">圆形</button> <button class="shape-btn" data-type="triangle">三角形</button> </div> </div> <div class="tool-section"> <h4>图标库</h4> <div class="icon-grid" id="icon-library"> <!-- 图标将通过AJAX加载 --> </div> </div> </div> <!-- 中央画布区域 --> <div class="canvas-area"> <canvas id="logo-canvas" width="800" height="600"></canvas> <div class="canvas-controls"> <button id="clear-canvas">清空画布</button> <button id="undo-action">撤销</button> <button id="redo-action">重做</button> </div> </div> <!-- 右侧属性面板 --> <div class="sidebar-right"> <div class="properties-panel"> <h4>属性设置</h4> <div class="property-group"> <label>填充颜色</label> <input type="color" id="fill-color" value="#3498db"> </div> <div class="property-group"> <label>边框颜色</label> <input type="color" id="stroke-color" value="#2c3e50"> </div> <div class="property-group"> <label>字体</label> <select id="font-family"> <option value="Arial">Arial</option> <option value="Helvetica">Helvetica</option> <option value="Times New Roman">Times New Roman</option> </select> </div> </div> <div class="layers-panel"> <h4>图层管理</h4> <ul id="layers-list"> <!-- 图层列表动态生成 --> </ul> </div> </div> </div> </div> 3.2 Canvas初始化与Fabric.js集成 // assets/js/logo-designer.js document.addEventListener('DOMContentLoaded', function() { // 初始化Canvas const canvas = new fabric.Canvas('logo-canvas', { backgroundColor: '#ffffff', preserveObjectStacking: true }); // 设置画布尺寸 canvas.setDimensions({ width: 800, height: 600 }); // 形状添加功能 document.querySelectorAll('.shape-btn').forEach(button => { button.addEventListener('click', function() { const shapeType = this.getAttribute('data-type'); addShapeToCanvas(shapeType); }); }); // 添加形状函数 function addShapeToCanvas(type) { let shape; const fillColor = document.getElementById('fill-color').value; const strokeColor = document.getElementById('stroke-color').value; switch(type) { case 'rectangle': shape = new fabric.Rect({ left: 100, top: 100, fill: fillColor, width: 100, height: 100, stroke: strokeColor, strokeWidth: 2 }); break; case 'circle': shape = new fabric.Circle({ left: 100, top: 100, radius: 50, fill: fillColor, stroke: strokeColor, strokeWidth: 2 }); break; case 'triangle': shape = new fabric.Triangle({ left: 100, top: 100, fill: fillColor, width: 100, height: 100, stroke: strokeColor, strokeWidth: 2 }); break; } if(shape) { canvas.add(shape); updateLayersList(); } } // 文本添加功能 document.getElementById('add-text').addEventListener('click', function() { const text = new fabric.IText('编辑文本', { left: 150, top: 150, fontFamily: document.getElementById('font-family').value, fill: document.getElementById('fill-color').value, fontSize: 40 }); canvas.add(text); canvas.setActiveObject(text); updateLayersList(); }); // 更新图层列表 function updateLayersList() { const layersList = document.getElementById('layers-list'); layersList.innerHTML = ''; canvas.getObjects().forEach((obj, index) => { const li = document.createElement('li'); li.className = 'layer-item'; li.innerHTML = ` <span>${obj.type || '对象'}</span> <div class="layer-actions"> <button class="btn-layer-up" data-index="${index}">上移</button> <button class="btn-layer-down" data-index="${index}">下移</button> <button class="btn-layer-delete" data-index="${index}">删除</button> </div> `; layersList.appendChild(li); }); } // 导出PNG功能 document.getElementById('export-png').addEventListener('click', function() { const dataURL = canvas.toDataURL({ format: 'png', quality: 1 }); const link = document.createElement('a'); link.download = 'logo-design.png'; link.href = dataURL; link.click(); }); // 初始化图标库 loadIconLibrary(); function loadIconLibrary() { // 通过AJAX从服务器获取图标 fetch('/wp-json/logo-designer/v1/icons') .then(response => response.json()) .then(icons => { const iconGrid = document.getElementById('icon-library'); icons.forEach(icon => { const iconElement = document.createElement('div'); iconElement.className = 'icon-item'; iconElement.innerHTML = `<i class="${icon.class}"></i>`; iconElement.addEventListener('click', () => addIconToCanvas(icon)); iconGrid.appendChild(iconElement); }); }); } function addIconToCanvas(icon) { // 添加图标到画布的逻辑 // 这里可以使用fabric.Path或fabric.Group来处理SVG图标 } }); 3.3 后端API开发 <?php // functions.php - WordPress主题函数文件 // 注册REST API端点 add_action('rest_api_init', function() { // 获取图标库 register_rest_route('logo-designer/v1', '/icons', array( 'methods' => 'GET', 'callback' => 'get_icon_library', 'permission_callback' => '__return_true' )); // 保存设计 register_rest_route('logo-designer/v1', '/save', array( 'methods' => 'POST', 'callback' => 'save_logo_design', 'permission_callback' => function() { return current_user_can('edit_posts'); } )); }); // 获取图标库函数 function get_icon_library() { $icons = array( array('id' => 1, 'name' => '星星', 'class' => 'fas fa-star'), array('id' => 2, 'name' => '爱心', 'class' => 'fas fa-heart'), array('id' => 3, 'name' => '云朵', 'class' => 'fas fa-cloud'), array('id' => 4, 'name' => '树叶', 'class' => 'fas fa-leaf'), array('id' => 5, 'name' => '火焰', 'class' => 'fas fa-fire'), array('id' => 6, 'name' => '水滴', 'class' => 'fas fa-tint'), ); return rest_ensure_response($icons); } // 保存设计函数 function save_logo_design($request) { $user_id = get_current_user_id(); $design_data = $request->get_param('design_data'); $design_name = $request->get_param('design_name'); if(empty($design_data) || empty($design_name)) { return new WP_Error('invalid_data', '设计数据或名称为空', array('status' => 400)); } // 保存到用户meta $saved_designs = get_user_meta($user_id, 'logo_designs', true); if(empty($saved_designs)) { $saved_designs = array(); } $new_design = array( 'id' => uniqid(), 'name' => sanitize_text_field($design_name), 'data' => $design_data, 'created_at' => current_time('mysql'), 'updated_at' => current_time('mysql') ); $saved_designs[] = $new_design; update_user_meta($user_id, 'logo_designs', $saved_designs); return rest_ensure_response(array( 'success' => true, 'message' => '设计已保存', 'design_id' => $new_design['id'] )); } // 添加快捷码支持 add_shortcode('logo_designer', 'render_logo_designer'); function render_logo_designer($atts) { ob_start(); include get_template_directory() . '/template-parts/logo-designer.php'; return ob_get_clean(); } ?> 四、名片制作工具开发实战 4.1 名片设计器界面 <!-- business-card-designer.php --> <div class="card-designer-container"> <div class="designer-header"> <h2>在线名片设计工具</h2> <div class="template-selector"> <h4>选择模板</h4> <div class="template-grid"> <div class="template-item" data-template="modern"> <div class="template-preview modern-template"></div> <span>现代风格</span> </div> <div class="template-item" data-template="classic"> <div class="template-preview classic-template"></div> <span>经典风格</span> </div> <div class="template-item" data-template="minimal"> <div class="template-preview minimal-template"></div> <span>极简风格</span> </div> </div> </div> </div> <div class="card-design-area"> <!-- 正面设计 --> <div class="card-side front-side"> <div class="card-canvas-container"> <canvas id="card-front-canvas" width="1050" height="600"></canvas> <div class="bleed-marks"></div> </div> <div class="side-label">正面</div> </div> <!-- 背面设计 --> <div class="card-side back-side"> <div class="card-canvas-container"> <canvas id="card-back-canvas" width="1050" height="600"></canvas> <div class="bleed-marks"></div> </div> <div class="side-label">背面</div> </div> </div> <div class="card-controls"> <div class="info-fields"> <h4>企业信息</h4> <div class="field-group"> <input type="text" id="company-name" placeholder="公司名称"> <input type="text" id="person-name" placeholder="姓名"> <input type="text" id="person-title" placeholder="职位"> </div> <div class="field-group"> <input type="tel" id="phone-number" placeholder="电话"> <input type="email" id="email-address" placeholder="邮箱"> <input type="text" id="website-url" placeholder="网址"> </div> <div class="field-group"> <textarea id="company-address" placeholder="公司地址"></textarea> </div> <button id="apply-info" class="btn btn-primary">应用到名片</button> </div> <div class="export-options"> <h4>导出选项</h4> <button id="export-card-pdf" class="btn btn-success">导出打印PDF</button> <button id="export-card-image" class="btn btn-info">导出图片</button> <div class="quality-options"> <label>打印质量:</label> <select id="print-quality"> <option value="standard">标准(300DPI)</option> <option value="high">高质量(600DPI)</option> </select> </div> </div> </div> </div> 4.2 名片设计逻辑实现 // assets/js/business-card-designer.js class BusinessCardDesigner { constructor() { this.frontCanvas = new fabric.Canvas('card-front-canvas'); this.backCanvas = new fabric.Canvas('card-back-canvas'); this.currentTemplate = 'modern'; this.cardSize = { width: 90, height: 54 }; // 毫米 this.bleedSize = 3; // 出血尺寸 this.init(); } init() { this.setupCanvas(); this.bindEvents(); this.loadTemplate(this.currentTemplate); } setupCanvas() { // 设置画布尺寸(考虑出血) const scale = 10; // 放大10倍以便设计 const bleedPx = this.bleedSize * scale; this.frontCanvas.setDimensions({ width: (this.cardSize.width + this.bleedSize * 2) * scale, height: (this.cardSize.height + this.bleedSize * 2) * scale }); this.backCanvas.setDimensions({ width: (this.cardSize.width + this.bleedSize * 2) * scale, }); // 添加出血线标记 this.addBleedMarks(this.frontCanvas); this.addBleedMarks(this.backCanvas); } addBleedMarks(canvas) { const width = canvas.width; const height = canvas.height; const bleedPx = this.bleedSize * 10; // 创建出血线 const bleedLines = new fabric.Group([ // 外框线(出血边界) new fabric.Rect({ left: 0, top: 0, width: width, height: height, fill: 'transparent', stroke: '#ff0000', strokeWidth: 1, strokeDashArray: [5, 5] }), // 内框线(裁切边界) new fabric.Rect({ left: bleedPx, top: bleedPx, width: width - bleedPx * 2, height: height - bleedPx * 2, fill: 'transparent', stroke: '#0000ff', strokeWidth: 1 }) ], { selectable: false, evented: false }); canvas.add(bleedLines); canvas.sendToBack(bleedLines); } bindEvents() { // 模板选择 document.querySelectorAll('.template-item').forEach(item => { item.addEventListener('click', () => { const template = item.getAttribute('data-template'); this.loadTemplate(template); }); }); // 应用企业信息 document.getElementById('apply-info').addEventListener('click', () => { this.applyBusinessInfo(); }); // 导出PDF document.getElementById('export-card-pdf').addEventListener('click', () => { this.exportToPDF(); }); // 导出图片 document.getElementById('export-card-image').addEventListener('click', () => { this.exportToImage(); }); } loadTemplate(templateName) { this.currentTemplate = templateName; // 清空画布但保留出血线 const frontObjects = this.frontCanvas.getObjects(); const backObjects = this.backCanvas.getObjects(); frontObjects.forEach(obj => { if (!obj.isType('group') || !obj.getObjects().some(o => o.stroke === '#ff0000')) { this.frontCanvas.remove(obj); } }); backObjects.forEach(obj => { if (!obj.isType('group') || !obj.getObjects().some(o => o.stroke === '#ff0000')) { this.backCanvas.remove(obj); } }); // 加载模板 switch(templateName) { case 'modern': this.loadModernTemplate(); break; case 'classic': this.loadClassicTemplate(); break; case 'minimal': this.loadMinimalTemplate(); break; } } loadModernTemplate() { // 正面设计 const frontBackground = new fabric.Rect({ left: this.bleedSize * 10, top: this.bleedSize * 10, width: this.cardSize.width * 10, height: this.cardSize.height * 10, fill: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', selectable: false }); const companyName = new fabric.Text('公司名称', { left: 150, top: 100, fontSize: 32, fontFamily: 'Arial', fill: '#ffffff', fontWeight: 'bold' }); this.frontCanvas.add(frontBackground, companyName); // 背面设计 const backBackground = new fabric.Rect({ left: this.bleedSize * 10, top: this.bleedSize * 10, width: this.cardSize.width * 10, height: this.cardSize.height * 10, fill: '#f8f9fa', selectable: false }); const contactInfo = new fabric.Text('联系方式', { left: 150, top: 150, fontSize: 24, fontFamily: 'Arial', fill: '#333333' }); this.backCanvas.add(backBackground, contactInfo); } applyBusinessInfo() { const companyName = document.getElementById('company-name').value; const personName = document.getElementById('person-name').value; const personTitle = document.getElementById('person-title').value; const phoneNumber = document.getElementById('phone-number').value; const emailAddress = document.getElementById('email-address').value; const websiteUrl = document.getElementById('website-url').value; const companyAddress = document.getElementById('company-address').value; // 更新正面信息 this.updateFrontCard(companyName, personName, personTitle); // 更新背面信息 this.updateBackCard(phoneNumber, emailAddress, websiteUrl, companyAddress); } updateFrontCard(company, name, title) { // 查找并更新正面文本元素 const frontObjects = this.frontCanvas.getObjects(); frontObjects.forEach(obj => { if (obj.isType('text')) { if (obj.text === '公司名称') { obj.set('text', company || '公司名称'); } } }); // 添加姓名和职位 const nameText = new fabric.Text(name || '姓名', { left: 150, top: 200, fontSize: 24, fontFamily: 'Arial', fill: '#ffffff' }); const titleText = new fabric.Text(title || '职位', { left: 150, top: 240, fontSize: 18, fontFamily: 'Arial', fill: '#ffffff', fontStyle: 'italic' }); this.frontCanvas.add(nameText, titleText); this.frontCanvas.renderAll(); } updateBackCard(phone, email, website, address) { // 更新背面联系方式 const contactText = `电话: ${phone || '未填写'}n邮箱: ${email || '未填写'}n网址: ${website || '未填写'}n地址: ${address || '未填写'}`; const backObjects = this.backCanvas.getObjects(); backObjects.forEach(obj => { if (obj.isType('text') && obj.text === '联系方式') { obj.set('text', contactText); } }); this.backCanvas.renderAll(); } async exportToPDF() { // 创建PDF文档 const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'landscape', unit: 'mm', format: 'a4' }); // 获取画布数据 const frontDataURL = this.frontCanvas.toDataURL({ format: 'jpeg', quality: 1 }); const backDataURL = this.backCanvas.toDataURL({ format: 'jpeg', quality: 1 }); // 添加正面到PDF pdf.addImage(frontDataURL, 'JPEG', 10, 10, this.cardSize.width, this.cardSize.height); // 添加背面到PDF(同一页面右侧) pdf.addImage(backDataURL, 'JPEG', 110, 10, this.cardSize.width, this.cardSize.height); // 添加裁切标记 this.addCuttingMarksToPDF(pdf); // 保存PDF pdf.save('business-card.pdf'); } addCuttingMarksToPDF(pdf) { // 添加裁切标记 pdf.setDrawColor(0, 0, 0); pdf.setLineWidth(0.1); // 正面裁切标记 const frontX = 10; const frontY = 10; // 添加角标记 this.drawCuttingMark(pdf, frontX, frontY); this.drawCuttingMark(pdf, frontX + this.cardSize.width, frontY); this.drawCuttingMark(pdf, frontX, frontY + this.cardSize.height); this.drawCuttingMark(pdf, frontX + this.cardSize.width, frontY + this.cardSize.height); // 背面裁切标记 const backX = 110; const backY = 10; this.drawCuttingMark(pdf, backX, backY); this.drawCuttingMark(pdf, backX + this.cardSize.width, backY); this.drawCuttingMark(pdf, backX, backY + this.cardSize.height); this.drawCuttingMark(pdf, backX + this.cardSize.width, backY + this.cardSize.height); } drawCuttingMark(pdf, x, y) { const markLength = 3; // 水平线 pdf.line(x - markLength, y, x + markLength, y); // 垂直线 pdf.line(x, y - markLength, x, y + markLength); } exportToImage() { // 导出为图片 const frontDataURL = this.frontCanvas.toDataURL({ format: 'png', quality: 1 }); const link = document.createElement('a'); link.download = 'business-card-front.png'; link.href = frontDataURL; link.click(); } } // 初始化名片设计器document.addEventListener('DOMContentLoaded', function() { window.cardDesigner = new BusinessCardDesigner(); }); ### 4.3 名片设计器后端支持 <?php// functions.php - 添加名片设计器功能 // 注册名片设计器短代码add_shortcode('business_card_designer', 'render_business_card_designer');function render_business_card_designer($atts) { ob_start(); include get_template_directory() . '/template-parts/business-card-designer.php'; return ob_get_clean(); } // 添加名片设计保存功能add_action('rest_api_init', function() { // 保存名片设计 register_rest_route('card-designer/v1', '/save', array( 'methods' => 'POST', 'callback' => 'save_card_design', 'permission_callback' => function() { return current_user_can('edit_posts'); } )); // 获取用户保存的名片设计 register_rest_route('card-designer/v1', '/designs', array( 'methods' => 'GET', 'callback' => 'get_user_card_designs', 'permission_callback' => function() { return is_user_logged_in(); } )); }); function save_card_design($request) { $user_id = get_current_user_id(); $design_data = $request->get_param('design_data'); $design_name = $request->get_param('design_name'); $card_type = $request->get_param('card_type'); // 'front' or 'back' if(empty($design_data) || empty($design_name)) { return new WP_Error('invalid_data', '设计数据或名称为空', array('status' => 400)); } // 生成预览图 $preview_data = generate_card_preview($design_data); // 保存到数据库 global $wpdb; $table_name = $wpdb->prefix . 'card_designs'; $result = $wpdb->insert( $table_name, array( 'user_id' => $user_id, 'design_name' => sanitize_text_field($design_name), 'design_data' => json_encode($design_data), 'card_type' => $card_type, 'preview_data' => $preview_data, 'created_at' => current_time('mysql') ), array('%d', '%s', '%s', '%s', '%s', '%s') ); if($result === false) { return new WP_Error('db_error', '保存失败', array('status' => 500)); } return rest_ensure_response(array( 'success' => true, 'message' => '名片设计已保存', 'design_id' => $wpdb->insert_id )); } function generate_card_preview($design_data) { // 这里可以添加生成预览图的逻辑 // 可以使用GD库或ImageMagick生成缩略图 return ''; // 返回base64编码的预览图 } // 创建名片设计数据库表register_activation_hook(__FILE__, 'create_card_designs_table');function create_card_designs_table() { global $wpdb; $table_name = $wpdb->prefix . 'card_designs'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, user_id bigint(20) NOT NULL, design_name varchar(255) NOT NULL, design_data longtext NOT NULL, card_type varchar(50) NOT NULL, preview_data longtext, created_at datetime DEFAULT '0000-00-00 00:00:00' NOT NULL, PRIMARY KEY (id), KEY user_id (user_id) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); }?> --- ## 五、样式设计与用户体验优化 ### 5.1 响应式CSS设计 / assets/css/design-tools.css / / Logo设计器样式 /.logo-designer-container { display: flex; flex-direction: column; height: 80vh; background: #f5f7fa; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.1); } .designer-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; } .designer-body { display: flex; flex: 1; overflow: hidden; } .sidebar-left, .sidebar-right { width: 250px; background: white; padding: 15px; overflow-y: auto; border-right: 1px solid #e0e0e0; } .sidebar-right { border-right: none; border-left: 1px solid #e0e0e0; } .canvas-area { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 20px; background: #f8f9fa; } logo-canvas { background: white; border: 1px solid #ddd; border-radius: 5px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); } .canvas-controls { margin-top: 15px; display: flex; gap: 10px; } .tool-section { margin-bottom: 25px; } .tool-section h4 { margin-bottom: 10px; color: #333; font-size: 14px; text-transform: uppercase; letter-spacing: 1px; } .shape-list { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; } .shape-btn { padding: 8px; border: 1px solid #ddd; background: white; border-radius: 4px; cursor: pointer; transition: all 0.3s ease; } .shape-btn:hover { background: #f0f0f0; border-color: #667eea; } .icon-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } .icon-item { padding: 10px; border: 1px solid #e0e0e0; border-radius: 4px; text-align: center; cursor: pointer; transition: all 0.3s ease; } .icon-item:hover { background: #f0f7ff; border-color: #4dabf7; transform: translateY(-2px); } .icon-item i { font-size: 20px; color: #495057; } .property-group { margin-bottom: 15px; } .property-group label { display: block; margin-bottom: 5px; font-size: 12px; color: #666; font-weight: 500; } .property-group input,.property-group select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px; } .layers-panel ul { list-style: none; padding: 0; margin: 0; } .layer-item { display: flex; justify-content: space-between; align-items: center; padding: 8px 10px; border-bottom: 1px solid #eee; font-size: 13px; } .layer-item:hover { background: #f8f9fa; } .layer-actions { display: flex; gap: 5px; } .layer-actions button { padding: 2px 6px; font-size: 11px; border: 1px solid #ddd; background: white; border-radius: 3px; cursor: pointer; } / 名片设计器样式 /.card-designer-container { background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 20px rgba(0,0,0,0.1); } .template-selector { padding: 15px; background: #f8f9fa; border-bottom: 1px solid #e0e0

发表评论

详细指南,在WordPress中集成在线客服会话存档与质检分析系统

详细指南:在WordPress中集成在线客服会话存档与质检分析系统 摘要 随着电子商务和在线服务的快速发展,在线客服系统已成为企业与客户沟通的重要桥梁。然而,仅仅拥有客服系统并不足够,如何有效管理、存档和分析客服会话数据,提升服务质量,已成为企业数字化转型的关键环节。本文将详细介绍如何在WordPress平台中通过代码二次开发,集成在线客服会话存档与质检分析系统,实现这一常用互联网小工具功能。我们将从系统架构设计、数据库规划、前后端开发到数据分析功能实现,提供完整的技术解决方案。 一、系统需求分析与架构设计 1.1 业务需求分析 在开始技术实现之前,我们首先需要明确系统的业务需求: 会话存档功能:自动保存所有在线客服会话记录,包括文字、图片、文件等多媒体内容 质检分析功能:对客服会话进行质量评估,包括响应时间、服务态度、问题解决率等指标 数据可视化:通过图表展示客服绩效、会话趋势、常见问题等数据 权限管理:不同角色的用户(管理员、客服主管、普通客服)拥有不同的数据访问权限 搜索与筛选:支持按时间、客服人员、客户、关键词等多维度检索会话记录 实时监控:实时查看当前在线会话状态和客服工作状态 1.2 技术架构设计 基于WordPress的扩展性,我们设计以下技术架构: 前端展示层:使用WordPress主题模板和自定义页面展示数据 业务逻辑层:通过自定义插件处理会话数据的存储、分析和展示逻辑 数据存储层:扩展WordPress数据库,创建专门的数据表存储会话和质检数据 第三方集成层:与主流在线客服系统(如LiveChat、Zendesk、自定义系统)API对接 数据分析层:集成数据分析工具和算法,实现智能质检功能 1.3 开发环境准备 在开始开发前,确保具备以下环境: WordPress 5.0+ 版本 PHP 7.4+ 版本 MySQL 5.6+ 或 MariaDB 10.1+ 代码编辑器(VS Code、PHPStorm等) 本地开发环境(XAMPP、MAMP、Local等) 二、数据库设计与扩展 2.1 自定义数据表设计 为了存储客服会话和质检数据,我们需要在WordPress数据库中添加以下数据表: -- 客服会话主表 CREATE TABLE wp_chat_sessions ( session_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, session_key VARCHAR(100) NOT NULL, customer_id BIGINT(20) UNSIGNED, customer_name VARCHAR(255), customer_email VARCHAR(255), agent_id BIGINT(20) UNSIGNED, agent_name VARCHAR(255), start_time DATETIME NOT NULL, end_time DATETIME, duration INT(11), status ENUM('active', 'closed', 'transferred', 'timeout') DEFAULT 'active', channel VARCHAR(50), rating TINYINT(1), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (session_id), UNIQUE KEY session_key (session_key), KEY customer_id (customer_id), KEY agent_id (agent_id), KEY start_time (start_time) ); -- 会话消息表 CREATE TABLE wp_chat_messages ( message_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, session_id BIGINT(20) UNSIGNED NOT NULL, sender_type ENUM('customer', 'agent', 'system') NOT NULL, sender_id BIGINT(20) UNSIGNED, message_type ENUM('text', 'image', 'file', 'emoji', 'system') DEFAULT 'text', content LONGTEXT, media_url VARCHAR(500), file_name VARCHAR(255), file_size INT(11), sent_time DATETIME NOT NULL, read_status TINYINT(1) DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (message_id), KEY session_id (session_id), KEY sent_time (sent_time), FOREIGN KEY (session_id) REFERENCES wp_chat_sessions(session_id) ON DELETE CASCADE ); -- 质检分析表 CREATE TABLE wp_chat_quality ( quality_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, session_id BIGINT(20) UNSIGNED NOT NULL, evaluator_id BIGINT(20) UNSIGNED, evaluation_time DATETIME, response_time_score TINYINT(1), professionalism_score TINYINT(1), problem_solving_score TINYINT(1), communication_score TINYINT(1), overall_score DECIMAL(3,2), positive_keywords TEXT, negative_keywords TEXT, tags VARCHAR(500), notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (quality_id), UNIQUE KEY session_evaluator (session_id, evaluator_id), KEY session_id (session_id), KEY evaluator_id (evaluator_id), FOREIGN KEY (session_id) REFERENCES wp_chat_sessions(session_id) ON DELETE CASCADE ); -- 客服绩效表 CREATE TABLE wp_agent_performance ( performance_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, agent_id BIGINT(20) UNSIGNED NOT NULL, date DATE NOT NULL, total_sessions INT(11) DEFAULT 0, avg_response_time DECIMAL(6,2), avg_rating DECIMAL(3,2), avg_quality_score DECIMAL(3,2), first_response_time DECIMAL(6,2), resolution_rate DECIMAL(5,2), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (performance_id), UNIQUE KEY agent_date (agent_id, date), KEY agent_id (agent_id), KEY date (date) ); 2.2 数据库操作类实现 创建数据库操作类,封装所有数据库交互逻辑: <?php /** * 客服会话数据库操作类 */ class ChatSessionDB { private $wpdb; private $sessions_table; private $messages_table; private $quality_table; private $performance_table; public function __construct() { global $wpdb; $this->wpdb = $wpdb; $this->sessions_table = $wpdb->prefix . 'chat_sessions'; $this->messages_table = $wpdb->prefix . 'chat_messages'; $this->quality_table = $wpdb->prefix . 'chat_quality'; $this->performance_table = $wpdb->prefix . 'agent_performance'; } /** * 创建新会话 */ public function create_session($data) { $defaults = array( 'session_key' => uniqid('chat_'), 'start_time' => current_time('mysql'), 'status' => 'active' ); $data = wp_parse_args($data, $defaults); $result = $this->wpdb->insert( $this->sessions_table, $data, array('%s', '%d', '%s', '%s', '%d', '%s', '%s', '%s', '%d', '%s', '%d') ); if ($result) { return $this->wpdb->insert_id; } return false; } /** * 添加会话消息 */ public function add_message($session_id, $sender_type, $content, $message_type = 'text', $media_data = array()) { $data = array( 'session_id' => $session_id, 'sender_type' => $sender_type, 'message_type' => $message_type, 'content' => $content, 'sent_time' => current_time('mysql') ); // 处理媒体文件 if (!empty($media_data)) { $data['media_url'] = isset($media_data['url']) ? $media_data['url'] : ''; $data['file_name'] = isset($media_data['name']) ? $media_data['name'] : ''; $data['file_size'] = isset($media_data['size']) ? $media_data['size'] : 0; } // 如果是客服发送的消息,记录客服ID if ($sender_type === 'agent' && is_user_logged_in()) { $data['sender_id'] = get_current_user_id(); } $result = $this->wpdb->insert( $this->messages_table, $data, array('%d', '%s', '%d', '%s', '%s', '%s', '%s', '%d', '%s') ); return $result ? $this->wpdb->insert_id : false; } /** * 获取会话消息 */ public function get_session_messages($session_id, $limit = 100, $offset = 0) { $query = $this->wpdb->prepare( "SELECT * FROM {$this->messages_table} WHERE session_id = %d ORDER BY sent_time ASC LIMIT %d OFFSET %d", $session_id, $limit, $offset ); return $this->wpdb->get_results($query); } /** * 更新会话状态 */ public function update_session_status($session_id, $status, $end_time = null) { $data = array('status' => $status); if ($end_time) { $data['end_time'] = $end_time; // 计算会话时长 $session = $this->get_session($session_id); if ($session && $session->start_time) { $start = strtotime($session->start_time); $end = strtotime($end_time); $data['duration'] = $end - $start; } } return $this->wpdb->update( $this->sessions_table, $data, array('session_id' => $session_id), array('%s', '%s', '%d'), array('%d') ); } /** * 添加质检记录 */ public function add_quality_evaluation($session_id, $scores, $evaluator_id = null, $notes = '') { // 计算总分 $total = 0; $count = 0; foreach ($scores as $score) { if (!is_null($score)) { $total += $score; $count++; } } $overall_score = $count > 0 ? round($total / $count, 2) : 0; $data = array( 'session_id' => $session_id, 'evaluator_id' => $evaluator_id ?: get_current_user_id(), 'evaluation_time' => current_time('mysql'), 'overall_score' => $overall_score, 'notes' => $notes ); // 添加各项分数 $score_fields = array( 'response_time_score', 'professionalism_score', 'problem_solving_score', 'communication_score' ); foreach ($score_fields as $field) { if (isset($scores[$field])) { $data[$field] = $scores[$field]; } } // 关键词分析(简化版,实际应用中可能需要更复杂的NLP处理) if (isset($scores['positive_keywords'])) { $data['positive_keywords'] = is_array($scores['positive_keywords']) ? implode(',', $scores['positive_keywords']) : $scores['positive_keywords']; } if (isset($scores['negative_keywords'])) { $data['negative_keywords'] = is_array($scores['negative_keywords']) ? implode(',', $scores['negative_keywords']) : $scores['negative_keywords']; } return $this->wpdb->insert( $this->quality_table, $data, array('%d', '%d', '%s', '%d', '%d', '%d', '%d', '%f', '%s', '%s', '%s', '%s') ); } /** * 获取客服绩效数据 */ public function get_agent_performance($agent_id, $start_date, $end_date) { $query = $this->wpdb->prepare( "SELECT * FROM {$this->performance_table} WHERE agent_id = %d AND date BETWEEN %s AND %s ORDER BY date ASC", $agent_id, $start_date, $end_date ); return $this->wpdb->get_results($query); } /** * 统计会话数据 */ public function get_session_stats($filters = array()) { $where = array('1=1'); $params = array(); // 时间筛选 if (!empty($filters['start_date'])) { $where[] = "start_time >= %s"; $params[] = $filters['start_date']; } if (!empty($filters['end_date'])) { $where[] = "start_time <= %s"; $params[] = $filters['end_date'] . ' 23:59:59'; } // 客服筛选 if (!empty($filters['agent_id'])) { $where[] = "agent_id = %d"; $params[] = $filters['agent_id']; } // 状态筛选 if (!empty($filters['status'])) { $where[] = "status = %s"; $params[] = $filters['status']; } $where_clause = implode(' AND ', $where); // 基础统计 $query = $this->wpdb->prepare( "SELECT COUNT(*) as total_sessions, AVG(duration) as avg_duration, AVG(rating) as avg_rating, SUM(CASE WHEN status = 'closed' THEN 1 ELSE 0 END) as closed_sessions, SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active_sessions FROM {$this->sessions_table} WHERE {$where_clause}", $params ); return $this->wpdb->get_row($query); } } ?> 三、WordPress插件开发 3.1 插件基础结构 创建插件主文件 chat-archive-analyzer.php: <?php /** * Plugin Name: 在线客服会话存档与质检分析系统 * Plugin URI: https://yourwebsite.com/ * Description: 在WordPress中集成在线客服会话存档与质检分析功能 * Version: 1.0.0 * Author: 您的名称 * License: GPL v2 or later * Text Domain: chat-archive-analyzer */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('CHAT_ARCHIVE_VERSION', '1.0.0'); define('CHAT_ARCHIVE_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('CHAT_ARCHIVE_PLUGIN_URL', plugin_dir_url(__FILE__)); // 自动加载类 spl_autoload_register(function ($class) { $prefix = 'ChatArchive\'; $base_dir = CHAT_ARCHIVE_PLUGIN_DIR . 'includes/'; $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { return; } $relative_class = substr($class, $len); $file = $base_dir . str_replace('\', '/', $relative_class) . '.php'; if (file_exists($file)) { require $file; } }); // 初始化插件 class ChatArchiveAnalyzer { private static $instance = null; private $db; 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('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts')); add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts')); // AJAX处理 add_action('wp_ajax_chat_archive_action', array($this, 'handle_ajax_request')); add_action('wp_ajax_nopriv_chat_archive_action', array($this, 'handle_ajax_request')); // 短代码 add_shortcode('chat_session_archive', array($this, 'shortcode_session_archive')); add_shortcode('chat_analytics_dashboard', array($this, 'shortcode_analytics_dashboard')); } public function activate() { require_once CHAT_ARCHIVE_PLUGIN_DIR . 'includes/class-installer.php'; ChatArchiveInstaller::install(); // 创建默认选项 $default_options = array( 'auto_archive' => true, 'retention_days' => 365, 'quality_auto_evaluation' => false, 'enable_realtime_monitoring' => true, 'dashboard_access_roles' => array('administrator', 'editor') ); add_option('chat_archive_settings', $default_options); } public function deactivate() { // 清理临时数据 wp_clear_scheduled_hook('chat_archive_daily_maintenance'); } public function init() { // 初始化数据库类 $this->db = new ChatSessionDB(); // 加载文本域 load_plugin_textdomain('chat-archive-analyzer', false, dirname(plugin_basename(__FILE__)) . '/languages'); // 初始化API if (class_exists('ChatArchiveAPI')) { new ChatArchiveAPI(); } // 初始化第三方集成 $this->init_third_party_integrations(); } private function init_third_party_integrations() { // 检查并加载可用的第三方客服系统集成 $integrations_dir = CHAT_ARCHIVE_PLUGIN_DIR . 'includes/integrations/'; if (file_exists($integrations_dir)) { $integrations = glob($integrations_dir . '*.php'); foreach ($integrations as $integration_file) { $integration_name = basename($integration_file, '.php'); // 检查是否启用了该集成 $settings = get_option('chat_archive_settings', array()); $enabled_integrations = isset($settings['enabled_integrations']) ? $settings['enabled_integrations'] : array(); if (in_array($integration_name, $enabled_integrations) || empty($enabled_integrations)) { require_once $integration_file; $class_name = 'ChatArchive\Integrations\' . ucfirst($integration_name); if (class_exists($class_name)) { new $class_name(); } } } } } public function add_admin_menu() { // 主菜单 add_menu_page( __('客服会话存档', 'chat-archive-analyzer'), __('客服会话', 'chat-archive-analyzer'), 'manage_options', 'chat-archive', array($this, 'render_admin_page'), 'dashicons-format-chat', 30 ); // 子菜单 add_submenu_page( 'chat-archive', __('会话存档', 'chat-archive-analyzer'), __('会话存档', 'chat-archive-analyzer'), 'manage_options', 'chat-archive-sessions', array($this, 'render_sessions_page') ); add_submenu_page( 'chat-archive', __('质检分析', 'chat-archive-analyzer'), __('质检分析', 'chat-archive-analyzer'), 'manage_options', 'chat-archive-quality', array($this, 'render_quality_page') ); add_submenu_page( 'chat-archive', __('数据分析', 'chat-archive-analyzer'), __('数据分析', 'chat-archive-analyzer'), 'manage_options', 'chat-archive-analytics', array($this, 'render_analytics_page') ); add_submenu_page( 'chat-archive', __('实时监控', 'chat-archive-analyzer'), __('实时监控', 'chat-archive-analyzer'), 'manage_options', 'chat-archive-monitor', array($this, 'render_monitor_page') ); add_submenu_page( 'chat-archive', __('设置', 'chat-archive-analyzer'), __('设置', 'chat-archive-analyzer'), 'manage_options', 'chat-archive-settings', array($this, 'render_settings_page') ); } public function render_admin_page() { include CHAT_ARCHIVE_PLUGIN_DIR . 'templates/admin/dashboard.php'; } public function render_sessions_page() { include CHAT_ARCHIVE_PLUGIN_DIR . 'templates/admin/sessions.php'; } public function render_quality_page() { include CHAT_ARCHIVE_PLUGIN_DIR . 'templates/admin/quality.php'; } public function render_analytics_page() { include CHAT_ARCHIVE_PLUGIN_DIR . 'templates/admin/analytics.php'; } public function render_monitor_page() { include CHAT_ARCHIVE_PLUGIN_DIR . 'templates/admin/monitor.php'; } public function render_settings_page() { include CHAT_ARCHIVE_PLUGIN_DIR . 'templates/admin/settings.php'; } public function enqueue_admin_scripts($hook) { // 只在插件页面加载 if (strpos($hook, 'chat-archive') === false) { return; } // CSS wp_enqueue_style( 'chat-archive-admin', CHAT_ARCHIVE_PLUGIN_URL . 'assets/css/admin.css', array(), CHAT_ARCHIVE_VERSION ); // JavaScript wp_enqueue_script( 'chat-archive-admin', CHAT_ARCHIVE_PLUGIN_URL . 'assets/js/admin.js', array('jquery', 'jquery-ui-datepicker', 'chart-js'), CHAT_ARCHIVE_VERSION, true ); // 加载Chart.js wp_enqueue_script( 'chart-js', 'https://cdn.jsdelivr.net/npm/chart.js', array(), '3.7.0', true ); // 本地化脚本 wp_localize_script('chat-archive-admin', 'chatArchive', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('chat_archive_nonce'), 'strings' => array( 'loading' => __('加载中...', 'chat-archive-analyzer'), 'error' => __('发生错误', 'chat-archive-analyzer'), 'confirm_delete' => __('确定要删除这条记录吗?', 'chat-archive-analyzer') ) )); } public function enqueue_frontend_scripts() { // 前端样式和脚本(用于短代码) if (is_page() || is_single()) { global $post; if (has_shortcode($post->post_content, 'chat_session_archive') || has_shortcode($post->post_content, 'chat_analytics_dashboard')) { wp_enqueue_style( 'chat-archive-frontend', CHAT_ARCHIVE_PLUGIN_URL . 'assets/css/frontend.css', array(), CHAT_ARCHIVE_VERSION ); wp_enqueue_script( 'chat-archive-frontend', CHAT_ARCHIVE_PLUGIN_URL . 'assets/js/frontend.js', array('jquery'), CHAT_ARCHIVE_VERSION, true ); } } } public function handle_ajax_request() { // 验证nonce if (!check_ajax_referer('chat_archive_nonce', 'nonce', false)) { wp_die(__('安全验证失败', 'chat-archive-analyzer'), 403); } $action = isset($_POST['action_type']) ? sanitize_text_field($_POST['action_type']) : ''; switch ($action) { case 'get_sessions': $this->ajax_get_sessions(); break; case 'get_session_detail': $this->ajax_get_session_detail(); break; case 'add_quality_evaluation': $this->ajax_add_quality_evaluation(); break; case 'get_analytics_data': $this->ajax_get_analytics_data(); break; case 'export_sessions': $this->ajax_export_sessions(); break; default: wp_send_json_error(array('message' => __('未知操作', 'chat-archive-analyzer'))); } } private function ajax_get_sessions() { // 验证权限 if (!current_user_can('manage_options')) { wp_send_json_error(array('message' => __('权限不足', 'chat-archive-analyzer'))); } $page = isset($_POST['page']) ? intval($_POST['page']) : 1; $per_page = isset($_POST['per_page']) ? intval($_POST['per_page']) : 20; $filters = isset($_POST['filters']) ? $_POST['filters'] : array(); // 清理过滤条件 $clean_filters = array(); if (isset($filters['start_date'])) { $clean_filters['start_date'] = sanitize_text_field($filters['start_date']); } if (isset($filters['end_date'])) { $clean_filters['end_date'] = sanitize_text_field($filters['end_date']); } if (isset($filters['agent_id'])) { $clean_filters['agent_id'] = intval($filters['agent_id']); } if (isset($filters['status'])) { $clean_filters['status'] = sanitize_text_field($filters['status']); } if (isset($filters['search'])) { $clean_filters['search'] = sanitize_text_field($filters['search']); } // 获取会话数据 $sessions = $this->db->get_sessions($clean_filters, $page, $per_page); $total = $this->db->count_sessions($clean_filters); wp_send_json_success(array( 'sessions' => $sessions, 'total' => $total, 'total_pages' => ceil($total / $per_page), 'current_page' => $page )); } public function shortcode_session_archive($atts) { // 检查用户权限 if (!is_user_logged_in()) { return '<p>' . __('请登录后查看会话记录', 'chat-archive-analyzer') . '</p>'; } $atts = shortcode_atts(array( 'agent_id' => get_current_user_id(), 'limit' => 10, 'show_search' => true ), $atts, 'chat_session_archive'); ob_start(); include CHAT_ARCHIVE_PLUGIN_DIR . 'templates/frontend/session-archive.php'; return ob_get_clean(); } public function shortcode_analytics_dashboard($atts) { // 检查用户权限 $user = wp_get_current_user(); $allowed_roles = array('administrator', 'editor', 'shop_manager'); if (!array_intersect($allowed_roles, $user->roles)) { return '<p>' . __('您没有权限查看此页面', 'chat-archive-analyzer') . '</p>'; } $atts = shortcode_atts(array( 'period' => 'month', 'show_agents' => true, 'show_trends' => true ), $atts, 'chat_analytics_dashboard'); ob_start(); include CHAT_ARCHIVE_PLUGIN_DIR . 'templates/frontend/analytics-dashboard.php'; return ob_get_clean(); } } // 启动插件ChatArchiveAnalyzer::get_instance();?> ### 3.2 安装器类实现 创建 `includes/class-installer.php`: <?phpnamespace ChatArchive; class Installer { public static function install() { self::create_tables(); self::create_roles(); self::schedule_events(); } private static function create_tables() { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); // 这里包含之前设计的SQL表创建语句 require_once CHAT_ARCHIVE_PLUGIN_DIR . 'includes/schema.php'; dbDelta($sql); } private static function create_roles() { // 添加客服主管角色 add_role('chat_supervisor', __('客服主管', 'chat-archive-analyzer'), array( 'read' => true, 'edit_posts' => true, 'delete_posts' => true, 'manage_chat_sessions' => true, 'evaluate_chat_quality' => true, 'view_chat_analytics' => true )); // 添加客服角色 add_role('chat_agent', __('客服专员', 'chat-archive-analyzer'), array( 'read' => true, 'edit_posts' => false, 'view_own_chat_sessions' => true, 'view_own_performance' => true )); // 为管理员添加自定义权限 $admin_role = get_role('administrator'); if ($admin_role) { $admin_role->add_cap('manage_chat_sessions'); $admin_role->add_cap('evaluate_chat_quality'); $admin_role->add_cap('view_chat_analytics'); $admin_role->add_cap('configure_chat_system'); } } private static function schedule_events() { if (!wp_next_scheduled('chat_archive_daily_maintenance')) { wp_schedule_event(time(), 'daily', 'chat_archive_daily_maintenance'); } if (!wp_next_scheduled('chat_archive_hourly_processing')) { wp_schedule_event(time(), 'hourly', 'chat_archive_hourly_processing'); } } }?> --- ## 四、前端界面与用户体验 ### 4.1 管理界面模板 创建 `templates/admin/sessions.php`: <div class="wrap chat-archive-wrapper"> <h1><?php _e('客服会话存档', 'chat-archive-analyzer'); ?></h1> <!-- 过滤条件 --> <div class="chat-filter-section"> <form id="chat-session-filter" method="get"> <input type="hidden" name="page" value="chat-archive-sessions"> <div class="filter-row"> <div class="filter-group"> <label for="filter-date-start"><?php _e('开始日期', 'chat-archive-analyzer'); ?></label> <input type="date" id="filter-date-start" name="date_start" class="regular-text"> </div> <div class="filter-group"> <label for="filter-date-end"><?php _e('结束日期', 'chat-archive-analyzer'); ?></label> <input type="date" id="filter-date-end" name="date_end" class="regular-text"> </div> <div class="filter-group"> <label for="filter-agent"><?php _e('客服人员', 'chat-archive-analyzer'); ?></label> <select id="filter-agent" name="agent_id"> <option value=""><?php _e('所有客服', 'chat-archive-analyzer'); ?></option> <?php $agents = get_users(array('role__in' => array('chat_agent', 'administrator', 'editor'))); foreach ($agents as $agent) { echo '<option value="' . $agent->ID . '">' . $agent->display_name . '</option>'; } ?> </select> </div> <div class="filter-group"> <label for="filter-status"><?php _e('会话状态', 'chat-archive-analyzer'); ?></label> <select id="filter-status" name="status"> <option value=""><?php _e('所有状态', 'chat-archive-analyzer'); ?></option> <option value="active"><?php _e('进行中', 'chat-archive-analyzer'); ?></option> <option value="closed"><?php _e('已结束', 'chat-archive-analyzer'); ?></option> <option value="transferred"><?php _e('已转接', 'chat-archive-analyzer'); ?></option> </select> </div> <div class="filter-group"> <label for="filter-search"><?php _e('关键词搜索', 'chat-archive-analyzer'); ?></label> <input type="text" id="filter-search" name="search" placeholder="<?php _e('客户姓名、邮箱或会话内容', 'chat-archive-analyzer'); ?>"> </div> <div class="filter-actions"> <button type="submit" class="button button-primary"><?php _e('筛选', 'chat-archive-analyzer'); ?></button> <button type="button" id="reset-filters" class="button"><?php _e('重置', 'chat-archive-analyzer'); ?></button> </div> </div> </form> </div> <!-- 会话列表 --> <div class="chat-sessions-list"> <div class="tablenav top"> <div class="alignleft actions"> <select id="bulk-action-selector"> <option value=""><?php _e('批量操作', 'chat-archive-analyzer'); ?></option> <option value="export"><?php _e('导出选中', 'chat-archive-analyzer'); ?></option> <option value="delete"><?php _e('删除选中', 'chat-archive-analyzer'); ?></option> </select> <button id="do-bulk-action" class="button"><?php _e('应用', 'chat-archive-analyzer'); ?></button> </div> <div class="tablenav-pages"> <span class="displaying-num"></span> <span class="pagination-links"> <button class="button first-page" disabled><?php _e('首页', 'chat-archive-analyzer'); ?></button> <button class="button prev-page" disabled><?php _e('上一页', 'chat-archive-analyzer'); ?></button> <span class="paging-input"> <input type="text" class="current-page" size="2" value="1"> <?php _e('共', 'chat-archive-analyzer'); ?> <span class="total-pages">1</span> <?php _e('页', 'chat-archive-analyzer'); ?>

发表评论

手把手教学,为你的网站添加智能化的内容自动摘要生成器

手把手教学:为你的WordPress网站添加智能内容自动摘要生成器 引言:为什么你的网站需要智能摘要功能? 在信息爆炸的互联网时代,用户浏览网页的时间越来越碎片化。根据研究,普通用户在决定是否继续阅读一篇文章前,平均只会花费15秒浏览页面内容。一个精心设计的摘要不仅能帮助用户快速了解文章核心内容,还能显著提高页面停留时间和内容转化率。 传统的WordPress摘要功能通常只是简单截取文章开头部分文字,这种方法往往无法准确反映文章的核心观点。而智能摘要生成器通过算法分析文章内容,提取关键信息,生成简洁、准确的摘要,为用户提供更好的阅读体验。 本文将带你从零开始,通过WordPress代码二次开发,为你的网站添加一个智能化的内容自动摘要生成器。无论你是WordPress开发者还是有一定技术基础的网站管理员,都能通过本教程实现这一功能。 第一部分:准备工作与环境配置 1.1 理解WordPress摘要系统的工作原理 在开始开发之前,我们需要了解WordPress默认的摘要系统是如何工作的: WordPress使用the_excerpt()函数显示摘要 默认情况下,如果没有手动设置摘要,WordPress会自动截取文章前55个单词 可以通过excerpt_length过滤器修改摘要长度 可以通过excerpt_more过滤器修改摘要末尾的"阅读更多"文本 1.2 开发环境准备 为了安全地进行代码开发,我们建议采取以下步骤: 备份你的网站:在进行任何代码修改前,请务必备份整个网站和数据库 创建子主题:如果你正在使用主题,建议创建子主题进行修改 启用调试模式:在wp-config.php中添加以下代码以便查看错误信息: define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); 安装代码编辑器:推荐使用VS Code、Sublime Text或PHPStorm 1.3 理解我们将要构建的系统架构 我们的智能摘要生成器将包含以下组件: 文本处理模块:清理和预处理文章内容 关键词提取模块:识别文章中的关键术语和概念 摘要生成算法:基于文本分析生成连贯摘要 缓存机制:提高性能,避免重复处理相同内容 管理界面:允许用户配置摘要生成参数 第二部分:构建基础摘要生成功能 2.1 创建插件文件结构 首先,我们创建一个独立的WordPress插件来实现这个功能: 在wp-content/plugins/目录下创建新文件夹smart-excerpt-generator 在该文件夹中创建以下文件: smart-excerpt-generator.php (主插件文件) includes/ (目录) class-text-processor.php class-keyword-extractor.php class-summary-generator.php assets/ (目录) css/admin-style.css js/admin-script.js templates/ (目录) admin-settings.php 2.2 编写插件主文件 打开smart-excerpt-generator.php,添加以下代码: <?php /** * Plugin Name: 智能摘要生成器 * Plugin URI: https://yourwebsite.com/ * Description: 为WordPress文章自动生成智能摘要 * Version: 1.0.0 * Author: 你的名字 * License: GPL v2 or later * Text Domain: smart-excerpt */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('SEG_VERSION', '1.0.0'); define('SEG_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('SEG_PLUGIN_URL', plugin_dir_url(__FILE__)); // 自动加载类文件 spl_autoload_register(function ($class) { $prefix = 'SmartExcerpt\'; $base_dir = SEG_PLUGIN_DIR . 'includes/'; $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { return; } $relative_class = substr($class, $len); $file = $base_dir . str_replace('\', '/', $relative_class) . '.php'; if (file_exists($file)) { require $file; } }); // 初始化插件 function seg_init_plugin() { // 检查PHP版本 if (version_compare(PHP_VERSION, '7.0.0', '<')) { add_action('admin_notices', function() { echo '<div class="notice notice-error"><p>'; echo '智能摘要生成器需要PHP 7.0或更高版本。当前版本:' . PHP_VERSION; echo '</p></div>'; }); return; } // 实例化主控制器 $plugin = new SmartExcerptMainController(); $plugin->run(); } add_action('plugins_loaded', 'seg_init_plugin'); 2.3 创建文本处理器类 在includes/目录下创建class-text-processor.php: <?php namespace SmartExcerpt; class TextProcessor { /** * 清理HTML内容,提取纯文本 */ public function extract_plain_text($content) { // 移除HTML标签 $text = strip_tags($content); // 解码HTML实体 $text = html_entity_decode($text, ENT_QUOTES | ENT_HTML5, 'UTF-8'); // 移除多余空白字符 $text = preg_replace('/s+/', ' ', $text); // 移除短代码 $text = strip_shortcodes($text); return trim($text); } /** * 将文本分割成句子 */ public function split_into_sentences($text) { // 使用正则表达式分割句子 $sentences = preg_split('/(?<=[.?!。?!])s+/', $text, -1, PREG_SPLIT_NO_EMPTY); // 过滤空句子和过短句子 $sentences = array_filter($sentences, function($sentence) { return strlen(trim($sentence)) > 10; }); return array_values($sentences); } /** * 计算句子权重 */ public function calculate_sentence_score($sentence, $keywords) { $score = 0; // 基于关键词出现频率评分 foreach ($keywords as $keyword => $weight) { $count = substr_count(strtolower($sentence), strtolower($keyword)); $score += $count * $weight; } // 基于句子位置评分(开头和结尾的句子通常更重要) // 这个因素将在调用此方法时通过参数传递 // 基于句子长度评分(中等长度的句子通常包含更多信息) $length = strlen($sentence); if ($length > 50 && $length < 200) { $score += 2; } return $score; } } 2.4 创建关键词提取器类 在includes/目录下创建class-keyword-extractor.php: <?php namespace SmartExcerpt; class KeywordExtractor { private $stop_words; public function __construct() { // 中文停用词列表(部分示例) $this->stop_words = [ '的', '了', '在', '是', '我', '有', '和', '就', '不', '人', '都', '一', '一个', '上', '也', '很', '到', '说', '要', '去', '你', '会', '着', '没有', '看', '好', '自己', '这', 'the', 'and', 'a', 'an', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'is', 'are', 'was', 'were', 'be', 'been', 'being' ]; } /** * 从文本中提取关键词 */ public function extract($text, $max_keywords = 10) { // 分词处理(简化版,实际应用中可能需要使用分词库) $words = $this->tokenize($text); // 移除停用词 $words = $this->remove_stop_words($words); // 计算词频 $word_freq = array_count_values($words); // 按频率排序 arsort($word_freq); // 取前N个关键词 $keywords = array_slice($word_freq, 0, $max_keywords, true); // 计算权重(归一化处理) $total = array_sum($keywords); $weighted_keywords = []; foreach ($keywords as $word => $count) { $weighted_keywords[$word] = $count / $total * 100; } return $weighted_keywords; } /** * 简单分词函数(针对中文和英文) */ private function tokenize($text) { // 将文本转换为小写 $text = mb_strtolower($text, 'UTF-8'); // 使用正则表达式分割单词 // 匹配中文词语和英文单词 preg_match_all('/[x{4e00}-x{9fa5}]|[a-zA-Z]+/', $text, $matches); return $matches[0]; } /** * 移除停用词 */ private function remove_stop_words($words) { return array_filter($words, function($word) { return !in_array($word, $this->stop_words) && strlen($word) > 1; }); } } 第三部分:实现智能摘要生成算法 3.1 创建摘要生成器主类 在includes/目录下创建class-summary-generator.php: <?php namespace SmartExcerpt; class SummaryGenerator { private $text_processor; private $keyword_extractor; public function __construct() { $this->text_processor = new TextProcessor(); $this->keyword_extractor = new KeywordExtractor(); } /** * 生成文章摘要 */ public function generate($content, $max_length = 150) { // 检查是否有缓存 $cache_key = 'seg_summary_' . md5($content . $max_length); $cached_summary = get_transient($cache_key); if ($cached_summary !== false) { return $cached_summary; } // 提取纯文本 $plain_text = $this->text_processor->extract_plain_text($content); // 如果文本太短,直接返回 if (strlen($plain_text) < $max_length) { return $plain_text; } // 提取关键词 $keywords = $this->keyword_extractor->extract($plain_text); // 分割成句子 $sentences = $this->text_processor->split_into_sentences($plain_text); // 计算每个句子的得分 $scored_sentences = []; $total_sentences = count($sentences); foreach ($sentences as $index => $sentence) { $score = $this->text_processor->calculate_sentence_score($sentence, $keywords); // 基于句子位置调整分数 $position_score = $this->calculate_position_score($index, $total_sentences); $score += $position_score; $scored_sentences[] = [ 'text' => $sentence, 'score' => $score, 'index' => $index ]; } // 按分数排序 usort($scored_sentences, function($a, $b) { return $b['score'] <=> $a['score']; }); // 选择最佳句子生成摘要 $selected_sentences = []; $current_length = 0; foreach ($scored_sentences as $sentence) { $sentence_length = strlen($sentence['text']); if ($current_length + $sentence_length <= $max_length) { $selected_sentences[] = $sentence; $current_length += $sentence_length; } if ($current_length >= $max_length * 0.8) { break; } } // 按原始顺序排序选中的句子 usort($selected_sentences, function($a, $b) { return $a['index'] <=> $b['index']; }); // 构建摘要 $summary = ''; foreach ($selected_sentences as $sentence) { $summary .= $sentence['text'] . ' '; } $summary = trim($summary); // 如果摘要太短,使用传统方法生成 if (strlen($summary) < $max_length * 0.5) { $summary = $this->generate_fallback_summary($plain_text, $max_length); } // 缓存结果(24小时) set_transient($cache_key, $summary, DAY_IN_SECONDS); return $summary; } /** * 计算句子位置分数 */ private function calculate_position_score($index, $total) { // 开头和结尾的句子通常更重要 $position = $index / max(1, $total - 1); if ($position < 0.1) { // 前10% return 3; } elseif ($position > 0.9) { // 后10% return 2; } elseif ($position < 0.2) { // 前10%-20% return 1; } return 0; } /** * 备用摘要生成方法 */ private function generate_fallback_summary($text, $max_length) { // 简单截取前N个字符,确保在完整单词处结束 if (strlen($text) <= $max_length) { return $text; } $truncated = substr($text, 0, $max_length); $last_space = strrpos($truncated, ' '); if ($last_space !== false) { $truncated = substr($truncated, 0, $last_space); } return $truncated . '...'; } } 3.2 创建主控制器类 在includes/目录下创建class-main-controller.php: <?php namespace SmartExcerpt; class MainController { private $summary_generator; public function __construct() { $this->summary_generator = new SummaryGenerator(); } public function run() { // 初始化钩子 $this->init_hooks(); } private function init_hooks() { // 替换默认的摘要生成函数 add_filter('get_the_excerpt', [$this, 'filter_the_excerpt'], 10, 2); // 添加快捷码支持 add_shortcode('smart_excerpt', [$this, 'shortcode_smart_excerpt']); // 添加RSS摘要支持 add_filter('the_excerpt_rss', [$this, 'filter_rss_excerpt']); // 添加管理菜单 add_action('admin_menu', [$this, 'add_admin_menu']); // 添加设置链接 add_filter('plugin_action_links_' . plugin_basename(SEG_PLUGIN_DIR . 'smart-excerpt-generator.php'), [$this, 'add_settings_link']); } /** * 过滤文章摘要 */ public function filter_the_excerpt($excerpt, $post) { // 如果已经有手动设置的摘要,直接返回 if (!empty($excerpt)) { return $excerpt; } // 获取文章内容 $content = $post->post_content; // 获取摘要长度设置 $length = get_option('seg_excerpt_length', 150); // 生成智能摘要 $smart_excerpt = $this->summary_generator->generate($content, $length); // 添加"阅读更多"链接 $more_text = get_option('seg_excerpt_more', '... 阅读更多'); $smart_excerpt .= ' <a href="' . get_permalink($post->ID) . '" class="read-more">' . $more_text . '</a>'; return $smart_excerpt; } /** * 快捷码函数 */ public function shortcode_smart_excerpt($atts) { $atts = shortcode_atts([ 'length' => 150, 'post_id' => null, 'more_text' => '...' ], $atts); $post_id = $atts['post_id'] ?: get_the_ID(); $post = get_post($post_id); if (!$post) { return ''; } $excerpt = $this->summary_generator->generate($post->post_content, $atts['length']); if (!empty($atts['more_text'])) { $excerpt .= ' <a href="' . get_permalink($post_id) . '">' . $atts['more_text'] . '</a>'; } return '<div class="smart-excerpt">' . $excerpt . '</div>'; } /** * 过滤RSS摘要 */ public function filter_rss_excerpt($excerpt) { global $post; if (get_option('seg_enable_for_rss', false)) { $length = get_option('seg_rss_excerpt_length', 100); return $this->summary_generator->generate($post->post_content, $length); } return $excerpt; } /** * 添加管理菜单 */ public function add_admin_menu() { add_options_page( 第四部分:创建管理界面与设置选项 4.1 完善主控制器类(续) /** * 添加管理菜单 */ public function add_admin_menu() { add_options_page( '智能摘要设置', '智能摘要', 'manage_options', 'smart-excerpt-settings', [$this, 'render_settings_page'] ); } /** * 添加设置链接到插件列表 */ public function add_settings_link($links) { $settings_link = '<a href="' . admin_url('options-general.php?page=smart-excerpt-settings') . '">设置</a>'; array_unshift($links, $settings_link); return $links; } /** * 渲染设置页面 */ public function render_settings_page() { // 检查用户权限 if (!current_user_can('manage_options')) { wp_die('您没有权限访问此页面'); } // 保存设置 if (isset($_POST['submit_settings']) && check_admin_referer('seg_settings_nonce')) { $this->save_settings(); echo '<div class="notice notice-success"><p>设置已保存!</p></div>'; } // 加载设置页面模板 include SEG_PLUGIN_DIR . 'templates/admin-settings.php'; } /** * 保存设置 */ private function save_settings() { // 摘要长度 if (isset($_POST['excerpt_length'])) { $length = intval($_POST['excerpt_length']); if ($length > 0) { update_option('seg_excerpt_length', $length); } } // "阅读更多"文本 if (isset($_POST['excerpt_more'])) { update_option('seg_excerpt_more', sanitize_text_field($_POST['excerpt_more'])); } // 是否启用RSS摘要 $enable_rss = isset($_POST['enable_rss']) ? 1 : 0; update_option('seg_enable_for_rss', $enable_rss); // RSS摘要长度 if (isset($_POST['rss_excerpt_length'])) { $rss_length = intval($_POST['rss_excerpt_length']); if ($rss_length > 0) { update_option('seg_rss_excerpt_length', $rss_length); } } // 是否启用缓存 $enable_cache = isset($_POST['enable_cache']) ? 1 : 0; update_option('seg_enable_cache', $enable_cache); // 缓存过期时间 if (isset($_POST['cache_expiration'])) { $expiration = intval($_POST['cache_expiration']); if ($expiration > 0) { update_option('seg_cache_expiration', $expiration); } } // 是否在文章编辑页面显示摘要预览 $show_preview = isset($_POST['show_preview']) ? 1 : 0; update_option('seg_show_preview', $show_preview); } } 4.2 创建管理设置页面模板 在templates/目录下创建admin-settings.php: <div class="wrap"> <h1>智能摘要生成器设置</h1> <form method="post" action=""> <?php wp_nonce_field('seg_settings_nonce'); ?> <table class="form-table"> <tr> <th scope="row"> <label for="excerpt_length">摘要长度(字符数)</label> </th> <td> <input type="number" id="excerpt_length" name="excerpt_length" value="<?php echo esc_attr(get_option('seg_excerpt_length', 150)); ?>" min="50" max="500" step="10"> <p class="description">摘要的最大字符长度,建议值:150-200</p> </td> </tr> <tr> <th scope="row"> <label for="excerpt_more">"阅读更多"文本</label> </th> <td> <input type="text" id="excerpt_more" name="excerpt_more" value="<?php echo esc_attr(get_option('seg_excerpt_more', '... 阅读更多')); ?>" class="regular-text"> <p class="description">摘要末尾显示的"阅读更多"链接文本</p> </td> </tr> <tr> <th scope="row">RSS摘要设置</th> <td> <label> <input type="checkbox" id="enable_rss" name="enable_rss" value="1" <?php checked(get_option('seg_enable_for_rss', false)); ?>> 为RSS订阅启用智能摘要 </label> <div style="margin-top: 10px;"> <label for="rss_excerpt_length">RSS摘要长度:</label> <input type="number" id="rss_excerpt_length" name="rss_excerpt_length" value="<?php echo esc_attr(get_option('seg_rss_excerpt_length', 100)); ?>" min="50" max="300" step="10"> </div> </td> </tr> <tr> <th scope="row">缓存设置</th> <td> <label> <input type="checkbox" id="enable_cache" name="enable_cache" value="1" <?php checked(get_option('seg_enable_cache', true)); ?>> 启用摘要缓存 </label> <div style="margin-top: 10px;"> <label for="cache_expiration">缓存过期时间(小时):</label> <input type="number" id="cache_expiration" name="cache_expiration" value="<?php echo esc_attr(get_option('seg_cache_expiration', 24)); ?>" min="1" max="720" step="1"> <p class="description">缓存摘要结果以提高性能</p> </div> </td> </tr> <tr> <th scope="row">文章编辑页面</th> <td> <label> <input type="checkbox" id="show_preview" name="show_preview" value="1" <?php checked(get_option('seg_show_preview', true)); ?>> 显示摘要预览 </label> <p class="description">在文章编辑页面显示自动生成的摘要预览</p> </td> </tr> <tr> <th scope="row">摘要生成测试</th> <td> <button type="button" id="test_summary" class="button">测试摘要生成</button> <div id="test_result" style="margin-top: 10px; padding: 10px; background: #f5f5f5; display: none;"></div> </td> </tr> </table> <p class="submit"> <input type="submit" name="submit_settings" class="button-primary" value="保存设置"> <button type="button" id="clear_cache" class="button">清除缓存</button> </p> </form> <div class="card"> <h2>使用说明</h2> <p>1. 智能摘要会自动替换WordPress默认的摘要生成功能</p> <p>2. 如果文章已有手动设置的摘要,将优先使用手动摘要</p> <p>3. 在文章中使用短码:[smart_excerpt length="200" more_text="查看更多"]</p> <p>4. 可以通过过滤器自定义摘要生成行为,详见插件文档</p> </div> </div> <script> jQuery(document).ready(function($) { // 测试摘要生成 $('#test_summary').on('click', function() { var testText = "人工智能是计算机科学的一个分支,它企图了解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器。该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。人工智能从诞生以来,理论和技术日益成熟,应用领域也不断扩大。可以设想,未来人工智能带来的科技产品,将会是人类智慧的容器。人工智能可以对人的意识、思维的信息过程的模拟。人工智能不是人的智能,但能像人那样思考,也可能超过人的智能。"; $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'seg_test_summary', text: testText, length: $('#excerpt_length').val() }, success: function(response) { $('#test_result').html('<strong>测试结果:</strong><br>' + response).show(); } }); }); // 清除缓存 $('#clear_cache').on('click', function(e) { e.preventDefault(); if (confirm('确定要清除所有摘要缓存吗?')) { $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'seg_clear_cache' }, success: function(response) { alert(response.message); } }); } }); }); </script> 4.3 添加AJAX处理功能 在主控制器类中添加AJAX处理方法: // 在MainController类的init_hooks方法中添加: add_action('wp_ajax_seg_test_summary', [$this, 'ajax_test_summary']); add_action('wp_ajax_seg_clear_cache', [$this, 'ajax_clear_cache']); // 添加文章编辑页面摘要预览 add_action('add_meta_boxes', [$this, 'add_excerpt_preview_metabox']); // 在MainController类中添加新方法: /** * AJAX测试摘要生成 */ public function ajax_test_summary() { if (!current_user_can('manage_options')) { wp_die('权限不足'); } $text = isset($_POST['text']) ? $_POST['text'] : ''; $length = isset($_POST['length']) ? intval($_POST['length']) : 150; if (empty($text)) { wp_die('请输入测试文本'); } $summary = $this->summary_generator->generate($text, $length); echo wp_kses_post($summary); wp_die(); } /** * AJAX清除缓存 */ public function ajax_clear_cache() { if (!current_user_can('manage_options')) { wp_send_json_error(['message' => '权限不足']); } global $wpdb; // 删除所有智能摘要缓存 $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", '_transient_seg_summary_%' ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", '_transient_timeout_seg_summary_%' ) ); wp_send_json_success(['message' => '缓存已清除']); } /** * 添加摘要预览元框 */ public function add_excerpt_preview_metabox() { if (get_option('seg_show_preview', true)) { add_meta_box( 'seg_excerpt_preview', '智能摘要预览', [$this, 'render_excerpt_preview_metabox'], 'post', 'side', 'high' ); } } /** * 渲染摘要预览元框 */ public function render_excerpt_preview_metabox($post) { $content = $post->post_content; $length = get_option('seg_excerpt_length', 150); echo '<div id="seg_preview_container">'; echo '<p>自动生成的摘要预览:</p>'; echo '<div id="seg_preview_content" style="background: #f5f5f5; padding: 10px; border-radius: 3px; min-height: 100px;">'; if (!empty($content)) { $preview = $this->summary_generator->generate($content, $length); echo wp_kses_post($preview); } else { echo '<em>请输入文章内容以查看摘要预览</em>'; } echo '</div>'; echo '<button type="button" id="seg_refresh_preview" class="button button-small" style="margin-top: 10px;">刷新预览</button>'; echo '</div>'; // 添加JavaScript ?> <script> jQuery(document).ready(function($) { // 刷新预览 $('#seg_refresh_preview').on('click', function() { var content = $('#content').val(); var length = <?php echo $length; ?>; $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'seg_update_preview', content: content, length: length, post_id: <?php echo $post->ID; ?>, nonce: '<?php echo wp_create_nonce('seg_preview_nonce'); ?>' }, success: function(response) { if (response.success) { $('#seg_preview_content').html(response.data); } } }); }); // 自动刷新预览(每30秒) setInterval(function() { if ($('#content').is(':focus')) { $('#seg_refresh_preview').click(); } }, 30000); }); </script> <?php } // 添加预览更新AJAX处理 add_action('wp_ajax_seg_update_preview', [$this, 'ajax_update_preview']); /** * AJAX更新预览 */ public function ajax_update_preview() { check_ajax_referer('seg_preview_nonce', 'nonce'); if (!current_user_can('edit_posts')) { wp_send_json_error('权限不足'); } $content = isset($_POST['content']) ? $_POST['content'] : ''; $length = isset($_POST['length']) ? intval($_POST['length']) : 150; if (empty($content)) { wp_send_json_success('<em>请输入文章内容以查看摘要预览</em>'); } $summary = $this->summary_generator->generate($content, $length); wp_send_json_success(wp_kses_post($summary)); } 第五部分:高级功能与优化 5.1 添加自定义过滤器 为了让其他开发者可以自定义摘要生成行为,我们添加一些过滤器: // 在SummaryGenerator类的generate方法开始处添加: $content = apply_filters('seg_before_generate', $content, $max_length); // 在生成摘要后添加: $summary = apply_filters('seg_after_generate', $summary, $content, $max_length); // 在MainController类的filter_the_excerpt方法中添加: $smart_excerpt = apply_filters('seg_custom_excerpt', $smart_excerpt, $post, $length); // 创建新的过滤器类 // 在includes/目录下创建class-custom-filters.php: <?php namespace SmartExcerpt; class CustomFilters { /** * 示例过滤器:为特定分类的文章添加前缀 */ public static function add_category_prefix($excerpt, $post, $length) { $categories = get_the_category($post->ID); if (!empty($categories)) { $category_names = array_map(function($cat) { return $cat->name; }, $categories); $prefix = '【' . implode('、', $category_names) . '】'; $excerpt = $prefix . ' ' . $excerpt; } return $excerpt; } /** * 示例过滤器:移除摘要中的短链接 */ public static function remove_short_urls($summary, $content, $max_length) { // 移除类似bit.ly、t.cn等短链接 $summary = preg_replace('/b(https?://)?(bit.ly|t.cn|goo.gl|tinyurl.com)/S+/i', '', $summary); return trim($summary); } /** * 示例过滤器:为长摘要添加分页 */ public static function add_pagination($excerpt, $post) { $full_content = $post->post_content; $excerpt_length = strlen($excerpt); $full_length = strlen(strip_tags($full_content)); if ($full_length > $excerpt_length * 2) { $excerpt .= ' <span class="summary-pagination">[1/2]</span>'; } return $excerpt; } } // 在MainController类的init_hooks方法中添加过滤器示例: add_filter('seg_custom_excerpt', ['SmartExcerptCustomFilters', 'add_category_prefix'], 10, 3); add_filter('seg_after_generate', ['SmartExcerptCustomFilters', 'remove_short_urls'], 10, 3); 5.2 添加性能优化功能 // 在includes/目录下创建class-performance-optimizer.php: <?php namespace SmartExcerpt; class PerformanceOptimizer { /** * 批量生成摘要缓存 */ public static function batch_generate_cache($post_ids = []) { if (empty($post_ids)) { // 获取最近100篇文章 $args = [ 'post_type' => 'post', 'post_status' => 'publish', 'posts_per_page' => 100, 'fields' => 'ids' ]; $query = new WP_Query($args); $post_ids = $query->posts; } $summary_generator = new SummaryGenerator(); $results = [ 'total' => count($post_ids),

发表评论

WordPress 插件开发教程,集成网站实时股票行情与财经数据工具

WordPress插件开发教程:集成实时股票行情与财经数据工具 引言:为什么需要自定义财经数据插件 在当今数字化时代,财经数据的实时获取和展示对于许多网站来说至关重要。无论是金融博客、投资社区还是企业官网,实时股票行情和财经数据都能显著提升网站的专业性和用户粘性。虽然市场上有一些现成的财经插件,但它们往往功能单一、定制性差,或者存在样式冲突、加载缓慢等问题。 通过自主开发WordPress插件,我们可以完全掌控数据展示方式、更新频率和用户界面,同时确保与网站主题完美兼容。本教程将引导您从零开始创建一个功能全面的实时股票行情与财经数据工具,并探讨如何通过WordPress代码二次开发实现常用互联网小工具功能。 第一章:WordPress插件开发基础 1.1 插件开发环境搭建 在开始开发之前,我们需要准备合适的开发环境: 本地开发环境:推荐使用XAMPP、MAMP或Local by Flywheel搭建本地WordPress环境 代码编辑器:Visual Studio Code、PHPStorm或Sublime Text 浏览器开发者工具:用于调试前端代码 版本控制系统:Git用于代码版本管理 1.2 创建插件基本结构 每个WordPress插件都需要一个主文件,通常以插件名称命名。让我们创建插件的初始结构: wp-content/plugins/stock-finance-tool/ │ ├── stock-finance-tool.php # 主插件文件 ├── includes/ # 包含核心功能文件 │ ├── class-stock-api.php # 股票API处理类 │ ├── class-data-cache.php # 数据缓存类 │ └── class-shortcodes.php # 短代码处理类 ├── admin/ # 后台管理文件 │ ├── css/ # 后台样式 │ ├── js/ # 后台脚本 │ └── class-admin-settings.php # 设置页面类 ├── public/ # 前端文件 │ ├── css/ # 前端样式 │ ├── js/ # 前端脚本 │ └── templates/ # 前端模板 ├── assets/ # 静态资源 │ └── images/ # 图片资源 └── uninstall.php # 插件卸载脚本 1.3 插件主文件配置 首先,我们需要在插件主文件中添加标准的WordPress插件头部信息: <?php /** * Plugin Name: 实时股票行情与财经数据工具 * Plugin URI: https://yourwebsite.com/stock-plugin * Description: 在WordPress网站中集成实时股票行情、财经数据和小工具功能 * Version: 1.0.0 * Author: 您的名称 * Author URI: https://yourwebsite.com * License: GPL v2 or later * Text Domain: stock-finance-tool * Domain Path: /languages */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('SFT_VERSION', '1.0.0'); define('SFT_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('SFT_PLUGIN_URL', plugin_dir_url(__FILE__)); define('SFT_PLUGIN_BASENAME', plugin_basename(__FILE__)); // 插件初始化 add_action('plugins_loaded', 'sft_init_plugin'); function sft_init_plugin() { // 加载文本域用于国际化 load_plugin_textdomain('stock-finance-tool', false, dirname(SFT_PLUGIN_BASENAME) . '/languages'); // 检查依赖 if (!function_exists('curl_init')) { add_action('admin_notices', function() { echo '<div class="notice notice-error"><p>'; echo esc_html__('实时股票行情插件需要cURL扩展支持,请联系您的主机提供商启用cURL。', 'stock-finance-tool'); echo '</p></div>'; }); return; } // 引入核心类文件 require_once SFT_PLUGIN_DIR . 'includes/class-stock-api.php'; require_once SFT_PLUGIN_DIR . 'includes/class-data-cache.php'; require_once SFT_PLUGIN_DIR . 'includes/class-shortcodes.php'; // 初始化核心类 $stock_api = new SFT_Stock_API(); $data_cache = new SFT_Data_Cache(); $shortcodes = new SFT_Shortcodes(); // 根据环境加载管理或前端功能 if (is_admin()) { require_once SFT_PLUGIN_DIR . 'admin/class-admin-settings.php'; new SFT_Admin_Settings(); } else { // 前端初始化 add_action('wp_enqueue_scripts', 'sft_enqueue_frontend_assets'); } } // 注册激活和停用钩子 register_activation_hook(__FILE__, 'sft_activate_plugin'); register_deactivation_hook(__FILE__, 'sft_deactivate_plugin'); function sft_activate_plugin() { // 创建必要的数据库表 global $wpdb; $charset_collate = $wpdb->get_charset_collate(); $table_name = $wpdb->prefix . 'sft_stock_data'; $sql = "CREATE TABLE IF NOT EXISTS $table_name ( id mediumint(9) NOT NULL AUTO_INCREMENT, symbol varchar(20) NOT NULL, data longtext NOT NULL, last_updated datetime DEFAULT CURRENT_TIMESTAMP NOT NULL, PRIMARY KEY (id), UNIQUE KEY symbol (symbol) ) $charset_collate;"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); // 设置默认选项 add_option('sft_api_provider', 'alpha_vantage'); add_option('sft_cache_duration', 300); add_option('sft_default_symbols', 'AAPL,MSFT,GOOGL,AMZN,TSLA'); add_option('sft_currency', 'USD'); } function sft_deactivate_plugin() { // 清理定时任务 wp_clear_scheduled_hook('sft_daily_data_update'); } // 前端资源加载 function sft_enqueue_frontend_assets() { wp_enqueue_style( 'sft-frontend-style', SFT_PLUGIN_URL . 'public/css/frontend.css', array(), SFT_VERSION ); wp_enqueue_script( 'sft-frontend-script', SFT_PLUGIN_URL . 'public/js/frontend.js', array('jquery'), SFT_VERSION, true ); // 本地化脚本,传递数据到JavaScript wp_localize_script('sft-frontend-script', 'sft_ajax_object', array( 'ajax_url' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('sft_nonce') )); } 第二章:集成股票行情API 2.1 选择合适的财经数据API 市场上有多种财经数据API可供选择,每种都有其优缺点: Alpha Vantage:免费层提供较全面的数据,但调用频率有限制 Yahoo Finance (非官方API):免费但稳定性存疑 Twelve Data:提供免费和付费方案,数据质量较好 Finnhub:免费层提供实时数据,但功能有限 国内选择:新浪财经、腾讯财经等提供免费接口 本教程将以Alpha Vantage为例,但代码设计将支持多种API提供商。 2.2 创建API处理类 <?php // includes/class-stock-api.php class SFT_Stock_API { private $api_key; private $api_provider; private $cache_duration; public function __construct() { $this->api_key = get_option('sft_api_key', ''); $this->api_provider = get_option('sft_api_provider', 'alpha_vantage'); $this->cache_duration = get_option('sft_cache_duration', 300); // 注册AJAX处理 add_action('wp_ajax_sft_get_stock_data', array($this, 'ajax_get_stock_data')); add_action('wp_ajax_nopriv_sft_get_stock_data', array($this, 'ajax_get_stock_data')); } /** * 获取股票数据 */ public function get_stock_data($symbol, $force_refresh = false) { // 检查缓存 if (!$force_refresh) { $cached_data = $this->get_cached_data($symbol); if ($cached_data !== false) { return $cached_data; } } // 根据选择的API提供商调用相应方法 switch ($this->api_provider) { case 'alpha_vantage': $data = $this->get_alpha_vantage_data($symbol); break; case 'twelve_data': $data = $this->get_twelve_data_data($symbol); break; case 'yahoo': $data = $this->get_yahoo_data($symbol); break; default: $data = array('error' => '不支持的API提供商'); } // 缓存数据 if (!isset($data['error'])) { $this->cache_data($symbol, $data); } return $data; } /** * Alpha Vantage API调用 */ private function get_alpha_vantage_data($symbol) { if (empty($this->api_key)) { return array('error' => 'API密钥未配置'); } $url = add_query_arg(array( 'function' => 'GLOBAL_QUOTE', 'symbol' => $symbol, 'apikey' => $this->api_key ), 'https://www.alphavantage.co/query'); $response = wp_remote_get($url, array( 'timeout' => 15, 'user-agent' => 'WordPress Stock Finance Tool/' . SFT_VERSION )); if (is_wp_error($response)) { return array('error' => $response->get_error_message()); } $body = wp_remote_retrieve_body($response); $data = json_decode($body, true); if (isset($data['Global Quote']) && !empty($data['Global Quote'])) { $quote = $data['Global Quote']; return array( 'symbol' => $symbol, 'price' => $quote['05. price'], 'change' => $quote['09. change'], 'change_percent' => $quote['10. change percent'], 'volume' => $quote['06. volume'], 'latest_trading_day' => $quote['07. latest trading day'], 'previous_close' => $quote['08. previous close'], 'open' => $quote['02. open'], 'high' => $quote['03. high'], 'low' => $quote['04. low'] ); } elseif (isset($data['Error Message'])) { return array('error' => $data['Error Message']); } else { return array('error' => '无法解析API响应'); } } /** * 获取缓存数据 */ private function get_cached_data($symbol) { global $wpdb; $table_name = $wpdb->prefix . 'sft_stock_data'; $result = $wpdb->get_row($wpdb->prepare( "SELECT data, last_updated FROM $table_name WHERE symbol = %s", $symbol )); if ($result) { $last_updated = strtotime($result->last_updated); $current_time = current_time('timestamp'); // 检查缓存是否过期 if (($current_time - $last_updated) < $this->cache_duration) { return unserialize($result->data); } } return false; } /** * 缓存数据 */ private function cache_data($symbol, $data) { global $wpdb; $table_name = $wpdb->prefix . 'sft_stock_data'; $wpdb->replace( $table_name, array( 'symbol' => $symbol, 'data' => serialize($data), 'last_updated' => current_time('mysql') ), array('%s', '%s', '%s') ); } /** * AJAX获取股票数据 */ public function ajax_get_stock_data() { // 验证nonce if (!wp_verify_nonce($_POST['nonce'], 'sft_nonce')) { wp_die('安全验证失败'); } $symbol = sanitize_text_field($_POST['symbol']); $data = $this->get_stock_data($symbol); wp_send_json($data); } /** * 批量获取多个股票数据 */ public function get_multiple_stocks($symbols) { $results = array(); $symbol_array = explode(',', $symbols); foreach ($symbol_array as $symbol) { $symbol = trim($symbol); if (!empty($symbol)) { $results[$symbol] = $this->get_stock_data($symbol); } } return $results; } /** * 获取历史数据 */ public function get_historical_data($symbol, $interval = 'daily', $output_size = 'compact') { if (empty($this->api_key)) { return array('error' => 'API密钥未配置'); } $url = add_query_arg(array( 'function' => 'TIME_SERIES_' . strtoupper($interval), 'symbol' => $symbol, 'outputsize' => $output_size, 'apikey' => $this->api_key ), 'https://www.alphavantage.co/query'); $response = wp_remote_get($url); if (is_wp_error($response)) { return array('error' => $response->get_error_message()); } $body = wp_remote_retrieve_body($response); return json_decode($body, true); } } 第三章:创建数据缓存系统 3.1 设计高效缓存机制 为了提高插件性能并减少API调用次数,我们需要实现一个高效的缓存系统: <?php // includes/class-data-cache.php class SFT_Data_Cache { private $cache_group = 'sft_stock_data'; public function __construct() { // 注册清理过期缓存的定时任务 add_action('sft_clean_expired_cache', array($this, 'clean_expired_cache')); if (!wp_next_scheduled('sft_clean_expired_cache')) { wp_schedule_event(time(), 'daily', 'sft_clean_expired_cache'); } } /** * 设置缓存 */ public function set($key, $data, $expiration = 300) { $cache_data = array( 'data' => $data, 'expires' => time() + $expiration ); return wp_cache_set($key, $cache_data, $this->cache_group, $expiration); } /** * 获取缓存 */ public function get($key) { $cached = wp_cache_get($key, $this->cache_group); if ($cached === false) { return false; } // 检查是否过期 if (isset($cached['expires']) && $cached['expires'] < time()) { wp_cache_delete($key, $this->cache_group); return false; } return $cached['data']; } /** * 删除缓存 */ public function delete($key) { return wp_cache_delete($key, $this->cache_group); } /** * 清理所有插件缓存 */ public function flush() { return wp_cache_flush(); } /** * 清理过期缓存 */ public function clean_expired_cache() { global $wpdb; // 清理数据库缓存 $table_name = $wpdb->prefix . 'sft_stock_data'; $expiration_time = date('Y-m-d H:i:s', time() - 86400); // 24小时前的数据 $wpdb->query($wpdb->prepare( "DELETE FROM $table_name WHERE last_updated < %s", $expiration_time )); // 清理对象缓存中的旧数据 // 注意:wp_cache_flush()会清理所有缓存,可能影响其他插件 // 在生产环境中需要更精细的缓存管理 } /** * 获取缓存统计信息 */ public function get_stats() { global $wpdb; $table_name = $wpdb->prefix . 'sft_stock_data'; $total = $wpdb->get_var("SELECT COUNT(*) FROM $table_name"); $oldest = $wpdb->get_var("SELECT MIN(last_updated) FROM $table_name"); $newest = $wpdb->get_var("SELECT MAX(last_updated) FROM $table_name"); return array( 'total_cached' => $total, 'oldest_cache' => $oldest, 'newest_cache' => $newest ); } } 第四章:实现短代码功能 4.1 创建基本短代码 短代码是WordPress中非常强大的功能,允许用户在文章和页面中轻松插入动态内容: <?php // includes/class-shortcodes.php class SFT_Shortcodes { private $api; public function __construct() { $this->api = new SFT_Stock_API(); 4.1 创建基本短代码(续) // 注册短代码 add_shortcode('stock_quote', array($this, 'stock_quote_shortcode')); add_shortcode('stock_ticker', array($this, 'stock_ticker_shortcode')); add_shortcode('financial_data', array($this, 'financial_data_shortcode')); add_shortcode('currency_converter', array($this, 'currency_converter_shortcode')); add_shortcode('crypto_price', array($this, 'crypto_price_shortcode')); // 注册Gutenberg块 add_action('init', array($this, 'register_gutenberg_blocks')); } /** * 股票报价短代码 */ public function stock_quote_shortcode($atts) { $atts = shortcode_atts(array( 'symbol' => 'AAPL', 'show_change' => 'true', 'show_volume' => 'false', 'refresh' => '60', 'style' => 'default', 'currency' => 'USD' ), $atts, 'stock_quote'); // 获取股票数据 $data = $this->api->get_stock_data($atts['symbol']); if (isset($data['error'])) { return '<div class="sft-error">错误: ' . esc_html($data['error']) . '</div>'; } // 生成唯一ID用于JavaScript $widget_id = 'sft-stock-' . uniqid(); // 格式化输出 ob_start(); ?> <div id="<?php echo esc_attr($widget_id); ?>" class="sft-stock-quote sft-style-<?php echo esc_attr($atts['style']); ?>" data-symbol="<?php echo esc_attr($atts['symbol']); ?>" data-refresh="<?php echo esc_attr($atts['refresh']); ?>"> <div class="sft-stock-header"> <h4 class="sft-stock-symbol"><?php echo esc_html($atts['symbol']); ?></h4> <span class="sft-stock-price"><?php echo esc_html($this->format_currency($data['price'], $atts['currency'])); ?></span> </div> <?php if ($atts['show_change'] === 'true') : ?> <div class="sft-stock-change <?php echo (floatval($data['change']) >= 0) ? 'sft-positive' : 'sft-negative'; ?>"> <span class="sft-change-amount"><?php echo esc_html($data['change']); ?></span> <span class="sft-change-percent">(<?php echo esc_html($data['change_percent']); ?>)</span> </div> <?php endif; ?> <?php if ($atts['show_volume'] === 'true') : ?> <div class="sft-stock-volume"> 成交量: <?php echo esc_html($this->format_number($data['volume'])); ?> </div> <?php endif; ?> <div class="sft-stock-footer"> <span class="sft-updated-time">更新于: <?php echo esc_html(date('H:i:s')); ?></span> </div> </div> <script> jQuery(document).ready(function($) { // 自动刷新功能 var refreshInterval = <?php echo intval($atts['refresh']) * 1000; ?>; if (refreshInterval > 0) { setInterval(function() { $.ajax({ url: sft_ajax_object.ajax_url, type: 'POST', data: { action: 'sft_get_stock_data', symbol: '<?php echo esc_js($atts['symbol']); ?>', nonce: sft_ajax_object.nonce }, success: function(response) { if (!response.error) { $('#<?php echo esc_js($widget_id); ?> .sft-stock-price').text(response.price); $('#<?php echo esc_js($widget_id); ?> .sft-change-amount').text(response.change); $('#<?php echo esc_js($widget_id); ?> .sft-change-percent').text('(' + response.change_percent + ')'); $('#<?php echo esc_js($widget_id); ?> .sft-stock-volume').text('成交量: ' + response.volume); $('#<?php echo esc_js($widget_id); ?> .sft-updated-time').text('更新于: ' + new Date().toLocaleTimeString()); // 更新颜色 var changeClass = parseFloat(response.change) >= 0 ? 'sft-positive' : 'sft-negative'; $('#<?php echo esc_js($widget_id); ?> .sft-stock-change') .removeClass('sft-positive sft-negative') .addClass(changeClass); } } }); }, refreshInterval); } }); </script> <?php return ob_get_clean(); } /** * 股票行情滚动条短代码 */ public function stock_ticker_shortcode($atts) { $atts = shortcode_atts(array( 'symbols' => 'AAPL,MSFT,GOOGL,AMZN,TSLA', 'speed' => '50', 'direction' => 'left', 'separator' => '|', 'show_change' => 'true' ), $atts, 'stock_ticker'); $symbols = array_map('trim', explode(',', $atts['symbols'])); $data = $this->api->get_multiple_stocks($atts['symbols']); ob_start(); ?> <div class="sft-stock-ticker" data-speed="<?php echo esc_attr($atts['speed']); ?>" data-direction="<?php echo esc_attr($atts['direction']); ?>"> <div class="sft-ticker-container"> <div class="sft-ticker-content"> <?php foreach ($symbols as $symbol) : if (isset($data[$symbol]) && !isset($data[$symbol]['error'])) : $stock = $data[$symbol]; ?> <div class="sft-ticker-item"> <span class="sft-ticker-symbol"><?php echo esc_html($symbol); ?></span> <span class="sft-ticker-price"><?php echo esc_html($stock['price']); ?></span> <?php if ($atts['show_change'] === 'true') : ?> <span class="sft-ticker-change <?php echo (floatval($stock['change']) >= 0) ? 'sft-positive' : 'sft-negative'; ?>"> <?php echo esc_html($stock['change']); ?> (<?php echo esc_html($stock['change_percent']); ?>) </span> <?php endif; ?> <span class="sft-ticker-separator"><?php echo esc_html($atts['separator']); ?></span> </div> <?php endif; endforeach; ?> </div> </div> </div> <style> .sft-stock-ticker { overflow: hidden; white-space: nowrap; background: #f8f9fa; padding: 10px; border-radius: 4px; } .sft-ticker-container { display: inline-block; animation: sft-ticker-<?php echo esc_attr($atts['direction']); ?> <?php echo esc_attr(100000/$atts['speed']); ?>s linear infinite; } @keyframes sft-ticker-left { 0% { transform: translateX(100%); } 100% { transform: translateX(-100%); } } @keyframes sft-ticker-right { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } } </style> <?php return ob_get_clean(); } /** * 货币转换器短代码 */ public function currency_converter_shortcode($atts) { $atts = shortcode_atts(array( 'from' => 'USD', 'to' => 'EUR', 'amount' => '1', 'show_form' => 'true', 'update_frequency' => '3600' ), $atts, 'currency_converter'); $conversion_rate = $this->get_currency_rate($atts['from'], $atts['to']); $converted_amount = floatval($atts['amount']) * $conversion_rate; ob_start(); ?> <div class="sft-currency-converter" data-from="<?php echo esc_attr($atts['from']); ?>" data-to="<?php echo esc_attr($atts['to']); ?>"> <div class="sft-conversion-result"> <p> <?php echo esc_html($atts['amount']); ?> <?php echo esc_html($atts['from']); ?> = <strong><?php echo number_format($converted_amount, 4); ?></strong> <?php echo esc_html($atts['to']); ?> </p> <p class="sft-rate-info"> 汇率: 1 <?php echo esc_html($atts['from']); ?> = <?php echo number_format($conversion_rate, 6); ?> <?php echo esc_html($atts['to']); ?> </p> </div> <?php if ($atts['show_form'] === 'true') : ?> <div class="sft-converter-form"> <div class="sft-form-row"> <input type="number" class="sft-amount" value="<?php echo esc_attr($atts['amount']); ?>" step="0.01" min="0" placeholder="金额"> <select class="sft-from-currency"> <?php echo $this->get_currency_options($atts['from']); ?> </select> <span class="sft-convert-arrow">→</span> <select class="sft-to-currency"> <?php echo $this->get_currency_options($atts['to']); ?> </select> <button type="button" class="sft-convert-button">转换</button> </div> <div class="sft-form-result" style="display:none;"> <p class="sft-result-text"></p> </div> </div> <script> jQuery(document).ready(function($) { $('.sft-convert-button').on('click', function() { var $converter = $(this).closest('.sft-currency-converter'); var amount = $converter.find('.sft-amount').val(); var from = $converter.find('.sft-from-currency').val(); var to = $converter.find('.sft-to-currency').val(); $.ajax({ url: sft_ajax_object.ajax_url, type: 'POST', data: { action: 'sft_convert_currency', amount: amount, from: from, to: to, nonce: sft_ajax_object.nonce }, success: function(response) { if (response.success) { $converter.find('.sft-result-text').html( amount + ' ' + from + ' = <strong>' + response.result + '</strong> ' + to ); $converter.find('.sft-form-result').show(); } } }); }); }); </script> <?php endif; ?> </div> <?php return ob_get_clean(); } /** * 获取货币汇率 */ private function get_currency_rate($from, $to) { $cache_key = 'currency_rate_' . $from . '_' . $to; $cached_rate = get_transient($cache_key); if ($cached_rate !== false) { return $cached_rate; } // 使用免费汇率API $api_url = "https://api.exchangerate-api.com/v4/latest/{$from}"; $response = wp_remote_get($api_url); if (!is_wp_error($response)) { $data = json_decode(wp_remote_retrieve_body($response), true); if (isset($data['rates'][$to])) { $rate = $data['rates'][$to]; set_transient($cache_key, $rate, HOUR_IN_SECONDS); return $rate; } } // 备用汇率(如果API失败) $fallback_rates = array( 'USD_EUR' => 0.85, 'EUR_USD' => 1.18, 'USD_GBP' => 0.73, 'GBP_USD' => 1.37, 'USD_JPY' => 110.5, 'JPY_USD' => 0.0091 ); $key = $from . '_' . $to; return isset($fallback_rates[$key]) ? $fallback_rates[$key] : 1.0; } /** * 获取货币选项 */ private function get_currency_options($selected) { $currencies = array( 'USD' => '美元', 'EUR' => '欧元', 'GBP' => '英镑', 'JPY' => '日元', 'CNY' => '人民币', 'CAD' => '加元', 'AUD' => '澳元', 'CHF' => '瑞士法郎', 'HKD' => '港币' ); $options = ''; foreach ($currencies as $code => $name) { $selected_attr = ($code === $selected) ? ' selected' : ''; $options .= "<option value="{$code}"{$selected_attr}>{$name} ({$code})</option>"; } return $options; } /** * 格式化货币 */ private function format_currency($amount, $currency) { $formats = array( 'USD' => '$%s', 'EUR' => '€%s', 'GBP' => '£%s', 'JPY' => '¥%s', 'CNY' => '¥%s' ); $format = isset($formats[$currency]) ? $formats[$currency] : '%s ' . $currency; return sprintf($format, number_format(floatval($amount), 2)); } /** * 格式化数字(千位分隔符) */ private function format_number($number) { return number_format(floatval($number)); } /** * 注册Gutenberg块 */ public function register_gutenberg_blocks() { // 检查Gutenberg是否可用 if (!function_exists('register_block_type')) { return; } // 注册股票报价块 register_block_type('sft/stock-quote', array( 'editor_script' => 'sft-gutenberg-blocks', 'render_callback' => array($this, 'render_stock_quote_block'), 'attributes' => array( 'symbol' => array( 'type' => 'string', 'default' => 'AAPL' ), 'showChange' => array( 'type' => 'boolean', 'default' => true ), 'refreshInterval' => array( 'type' => 'number', 'default' => 60 ) ) )); } /** * 渲染Gutenberg块 */ public function render_stock_quote_block($attributes) { return $this->stock_quote_shortcode(array( 'symbol' => $attributes['symbol'], 'show_change' => $attributes['showChange'] ? 'true' : 'false', 'refresh' => $attributes['refreshInterval'] )); } } 第五章:创建管理后台界面 5.1 设置页面开发 <?php // admin/class-admin-settings.php class SFT_Admin_Settings { private $options_page; public function __construct() { add_action('admin_menu', array($this, 'add_admin_menu')); add_action('admin_init', array($this, 'register_settings')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); // 添加插件设置链接 add_filter('plugin_action_links_' . SFT_PLUGIN_BASENAME, array($this, 'add_settings_link')); } /** * 添加管理菜单 */ public function add_admin_menu() { $this->options_page = add_menu_page( '股票财经工具设置', '股票财经工具', 'manage_options', 'sft-settings', array($this, 'render_settings_page'), 'dashicons-chart-line', 80 ); // 添加子菜单 add_submenu_page( 'sft-settings', 'API设置', 'API设置', 'manage_options', 'sft-settings', array($this, 'render_settings_page') ); add_submenu_page( 'sft-settings', '小工具管理', '小工具管理', 'manage_options', 'sft-widgets', array($this, 'render_widgets_page') ); add_submenu_page( 'sft-settings', '数据缓存', '数据缓存', 'manage_options', 'sft-cache', array($this, 'render_cache_page') ); add_submenu_page( 'sft-settings', '使用说明', '使用说明', 'manage_options', 'sft-documentation', array($this, 'render_documentation_page') ); } /** * 渲染设置页面 */ public function render_settings_page() { if (!current_user_can('manage_options')) {

发表评论