首页 / 应用软件 / 手把手教程,为WordPress实现基于地理围栏的网站内容区域化展示工具

手把手教程,为WordPress实现基于地理围栏的网站内容区域化展示工具

手把手教程:为WordPress实现基于地理围栏的网站内容区域化展示工具

引言:地理围栏技术与网站内容区域化的价值

在当今全球化的互联网环境中,网站访问者可能来自世界各地。然而,并非所有内容都适合向所有地区的用户展示。基于地理围栏的内容区域化技术能够根据用户的地理位置,智能展示相关性强、合规性高的内容,这已成为现代网站开发的重要趋势。

地理围栏(Geo-fencing)是一种基于位置的服务技术,通过GPS、RFID、Wi-Fi或蜂窝数据等定位方式,在现实世界中创建一个虚拟的边界。当设备进入或离开这个边界时,可以触发预设的操作或通知。在网站开发中,这一技术可以用于根据用户的地理位置展示不同的内容、广告或功能。

对于WordPress网站而言,实现基于地理围栏的内容区域化展示具有多重价值:

  1. 提升用户体验:向用户展示与其地理位置相关的内容,如本地新闻、活动、产品等
  2. 合规性管理:根据不同地区的法律法规展示合规内容
  3. 营销精准化:针对不同地区实施差异化的营销策略
  4. 内容优化:根据地区特点优化内容展示,提高转化率

本教程将详细介绍如何通过WordPress代码二次开发,实现一个基于地理围栏的网站内容区域化展示工具,让您的网站具备智能化的区域内容展示能力。

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

1.1 理解WordPress开发基础

在开始开发之前,我们需要确保具备以下基础知识:

  • WordPress主题和插件的基本结构
  • PHP编程基础
  • JavaScript/jQuery基础
  • WordPress钩子(Hooks)和过滤器(Filters)的使用
  • 基本的HTML/CSS知识

1.2 开发环境搭建

  1. 本地开发环境:建议使用XAMPP、MAMP或Local by Flywheel等工具搭建本地WordPress开发环境
  2. 代码编辑器:推荐使用VS Code、PHPStorm或Sublime Text
  3. 版本控制:使用Git进行代码版本管理
  4. 测试工具:浏览器开发者工具、Postman等API测试工具

1.3 创建自定义插件

我们将创建一个独立的WordPress插件来实现地理围栏功能,这样可以确保功能的独立性和可移植性。

在WordPress的wp-content/plugins/目录下创建新文件夹geo-fencing-content,并在其中创建主插件文件:

<?php
/**
 * Plugin Name: 地理围栏内容区域化工具
 * Plugin URI: https://yourwebsite.com/
 * Description: 基于用户地理位置实现网站内容区域化展示的WordPress插件
 * Version: 1.0.0
 * Author: 您的名称
 * License: GPL v2 or later
 * Text Domain: geo-fencing-content
 */

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

