discuz纯代码实现隐藏内容VIP用户组直接可见,高级进阶版本

功能:

1、特定用户组直接查看 [hide] 内容:如管理员、版主、VIP 无需回复即可见。
2、不同用户组 24 小时查看次数限制:如管理员 10 次,版主 5 次,VIP 3 次。
3、每次查看后提示剩余次数:显示“您今日剩余查看次数:X 次”。
4、超限提示:24 小时内超限后提示无法查看。
5、刷新页面不扣次数:确保同一帖子在同一会话(session)内刷新不重复扣除查看次数。
6、数据库要求:使用 InnoDB 引擎,utf8mb4_unicode_ci 字符集。
7、兼容性:Discuz! X3.4/X3.5,非指定用户组或未登录用户需回复查看。

实现步骤
步骤 1:创建数据库表
创建 pre_hide_view_log 表,记录用户查看次数和时间,满足 InnoDB 和 utf8mb4_unicode_ci 要求。

SQL 语句:

CREATE TABLE `pre_hide_view_log` (
  `uid` INT(10) UNSIGNED NOT NULL COMMENT '用户ID,与pre_common_member一致',
  `view_count` INT(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '24小时内查看次数',
  `last_view_time` INT(10) UNSIGNED NOT NULL DEFAULT 0 COMMENT '最后查看时间戳',
  `viewed_tids` TEXT NOT NULL COMMENT '已查看的帖子ID(JSON格式)',
  PRIMARY KEY (`uid`),
  INDEX `idx_last_view_time` (`last_view_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='记录用户查看[hide]内容的日志';

说明:

uid:用户 ID,主键。
view_count:24 小时内查看次数。
last_view_time:最后查看时间戳。
InnoDB:支持事务和并发。
utf8mb4_unicode_ci:支持表情符号和多语言。
执行:

使用 phpMyAdmin、MySQL 客户端或 Discuz! 后台“数据库”工具运行。
验证:SHOW CREATE TABLE pre_hide_view_log;。
步骤 2:添加语言变量
为提示信息添加多语言支持,编辑语言文件(如 data/language/forum/lang_template.php)或在代码中定义。

语言变量(插入 lang_template.php 或直接在代码中使用):

$lang['hide_remaining_views'] = '您今日剩余查看次数:{count} 次';
$lang['hide_view_limit_exceeded'] = '您24小时内的查看次数已达上限({count}次),请明天再试!';
$lang['hide_system_error'] = '系统错误,请稍后再试!';

说明:

{count} 为占位符,动态替换次数。
若不改语言文件,可在代码中定义 $lang 数组。
步骤 3:修改核心文件
修改 source/function/function_discuzcode.php,实现核心逻辑和美化提示。

完整代码(替换或插入 [hide] 相关逻辑):

if (strpos($message, '[hide]') !== false) {
    global $_G;
    // 定义用户组与24小时查看次数限制(管理员除外)
    $group_limits = array(
        2 => 5,  // 超级版主:5次
        3 => 5,  // 版主:5次
        10 => 3  // VIP用户组:3次
    );
    $time_window = 86400; // 24小时(秒)

    // 中文语言变量
    $lang = array(
        'hide_remaining_views' => '您今日剩余查看次数:%d 次',
        'hide_view_limit_exceeded' => '您24小时内的查看次数已达上限(%d次),请明天再试!',
        'hide_system_error' => '系统错误,请稍后再试!',
        'hide_admin_unlimited' => '管理员权限:无查看次数限制'
    );

    // 检查是否已登录
    if ($_G['uid']) {
        $current_time = TIMESTAMP;
        // 获取帖子ID,增强回退机制
        $tid = isset($_G['tid']) ? intval($_G['tid']) : (isset($_GET['tid']) ? intval($_GET['tid']) : 0);

        // 管理员(groupid=1)无限制
        if ($_G['groupid'] == 1) {
            $message = preg_replace("/\[hide\](.*?)\[\/hide\]/is", "\\1<div class=\"hide-notice fade-in\">{$lang['hide_admin_unlimited']}</div>", $message);
        }
        // 其他受限用户组
        elseif (isset($group_limits[$_G['groupid']])) {
            $max_views = $group_limits[$_G['groupid']];

            // 初始化 session
            if (!isset($_SESSION['hide_viewed_tids'])) {
                $_SESSION['hide_viewed_tids'] = array();
            } elseif (!is_array($_SESSION['hide_viewed_tids'])) {
                $_SESSION['hide_viewed_tids'] = array();
            }

            // 获取查看记录
            $view_log = DB::fetch_first("SELECT * FROM ".DB::table('hide_view_log')." WHERE uid = ".$_G['uid']);

            // 事务处理
            DB::query("START TRANSACTION");
            try {
                if ($view_log) {
                    // 检查是否需要重置
                    if ($current_time - $view_log['last_view_time'] >= $time_window) {
                        DB::update('hide_view_log', array(
                            'view_count' => 0,
                            'last_view_time' => $current_time,
                            'viewed_tids' => ''
                        ), array('uid' => $_G['uid']));
                        $view_count = 0;
                        $viewed_tids = array();
                        $_SESSION['hide_viewed_tids'] = array();
                    } else {
                        $view_count = $view_log['view_count'];
                        $viewed_tids = $view_log['viewed_tids'] ? json_decode($view_log['viewed_tids'], true) : array();
                        if (!is_array($viewed_tids)) {
                            $viewed_tids = array();
                        }
                    }
                } else {
                    // 新用户
                    DB::insert('hide_view_log', array(
                        'uid' => $_G['uid'],
                        'view_count' => 0,
                        'last_view_time' => $current_time,
                        'viewed_tids' => ''
                    ));
                    $view_count = 0;
                    $viewed_tids = array();
                }

                // 判断是否计次
                $should_count = ($tid && !in_array($tid, $viewed_tids) && !in_array($tid, $_SESSION['hide_viewed_tids']));

                // 检查次数限制
                if ($view_count < $max_views) {
                    if ($should_count && $tid) {
                        // 新帖子
                        $new_count = $view_count + 1;
                        $viewed_tids[] = $tid;
                        if (count($viewed_tids) > 100) {
                            $viewed_tids = array_slice($viewed_tids, -100);
                        }
                        DB::update('hide_view_log', array(
                            'view_count' => $new_count,
                            'last_view_time' => $current_time,
                            'viewed_tids' => json_encode($viewed_tids)
                        ), array('uid' => $_G['uid']));
                        $_SESSION['hide_viewed_tids'][] = $tid;
                        if (count($_SESSION['hide_viewed_tids']) > 100) {
                            $_SESSION['hide_viewed_tids'] = array_slice($_SESSION['hide_viewed_tids'], -100);
                        }
                    } else {
                        // 刷新
                        $new_count = $view_count;
                    }
                    $remaining_views = $max_views - $new_count;
                    $notice = sprintf($lang['hide_remaining_views'], $remaining_views);
                    $message = preg_replace("/\[hide\](.*?)\[\/hide\]/is", "\\1<div class=\"hide-notice fade-in\">$notice</div>", $message);
                } else {
                    // 超限
                    $error = sprintf($lang['hide_view_limit_exceeded'], $max_views);
                    $message = preg_replace("/\[hide\](.*?)\[\/hide\]/is", "<div class=\"hide-locked fade-in\">$error</div>", $message);
                }
                DB::query("COMMIT");
            } catch (Exception $e) {
                DB::query("ROLLBACK");
                $message = preg_replace("/\[hide\](.*?)\[\/hide\]/is", "<div class=\"hide-locked fade-in\">{$lang['hide_system_error']}</div>", $message);
            }
        } else {
            // 非指定用户组
            $message = preg_replace("/\[hide\](.*?)\[\/hide\]/is", "<div class=\"locked\">需回复后查看隐藏内容</div>", $message);
        }
    } else {
        // 未登录
        $message = preg_replace("/\[hide\](.*?)\[\/hide\]/is", "<div class=\"locked\">需回复后查看隐藏内容</div>", $message);
    }
}

代码说明:

用户组与限制:
$group_limits 定义用户组和 24 小时上限(可调整,如 11 => 5)。
示例:管理员(10 次)、版主(5 次)、VIP(3 次)。
刷新控制:
$_G['session']['hide_viewed_tids'] 记录已查看的 tid。
$should_count 判断是否为新帖子:
新帖子:计数,记录 tid。
刷新:不计数,显示当前剩余次数。
重置时清空 hide_viewed_tids。
计数逻辑:
查询 pre_hide_view_log,获取 view_count 和 last_view_time。
超过 24 小时($time_window = 86400),重置计数和 session。
未达上限时更新计数(仅新帖子)。
事务:
START TRANSACTION 和 COMMIT 确保 InnoDB 一致性。
异常时 ROLLBACK,显示错误。
提示:
成功:<div class="hide-notice fade-in">剩余 X 次</div>。
超限:<div class="hide-locked fade-in">上限(X 次)</div>。
错误:<div class="hide-locked fade-in">系统错误</div>。
使用 $lang 支持多语言。
字符集:
utf8mb4_unicode_ci 兼容表情和多语言。
默认逻辑:
非指定用户组或未登录显示“需回复查看”。
步骤 4:添加美化样式
编辑 template/default/common/common.css(或当前主题的 CSS 文件),添加美化样式:

/* 剩余次数或管理员提示 */
.hide-notice {
    margin-top: 10px;
    padding: 10px 15px;
    background: linear-gradient(135deg, #e6f4ea 0%, #d4edda 100%);
    border: 1px solid #c3e6cb;
    border-radius: 8px;
    color: #155724;
    font-size: 14px;
    display: flex;
    align-items: center;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.hide-notice::before {
    content: "\2713"; /* 勾选图标 */
    margin-right: 8px;
    font-weight: bold;
}

/* 超限或错误提示 */
.hide-locked {
    margin-top: 10px;
    padding: 10px 15px;
    background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
    border: 1px solid #f5c6cb;
    border-radius: 8px;
    color: #721c24;
    font-size: 14px;
    display: flex;
    align-items: center;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.hide-locked::before {
    content: "\2717"; /* 叉号图标 */
    margin-right: 8px;
    font-weight: bold;
}

/* 淡入动画 */
.fade-in {
    animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
    0% { opacity: 0; transform: translateY(10px); }
    100% { opacity: 1; transform: translateY(0); }
}

样式说明:

.hide-notice:
渐变背景(绿色系,清新)。
圆角(8px),阴影提升立体感。
勾选图标(✓),增强反馈。
弹性布局,文字居中对齐。
.hide-locked:
渐变背景(红色系,警告感)。
叉号图标(✗),突出错误。
同圆角和阴影。
.fade-in:
0.5 秒淡入动画,提示从下方滑入。
兼容性:
样式适配大多数 Discuz! 主题。
无依赖外部库,纯 CSS。
步骤 5:清理缓存
修改完成后,登录 Discuz! 后台:

导航至 工具 -> 更新缓存。
选择“全部”并提交,确保代码和样式生效。
步骤 6:测试
全面验证功能、美化和刷新逻辑:

数据库:
检查:SHOW CREATE TABLE pre_hide_view_log;
确认:InnoDB,utf8mb4_unicode_ci。
管理员(ID=1):
登录,查看 [hide] 帖子:
帖子 A 第 1 次:显示内容 + 绿色提示“剩余 9 次”(圆角、动画)。
刷新帖子 A:仍为“剩余 9 次”,view_count 不变。
帖子 B 第 1 次:显示“剩余 8 次”。
第 10 次:显示“剩余 0 次”。
第 11 次:显示红色提示“上限(10 次)”。
24 小时后(或修改 last_view_time 减 86400):确认重置为“剩余 9 次”。
数据库:SELECT * FROM pre_hide_view_log;。
版主(ID=2/3):
登录:
帖子 A 第 1 次:显示“剩余 4 次”。
刷新:仍为“剩余 4 次”。
第 5 次:显示“剩余 0 次”。
第 6 次:显示“上限(5 次)”。
VIP(ID=10):
登录:
帖子 A 第 1 次:显示“剩余 2 次”。
刷新:仍为“剩余 2 次”。
第 3 次:显示“剩余 0 次”。
第 4 次:显示“上限(3 次)”。
刷新逻辑:
多次刷新,确认 view_count 不增,提示一致。
新帖子触发计数,session 记录 tid。
样式:
确认提示框:
绿色(剩余次数):渐变、圆角、勾选图标、淡入。
红色(超限/错误):渐变、圆角、叉号图标、淡入。
检查动画流畅性。
其他用户:
普通会员/未登录:显示“需回复查看”。
数据库无记录。
字符集:
帖子含表情(如 😊)或多语言(如中文、日文),确认正常。
并发:
多用户同时查看,确认计数正确(InnoDB 行级锁)。
错误:
模拟数据库错误,确认显示“系统错误”。
注意事项
备份:
备份 function_discuzcode.php、common.css 和数据库。
导出 pre_common_* 表。
用户组 ID:
后台(用户 -> 用户组)确认。
示例:VIP 组 ID 为 11,更新:

$group_limits = array(1 => 10, 2 => 5, 3 => 5, 11 => 3);

刷新控制:
$_G['session']['hide_viewed_tids'] 依赖 session。
Session 过期(默认 1 小时)不影响计数(数据库控制)。
若需持久化,扩展 pre_hide_view_log:
sql

ALTER TABLE pre_hide_view_log ADD `viewed_tids` TEXT;

InnoDB:
行级锁支持高并发。
事务确保一致性。
utf8mb4_unicode_ci:
确保 config_global.php 的 charset 兼容。
检查 pre_common_setting 的 bbcode。
性能:
适合中小型论坛。
高流量可加缓存:

$cache_key = 'hide_view_'.$_G['uid'];
$view_log = memory('get', $cache_key);
if ($view_log === false) {
    $view_log = DB::fetch_first("SELECT * FROM ".DB::table('hide_view_log')." WHERE uid = ".$_G['uid']);
    memory('set', $cache_key, $view_log, 3600);
}

安全:
DB:: 防止 SQL 注入。
验证 $_G['uid'] 和 $_G['tid']。
事务处理异常。
版本兼容:
针对 X3.4/X3.5。若用 Discuz! Q:
验证 [hide] 逻辑。
检查 lang('forum/template', 'post_hide_reply_hidden')。
样式兼容:
若主题覆盖 common.css,将样式添加到主题 CSS(如 template/your_theme/common/common.css)。
测试深色/浅色模式。

功能:清理 pre_hide_view_log 表中过期的记录(超过 24 小时)
✅ 第一步:创建计划任务文件
在你的 Discuz 根目录下的 ./source/include/cron/ 目录中新建一个文件,例如:cron_clear_hide_log.php

文件内容如下:

<?php

if (!defined('IN_DISCUZ')) {
    exit('Access Denied');
}

// 清理所有数据
DB::query("DELETE FROM " . DB::table('hide_view_log'));

// 或者如果你想彻底清空表
// DB::query("TRUNCATE TABLE " . DB::table('hide_view_log'));

后台 → 工具 → 计划任务 → 添加新任务,任务脚本选择:cron_clear_hide_log.php,每日0时0分执行

阅读剩余
THE END