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分执行