// 定义插件常量
define('GFC_VERSION', '1.0.0');
define('GFC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('GFC_PLUGIN_URL', plugin_dir_url(__FILE__));

// 初始化插件
require_once GFC_PLUGIN_DIR . 'includes/class-geo-fencing-core.php';

function gfc_init() {
    $plugin = new Geo_Fencing_Core();
    $plugin->run();
}
add_action('plugins_loaded', 'gfc_init');

第二部分:地理定位技术与实现方案

2.1 地理位置获取方案比较

实现地理围栏功能的第一步是获取用户的准确地理位置。以下是几种常用的技术方案:

  1. HTML5 Geolocation API:浏览器原生支持,精度较高,但需要用户授权
  2. IP地址定位:通过用户IP地址推断地理位置,无需用户授权但精度有限
  3. 第三方定位服务:如MaxMind、IPinfo等专业服务,精度和可靠性较高

2.2 实现IP地址定位功能

我们将首先实现基于IP地址的地理定位功能,作为基础方案。创建includes/class-geo-location.php文件:

<?php
class Geo_Location {
    
    private $api_key;
    private $cache_time;
    
    public function __construct() {
        // 可以从插件设置中获取API密钥
        $this->api_key = get_option('gfc_ipapi_key', '');
        $this->cache_time = 24 * 60 * 60; // 缓存24小时
    }
    
    /**
     * 获取用户IP地址
     */
    public function get_user_ip() {
        $ip_keys = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR');
        
        foreach ($ip_keys as $key) {
            if (array_key_exists($key, $_SERVER) === true) {
                foreach (explode(',', $_SERVER[$key]) as $ip) {
                    $ip = trim($ip);
                    
                    // 验证IP地址格式
                    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
                        return $ip;
                    }
                }
            }
        }
        
        return $_SERVER['REMOTE_ADDR'];
    }
    
    /**
     * 通过IP地址获取地理位置信息
     */
    public function get_location_by_ip($ip = '') {
        if (empty($ip)) {
            $ip = $this->get_user_ip();
        }
        
        // 检查缓存
        $cache_key = 'gfc_location_' . md5($ip);
        $cached_data = get_transient($cache_key);
        
        if ($cached_data !== false) {
            return $cached_data;
        }
        
        // 使用ip-api.com免费服务(限制:45次/分钟)
        $url = "http://ip-api.com/json/{$ip}";
        
        if (!empty($this->api_key)) {
            // 如果有付费API密钥,可以使用更专业的服务
            // $url = "https://api.ipgeolocation.io/ipgeo?apiKey={$this->api_key}&ip={$ip}";
        }
        
        $response = wp_remote_get($url, array('timeout' => 5));
        
        if (is_wp_error($response)) {
            // 如果API请求失败,使用备用方法
            return $this->get_fallback_location();
        }
        
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);
        
        // 标准化返回数据
        $location_data = array(
            'country' => isset($data['country']) ? $data['country'] : '',
            'country_code' => isset($data['countryCode']) ? $data['countryCode'] : '',
            'region' => isset($data['region']) ? $data['region'] : '',
            'region_name' => isset($data['regionName']) ? $data['regionName'] : '',
            'city' => isset($data['city']) ? $data['city'] : '',
            'zip' => isset($data['zip']) ? $data['zip'] : '',
            'lat' => isset($data['lat']) ? $data['lat'] : 0,
            'lon' => isset($data['lon']) ? $data['lon'] : 0,
            'timezone' => isset($data['timezone']) ? $data['timezone'] : '',
            'isp' => isset($data['isp']) ? $data['isp'] : '',
            'ip' => $ip
        );
        
        // 缓存结果
        set_transient($cache_key, $location_data, $this->cache_time);
        
        return $location_data;
    }
    
    /**
     * 备用定位方法
     */
    private function get_fallback_location() {
        // 这里可以添加备用定位逻辑
        // 例如使用其他免费API或默认位置
        
        return array(
            'country' => '未知',
            'country_code' => 'UN',
            'region' => '',
            'city' => '',
            'lat' => 0,
            'lon' => 0,
            'ip' => $this->get_user_ip()
        );
    }
    
    /**
     * 计算两点之间的距离(用于地理围栏判断)
     */
    public function calculate_distance($lat1, $lon1, $lat2, $lon2, $unit = 'km') {
        $theta = $lon1 - $lon2;
        $dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) + 
                cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
        
        $dist = acos($dist);
        $dist = rad2deg($dist);
        $miles = $dist * 60 * 1.1515;
        
        switch ($unit) {
            case 'km':
                return $miles * 1.609344;
            case 'm':
                return $miles * 1.609344 * 1000;
            case 'mile':
            default:
                return $miles;
        }
    }
}

第三部分:地理围栏系统设计与实现

3.1 地理围栏数据结构设计

我们需要设计一个灵活的地理围栏系统,支持多种形状的围栏(圆形、多边形等)。首先在数据库中创建存储地理围栏的表。

创建数据库表的代码可以放在插件激活钩子中。在class-geo-fencing-core.php中添加:

class Geo_Fencing_Core {
    
    public function __construct() {
        // 构造函数
    }
    
    public function run() {
        // 注册激活钩子
        register_activation_hook(__FILE__, array($this, 'activate_plugin'));
        
        // 注册其他钩子和过滤器
        $this->register_hooks();
    }
    
    public function activate_plugin() {
        // 创建数据库表
        $this->create_database_tables();
        
        // 设置默认选项
        $this->set_default_options();
    }
    
