简介:帝国CMS新闻模型内容隐藏付费查看方案,是一套面向网站管理员与开发者的轻量级付费阅读工具,通过后台权限配置、多级会员等级判断以及SQL字段调整,可实现部分正文仅对付费用户或指定等级会员开放。资源内整合了后台SQL执行脚本、输入表单替换HTML代码、Ecmsshop插件安装文档以及PHP、JS、CSS等前后端文件,共20个文件,压缩包仅120KB,便于快速部署;目前已有554人学习,被用于新闻资讯、在线教育、知识付费等场景。借助这套方案,运营者不必从零开发支付逻辑,只需替换相应表单与接口参数,即可搭建会员免费查看、付费解锁内容及支付对接的完整流程。同时支持设置特定会员免费查看,让不同等级用户获得差异化体验。包内目录结构清晰,SQL脚本与HTML片段可直接套用,适合快速二次开发。
1. “帝国CMS新闻模型部分内容隐藏付费查看”为什么成了内容站的刚需
做行业资讯、报告解读、付费专栏这类站点的人,大概率都碰到过同一个诉求:文章前两段公开,中间干货或者结论部分要付费才能看,同时站里的 VIP 会员又能免费看。这个需求在帝国CMS上尤其常见,因为帝国CMS的内容模型、会员系统已经相当完整,但官方自带的收费功能更偏向“整篇收费”,做不到“文章前两段免费、后半段付费”这种颗粒度。于是大家开始折腾“新闻模型部分内容隐藏付费查看(支持设置会员免费查看)”这套玩法,本质上它就是:把一篇文章拆成公开段和隐藏段,根据当前访问者的登录状态、会员组、是否购买过这三个条件决定要不要把隐藏段吐出来。
这篇文章我按自己做过这个功能的完整路径来讲,从方案选型、后台字段设计、模板改造写到付费接口和常见翻车点。适合用帝国CMS做内容站、又想引入付费阅读或会员权益的开发者。你不需要懂很深的口语化玄学,跟着步骤走就能在自己的站上复现,只有几个地方容易踩坑,我会在单独一章里全部讲透。
2. 先选型:三种“隐藏+收费”方案的边界与代价
2.1 模板PHP直判:最直观,但要分清动态与静态
把“是否输出收费内容”的判断直接写进内容页模板,是新手最容易想到的第一个方案。逻辑很直白:在模板里取当前文章字段,取当前会员组ID,然后判断一次,条件成立就输出 $navinfor['pay_content'],不成立就输出一个“付费查看”的按钮和提示。
这个方案适合内容更新频率低、访问量不大、并且你没有开启整站全静态生成的站点。因为在帝国CMS里,内容页模板支持直接嵌入PHP代码,只要后台开启了“模板中使用PHP代码”的选项,代码会在每次访问时被解析并执行。但要注意,如果你用的是生成静态页的模式,这个判断只会在“生成静态页的那一刻”执行一次。默认游客访问静态页看到的都是生成时的那版HTML,这就会导致“用户付费后页面还是锁着的”这种血泪场面。后面我会在避坑章节用完整案例说这件事。
这个方案的最大优点是无需额外写接口和前端JS,模板里几段代码就能跑通。缺点是只要开过静态化,实时性就完蛋;另外,如果你不小心把收费内容字段直接输出了,哪怕用CSS隐藏,查看源代码照样能看到完整内容。
2.2 前端隐藏+JS解锁:体验顺滑,却挡不住“查看源代码”
第二种做法是把完整内容输出到页面上,但是用CSS或者注释块把收费部分隐藏起来,然后等用户点击“付费查看”按钮时,用JS把隐藏部分展开。这种方案体验非常好,点击立即展开,不需要刷新页面,也不需要额外请求服务器,因为内容本来就在当前HTML里。
但是这里有一个非常致命的边界:任何一个访问者打开这个页面,按一下键盘上的“查看源代码”,就能直接把付费内容复制走。哪怕你加了JS动态渲染,只要首屏HTML里存在付费文本,它就不具备“保护”属性。有些开发者误以为把隐藏内容塞进JS变量就安全了,其实在浏览器开发者工具里照样一眼看穿。所以这个方案只适合图省事、内容价值不高的站点,或者你根本不担心搬运,只是想做一个“形式上的付费门槛”。
另外它还有一个隐性问题:SEO。搜索引擎的爬虫不会点击付费按钮,它在页面上能抓到的还是完整内容,这等于你把整篇文章免费送给了搜索引擎,用户搜索时在摘要里就能看到大部分正文,付费转化率会明显受影响。
2.3 后端接口下放内容:保证页面零泄露的正解
第三种方案是把公开内容和收费内容彻底分开:内容页模板只输出公开部分,收费部分存放在后台字段里,不在页面HTML中出现。用户想要看收费部分时必须触发一个动作,比如点击按钮后通过AJAX请求接口,接口会校验当前登录态、会员组、是否已购买,通过后才把收费内容作为JSON返回值交给前端JS渲染到页面上。
这个方案相对复杂一点,但它同时解决了三个问题:第一,首屏HTML里完全没有收费内容,查看源代码拿不到;第二,接口是实时校验的,用户花钱购买后立刻生效,不需要等静态页重新生成;第三,你可以把接口放在自己的后台目录下,配合帝国CMS的登录态和会员组机制,把免费查看的会员组判断也一起做进去。这是我最推荐的做法,也是后面几章里主要展开的路径。实际的落地流程是:后台加字段 → 模板改判断 → 写一个付费查看接口 → 前端AJAX联动 → 搭配会员组条件放行。往下走,每一步都有可以照抄的代码。
3. 落地核心:新闻模型自定义字段与内容页模板改造
3.1 后台加 pay_content 字段:字段参数怎么定
以最常见的新闻模型为例,后台路径大致是“系统设置 → 数据表管理 → 新闻模型数据表 → 管理字段”,不同帝国CMS版本菜单叫法略有差异。进入字段管理界面后,点击“新增字段”。这里的字段名建议命名为 pay_content,含义直白,模板里引用也方便。
字段类型这一项要慎重。如果你收费部分也包含图片、表格、段落格式,建议选择“编辑器”类型,这样在后台录新闻时可以直接用富文本排版,和正文编辑体验一致。如果你只需要存放纯文本,选“文本域”或者“大文本”就够了。新增字段的时候有几个参数需要注意:后台“录入表单”选“显示”,这样编辑新闻时才能看到输入框;“前台列表显示”必须选“否”,否则帝国CMS会在列表页自动把该字段值带出来,这是一个非常隐蔽的泄露点,后面避坑章会细说;“前台搜索”选“不参与搜索”,避免把收费内容被搜出来。
第二步,确认你的新闻模型前台的模板变量叫法。帝国CMS内容页模板里,我们可以用全局数组 $navinfor 来取当前文章的字段值。新增字段之后,$navinfor['pay_content'] 就是收费内容。如果你用标签方式 [!--pay_content--] 也会生效,但我们在模板里要写PHP判断,所以我更习惯用 $navinfor 数组方式,它方便做条件分支。
3.2 内容页模板PHP逻辑:示例脚本与参数说明
在内容页模板的合适位置,一般是正文输出结束之后,插入下面这段PHP代码。先确认系统设置里已经开启“模板中使用PHP代码”,代码生效的前提是当前访问用户信息已经在全局变量中存在。未登录用户和已登录用户的判断用 $userid,帝国CMS的常规变量中未登录时它是空值或0。
<?php // 内容页模板:部分内容隐藏付费查看 global $navinfor, $userid, $groupid; // 取出后台录入的收费内容 $payContent = trim($navinfor['pay_content']); if(empty($payContent)) { // 没有设置收费内容,直接输出全文 echo $navinfor['newstext']; } else { // 设置可免费查看的会员组ID,例如数组中的 2 和 3 $freeGroups = array(2, 3); $isFreeGroup = in_array($groupid, $freeGroups); // 检查当前用户是否已经购买过这篇文章 $hasPaid = check_article_paid($navinfor['id'], $userid); if($isFreeGroup || $hasPaid) { // 会员免费查看或已购买:输出完整内容 echo $navinfor['newstext'] . '<div class="pay-content">' . $payContent . '</div>'; } else { // 游客或未购买:输出公开部分 + 付费提示 echo $navinfor['newstext']; echo '<div class="pay-tips"> <p>剩余内容为付费部分,请登录后购买,或开通会员免费查看。</p> <button class="pay-btn"><?php // 判断用户是否已购买指定文章 function check_article_paid($artid, $userid) { global $empire, $dbtbpre; if(empty($artid) || empty($userid)) { return false; } $artid = intval($artid); $userid = intval($userid); // 查询自建的购买记录表 ecms_pay_log $sql = "SELECT id FROM {$dbtbpre}ecms_pay_log WHERE artid='$artid' AND userid='$userid' LIMIT 1"; $r = $empire->fetch1($sql); return $r ? true : false; }这里涉及一张自建数据表 ecms_pay_log,建议字段包括:id(自增主键)、artid(文章ID)、userid(购买用户ID)、price(支付金额)、paytime(购买时间)。你可以在帝国CMS后台通过“系统设置 → 数据表管理 → 新建数据表”的方式创建,也可以直接用SQL执行。无论哪种方式,记得给 artid + userid 这两个字段设一个唯一索引,这能在程序逻辑出现极端并发时兜底,防止同一用户对同一篇文章被重复扣费。购买记录表和会员免费组判断配合起来,就实现了“部分文章对指定会员组免费,其他用户必须付费”的完整逻辑。注意 $empire 和 $dbtbpre 是帝国CMS两个核心全局变量,分别代表数据库查询对象和表前缀,在函数内必须用 global 引入,否则你会被“数据库查询失败”这类错误卡住。
4.3 付费按钮和接口返回逻辑的联动
模板里已经输出了“付费查看全文”按钮,现在要让点击按钮之后真正完成付费或者解锁。完整的交付路径是:前端AJAX请求 → 后端接口校验 → 扣费并写购买记录 → 返回完整收费内容 → 前端替换展示。
接口文件可以命名为 pay_article.php,放在 e/extend/ 目录下。代码必须引入帝国CMS的核心初始化文件,然后从POST参数里取文章ID,结合当前登录态做判断:
<?php // e/extend/pay_article.php require_once('../class/connect.php'); require_once('../class/db_sql.php'); require_once('../class/member.php'); $link = db_connect(); $empire = new dbquery(); header('Content-Type: application/json; charset=utf-8'); // 取参数与当前登录用户 $artid = intval($_POST['artid']); $userid = intval($userid); if(empty($artid) || empty($userid)) { exit(json_encode(array('code'=>0, 'msg'=>'请先登录'))); } // 查文章信息和收费内容 $art = $empire->fetch1("SELECT id, title, price, pay_content FROM {$dbtbpre}ecms_news WHERE id='$artid' LIMIT 1"); if(empty($art['id'])) { exit(json_encode(array('code'=>0, 'msg'=>'文章不存在'))); } // 检查是否已购买 if(check_article_paid($artid, $userid)) { exit(json_encode(array('code'=>1, 'data'=>$art['pay_content']))); } // 检查余额或点数,这里假设会员扩展表里存了 money 字段 $member = $empire->fetch1("SELECT money FROM {$dbtbpre}ecms_member WHERE userid='$userid' LIMIT 1"); if($member['money'] < $art['price']) { exit(json_encode(array('code'=>0, 'msg'=>'余额不足'))); } // 扣费并写入购买记录 $newMoney = $member['money'] - $art['price']; $empire->query("UPDATE {$dbtbpre}ecms_member SET money='$newMoney' WHERE userid='$userid'"); $empire->query("INSERT INTO {$dbtbpre}ecms_pay_log (artid, userid, price, paytime) VALUES ('$artid', '$userid', '{$art['price']}', '".time()."')"); exit(json_encode(array('code'=>1, 'data'=>$art['pay_content']))); ?>这段接口代码里,price 是你在新闻模型新加的收费价格字段,金额单位可以是元也可以是站内点数,但要和会员表的 money 字段口径保持一致。扣费和插入购买记录之间没有启用事务,高并发下有极小概率造成重复扣款。解决思路有两个:一是给 ecms_pay_log 加 artid+userid 唯一索引,二是把扣款语句写成 UPDATE ... SET money = money - 价格 WHERE userid = 用户 AND money >= 价格,这样当余额不足时,受影响行数为0,后续插入购买记录也不会执行。这里我保留了最简洁的写法,方便你理解主流程,生产环境记得把这两道保险加上。
5. 上线最容易翻车的五个坑:现象、原因与处理
5.1 模板PHP直接不执行
现象:内容页模板里写了完整的PHP判断代码,但打开页面后这部分代码被当作普通文本输出,页面上出现大段 php 源码,或者干脆被注释掉什么都不显示。
原因:最常见的是两个,一是系统设置里的“模板是否支持PHP代码”没有开启,二是模板的PHP代码块被帝国CMS自己的标签解析给篡改了。如果你把PHP代码写在模板的编辑界面里,且代码包含了像 [!--...--] 这样的文本,帝国CMS会在解析标签时先处理掉一部分代码。
解决:先去“系统设置 → 参数设置”确认模板PHP支持处于开启状态。代码块里尽量不要用帝国CMS的标签符,PHP代码单独放在模板尾部的嵌入区域,或者通过 include 引入外部PHP文件,比如把整个判断逻辑都放进 pay_function.php,模板里只写一行 。这样最干净,也不会和模板标签机制打架。
5.2 列表页和栏目页把收费内容带出来了
现象:首页、栏目页、搜索结果的摘要里出现了收费内容,甚至文章的多数正文在列表页就完整露出,用户体验变成“根本不需要付费”。
原因:帝国CMS的列表模板如果有调用新闻模型字段的地方,或者后台新增字段时“前台列表显示”参数选择了“是”,列表查询会把 pay_content 字段直接加载并输出到页面。
解决:立即在后台字段管理里把 pay_content 的“前台列表显示”改成“否”。另外检查一遍列表模板,确认没有手写引用 pay_content 的地方。如果你用灵动标签或者SQL标签取列表,也要在标签参数里排除这个字段。这个坑不显眼,但一旦泄露,付费率会直接归零。
5.3 生成静态页后“交了钱看不到”
现象:用户已经支付成功,接口也显示购买成功,但刷新内容页后还是看不到收费内容,页面始终是付费提示。
原因:你开启了帝国CMS整站生成静态页的功能。内容页模板里的PHP判断是在“生成HTML文件那一刻”执行的,也就是生成静态页时你用什么身份访问,生成出来的页面就以那个身份的判断结果为准。你以游客身份生成,页面就是锁定状态;就算用户购买后,也不会自动重新生成一次针对他的页面。
解决:核心方案是避开静态页上做实时权限判断。内容页模板只输出一个固定的“加载区域”容器,里面什么都不放,用AJAX从接口读取当前用户是否有权看到收费内容,如果有就显示完整内容。静态页本身不包含任何收费内容,也不会因为生成时机不同而出错。如果你不想改造接口,还有一个临时方案:把所有涉及付费查看的文章设置成“动态访问”,也就是在栏目设置里把内容页生成方式改为动态,但这会牺牲一点访问性能,流量大时不推荐。
5.4 会员免费组配置了却不生效
现象:后台明明把某会员组设为免费,但该组用户登录后访问文章,仍然看到付费提示。
原因:大概率是 $groupid 没有正确取到当前登录用户的组ID。一种情况是模板中PHP代码在执行时作用域不对,$groupid 没有被 global 声明,拿到的是空值;另一种情况是免费组ID写错了,后台显示的组ID和数组里存的ID不一致。还有一个小概率:用户的登录状态在帝国CMS中不是挂在 $groupid 上,而是挂在别的全局变量里,老版本CMS不同模板引擎下的变量名略有差异。
解决:先在模板PHP的入口处 var_dump($userid) 和 var_dump($groupid),确认登录用户访问页面后这两个值到底是什么。确认后再把免费组数组改成实际ID。代码里用 in_array 之前,加一个空值保护:$groupid = intval($groupid),这样未登录时按0处理,不会出现类型不匹配导致的判断失误。
5.5 挖走内容的方式:查看源代码与接口重放
现象:发布付费文章后,第二天在其他平台看到完整内容的转载。
原因:查看页面源代码,发现完整HTML里就包含了收费内容,或者是接口返回的数据没有做任何时效性或身份校验,别人可以直接用脚本请求接口批量拉全站收费内容。
解决:前端隐藏方案必须淘汰,这是前提。接口方案要注意几点:第一,接口必须校验登录态和购买状态;第二,收费内容返回时不要一次性返回整篇全部内容,配合全文输出即可,但绝不能允许接口不带参数批量查询;第三,在接口里加上每用户每分钟的请求频次限制。如果站内文章价值很高,可以进一步考虑接口返回的内容做一次简单加密,前端拿到后动态解码渲染,但这会明显增加开发量,视内容价值决定要不要做。
6. 再往前一步:试读字数控制与“已购内容”聚合页
6.1 用 mb_substr 控制试读长度
收费文章并不一定非要遵循“正文部分全部公开”的逻辑。很多内容站希望公开区域只显示开头一两千字,让用户对文章质量有感知但不至于看完关键内容。这个需求可以在模板里对公开部分做截取控制,不需要额外处理字段。
数组判断后如果进入未付费分支,可以先把 $navinfor['newstext'] 用 mb_substr 截断,再接一个“继续阅读”的跳转锚点。注意,如果公开部分包含HTML标签和图片,直接截断会破坏标签结构。更稳的做法是配合 strip_tags 先去掉标签再截字,最后加上省略号。代码示例如下:
<?php // 按字符数截取公开部分作为试读 global $navinfor; $newsBody = $navinfor['newstext']; $newsBodyPlain = strip_tags($newsBody); $previewText = mb_substr($newsBodyPlain, 0, 800, 'utf-8'); echo '<div class="preview-text">' . htmlspecialchars($previewText) . '...</div>';这个技巧适合把高价值文章做成“标题+开头一段+付费”,内容中段的精华不再免费露出。它将正文彻底降级为素材源,真正对外展示的部分完全由代码控制。如果不想丢失富文本排版,可以按HTML结构截取,但那需要额外处理标签闭合,做一个简易HTML截断函数,这里不再展开。
6.2 给登录用户一个“我的已购”聚合页
付费功能上线以后,用户最常问的一句话是“我买过的文章去哪里看”。帝国CMS默认的会员中心没有这个列表,需要自己做一个简单页面。实现方式不复杂:查询 ecms_pay_log 表,关联新闻模型主表,倒序输出已购文章标题和购买时间,标题加上链接即可。
接口或者会员页面里,可以这样查询:
SELECT l.*, n.title, n.classid FROM {$dbtbpre}ecms_pay_log l LEFT JOIN {$dbtbpre}ecms_news n ON l.artid = n.id WHERE l.userid = '当前用户ID' ORDER BY l.paytime DESC LIMIT 20这里用了一个两个表的联接,把购买记录里只存的文章ID,扩展成可直接点击的标题和栏目信息。给这个列表加上分页,就能成为一个功能完整的“已购文章”页面。页面入口可以放在帝国CMS会员中心菜单里,也可以在导航栏单独加一个链接。这样做的好处是用户对已购内容有确认感,复购和信任度都会提升。我自己第一次做这个功能时也翻过车——当时只是在内容页上做了付费判断,没有已购回看入口,结果用户退款申诉率明显上升,后来补上聚合页才算闭环。所以功能范围建议一次做全,宁可前期多写一个查询页,也不要让用户付费之后找不到内容入口。
以上这套组合下来,你得到的就是一个符合标题诉求的帝国CMS新闻模型付费阅读方案:后台新增收费字段,内容页区分公开和付费部分,付费接口校验并扣费,会员免费组直接放行,再加一个已购列表做收尾。每个环节都用最简单直接的方式落地,边界和坑在前面也都做了说明。希望帮到你。
本文还有配套的精品资源,点击获取