子比主题限制用户修改头像次数,支持普通用户和两级会员分别设置-zibll教程分享社区-zibll子比主题-zibll子比社区

子比主题限制用户修改头像次数,支持普通用户和两级会员分别设置

分享一个子比主题的小功能:限制用户修改头像的频率,支持普通用户、一级会员、二级会员分别设置“每 X 天最多修改 X 次”。

比如可以设置为:

  • 普通用户:每 30 天最多修改 1 次
  • 一级会员:每 30 天最多修改 3 次
  • 二级会员:每 30 天最多修改 5 次

天数和次数都能自己调整,适合想控制头像频繁更换、给会员增加权益的站点。

功能说明

  • 按最近 X 天内的成功修改次数计算,超过限制会提示下次可修改时间。
  • 上传失败不扣次数。
  • 会员升级、降级或过期后,按当前等级的规则计算,已有记录不会清零。
  • 支持限制同时提交,避免通过并发请求绕过次数限制。
  • 次数设为 0 表示禁止修改,设为 -1 表示不限次数。

20261001201828233-152fd77c-ff7c-4810-9570-c45fbcd50237

使用方法

将下面代码放到func.php即可

/**
 * Zibll 9.1:头像修改次数限制。
 * 追加到已有 func.php 时,不要重复粘贴第一行 <?php。
 */
function zal_avatar_rules() {
    return array(
        0 => array('days' => 30, 'times' => 1), // 普通用户
        1 => array('days' => 30, 'times' => 3), // 一级会员
        2 => array('days' => 30, 'times' => 5), // 二级会员
    );
    // days 为正整数;times:0 禁止修改,-1 不限次数。
}

class ZAL_Avatar_Limit {
    private static $user_id = 0;
    private static $lock_name = '';

    public static function check() {
        $uid = get_current_user_id();
        if (!$uid) { return; } // 未登录由主题处理。
        // 无效请求交给主题,避免占用锁。
        if (empty($_POST['upload_avatar_nonce']) ||
            !wp_verify_nonce($_POST['upload_avatar_nonce'], 'upload_avatar') ||
            empty($_FILES['file'])) { return; }

        global $wpdb;
        $name = 'zal_avatar_' . md5(DB_NAME . ':' . $wpdb->prefix . ':' . $uid);
        // 数据库连接级互斥锁;请求退出/连接关闭时释放,不依赖缓存。
        if ((string) $wpdb->get_var($wpdb->prepare('SELECT GET_LOCK(%s, 0)', $name)) !== '1') {
            self::deny('头像修改正在处理中,请稍后再试。');
        }
        self::$lock_name = $name;
        self::$user_id = $uid;
        register_shutdown_function(array(__CLASS__, 'release'));

        $rules = zal_avatar_rules();
        $level = function_exists('zib_get_user_vip_level') ? (int) zib_get_user_vip_level($uid) : 0;
        if (!isset($rules[$level])) { $level = 0; }
        $days = max(1, (int) $rules[$level]['days']);
        $limit = (int) $rules[$level]['times'];
        if ($limit < 0) { return; }
        if ($limit === 0) { self::deny('你当前的用户等级暂不允许修改头像。'); }

        // 绕过旧的用户元数据缓存,在持锁后读取最新计数。
        wp_cache_delete($uid, 'user_meta');
        $history = get_user_meta($uid, '_zal_avatar_history', true);
        $cutoff = time() - $days * DAY_IN_SECONDS;
        $recent = array();
        foreach ((array) $history as $stamp) {
            if (is_numeric($stamp) && (int) $stamp > $cutoff) { $recent[] = (int) $stamp; }
        }
        sort($recent, SORT_NUMERIC);
        if (count($recent) >= $limit) {
            // 降级或调低额度后,也需等到窗口内少于额度才恢复。
            $next = $recent[count($recent) - $limit] + $days * DAY_IN_SECONDS;
            self::deny(sprintf('最近 %d 天内最多修改头像 %d 次,次数已用完。下次可修改时间:%s。',
                $days, $limit, wp_date('Y-m-d H:i', $next)));
        }
    }

    public static function saved($uid) {
        if ((int) $uid !== self::$user_id || !self::$lock_name) { return; }
        wp_cache_delete($uid, 'user_meta');
        $history = get_user_meta($uid, '_zal_avatar_history', true);
        $history = is_array($history) ? array_values(array_filter($history, 'is_numeric')) : array();
        $history[] = time();
        // 保留历史,以便之后增加天数或切换会员等级时重新计算。
        update_user_meta($uid, '_zal_avatar_history', $history);
        self::release();
    }

    public static function release() {
        if (!self::$lock_name) { return; }
        global $wpdb;
        $wpdb->get_var($wpdb->prepare('SELECT RELEASE_LOCK(%s)', self::$lock_name));
        self::$lock_name = '';
        self::$user_id = 0;
    }

    private static function deny($message) {
        self::release();
        wp_send_json(array('error' => 1, 'ys' => 'danger', 'msg' => $message));
    }
}

add_action('wp_ajax_user_upload_avatar', array('ZAL_Avatar_Limit', 'check'), -100);
add_action('user_save_custom_avatar', array('ZAL_Avatar_Limit', 'saved'), 10, 1);

修改代码顶部的配置即可:

0 => array('days' => 30, 'times' => 1), // 普通用户
1 => array('days' => 30, 'times' => 3), // 一级会员
2 => array('days' => 30, 'times' => 5), // 二级会员

其中,days 是天数,times 是允许修改的次数。例如普通用户每 7 天可修改 2 次,就将对应配置改为:

0 => array('days' => 7, 'times' => 2),

使用前注意

本代码已对照 子比主题 9.1 源码编写,次数从安装代码后开始记录,之前的修改无法追溯。限制针对子比原生前台头像上传功能,其他插件或后台修改头像不在限制范围内。

请登录后发表评论

    没有回复内容