    private function create_database_tables() {
        global $wpdb;
        
        $charset_collate = $wpdb->get_charset_collate();
        $table_name = $wpdb->prefix . 'geo_fences';
        
        $sql = "CREATE TABLE IF NOT EXISTS $table_name (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            name varchar(100) NOT NULL,
            fence_type varchar(20) NOT NULL DEFAULT 'circle',
            coordinates text NOT NULL,
            radius float DEFAULT 0,
            content_rule text,
            priority int(11) DEFAULT 0,
            is_active tinyint(1) DEFAULT 1,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY is_active (is_active),
            KEY priority (priority)
        ) $charset_collate;";
        
        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
        dbDelta($sql);
        
        // 创建内容关联表
        $relation_table = $wpdb->prefix . 'geo_fence_content';
        
        $sql2 = "CREATE TABLE IF NOT EXISTS $relation_table (
            id mediumint(9) NOT NULL AUTO_INCREMENT,
            fence_id mediumint(9) NOT NULL,
            content_type varchar(50) NOT NULL,
            content_id bigint(20) NOT NULL,
            action_type varchar(50) NOT NULL DEFAULT 'show',
            conditions text,
            PRIMARY KEY (id),
            KEY fence_id (fence_id),
            KEY content_type (content_type, content_id)
        ) $charset_collate;";
        
        dbDelta($sql2);
    }
}

3.2 地理围栏管理界面

我们需要创建一个管理界面,让网站管理员可以添加、编辑和删除地理围栏。创建admin/class-geo-fencing-admin.php

class Geo_Fencing_Admin {
    
    private $plugin_name;
    private $version;
    
    public function __construct($plugin_name, $version) {
        $this->plugin_name = $plugin_name;
        $this->version = $version;
        
        add_action('admin_menu', array($this, 'add_admin_menu'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
    }
    
    public function add_admin_menu() {
        add_menu_page(
            '地理围栏管理',
            '地理围栏',
            'manage_options',
            'geo-fencing',
            array($this, 'display_admin_page'),
            'dashicons-location-alt',
            30
        );
        
        add_submenu_page(
            'geo-fencing',
            '地理围栏列表',
            '围栏列表',
            'manage_options',
            'geo-fencing',
            array($this, 'display_admin_page')
        );
        
        add_submenu_page(
            'geo-fencing',
            '添加新围栏',
            '添加围栏',
            'manage_options',
            'geo-fencing-add',
            array($this, 'display_add_fence_page')
        );
        
        add_submenu_page(
            'geo-fencing',
            '地理围栏设置',
            '设置',
            'manage_options',
            'geo-fencing-settings',
            array($this, 'display_settings_page')
        );
    }
    
    public function display_admin_page() {
        include GFC_PLUGIN_DIR . 'admin/views/fence-list.php';
    }
    
    public function display_add_fence_page() {
        include GFC_PLUGIN_DIR . 'admin/views/fence-edit.php';
    }
    
    public function display_settings_page() {
        include GFC_PLUGIN_DIR . 'admin/views/settings.php';
    }
    
    public function enqueue_admin_scripts($hook) {
        if (strpos($hook, 'geo-fencing') === false) {
            return;
        }
        
        // 加载Leaflet地图库
        wp_enqueue_style('leaflet-css', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css');
        wp_enqueue_script('leaflet-js', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js', array(), '1.7.1', true);
        
        // 加载插件自定义脚本
        wp_enqueue_script(
            $this->plugin_name . '-admin',
            GFC_PLUGIN_URL . 'admin/js/admin.js',
            array('jquery', 'leaflet-js'),
            $this->version,
            true
        );
        
        wp_enqueue_style(
            $this->plugin_name . '-admin',
            GFC_PLUGIN_URL . 'admin/css/admin.css',
            array(),
            $this->version
        );
        
        // 传递数据到JavaScript
        wp_localize_script($this->plugin_name . '-admin', 'gfc_admin_data', array(
            'ajax_url' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('gfc_admin_nonce'),
            'default_lat' => get_option('gfc_default_lat', 39.9042),
            'default_lng' => get_option('gfc_default_lng', 116.4074)
        ));
    }
}

3.3 地理围栏编辑器实现

创建admin/views/fence-edit.php文件,实现围栏编辑界面:

<div class="wrap">
    <h1><?php echo isset($_GET['action']) && $_GET['action'] == 'edit' ? '编辑地理围栏' : '添加新地理围栏'; ?></h1>
    
    <form method="post" action="<?php echo admin_url('admin-post.php'); ?>" id="gfc-fence-form">
        <input type="hidden" name="action" value="gfc_save_fence">
        <?php wp_nonce_field('gfc_save_fence_action', 'gfc_fence_nonce'); ?>
        
        <?php if (isset($_GET['id'])): ?>
        <input type="hidden" name="fence_id" value="<?php echo intval($_GET['id']); ?>">
        <?php endif; ?>
        
        <table class="form-table">
            <tr>
                <th scope="row"><label for="fence_name">围栏名称</label></th>
                <td>
                    <input type="text" name="fence_name" id="fence_name" class="regular-text" required>
                    <p class="description">用于识别此地理围栏的名称</p>
                </td>
            </tr>
            
            <tr>
                <th scope="row"><label for="fence_type">围栏类型</label></th>
                <td>
                    <select name="fence_type" id="fence_type">
                        <option value="circle">圆形围栏</option>
                        <option value="polygon">多边形围栏</option>
                        <option value="rectangle">矩形围栏</option>
                    </select>
                </td>
            </tr>
            
            <tr id="circle_fields">
                <th scope="row"><label for="center_point">中心点坐标</label></th>
                <td>
                    <div class="coordinates-input">
                        <input type="text" name="center_lat" id="center_lat" placeholder="纬度" class="small-text">
                        <input type="text" name="center_lng" id="center_lng" placeholder="经度" class="small-text">
                        <button type="button" class="button" id="get_current_location">获取当前位置</button>
                    </div>
                    <p class="description">格式:纬度,经度(例如:39.9042,116.4074)</p>
                </td>
            </tr>
            
            <tr id="circle_radius_field">
                <th scope="row"><label for="radius">半径</label></th>
                <td>
                    <input type="number" name="radius" id="radius" min="0.1" step="0.1" class="small-text">
                    <select name="radius_unit">
                        <option value="km">公里</option>
                        <option value="m">米</option>
                        <option value="mile">英里</option>
                    </select>
                </td>
            </tr>
            
            <tr id="polygon_fields" style="display:none;">

<tr id="polygon_fields" style="display:none;">

            <th scope="row"><label for="polygon_coords">多边形坐标</label></th>
            <td>
                <textarea name="polygon_coords" id="polygon_coords" rows="5" cols="50" placeholder="格式:纬度,经度&#10;例如:&#10;39.9042,116.4074&#10;39.9142,116.4174&#10;39.8942,116.4274"></textarea>
                <p class="description">每行输入一个坐标点,格式:纬度,经度</p>
            </td>
        </tr>
        
        <tr>
            <th scope="row"><label for="fence_priority">优先级</label></th>
            <td>
                <input type="number" name="fence_priority" id="fence_priority" value="0" min="0" class="small-text">
                <p class="description">数值越大优先级越高,当用户位于多个围栏重叠区域时生效</p>
            </td>
        </tr>
        
        <tr>
            <th scope="row"><label for="is_active">状态</label></th>
            <td>
                <label>
                    <input type="checkbox" name="is_active" id="is_active" value="1" checked> 启用此围栏
                </label>
            </td>
        </tr>
    </table>
    
    <div class="map-container">
        <h3>地图预览</h3>
        <div id="fence_map" style="height: 400px; width: 100%;"></div>
        <p class="description">在地图上点击可以设置围栏中心点或添加多边形顶点</p>
    </div>
    
    <h2>内容规则设置</h2>
    <div id="content_rules">
        <div class="content-rule">
            <h4>规则 #1</h4>
            <table class="form-table">
                <tr>
                    <th scope="row"><label>内容类型</label></th>
                    <td>
                        <select name="content_type[]" class="content-type-select">
                            <option value="post">文章</option>
                            <option value="page">页面</option>
                            <option value="category">分类目录</option>
                            <option value="custom">自定义内容</option>
                        </select>
                    </td>
                </tr>
                <tr>
                    <th scope="row"><label>内容选择</label></th>
                    <td>
                        <select name="content_id[]" class="content-id-select" style="width: 300px;">
                            <option value="">请选择内容</option>
                        </select>
                    </td>
                </tr>
                <tr>
                    <th scope="row"><label>操作类型</label></th>
                    <td>
                        <select name="action_type[]">
                            <option value="show">显示内容</option>
                            <option value="hide">隐藏内容</option>
                            <option value="replace">替换内容</option>
                            <option value="redirect">重定向</option>
                        </select>
                    </td>
                </tr>
                <tr class="replace-content" style="display:none;">
                    <th scope="row"><label>替换内容</label></th>
                    <td>
                        <textarea name="replace_content[]" rows="3" cols="50" placeholder="输入替换内容或短代码"></textarea>
                    </td>
                </tr>
                <tr class="redirect-url" style="display:none;">
                    <th scope="row"><label>重定向URL</label></th>
                    <td>
                        <input type="url" name="redirect_url[]" class="regular-text" placeholder="https://">
                    </td>
                </tr>
            </table>
            <hr>
        </div>
    </div>
    
    <button type="button" class="button" id="add_content_rule">添加新规则</button>
    
    <p class="submit">
        <input type="submit" name="submit" id="submit" class="button button-primary" value="保存围栏">
    </p>
</form>

</div>

<script>
jQuery(document).ready(function($) {

// 初始化地图
var map = L.map('fence_map').setView([39.9042, 116.4074], 10);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

// 地图点击事件
var marker;
var polygonPoints = [];
var polygonLayer;

map.on('click', function(e) {
    var lat = e.latlng.lat;
    var lng = e.latlng.lng;
    
    $('#center_lat').val(lat.toFixed(6));
    $('#center_lng').val(lng.toFixed(6));
    
    // 更新地图标记
    if (marker) {
        map.removeLayer(marker);
    }
    marker = L.marker([lat, lng]).addTo(map);
    
    // 如果是多边形模式,添加顶点
    if ($('#fence_type').val() === 'polygon') {
        polygonPoints.push([lat, lng]);
        updatePolygonPreview();
    }
});

// 围栏类型切换
$('#fence_type').on('change', function() {
    var type = $(this).val();
    
    if (type === 'circle') {
        $('#circle_fields, #circle_radius_field').show();
        $('#polygon_fields').hide();
    } else if (type === 'polygon') {
        $('#circle_fields, #circle_radius_field').hide();
        $('#polygon_fields').show();
        polygonPoints = [];
        if (polygonLayer) {
            map.removeLayer(polygonLayer);
        }
    }
});

// 更新多边形预览
function updatePolygonPreview() {
    if (polygonLayer) {
        map.removeLayer(polygonLayer);
    }
    
    if (polygonPoints.length >= 3) {
        polygonLayer = L.polygon(polygonPoints, {color: 'blue'}).addTo(map);
        map.fitBounds(polygonLayer.getBounds());
        
        // 更新坐标文本
        var coordsText = polygonPoints.map(function(point) {
            return point[0].toFixed(6) + ',' + point[1].toFixed(6);
        }).join('n');
        $('#polygon_coords').val(coordsText);
    }
}

// 获取当前位置
$('#get_current_location').on('click', function() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function(position) {
            var lat = position.coords.latitude;
            var lng = position.coords.longitude;
            
            $('#center_lat').val(lat.toFixed(6));
            $('#center_lng').val(lng.toFixed(6));
            
            // 更新地图
            if (marker) {
                map.removeLayer(marker);
            }
            marker = L.marker([lat, lng]).addTo(map);
            map.setView([lat, lng], 13);
        });
    } else {
        alert('您的浏览器不支持地理位置功能');
    }
});

// 操作类型切换
$(document).on('change', 'select[name="action_type[]"]', function() {
    var actionType = $(this).val();
    var ruleDiv = $(this).closest('.content-rule');
    
    ruleDiv.find('.replace-content, .redirect-url').hide();
    
    if (actionType === 'replace') {
        ruleDiv.find('.replace-content').show();
    } else if (actionType === 'redirect') {
        ruleDiv.find('.redirect-url').show();
    }
});

// 添加新规则
$('#add_content_rule').on('click', function() {
    var ruleCount = $('#content_rules .content-rule').length + 1;
    var newRule = $('#content_rules .content-rule:first').clone();
    
    newRule.find('h4').text('规则 #' + ruleCount);
    newRule.find('input, textarea, select').val('');
    newRule.find('.replace-content, .redirect-url').hide();
    
    $('#content_rules').append(newRule);
});

});
</script>


## 第四部分:核心功能实现与集成

### 4.1 地理围栏检测算法

创建`includes/class-geo-fence-detector.php`,实现围栏检测逻辑:

<?php
class Geo_Fence_Detector {


private $location_service;

public function __construct($location_service) {
    $this->location_service = $location_service;
}

/**
 * 检测用户是否在指定围栏内
 */
public function is_user_in_fence($fence_data, $user_location = null) {
    if (empty($user_location)) {
        $user_location = $this->location_service->get_location_by_ip();
    }
    
    if (empty($user_location['lat']) || empty($user_location['lon'])) {
        return false;
    }
    
    $user_lat = floatval($user_location['lat']);
    $user_lng = floatval($user_location['lon']);
    
    switch ($fence_data['fence_type']) {
        case 'circle':
            return $this->check_circle_fence($fence_data, $user_lat, $user_lng);
            
        case 'polygon':
            return $this->check_polygon_fence($fence_data, $user_lat, $user_lng);
            
        case 'rectangle':
            return $this->check_rectangle_fence($fence_data, $user_lat, $user_lng);
            
        default:
            return false;
    }
}

/**
 * 检测圆形围栏
 */
private function check_circle_fence($fence_data, $user_lat, $user_lng) {
    $center_coords = explode(',', $fence_data['coordinates']);
    if (count($center_coords) !== 2) {
        return false;
    }
    
    $center_lat = floatval(trim($center_coords[0]));
    $center_lng = floatval(trim($center_coords[1]));
    $radius = floatval($fence_data['radius']);
    
    // 计算距离
    $distance = $this->location_service->calculate_distance(
        $center_lat, $center_lng, 
        $user_lat, $user_lng, 
        'km'
    );
    
    return $distance <= $radius;
}

/**
 * 检测多边形围栏(使用射线法)
 */
private function check_polygon_fence($fence_data, $user_lat, $user_lng) {
    $coordinates = $this->parse_polygon_coordinates($fence_data['coordinates']);
    if (count($coordinates) < 3) {
        return false;
    }
    
    $inside = false;
    $n = count($coordinates);
    
    for ($i = 0, $j = $n - 1; $i < $n; $j = $i++) {
        $xi = $coordinates[$i][0];
        $yi = $coordinates[$i][1];
        $xj = $coordinates[$j][0];
        $yj = $coordinates[$j][1];
        
        $intersect = (($yi > $user_lng) != ($yj > $user_lng))
            && ($user_lat < ($xj - $xi) * ($user_lng - $yi) / ($yj - $yi) + $xi);
        
        if ($intersect) {
            $inside = !$inside;
        }
    }
    
    return $inside;
}

/**
 * 解析多边形坐标
 */
private function parse_polygon_coordinates($coords_string) {
    $coordinates = array();
    $lines = explode("n", $coords_string);
    
    foreach ($lines as $line) {
        $line = trim($line);
        if (empty($line)) continue;
        
        $parts = explode(',', $line);
        if (count($parts) === 2) {
            $lat = floatval(trim($parts[0]));
            $lng = floatval(trim($parts[1]));
            $coordinates[] = array($lat, $lng);
        }
    }
    
    return $coordinates;
}

/**
 * 检测矩形围栏
 */
private function check_rectangle_fence($fence_data, $user_lat, $user_lng) {
    $bounds = explode('|', $fence_data['coordinates']);
    if (count($bounds) !== 4) {
        return false;
    }
    
    $min_lat = floatval($bounds[0]);
    $max_lat = floatval($bounds[1]);
    $min_lng = floatval($bounds[2]);
    $max_lng = floatval($bounds[3]);
    
    return ($user_lat >= $min_lat && $user_lat <= $max_lat && 
            $user_lng >= $min_lng && $user_lng <= $max_lng);
}

/**
 * 获取用户所在的所有围栏
 */
public function get_user_fences($user_location = null) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'geo_fences';
    
    // 获取所有启用的围栏
    $fences = $wpdb->get_results(
        "SELECT * FROM $table_name WHERE is_active = 1 ORDER BY priority DESC",
        ARRAY_A
    );
    
    $user_fences = array();
    
    foreach ($fences as $fence) {
        if ($this->is_user_in_fence($fence, $user_location)) {
            $user_fences[] = $fence;
        }
    }
    
    return $user_fences;
}

/**
 * 获取适用于用户的内容规则
 */
public function get_content_rules_for_user() {
    $user_fences = $this->get_user_fences();
    $all_rules = array();
    
    foreach ($user_fences as $fence) {
        $fence_rules = $this->get_fence_content_rules($fence['id']);
        $all_rules = array_merge($all_rules, $fence_rules);
    }
    
    // 按优先级排序
    usort($all_rules, function($a, $b) {
        return $b['priority'] - $a['priority'];
    });
    
    return $all_rules;
}

/**
 * 获取围栏的内容规则
 */
private function get_fence_content_rules($fence_id) {
    global $wpdb;
    $table_name = $wpdb->prefix . 'geo_fence_content';
    
    return $wpdb->get_results(
        $wpdb->prepare(
            "SELECT * FROM $table_name WHERE fence_id = %d",
            $fence_id
        ),
        ARRAY_A
    );
}

}


### 4.2 WordPress内容过滤与替换

创建`includes/class-content-filter.php`,实现内容过滤功能:

<?php
class Geo_Content_Filter {


private $fence_detector;
private $user_rules;

public function __construct($fence_detector) {
    $this->fence_detector = $fence_detector;
    $this->user_rules = null;
}

public function init() {
    // 只在非管理页面应用规则
    if (!is_admin()) {
        // 文章内容过滤
        add_filter('the_content', array($this, 'filter_post_content'), 10, 1);
        
        // 小工具过滤
        add_filter('widget_display_callback', array($this, 'filter_widget'), 10, 3);
        
        // 菜单过滤
        add_filter('wp_nav_menu_objects', array($this, 'filter_menu_items'), 10, 2);
        
        // 重定向处理
        add_action('template_redirect', array($this, 'handle_redirects'));
        
        // 短代码支持
        add_shortcode('geo_content', array($this, 'geo_content_shortcode'));
    }
}

/**
 * 获取用户规则(懒加载)
 */
private function get_user_rules() {
    if ($this->user_rules === null) {
        $this->user_rules = $this->fence_detector->get_content_rules_for_user();
    }
    return $this->user_rules;
}

/**
 * 过滤文章内容
 */
public function filter_post_content($content) {
    global $post;
    
    if (empty($post)) {
        return $content;
    }
    
    $rules = $this->get_user_rules();
    
    foreach ($rules as $rule) {
        if ($this->rule_applies_to_content($rule, $post)) {
            $content = $this->apply_rule_to_content($rule, $content, $post);
        }
    }
    
    return $content;
}

/**
 * 检查规则是否适用于当前内容
 */
private function rule_applies_to_content($rule, $post) {
    switch ($rule['content_type']) {
        case 'post':
            return ($post->post_type == 'post' && $rule['content_id'] == $post->ID);
            
        case 'page':
            return ($post->post_type == 'page' && $rule['content_id'] == $post->ID);
            
        case 'category':
            return has_category($rule['content_id'], $post);
            
        case 'custom':
            // 自定义规则,可以扩展
            return $this->check_custom_rule($rule, $post);
            
        default:
            return false;
    }
}

/**
 * 应用规则到内容
 */
private function apply_rule_to_content($rule, $content, $post) {
    switch ($rule['action_type']) {
        case 'hide':
            return ''; // 完全隐藏内容
            
        case 'replace':
            return $this->get_replacement_content($rule);
            
        case 'show':
            // 默认就是
本文来自网络,不代表柔性供应链服务中心立场,转载请注明出处:https://mall.org.cn/5290.html

EXCHANGES®作者

上一篇
下一篇

为您推荐

发表回复

联系我们

联系我们

18559313275

在线咨询: QQ交谈

邮箱: vip@exchanges.center

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